From: Brett Parker Date: Sat, 17 Mar 2018 14:58:46 +0000 (+0000) Subject: Import Upstream version 1.2.2 X-Git-Tag: upstream/1.2.2^0 X-Git-Url: https://git.sommitrealweird.co.uk/quagga-debian.git/commitdiff_plain/6d99eb2938e976229cb50d848a1bc491532b54f9?ds=inline Import Upstream version 1.2.2 --- 6d99eb2938e976229cb50d848a1bc491532b54f9 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a8da62f --- /dev/null +++ b/.gitignore @@ -0,0 +1,45 @@ +compile +config.log +config.h +config.cache +config.status +config.guess +config.sub +ltmain.sh +stamp-h +stamp-h[0-9]* +*-stamp +Makefile +INSTALL +.deps +depcomp +missing +install-sh +mkinstalldirs +autom4te*.cache +configure.lineno +configure +config.h.in +aclocal.m4 +Makefile.in +zebra-[0-9.][0-9.][0-9.]*.tar.gz +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} +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/AUTHORS b/AUTHORS new file mode 100644 index 0000000..61867a8 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,6 @@ +Kunihiro Ishiguro +Toshiaki Takada +Yasuhiro Ohara +Alex D. Zinin +Gleb Natapov +Akihiro Mizutani diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..b8cf3a1 --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, 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 + + 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 +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) 19yy + + 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., 675 Mass Ave, Cambridge, MA 02139, 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) 19yy 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/COPYING.LIB b/COPYING.LIB new file mode 100644 index 0000000..2b4628e --- /dev/null +++ b/COPYING.LIB @@ -0,0 +1,482 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, 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 library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +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 +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +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 + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, 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 library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete 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 distribute a copy of this License along with the +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 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +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 Library, 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 Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +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. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you 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. + + If distribution of 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 satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +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 +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. 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. + + It may happen that this requirement contradicts the license +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 +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library 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. + + 9. 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 Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +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 +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 Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library 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 Library. + +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. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library 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. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library 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 Library +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 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 +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 + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. 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 LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. 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 library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..2ed5061 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,4 @@ +ChangeLog information for Quagga is now recorded in our source-code +management system. Please see: + + http://www.quagga.net/devel.php diff --git a/HACKING.md b/HACKING.md new file mode 100644 index 0000000..078de60 --- /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 new file mode 100644 index 0000000..73d3194 --- /dev/null +++ b/HACKING.pending @@ -0,0 +1,12 @@ +This file contains pointers to work done on quagga that is not in the +quagga git repository or quagga bugzilla. + +* public git repositories + +** git remote add mtr http://github.com/tomhenderson/quagga-mtr.git + +Tom Henderson of Boeing has created a repository to work on +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 diff --git a/INSTALL.quagga.txt b/INSTALL.quagga.txt new file mode 100644 index 0000000..11c85b1 --- /dev/null +++ b/INSTALL.quagga.txt @@ -0,0 +1,112 @@ +-------------------------------------------------------------------------- +Building and Installing Quagga from releases or snapshots: + +The 'INSTALL' file contains generic instructions on how to use 'configure' +scripts. + +Quagga requires a C compiler (and associated header files and +libraries) supporting the C99 standard. + +Quagga requires a reasonable make. It is considered a bug if quagga +does not compile with the system make on recent FreeBSD, NetBSD or +OpenBSD, and a very serious bug if it does not compile with GNU make. + +Quagga expects a POSIX.2 compliant system, more or less. Clean +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. 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 (stable branches currently supported, plus perhaps one) + FreeBSD-current + Linux [kernel/distribution information needed] + NetBSD 4.x + NetBSD 5.x + NetBSD 6.x + NetBSD-current + OpenBSD ? [info needed on what should work] + 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 +documentation, (doc/quagga.info). To read the info page included with +the Quagga sources without first installing Quagga: + + cd doc + # one of the following, depending on your info viewer preferences + info quagga.info + pinfo -r quagga.info + emacs -eval '(info "quagga.info")' + +The Quagga website (http://www.quagga.net) currently has the info +files available in various formats. + +-------------------------------------------------------------------------- +Building Quagga from git checkouts: + +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 +building from CVS checkout it is recommended that you try a CVS snapshot +instead. + +We declare that the following versions should work for building from +CVS checkouts. Earlier versions may work, but failure to do so is not +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) + +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 +for the route server docs, which is why 4.6 doesn't work.) + +In order to create PostScript or PDF versions of the Texinfo documentation, +you will need the convert utility, from the ImageMagick toolset installed, +and epstopdf from the TeTeX suite. + +To create the required autotools files (Makefile.in, configure, etc.), +run "./bootstrap.sh". After this you may run configure as for a +snapshot or release. + +Please refer to "Building and Installing Quagga" above for further +instructions. + +-------------------------------------------------------------------------- +Notes on required versions: + +The general goal is to use a modern baseline of tools, while not +imposing pain on those tracking supported (or almost supported) stable +distributions. The notes below explain what versions are present in +various environments. + +NetBSD 4 provides texinfo 4.7. +NetBSD 5 and 6 provides texinfo 4.8 + +Fedora Core ? provides autoconf 2.59. + +OpenBSD 3.6 provides texinfo 4.2. +OpenBSD [3.6] ports provides automake 1.4-p6 autoconf 2.5.9 libtool 1.5.8 + +-------------------------------------------------------------------------- diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..3c3b65e --- /dev/null +++ b/Makefile.am @@ -0,0 +1,26 @@ +## Process this file with automake to produce Makefile.in. + +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 qpb fpm zebra bgpd ripd ripngd ospfd ospf6d nhrpd \ + isisd watchquagga vtysh ospfclient doc m4 pkgsrc redhat tests \ + solaris pimd + +EXTRA_DIST = aclocal.m4 SERVICES TODO REPORTING-BUGS INSTALL.quagga.txt \ + update-autotools \ + vtysh/Makefile.in vtysh/Makefile.am \ + tools/zebra.el tools/multiple-bgpd.sh + +if HAVE_PANDOC + +HACKING.pdf: HACKING.md + pandoc -o $@ $< + +clean-local: + -$(LATEXMK) -C HACKING.tex + +endif + +ACLOCAL_AMFLAGS = -I m4 diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..6db831e --- /dev/null +++ b/NEWS @@ -0,0 +1,2594 @@ +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 +- [bgpd] MRT format changes to version 2. Those relying on + bgpd MRT table dumps may need to update their tools. +- [bgpd] Added new route-map set statement: "as-path exclude" +- Zebra RIB updates queue has evolved into a multi-level + structure to address RIB consistency issues. + +* Changes in Quagga 0.99.2 + +- [bgpd] Work queues added to bgpd to split up update processing, + particularly beneficial when a peer session goes down. AS_PATH + parsing rewritten to be clearer, more robust and ready for 4-byte. + +- [ripd] Simple authentication is no longer the default authentication + mode for ripd. The default is now no-authentication. Any setups which + used simple authentication will probably need to update their + configuration manually. + +- [ospfd] 1s dead-interval with sub-second Hellos feature added. + SPF timers now specified in milliseconds, and with adaptive + hold-time support. RFC3137 Stub-router support added. Default ABR + type is now 'cisco'. + +- Solaris least privileges support added. + +* Changes in Quagga 0.99.1 + +- Zserv is now buffered via threads and non-blocking in most cases for both + clients and zebra, which should improve responsiveness of daemons when + they must send many messages to zebra. + +- 'show thread cpu' now displays both cpu+system and wall-clock time, + where getrusage() is available. + +- Background threads added and workqueue API added, with a + 'show work-queues' command. Thread scheduling improved slightly. + +- Zebra now has a work-queue for RIB processing. See 'show work-queues' in + the zebra daemon vty. + +- Support for interface renaming on Linux netlink systems. + +- GNU Zebra bgpd merges, including BGP Graceful-restart and "match ip + route-source" command. + +- Automatic logging of backtraces should daemons crash to assist in + diagnosis. See the documentation for more information on configuring + logging correctly, and set --enable-gcc-rdynamic if compiling with gcc. + +* Changes in Quagga 0.98.0 + +- Logging facilities upgraded. One can now specify a severity level + for each logging destination. And a new "show logging" command gives + thorough information on the current logging system configuration. + +- Watchquagga daemon added. This is not well tested yet. Please try + monitor mode first before enabling restart features. It is important + to make sure that the various timers are configured with appropriate + values for your site. + +- BGP route-server support added. See the texinfo documentation. + +- OSPF API initialisation is disabled by default even if compiled in. You + can enable it with -a/--apiserver command line switch. + +- "write-config integrated" vtysh command replaced with "service + integrated-vtysh-config" command. + +- Router id is now handled by zebra daemon and all daemons receive changes + from it. Router id can be overriden in daemons' configurations of course. + To fix common router id in zebra daemon you can either install non-127 + address on loopback or use "router-id x.x.x.x" command. + +- "secondary" keyword is removed from ip address configuration. All + supported OS'es have their own vision what's secondary address and + how to handle it. + +- Zebra no longer enables forwarding by default. If you rely on zebra to + enable forwarding make sure to add ' forwarding' statements + to your zebra configuration file. + +- All libraries are built and used shared, on platforms where libtool + supports shared libraries. + +- Router advertisement syntax is changed. In usual cases (if you didn't do + any fancy stuff) it's enough to change lines in configuration from: + "ipv6 nd prefix-advertisement X:X:X:X::/X 2592000 604800 autoconfig on-link" + to: + "ipv6 nd prefix X:X:X:X::/X" + + All router advertisement options are documented in texi documentation. + +- --enable-nssa configure switch is removed. NSSA support is stable enough. + +- Daemons don't look at current directory for config file any more. + +* Changes in Quagga 0.96.5 + +- include files are installed in $(prefix)/include/quagga. Programs + building against these includes should -I$(prefix)/include and e.g. + #include + +- New option --enable-exampledir puts example files in a separate + directory from $(sysconfdir), easing NetBSD pkgsrc hierarchy rules + compliance. + +- New configure options --enable-configfile-mask and + --enable-logfile-mask to set umask values for config and log + values. Masks default to 0600, matching previous behavior. + +- Import current CVS isisd from SourceForge, then merge it with + the Quagga's Framework. + +* Changes in Quagga 0.96.4 + +- Further fixes to ospfd, some relating to the PtP revert. Interface +lookups should be a lot more robust now. + +- Fix for a remote triggerable crash in vty layer. + +- Improvements to ripd, and addition of split horizon support. + +- Improved bgpd table support, now dumps at time of day intervals rather +than time from startup intervals. Much improved support for IPv6 table +dumps. show commands for views improved. + +* Changes in Quagga 0.96.3 + +- revert the 'generic PtP' patch. Means Quagga will no longer work with +FreeSWAN, however, on the plus side this gets rid of a lot of niggly bugs +which the PtP patch introduced. + +* Changes in Quagga 0.96.2 + +- Fix crash in ospfd + +* Changes in Quagga 0.96.1 + +- Iron out problem with the privileges definitions + +* Changes in Quagga 0.96 + +- Privilege support, daemons now run with the minimal privileges needed, see + the documentation for details. + +- NSSA ABR support in ospfd. + +- OSPF-API support merged in. + +- 6WIND patch merged in. + +* Changes in zebra-0.93 + +* Changes in bgpd + +** Configuration is changed to new format. + +* Changes in ospfd + +** Crush bugs which reported on Zebra ML is fixed. + +** Opaque LSA and TE LSA support is added by KDD R&D Laboratories, + Inc. + +* Chages in ospf6d + +** Many bugs are fixed. + +* Changes in zebra-0.92a + +* Changes in bgpd + +** Fix "^$" community list bug. + +** Below command's Address Family specific configurations are added + + nexthop-self + route-reflector-client + route-server-client + soft-reconfiguration inbound + +* Changes in zebra + +** Treat kernel type routes as EGP routes. + +* Changes in zebra-0.92 + +** Overall security is improved. Default umask is 0077. + +* Changes in ripd + +** If output interface is in simple password authentication mode, +substruct one from rtemax. + +* Changes in bgpd + +** IPv4 multicast and IPv6 unicast configuration is changed to so +called new config. All of AFI and SAFI specific configuration is +moved to "address-family" node. When you have many IPv6 only +configuration, you will see many "no neighbor X:X::X:X activate" line +in your configuration to disable IPv4 unicast NLRI exchange. In that +case please use "no bgp default ipv4-unicast" command to suppress the +output. Until zebra-0.93, old config is still left for compatibility. + +Old config +========== +router bgp 7675 + bgp router-id 10.0.0.1 + redistribute connected + network 192.168.0.0/24 + neighbor 10.0.0.2 remote-as 7675 + ipv6 bgp network 3ffe:506::/33 + ipv6 bgp network 3ffe:1800:e800::/40 + ipv6 bgp aggregate-address 3ffe:506::/32 + ipv6 bgp redistribute connected + ipv6 bgp neighbor 3ffe:506:1000::2 remote-as 1 + +New config +========== +router bgp 7675 + bgp router-id 10.0.0.1 + network 192.168.0.0/24 + redistribute connected + neighbor 10.0.0.2 remote-as 7675 + neighbor 3ffe:506:1000::2 remote-as 1 + no neighbor 3ffe:506:1000::2 activate +! + address-family ipv6 + network 3ffe:506::/33 + network 3ffe:1800:e800::/40 + aggregate-address 3ffe:506::/32 + redistribute connected + neighbor 3ffe:506:1000::2 activate + exit-address-family + +* Changes in ospfd + +** Internal interface treatment is changed. Now ospfd can handle +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. + +** HAVE_IF_PSEUDO part is removed. Same feature is now supported by +default. + +* Changes in ripd + +** When redistributed route is withdrawn, perform poisoned reverse. + +* Changes in zebra + +** When interface's address is removed, kernel route pointing out to +the address is removed. + +** IPv6 RIB is now based upon new RIB code. + +** zebra can handle same connected route to one interface. + +** New command for interface address. Currently this commands are +only supported on GNU/Linux with netlink interface. + +"ip address A.B.C.D secondary" +"ip address A.B.C.D label LABEL" + +* Changes in bgpd + +** BGP flap dampening bugs are fixed. + +** BGP non-blocking TCP connection bug is fixed. + +** "show ip bgp summary" shows AS path and community entry number. + +** New commands have been added. + "show ip bgp cidr-only" + "show ip bgp ipv4 (unicast|multicast) cidr-only" + "show ip bgp A.B.C.D/M longer-prefixes" + "show ip bgp ipv4 (unicast|multicast) A.B.C.D/M longer-prefixes" + "show ipv6 bgp X:X::X:X/M longer-prefixes" + "show ipv6 mbgp X:X::X:X/M longer-prefixes" + +** IPv6 IBGP nexthop change is monitored. + +** Unknown transitive attribute is passed with partial flag bit on. + +* Changes in ospfd + +** 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 +becomes default. So both options are removed from configure.in. To +revert old behavior please specify below option. + +--enable-tcp-zebra # TCP/IP socket is used for protocol daemon and zebra. +--enable-oldrib # Turn on old RIB implementation. + +Old RIB implementation will be removed in zebra-0.91. + +** From this beta release --enable-multipath is supported. This +option is only effective on GNU/Linux kernel with +CONFIG_IP_ADVANCED_ROUTER and CONFIG_IP_ROUTE_MULTIPATH is set. + +--enable-multipath=ARG # ARG must be digit. When ARG is 0 unlimit multipath number. + +** From this release we do not include guile files. + +* Changes in lib + +** newlist.[ch] is merged with linklist.[ch]. + +** Now Zebra works on MacOS X public beta. + +** Access-list can have remark. "access-list WORD remark LINE" define +remark for specified access-list. + +** Key of key-chain is sorted by it's idetifier value. + +** prefix-list rule is slightly changed. The rule of "len <= ge-value +<= le-value" is changed to "len < ge-value <= le-value". + +** According to above prefix-list rule change, add automatic +conversion function of an old rule. ex.) 10.0.0.0/8 ge 8 -> 10.0.0.0/8 +le 32 + +** SMUX can handle SNMP trap. + +** In our event library, event thread is executed before any other +thread like timer, read and write event. + +** Robust method for writing configuration file and recover from +backing up config file. + +** Display "end" at the end of configuration. + +** Fix memory leak in vtysh_read(). + +** Fix memroy leak about access-list and prefix-list name. + +* Changes in zebra + +** UNIX domain socket server of zebra protocol is added. + +** Fix PointoPoint interface network bug. The destination network +should be installed into routing table instead of local network. + +** Metric value is reflected to kernel routing table. + +** "show ip route" display uptime of RIP,OSPF,BGP routes. + +** New RIB implementation is added. + +Now we have enhanced RIB (routing information base) implementation in +zebra. New RIB has many new features and fixed some bugs which exist +in old RIB code. + +*** Static route with distance value + + Static route can be specified with administrative distance. The + distance value 255 means it is not installed into the kernel. + Default value of distance for static route is 1. + + ip route A.B.C.D/M A.B.C.D <1-255> + ip route A.B.C.D/M IFNAME <1-255> + + If the least distance value's route's nexthop are unreachable, + select the least distance value route which has reachable nexthop is + selected. + + ip route 0.0.0.0/0 10.0.0.1 + ip route 0.0.0.0/0 11.0.0.1 2 + + In this case, when 10.0.0.1 is unreachable and 11.0.0.1 is + reachable. The route with nexthop 11.0.0.1 will be installed into + forwarding table. + + zebra> show ip route + S>* 0.0.0.0/0 [2/0] via 11.0.0.1 + S 0.0.0.0/0 [1/0] via 10.0.0.1 inactive + + If the nexthop is unreachable "inactive" is displayed. You can + specify any string to IFNAME. There is no need of the interface is + there when you configure the route. + + ip route 1.1.1.1/32 ppp0 + + When ppp0 comes up, the route is installed properly. + +*** Multiple nexthop routes for one prefix + + Multiple nexthop routes can be specified for one prefix. Even the + kernel support only one nexthop for one prefix user can configure + multiple nexthop. + + When you configure routes like below, prefix 10.0.0.1 has three + nexthop. + + ip route 10.0.0.1/32 10.0.0.2 + ip route 10.0.0.1/32 10.0.0.3 + ip route 10.0.0.1/32 eth0 + + If there is no route to 10.0.0.2 and 10.0.0.3. And interface eth0 + is reachable, then the last route is installed into the kernel. + + zebra> show ip route + S> 10.0.0.1/32 [1/0] via 10.0.0.2 inactive + via 10.0.0.3 inactive + * is directly connected, eth0 + + '*' means this nexthop is installed into the kernel. + +*** Multipath (more than one nexthop for one prefix) can be installed into the kernel. + + When the kernel support multipath, zebra can install multipath + routes into the kernel. Before doing that please make it sure that + setting --enable-multipath=ARG to configure script. ARG must be digit + value. When specify 0 to ARG, there is no limitation of the number + of the multipath. Currently only GNU/Linux with netlink interface is + supported. + + ip route 10.0.0.1/32 10.0.0.2 + ip route 10.0.0.1/32 10.0.0.3 + ip route 10.0.0.1/32 eth0 + + zebra> show ip route + S>* 10.0.0.1/32 [1/0] via 10.0.0.2 + * via 10.0.0.3 + is directly connected, eth0 + +*** Kernel message delete installed route. + + After zebra install static or dynamic route into the kernel. + + R>* 0.0.0.0/0 [120/3] via 10.0.0.1 + + If you delete this route outside zebra, old zebra does not reinstall + route again. Now the route is re-processed and properly reinstall the + static or dynamic route into the kernel. + +** GNU/Linux netlink socket handling is improved to fix race condition +between kernel message and user command responce. + +* Changes in bgpd + +** Add show neighbor's routes command. + + "show ip bgp neighbors (A.B.C.D|X:X::X:X) routes" + "show ip bgp ipv4 (unicast|multicast) neighbors (A.B.C.D|X:X::X:X) routes" + "show ipv6 bgp neighbors (A.B.C.D|X:X::X:X) routes" + "show ipv6 mbgp neighbors (A.B.C.D|X:X::X:X) routes" + +** BGP passive peer support problem is fixed. + +** Redistributed IGP nexthop is passed to BGP nexthop. + +** On multiaccess media, if the nexthop is reachable nexthop is passed +as it is. + +** Remove zebra-0.88 compatibility commands. + + "match ip prefix-list WORD" + "match ipv6 prefix-list WORD" + + Instead of above please use below commands. + + "match ip address prefix-list WORD" + "match ipv6 address prefix-list WORD" + +** Fix bug of holdtimer is not reset when bgp cleared. + +** "show ip bgp summary" display peer establish/drop count. + +** Change "match ip next-hop" argument from IP address to access-list +name. + +** When "bgp enforce-first-as" is enabled, check EBGP peer's update +has it's AS number in the first AS number in AS sequence. + +** New route-map command "set community-delete COMMUNITY-LIST" is +added. Community matched the CoMMUNITY-LIST is removed from the +community. + +** BGP-MIB implementation is finished. + +** When BGP connection comes from unconfigured IP address, close +socket immediately. + +** Do not compare router ID when the routes comes from EBGP peer. +When originator ID is same, take shorter cluster-list route. If +cluster-list is same take smaller IP address neighbor's route. + +** Add "bgp bestpath as-path ignore" command. When this option is +set, do not concider AS path length when route selection. + +** Add "bgp bestpath compare-routerid". When this option is set, +compare router ID when the routes comes from EBGP peer. + +** Add "bgp deterministic-med" process. + +** BGP flap dampening feature is added. + +** When IBGP nexthop is changed, it is reflected to RIB. + +** Change "neighbor route-refresh" command to "neighbor capability +route-refresh". + +* Changes in ripd + +** Change "match ip next-hop" argument from IP address to access-list +name. + +** "no ip rip (send|receive)" command accept version number argument. + +** Memory leak related classfull network generation is fixed. + +** When a route is in garbage collection process (invalid with metric +16) and a router receives the same route with valid metric then route +was not installed into zebra rib, but only into ripd rib. Moreover , +it will never get into zebra rib, because ripd wrongly assumes it's +already there. + +* Change in ospfd + +** Fix bug of refreshing default route. + +** --enable-nssa turn on undergoing NSSA feature. + +** Fix bug of Hello packet's option is not properly set when interface +comes up. + +** Reduce unconditional logging. + +** Add nexthop to OSPF path only when it is not there. + +** When there is no DR on network (suppose you have only one router +with interface priority 0). It's router LSA does not contain the link +information about this network. + +** When you change a priority of interface from/to 0 +ISM_NeighborChange event should be scheduled in order to elect new +DR/BDR on the network. + +** When we add some LSA into retransmit list we need to check whether +the present old LSA in retransmit list is not more recent than the new +one. + +** In states Loading and Full the slave must resend its last Database +Description packet in response to duplicate Database Description +packets received from the master. For this reason the slave must wait +RouterDeadInterval seconds before freeing the last Database +Description packet. Reception of a Database Description packet from +the master after this interval will generate a SeqNumberMismatch +neighbor event. RFC2328 Section 10.8 + +** Virtual link can not configured in stub area. + +** Clear a ls_upd_queue queue of the interface when interface goes +down. + +** "no router ospf" unregister redistribution requests from zebra. + +** New command for virtual-link configuration is added. + + "area A.B.C.D virtual-link A.B.C.D" + "area A.B.C.D virtual-link A.B.C.D hello-interval <1-65535> retransmit-interval <3-65535> transmit-delay <1-65535> dead-interval <1-65535>" + "area A.B.C.D virtual-link A.B.C.D hello-interval <1-65535> retransmit-interval <3-65535> transmit-delay <1-65535> dead-interval <1-65535> authentication-key AUTH_KEY" + "area A.B.C.D virtual-link A.B.C.D authentication-key AUTH_KEY" + "area A.B.C.D virtual-link A.B.C.D hello-interval <1-65535> retransmit-interval <3-65535> transmit-delay <1-65535> dead-interval <1-65535> message-digest-key <1-255> md5 KEY" + "area A.B.C.D virtual-link A.B.C.D message-digest-key <1-255> md5 KEY" + +** Clear cryptographic sequence number when neighbor status is changed +to NSM down. + +** Make Summary LSA's origination and refreshment as same as other +type of LSA. + +** New OSPF pakcet read method. Now maximum packet length may be 65535 +bytes (maximum IP packet length). + +** Checking the age of the found LSA and if the LSA is MAXAGE we +should call refresh instead of originate. + +** Install multipath information to zebra. + +** Fix socket descriptor leak when system call failed. + +* Changes in ospf6d + +** Whole functionality has been rewritten as new code. new command +"show ipv6 ospf6 spf node", "show ipv6 ospf6 spf tree", "show ipv6 +ospf6 spf table" has been added. + +** Change to do not send garbage route whose nexthop is not linklocal +address. + +** "redistribute ospf6" was generated in "router ospf6" in config +file. It is fixed. + +** LSDB sync bug is fixed. + +** Fix bug of using unavailable route. + +* Changes in vtysh + +** route-map and access-list configuration is merged into one +configuration. + +** /usr/local/etc/Zebra.conf is integrated configuration file. "write +memory" in vtysh will write whole configuration to this file. + +** When -b option is specified to vtysh, vtysh read +/usr/local/etc/Zebra.conf file then pass the confuguration to proper +protocol daemon. So make all protocol daemon's configuration file +empty then invoke all daemon. After that vtysh -b will setup saved +configuration. + +zebrastart.sh +============= +/usr/local/sbin/zebra -d +/usr/local/sbin/ripd -d +/usr/local/sbin/ospfd -d +/usr/local/sbin/bgpd -d +/usr/local/bin/vtysh -b + +* Changes in zebra-0.89 + +* Changes in lib + +** distribute-list can set all interface's access-list and prefix-list +configuration. + +* Changes in ripd + +** "show ip protocols" display proper distribute-list settings and +distance settings. + +** When metric infinity route received withdraw the route from kernel +immediately it used to be wait garbage collection. + +** key-chain can be used for simple password authentication. + +** RIPv2 MIB getnext interface bug is fixed. + +* Changes in vtysh + +** --with-libpam enable PAM authentication for vtysh. + +** Now vtysh read vtysh.conf. This file should be +${SYSCONFDIR}/etc/vtysh.conf for security reason. Usually it is +/usr/local/etc/vtysh.conf. + +** "username WORD nopassword" command is added to vtysh. + +* Chagees in ospfd + +** NBMA interface support is added. + +** OSPF area is sorted by area ID. + +** New implementation of OSPF refreesh. + +** OSPF-MIB read function is partly added. + +* Changes in bgpd + +** When the peering is done by ebgp-multihop, nexthop is looked up +like IBGP routes. + +** "show ip mbgp" commands are changed to "show ip bgp ipv4 +multicast". + +** New terminal commands are added. + "show ip bgp ipv4 (unicast|multicast) filter-list WORD" + "show ip bgp ipv4 (unicast|multicast) community" + "show ip bgp ipv4 (unicast|multicast) community-list WORD" + "show ip bgp ipv4 (unicast|multicast) community-list WORD exact-match" + +** MBGP soft-reconfiguration command is added. + "clear ip bgp x.x.x.x ipv4 (unicast|multicast) in" + "clear ip bgp x.x.x.x ipv4 (unicast|multicast) out" + "clear ip bgp x.x.x.x ipv4 (unicast|multicast) soft" + "clear ip bgp <1-65535> ipv4 (unicast|multicast) in" + "clear ip bgp <1-65535> ipv4 (unicast|multicast) out" + "clear ip bgp <1-65535> ipv4 (unicast|multicast) soft" + "clear ip bgp * ipv4 (unicast|multicast) in" + "clear ip bgp * ipv4 (unicast|multicast) out" + "clear ip bgp * ipv4 (unicast|multicast) soft" + +** MED related commands are added. + "bgp deterministic-med" + "bgp bestpath med confed" + "bgp bestpath med missing-as-worst" + +** "bgp default local-preference" command is added. + +** BGP confederation peer's routes are passed to zebra like IBGP route. + +** Community match command is added. + "show ip bgp community " + "show ip bgp community exact-match" + +** EBGP multihop route treatment bug is fixed. Now nexthop is +resolved by IGP routes. + +** Some commands are added to show routes by filter-list and community +value. + "show ip bgp ipv4 (unicast|multicast) filter-list WORD" + "show ip bgp ipv4 (unicast|multicast) community" + "show ip bgp ipv4 (unicast|multicast) community-list WORD" + "show ip bgp ipv4 (unicast|multicast) community-list WORD exact-match" + +* Changes in zebra + +** zebra read interface's address information using getifaddrs() when +it is available. + +** Reflect IPv6 interface's address change to protocol daemons. + +* Changes in zebra-0.88 + +* Changes in lib + +** "exact-match" option is added to "access-list" and "ipv6 +access-list" command. If this option is specified, the prefix and +prefix length is compared as exact match mode. + +* Changes in zebra + +** New Zebra message ZEBRA_REDISTRIBUTE_DEFAULT_ADD and +ZEBRA_REDISTRIBUTE_DEFAULT_DELTE are added. + +** Default administrative distance value is changed. + + Old New +------------------------------------------ +system 10 0 +kernel 20 0 +connected 30 0 +static 40 1 +rip 50 120 +ripng 50 120 +ospf 60 110 +ospf6 49 110 +bgp 70 200(iBGP) 20(eBGP) +------------------------------------------ + +** Distance value can be passed from protocol daemon to zebra. + +** "show ip route" shows [metric/distance] value pair. + +** Zebra Protocol is changed to support multi-path route and distance +value. + +* Changes in ospfd + +** "default-information originate [always]" command is added. + +** "default-metric <0-16777214>" command is added. + +** "show ip ospf database" command is integrated. LS-ID and AdvRouter can + be specifed. The commands are + + show ip ospf database TYPE LS-ID + show ip ospf database TYPE LS-ID ADV-ROUTER + show ip ospf database TYPE LS-ID self-originate + show ip ospf database TYPE self-originate + +** route-map support for `redistribute' command are added. + Supported `match' statements are + + match interface + match ip address + match next-hop + + Supported `set' statements are + + set metric + set metric-type + +** Pass OSPF metric value to zebra daemon. + +* Changes in ripd + +** When specified route-map does not exist, it means all deny. + +** "default-metric <1-16>" command is added. + +** "offset-list ACCESS-LIST-NAME <0-16>" and "offset-list +ACCESS-LIST-NAME <0-16> IFNAME" commands are added. + +** "redistribute ROUTE-TYPE metric <0-16>" command is added. + +** "default-information originate" command is added. + +** "ip split-horizon" and "no ip split-horizon" is added to interface +configuration. + +** "no router rip" command is added. + +** "ip rip authentication mode (md5|text)" is added to interface +configuration. + +** "ip rip authentication key-chain KEY-CHAIN" is added to interface +configuration. + +** Pass RIP metric value to zebra daemon. + +** Distance manipulation functions are added. + +* Changes in bgpd + +** Fix bug of next hop treatment for MPLS-VPN route exchange. + +** BGP peer MIB is updated. + +** Aggregated route has origin IGP, atomic-aggregate and proper +aggregator attribute. + +** Suppressed route now installed into BGP table. It is only +suppressed from announcement. + +** BGP router-id is properly set after "no router bgp ASN" and "router +bgp ASN". + +** Add check for nexthop is accessible or not for IBGP routes. + +** Add cehck for nexthop is on connected or not for EBGP routes. + +** "dump bgp route" command is changed to "dump bgp route-mrt" for +generating MRT compatible dump output. + +** Soft reconfiguration inbound and outbound is supported. + +** Route refresh feature is supported. + +* Changes in vtysh + +** VTY shell is now included into the distribution. + +* Changes in zebra-0.87 + +* Changes in lib + +** "show startup-config" command is added. + +** "show history" command is added. + +** Memory statistics command is changed. New command + + show memory all + show memory lib + show memory rip + show memory ospf + show memory bgp + +are added. + +** Filters can be removed only specify it's name. New command + + no access-list NAME + no ip community-list NAME + no ip as-path access-list NAME + no route-map NAME + +are added. + +** At any node, user can view/save user configuration. + + write terminal + write file + wirte memory + +are added to every node in default. + +** LCD completion is added. For example both "ip" and "ipv6" command +are exist, "i" then press TAB will be expanded to "ip". + +* Changes in bgpd + +** "show ip bgp" family shows total number of prefixes. + +** "no bgp default ipv4-unicast" command is added. + +** Extended Communities support is added. + +** "no neighbor PEER send-community extended" command is added. + +** MPLS-VPN PE-RR support is added. + + New address family vpnv4 unicast is introduced. + + ! + address-family vpnv4 unicast + neighobr PEER activate + network A.B.C.D rd RD tag TAG + exit-address-family + ! + + To make it route-reflector, please configure it under normal router +bgp ASN. + + ! + router bgp 7675 + no bgp default ipv4-unicast + bgp router-id 10.0.0.100 + bgp cluster-id 10.0.0.100 + neighbor 10.0.0.1 remote-as 65535 + neighbor 10.0.0.1 route-reflector-client + neighbor 10.0.0.2 remote-as 65535 + neighbor 10.0.0.2 route-reflector-client + neighbor 10.0.0.3 remote-as 65535 + neighbor 10.0.0.3 route-reflector-client + ! + address-family vpnv4 unicast + neighbor 10.0.0.1 activate + neighbor 10.0.0.2 activate + neighbor 10.0.0.3 activate + exit-address-family + ! + +* Changes in ospfd + +** Many many bugs are fixed. + +* Changes in ripd + +** Better interface up/down event handle. + +* Changes in zebra + +** Better interface up/down event handle. + +* Changes in zebra-0.86 + +* Changes in lib + +** Fix bug of exec-timeout command which may cause crush. + +** Multiple same policy for "access-list", "ip prefix-list, "as-path +access-list", "ip community-list" is not duplicated. + +** It used to be "ip prefix-list A.B.C.D/M" match routes which mask >= +M. Now default behavior is exact match so it only match routes which +mask == M. + +* Changes in bgpd + +** "match ip address prefix-list" is added to route-map. + +** A route without local preference is evaluated as 100 local preference. + +** Select smaller router-id route when other values are same. + +** Compare MED only both routes comes from same neighboring AS. + +** "bgp always-compare-med" command is added. + +** Now MED value is passed to IBGP peer. + +** When neighbor's filter is configured with non-existent access-list, +as-path access-list, ip prefix-list, route-map. The behavior is +changed from all permit to all deny. + +* Changes in ospfd + +** Fix bug of external route tag byte order. + +** OSPF Neighbor deletion bug which cause crush is fixed. + +** Some route calculation bug are fixed. + +** Add sanity check with router routing table. + +** Fix bug of memory leak about linklist. + +** Fix bug of 1-WayReceived in NSM. + +** Take care of BIGENDIAN architecture. + +** Fix bug of NSM state flapping between ExStart and Exchange. + +** Fix bug of Network-LSA originated in stub network. + +** Fix bug of MS flag unset. + +** Add to schedule router_lsa origination when the interface cost +changes. + +** Increment LS age by configured interface transmit_delay. + +** distribute-list is reimplemented. + +** Fix bug of refresh never occurs. + +** Fix bug of summary-LSAs reorigination. Correctly copy +OSPF_LSA_APPROVED flag to new LSA. when summary-LSA is reoriginatd. + +** Fix bug of re-origination when a neighbor disappears. + +** Fix bug of segmentation fault with DD retransmission. + +** Fix network-LSA re-origination problem. + +** Fix problem of remaining withdrawn routes on zebra. + +* Changes in ripd + +** Do not leave from multicast group when interface goes down bug is +fixed. + +* Changes in zebra + +** Remove client structure when client dies. + +** Take care static route when interface goes up/down. + +* Changes in zebra-0.85 + +* Changes in bgpd + +** "transparent-nexthop" and "transparenet-as" commands are added. + +** Route reflector's originator-id bug is fixed. + +* Changes in ospfd + +** Fix bug of OSPF LSA memory leak. + +** Fix bug of OSPF external route memory leak. + +** AS-external-LSA origination bug was fixed. + +** LS request treatment is completely rewritten. Now performance is +drastically improved. + +* Changes in ripd + +** RIPv1 update is done by class-full manner. + +* Changes in zebra-0.84b + +* Changes in lib + +** Fix bug of inet_pton return value handling + +* Changes in bgpd + +** Fix bug of BGP-4+ link-local address nexthop check for IBGP peer. + +** Don't allocate whole buffer for displaying "show ip bgp". Now it +consume only one screen size memory. + +* Changes in ripd + +** 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 + +** IPv6 address and prefix parser is added to VTY by Toshiaki Takada + . DEFUN string is "X:X::X:X" for IPv6 address, + "X:X::X:X/M" for IPv6 prefix. You can use it like this. + + DEFUN (func, cmd, "neighbor (A.B.C.D|X:X::X:X) remote-as <1-65535>") + +** VTY configuration is locked during configuration. This is for + avoiding unconditional crush from two terminals modify the + configuration at the same time. "who" command shows which termnal + lock the configuration. VTY which has '*' character at the head of + line is locking the configuration. + +** Old logging functions are removed. Functions like + log_open,log_close,openlog are deleted. Instead of that please use + zlog_* functions. zvlog_* used in ospf6d are deleted also. + +** "terminal monitor" command is added. "no terminal monitor" is for + disabling. This command simply display logging information to the + VTY. + +** dropline.[ch] files are deleted. + +* Changes in bgpd + +** BGP neighbor configuration are sorted by it's IP address. + +** BGP peer configuration and actual peer is separated. This is + preparation for Route Server support. + +** "no neighbor PEER" command is added. You can delete neighbor + without specifying AS number. + +** "no neighbor ebgp-multihop" command is added. + +** "no neighbor port PORT" command is added. + +** To conform RFC1771, "neighbor PEER send-community" is default + behavior. If you want to disable sending community attribute, + please specify "no neighbor PEER send-community" to the peer. + +** "neighbor maximum-prefix NUMBER" command is added. + +** Multi-protocol extention NLRI is proceeded only when the peer is + configured proper Address Family and Subsequent Address Family. If + not, those NLRI are simply ignored. + +** Aggregate-address support is improved. Currently below commands + works. + + "aggregate-address" + "aggregate-address summary-only" + "no aggregate-address" + "no aggregate-address summary-only" + + "ipv6 bgp aggregate-address" + "ipv6 bgp aggregate-address summary-only" + "no ipv6 bgp aggregate-address" + "no ipv6 bgp aggregate-address summary-only" + +** redistribute route-map bug is fixed. + +** MBGP support becomes default. "configure" option --enable-mbgp is + removed. + +** New command "neighbor PEER timers connect <1-65535>" is added. + +** New command "neighbor PEER override-capability" is added. + +** New command "show ip bgp neighbor A.B.C.D advertised-route" is added. + +** New command "show ip bgp neighbor A.B.C.D routes" is added. To use + this command, you have to configure neighbor with + "neighbor A.B.C.D soft-reconfiguration inbound" beforehand. + + +* Changes in zebra-0.83 + +* bgpd + +** Serious bug fix about fetching global and link-local address at the +same time. Due to this bug, corrupted IPv6 prefix is generated. If +you uses bgpd for BGP-4+ please update to this version. The bug is +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 +. + +** MBGP support is added by Robert Olsson . +Please specify --enable-mbgp to configure script. This option will be +removed in the future and MBGP support will be default. + +* Changes in zebra + +** When interface goes down, withdraw connected routes from routing +table. When interface goes up, restore the routes to the routing +table. + +** `show interface' show interface's statistics on Linux and BSD with +routing socket. + +** Now zebra can get MTU value on BSDI/OS. + +* Changes in bgpd + +** Add capability option support based upon +draft-ietf-idr-bgp4-cap-neg-04.txt. + +** Add `show ipv6 bgp prefix-list' command. + +** Check self AS appeared in received routes. + +** redistribute route-map support is added. + +** BGP packet dump feature compatible with MRT. + +* Changes in ripd + +** Fix bug of `timers basic' command's argument format. + +* Changes in ripngd + +** Calculate max RTE using interface's MTU value. + +* Changes in ospfd + +** Some correction to LSU processing. + +** Add check for lsa->refresh_list. + +* Changes in ospf6d + +** Many debug feature is added. + +* Changes in zebra-0.81 + +** SNMP support is disabled in default.--enable-snmp option is added +to configure script. + +* Changes in bgpd + +** Fix FSM bug which introduced in zebra-0.80. + +* Changes in zebra-0.80 + +* access-list + + New access-list name space `ipv6 access-list' is added. At the same + time, `access-list' statemant only accepts IPv4 prefix. Please be + careful if you use IPv6 filtering. You will need to change your + configuration. For IPv6 filtering please use `ipv6 access-list'. + + As of zebra-0.7x, user can use `access-list' for both IPv4 and IPv6 + filtering. + + ! zebra-0.7x + access-list DML-net permit 203.181.89.0/24 + access-list DML-net permit 3ffe:506::0/32 + access-list DML-net deny any + ! + + Above configuration is not valid for zebra-08x. Please add `ipv6' + before 'access-list' when you configure IPv6 filtering. + + ! zebra-0.8x + access-list DML-net permit 203.181.89.0/24 + access-list DML-net deny any + ! + ipv6 access-list DML-net permit 3ffe:506::0/32 + ipv6 access-list DML-net deny any + ! + +* prefix-list + + And also new prefix-list name space `ipv6 prefix-list' is added. It + is the same as the change of `access-list'. `ip prefix-list' now only + accept IPv4 prefix. It was source of confusion that `ip prefix-list' + can be used both IPv4 and IPv6 filtering. Now name space is separated + to clear the meaning of the filter. + + If you use `ip prefix-list' for IPv6 filtering, please change the + stetement. + + ! zebra-0.7x + ip prefix-list 6bone-filter seq 5 permit 3ffe::/17 le 24 ge 24 + ip prefix-list 6bone-filter seq 10 permit 3ffe:8000::/17 le 28 ge 28 + ip prefix-list 6bone-filter seq 12 deny 3ffe::/16 + ip prefix-list 6bone-filter seq 15 permit 2000::/3 le 16 ge 16 + ip prefix-list 6bone-filter seq 20 permit 2001::/16 le 35 ge 35 + ip prefix-list 6bone-filter seq 30 deny any + ! + + Now user can explicitly configure it as IPv6 prefix-list. + + ! zebra-0.8x + ipv6 prefix-list 6bone-filter seq 5 permit 3ffe::/17 le 24 ge 24 + ipv6 prefix-list 6bone-filter seq 10 permit 3ffe:8000::/17 le 28 ge 28 + ipv6 prefix-list 6bone-filter seq 12 deny 3ffe::/16 + ipv6 prefix-list 6bone-filter seq 15 permit 2000::/3 le 16 ge 16 + ipv6 prefix-list 6bone-filter seq 20 permit 2001::/16 le 35 ge 35 + ipv6 prefix-list 6bone-filter seq 30 deny any + ! + +* RIP configuration + + If you want to filter only default route (0.0.0.0/0) and permit other + routes, it was hard to do that. Now `ip prefix-list' can be used for + RIP route filtering. + + New statement: + + `distribute-list prefix PLIST_NAME (in|out) IFNAME' + + is added to ripd. So you can configure on eth0 interface accept all + routes other than default routes. + + ! + router rip + distribute-list prefix filter-default in eth0 + ! + ip prefix-list filter-default deny 0.0.0.0/0 le 0 + ip prefix-list filter-default permit any + ! + +* RIPng configuration + + Same change is done for ripngd. You can use `ipv6 prefix-list' for + filtering. + + ! + router ripng + distribute-list prefix filter-default in eth0 + ! + ipv6 prefix-list filter-default deny ::/0 le 0 + ipv6 prefix-list filter-default permit any + ! + +* BGP configuration + + So far, Multiprotocol Extensions for BGP-4 (RFC2283) configuration is + done with traditional IPv4 peering statement like blow. + + ! + router bgp 7675 + neighbor 3ffe:506::1 remote-as 2500 + neighbor 3ffe:506::1 prefix-list 6bone-filter out + ! + + For separating configuration IPv4 and IPv6, and for retaining Cisco + configuration compatibility, now IPv6 configuration is done by IPv6 + specific statement. IPv6 BGP configuration is done by statement which + start from `ipv6 bgp'. + + ! + router bgp 7675 + ! + ipv6 bgp neighbor 3ffe:506::1 remote-as 2500 + ipv6 bgp neighbor 3ffe:506::1 prefix-list 6bone-filter out + ! + + At the same time some IPv6 specific commands are deleted from IPv4 + configuration. + + o redistribute ripng + o redistribute ospf6 + o neighbor PEER version BGP_VERSION + o neighbor PEER interface IFNAME + + Those commands are only accepted as like below. + + o ipv6 bgp redistribute ripng + o ipv6 bgp redistribute ospf6 + o ipv6 bgp neighbor PEER version BGP_VERSION + o ipv6 bgp neighbor PEER interface IFNAME + + And below new commands are added. + + o ipv6 bgp network IPV6_PREFIX + o ipv6 bgp redistribute static + o ipv6 bgp redistribute connected + o ipv6 bgp neighbor PEER remote-as <1-65535> [passive] + o ipv6 bgp neighbor PEER ebgp-multihop [TTL] + o ipv6 bgp neighbor PEER description DESCRIPTION + o ipv6 bgp neighbor PEER shutdown + o ipv6 bgp neighbor PEER route-reflector-client + o ipv6 bgp neighbor PEER update-source IFNAME + o ipv6 bgp neighbor PEER next-hop-self + o ipv6 bgp neighbor PEER timers holdtime <0-65535> + o ipv6 bgp neighbor PEER timers keepalive <0-65535> + o ipv6 bgp neighbor PEER send-community + o ipv6 bgp neighbor PEER weight <0-65535> + o ipv6 bgp neighbor PEER default-originate + o ipv6 bgp neighbor PEER filter-list FILTER_LIST_NAME (in|out) + o ipv6 bgp neighbor PEER prefix-list PREFIX_LIST_NAME (in|out) + o ipv6 bgp neighbor PEER distribute-list AS_LIST_NAME (in|out) + o ipv6 bgp neighbor PEER route-map ROUTE_MAP_NAME (in|out) + + And some utility commands are introduced. + + o clear ipv6 bgp [PEER] + o show ipv6 bgp neighbors [PEER] + o show ipv6 bgp summary + + I hope these changes are easy to understand for current Zebra users... + +* To restrict connection to VTY interface. + + It used to be both IPv4 and IPv6 filter can be specified with one + access-list. Then the access-list can be appried to VTY interface + with `access-class' stetement in `line vty' node. Below is example in + zebra-0.7x. + + ! + access-list local-only permit 127.0.0.1/32 + access-list local-only permit ::1/128 + access-list local-only deny any + ! + line vty + access-class local-only + ! + + Now IPv4 and IPv6 filter have each name space. It is not possible to + specify IPv4 and IPv6 filter with one access-list. For setting IPv6 + access-list in `line vty', `ipv6 access-class' statement is + introduced. Let me show the configuration in zebra-0.8x. + + ! + access-list local-only permit 127.0.0.1/32 + access-list local-only deny any + ! + ipv6 access-list local-only permit ::1/128 + ipv6 access-list local-only dny any + ! + line vty + access-class local-only + ipv6 access-class local-only + ! + +* route-map + + New IPv6 related route-map match commands are added. + + o match ipv6 address + o match ipv6 next-hop + + Please change your configuration if you use IP match statement for + IPv6 route. + + zebra-0.7x config + ================= + ! + access-list all permit any + ! + route-map set-nexthop permit 10 + match ip address all + set ipv6 next-hop global 3ffe:506::1 + set ipv6 next-hop local fe80::cbb5:591a + ! + + zebra-0.8x config + ================= + ! + ipv6 access-list all permit any + ! + route-map set-nexthop permit 10 + match ipv6 address all + set ipv6 next-hop global 3ffe:506::1 + set ipv6 next-hop local fe80::cbb5:591a + ! + +* zebra connection + + Protocol daemon such as ripd, bgpd, ospfd will reconnect zebra daemon + when the connection fail. Those daemons try to connect zebra every 10 + seconds first three trial, then the interval changed to 60 seconds. + After all, if ten connections are fail, protocol daemon give up the + connection to the zebra daemon. + +* SNMP support (is not yet finished) + + Zebra uses SMUX protocol (RFC1227) for making communication with SNMP + agent. Currently lib/smux.c can be compiled only with ucd-snmp-4.0.1 + and http://ucd-snmp.ucdavis.edu/patches/012.patch. It can not be + compiled with ucd-snmp-3.6.2. + + After applying the patch to ucd-snmp-4.0.1, please configure it with + SMUX module. + + % configure --with-mib-modules=smux + + After compile & install ucd-snmp-4.0.1, you will need to configure + smuxpeer. I'm now using below configuration. + + /usr/local/share/snmp/snmpd.conf + ================================ + smuxpeer 1.3.6.1.6.3.1 test + + Above 1.3.6.1.6.3.1 and test is temporary configuration which is hard + coded in lib/smux.c. Yes, I know it is bad, I'll change it ASAP. + +* HUP signal treatment + + 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 + +** Broadcast address setting on Linux box bug is fixed. + +** Protocol daemon can install connected IPv6 route into the kernel. + +** Now zebra can handle blackhole route. + +* Changes in ripd + +** Add route-map feature for RIP protocol. + +** In case of RIP version 2 routing table entry has IPv4 address and +netmask pair which host part bit is on, ignore the entry. + +* Changes in ripngd + +** Change CMSG_DATA cast from (u_char *) to (int *). (u_char *) does +not work for NetBSD-currnet on SparcStation 10. + +* Changes in ospfd + +** MaxAge LSA treatment is added. + +** ABR/ASBR functionality is added. + +** Virtual Link funtionality is added. + +** ABR behaviors IBM/Cisco/Shortcut is added. + +* Changes in ospf6d + +** Enclosed KAME specific part with #ifdef #endif + +* Changes in zebra-0.78 + +* Changes in lib + +** SNMP support is started. + +** Now Zebra can work on BSD/OS 4.X. + +** Now Zebra can compiled on vanilla OpenBSD 2.5 but not yet working correcltly. + +* Changes in zebra + +** Interface index detection using ioctl() bug is fixed. + +** Interface information protocol is changed. Now interface +addition/deletion and interface's address addition/deletion is +separated. + +* Changes in bgpd + +** BGP hold timer bug is fixed. + +** BGP keepavlie timer becomes configurable. + +* Changes in ripd + +** When making reply to rip's REQUEST message, fill in +RIP_METRIC_INFINITY with network byte order using htonl (). + +** Pass host byte order address to IN_CLASSC and IN_CLASSB macro. + +* Changes in ospfd + +** LSA flooding works. + +** Fix bug of DD processing. + +** Fix bug of originating router-LSA bug is fixed. + +** LSA structure is changed to support LSA aging. + +* Changes in ospf6d + +** `ip6' statement in configuration is changed to `ipv6'. + +* Changes in zebra-0.77 + +* Changes in lib + +** SIGUSR1 reopen logging file. + +** route-map is extended to support multi-protocol routing +information. + +** When compiling under GNU libc 2.1 environment don't use inet6-apps. + +* Changes in zebra + +** Basic IPv6 router advertisement codes added. It is not yet usable. + +** Fix IPv6 route addition/deletion bug is fixed. + +** `show ip route A.B.C.D' works + +* Changes in bgpd + +** When invalid unfeasible routes length comes, bgpd send notify then +continue to process the packet. Now bgpd stop parsing invalid packet +then return to main loop. + +** BGP-4+ withdrawn routes parse bug is fixed. + +** When BGP-4+ information passed to non shared network's peer, trim +link-local next-hop information. + +** `no redistribute ROUTE_TYPE' withdraw installed routes from BGP +routing information. + +** `show ipv6 route IPV6ADDR' command added. + +** BGP start timer has jitter. + +** Holdtimer configuration bug is fixed. Now configuration does not +show unconfigured hold time value. + +* Changes in ripngd + +** Now update timer (default 30 seconds) has +/- 50% jitter value. + +** Add timers basic command. + +** `network' configuration is dynamically reflected. + +** `timers basic ' added. + +* Changes in ripd + +** Reconstruct almost codes. + +** `network' configuration is dynamically reflected. + +** RIP timers now conforms to RFC2453. So user can configure update, +timeout, garbage timer. + +** `timers basic ' works. + +* Changes in ospfd + +** Bug of originating network LSA is fixed. + +** `no router ospf' core dump bug is fixed. + +* Changes in ospf6d + +** Redistribute route works. + +* Changes in zebra-0.76 + +* Changes in lib + +** configure.in Linux IPv6 detection problem is fixed. + +** Include SERVICES file to the distribution + +** Update zebra.texi to zebra-0.76. + +* Changes in zebra-0.75 + +* Changes in lib + +** `termnal length 0' bug is fixed. + +* Changes in zebra + +** When zebra starts up, sweep all zebra installed routes. If -k or +--keep_kernel option is specified to zebra dameon. This function is +not performed. + +* Changes in ripngd + +** Aggreagte address command supported. In router ripngd, +`aggregate-address IPV6PREFIX' works. + +* Changes in bgpd + +** Input route-map's bug which cause segmentation violation is fixed. + +** route-map method improved. + +** BGP-4+ nexthop detection improved. + +** BGP-4+ route re-selection bug is fixed. + +** BGP-4+ iBGP route's nexthop calculation works. + +** After connection Established `show ip bgp neighbor' display BGP TCP +connection's source and destination address. + +** In case of BGP-4+ `show ip bgp neighbor' display BGP-4+ global and +local nexthop which used for originated route. This address will be +used when `next-hop-self'. + +* Changes in ospfd + +** Fix bug of DR election. + +** Set IP precedence field with IPTOS_PREC_INTERNET_CONTROL. + +** Schedule NeighborChange event if NSM status change. + +** Never include a neighbor in Hello packet, when the neighbor goes +down. + +* Changes in zebra-0.74 + +* Changes in lib + +** Now `terminal length 0' means no line output control. + +** `line LINES' command deleted. Instead of this please use `terminal +length <0-512>'. + +** `terminal length <0-512>' is each vty specific configuration so it +can not be configured in the configuration file. If you want to +configure system wide line control, please use `service +terminal-length <0-512>'. This configuration affects to the all vty +interface. + +* Changes in zebra + +** Installation of IPv6 route bug is fixed. + +* Changes in bgpd + +** Very serious bug of bgp_stop () is fixed. When multiple route to +the same destination exist, bgpd try to announce the information to +stopped peer. Then add orphan write thread is added. This cause +many strange behavior of bgpd. + +** Router-id parsing bug is fixed. + +** With BGP-4+ nexthop installation was done with global address but +it should be link-local address. This bug is fixed now. + +** When incoming route-map prepend AS, old AS path remained. Now bgpd +free old AS path. + +** `neighbor PEER weight <0-65535>' command added. + +* Changes in ripngd + +** Almost codes are rewritten to conform to RFC2080. + +* Changes in ospfd + +** SPF calculation timer is added. Currently it is set to 30 seconds. + +** SPF calculation works now. + +** OSPF routing table codes are added. + +** OSPF's internal routes installed into the kernel routing table. + +** Now `ospfd' works as non-area, non-external route support OSPF +router. + +** Call of log_rotate() is removed. + +* Changes in ospf6d + +** LSA data structure is changed. + +** Call of log_rotate() is removed. + +* Changes in zebra-0.73 + +* Changes in lib + +** `config terminal' is changed to `configure terminal'. + +** `terminal length <0-512>' command is added. + +** Variable length argument was specified by `...'. Now all strings +started with character `.' is variable length argument. + +* Changes in zebra + +** Internal route (such as iBGP, internal OSPF route) handling works +correctly. + +** In interface node, `ipv6 address' and `no ipv6 address' works. + +** Interface's address remain after `no ip address' bug is fixed. + +** Host route such as IPv4 with /32 mask and IPv6 with /128 mask +didn't set RTF_GATEWAY even it has gateway. This bug if fixed now. + +* Changes in bgpd + +** `match as-path' argument is used to be specify AS PATH value itself +directly (e.g. ^$). But it is changed to specify `ip as-apth +access-list' name. + +** iBGP route handle works without getting error from the kernel. + +** `set aggregator as AS A.B.C.D' command is added to route-map. + +** `set atomic-aggregate' command is added to bgpd's routemap. + +** Announcement of atomic aggregate attribute and aggregator attribute +works. + +** `update-source' bug is fixed. + +** When a route learned from eBGP is announced to iBGP, local +preference was set to zero. But now it set to +DEFAULT_LOCAL_PREF(100). + +* Changes in ripd + +** RIPv1 route filter bug is fixed. + +** Some memory leak is fixed. + +* Changes in ospfd + +** Fix bug of DR Election. + +** Fix bug of adjacency forming. + +* Changes in ospf6d + +** Clean up logging message. + +** Reflect routing information to zebra daemon. + +* Changes in zebra-0.72 + +* Changes in lib + +** When getsockname return IPv4 mapped IPv6 address. Convert it to +IPv4 address. + +* Changes in bgpd + +** Change route-map's next-hop related settings. + +set ip nexthop -> set ip next-hop +set ipv6 nexthop global -> set ipv6 next-hop global +set ipv6 nexthop local -> set ipv6 next-hop local + +** Add `next-hop-self' command. + +* Changes in ospfd + +** Fix bug of multiple `network area' directive crashes. + +* Changes in zebra-0.71 + +* Changes in lib + +** `log syslog' command is added. + +** Use getaddrinfo function to bind IPv4/IPv6 server socket. + +** `no banner motd' will suppress motd output when user connect to VTY. + +** Bind `quit' command to major nodes. + +* Changes in zebra + +** Point-to-point link address handling bug is fixed. + +* Changes in bgpd + +** AS path validity check is added. If malformed AS path is received +NOTIFY Malformed AS path is send to the peer. + +** Use getaddrinfo function to bind IPv4/IPv6 server socket. + +* Changes in ripd + +** Connected network announcement bug is fixed. + +** `broadcast' command is deleted. + +** `network' command is added. + +** `neighbor' command is added. + +** `redistribute' command is added. + +** `timers basic' command is added. + +** `route' command is added. + +* Changes in ripngd + +** Fix metric calculation bug. + +* Changes in ospfd + +** Check sum bug is fixed. + +* Chanegs in ospf6d + +** Routing table code is rewritten. + +* Changes in zebra-0.70 + +* Changes in zebra + +** Critical routing information base calculation bug check is fixed. + +** zebra ipv4 message is extended to support external/internal route +flavor. + +** Now if internal route doesn't has direct connected nexthop, then +nexthop is calculated by looking up IGP routing table. + +* Changes in bgpd + +** `neighbor PEER update-source IFNAME' command added as ALIAS to +`neighbor PEER interface IFNAME'. + +* Changes in ospfd + +** DD null pointer bug is fixed. + +* Changes in zebra-0.69 + +* Changes in zebra + +** zebra redistirbution supports dynamic notification of the route +change. If you add static route while running zebra, it will be +reflected to other protocol daemon which set `redistribute static'. + +** If static route installation is failed due to the error. The +static route is not added to the configuration and zebra routing +table. + +** zebra sets forwarding flag to on when it starts up. + +** `no ip forwarding' turn off IPv4 forwarding. + +** `no ipv6 forwarding' turn off IPv6 forwarding. + +** Change `show ipforward' command to `show ip forwarding'. + +** Change `show ipv6forward' command to `show ipv6 forwarding'. + +** `ip route A.B.C.D/M INTERFACE' works. So you can set `ip route +10.0.0.0/8 eth0'. + +* Changes in bgpd + +** `neighbor PEER send-community' command is added. If the option is +set, bgpd will send community attribute to the peer. + +** When a BGP route has no-export community attribute and +send-community is set to the peer, the route is not announced to the +peer. + +* Changes in ripngd + +** When ripngd terminates, delete all installed route. + +** `redistribute static', `redistribute connected' works. + +** Change `debug ripng event' to `debug ripng events'. + +** Change `show debug ripng' to `show debugging ripng'. + +** Bug of static route deletion is fixed. + +* Changes in ospfd + +** LS request and LS update can be send and received. + +* Changes in zebra-0.68 + +* Changes in lib + +** DEFUN() is extended to support (a|b|c) statement. + +** Input buffer overflow bug is fixed. + +* Changes in bgpd + +** `ip community-list' is added. + +** set community and match community is added to route-map statement. + +** aggregate-address A.B.C.D/M partly works. Now it works only +summary-only mode. + +* Changes in zebra + +** IPv6 network address delete bug is fixed. + +* Changes in ospfd + +** DR election bug fixed. + +** Now Database Description can be send or received. + +** Neighbor State Machine goes to Full state. + +* Changes in ospf6d + +** router zebra related bug is fixed. + +* Changes in zebra-0.67 + +* Changes in lib + +** `service password-encryption' is added for encrypted password. + +* Changes in bgpd + +** `set as-path prepend ASPATH' is added to route-map command. + +** `set weight WEIGHT' is added to route-map command. + +** `no set ipv6 nexthop global' and `no set ipv6 nexthop local' +command is added to route-map. + +** `neighbor IP_ADDR version BGP_VERSION' command's BGP_VERSION +argument changed. + +Old New +===================== +bgp4 4 +bgp4+ 4+ +bgp4+-draft-00 4- +===================== + +If you want to peer with old draft version of BGP-4+, please configure +like below: + +router bgp ASN + neighbor PEER version 4- + +** Some AS path isn't correctly compared during route selection. Now +it is fixed. + +* Changes in ospfd + +** `router zebra' is default behavior. + +* Changes in ospf6d + +** `router zebra' is default behavior. + +* Changes in zebra-0.66 + +* Changes in zebra + +** When other daemon such as gated install routes into the kernel then +zebra blocks. This is only occur with netlink socket. Now socket is +set as NONBLOCKING and problem is fixed. Reported and fixed by +Patrick Koppen + +* Changes in bgpd + +** Now `router zebra' is not needed to insert BGP routes into the +kernel. It is default behavior. If you don't want to install the BGP +routes to the kernel, please configure like below: + +! +router zebra + no redistribute bgp +! + +** redistribute connected works. + +** redistribute static now filter local loopback routes and link local +network. + +* Changes in ripd + +** Some network check is added. Patch is done by Carlos Alberto +Barcenilla + +* Changes in ripngd + +** Sometimes ripngd install wrong nexthop into the kernel. This bug +is fixed now. + +** Now `router zebra' is not needed to insert RIPng routes into the +kernel. It is default behavior. If you don't want to install the BGP +routes to the kernel, please configure like below: + +! +router zebra + no redistribute ripng +! + +* Changes in zebra-0.65 + +* Changes in lib + +** `C-c' changes current node to ENABLE_NODE. Previously it doesn't. + +** In ENABLE_NODE, `exit' command close vty connection. + +** `service advanced-vty' enable advanced vty function. If this +service is specified one can directly connect to ENABLE_NODE when +enable password is not set. + +** `lines LINES' command is added by Stephen R. van den Berg +. + +* Changes in zebra + +** Basic Linux policy based routing table support is added by Stephen +R. van den Berg . + +* Changes in bgpd + +** route-map command is improved: + `match ip next-hop': New command. + `match metric': New command. + `set metric': Doc fixed. + `set local-preference': DEFUN added. + +* Changes in ripd + +** Check of announced network is added. Now multicast address is +filtered. Reported by Carlos Alberto Barcenilla + + +** Check of network 127 is added. Reported by Carlos Alberto +Barcenilla + +* Changes in ripngd + +** Aging route bug is fixed. + +** `router zebra' semantics changed. ripngd automatically connect to +zebra. + +* Changes in ospfd + +** `no router ospf' works. + +* Changes in ospf6d + +** Bug fix about network vertex. + +* Changes in zebra-0.64.1. + +This is bug fix release. + +* Changes in lib + +** Add check of sin6_scope_id in struct sockaddr_in6. For compilation +on implementation which doesn't have sin6_scope_id. Reported by Wim +Biemolt . + +* Changes in zebra + +** Fix bug of display BGP routes as "O" instead of "B". Reported by +"William F. Maton" and Dave Hartzell +. + +* Changes in bgpd + +** `no network IPV6_NETWORK' statement and `no neighbor IP_ADDR timers +holdtime [TIMER]' statement doesn't work. Reported by Georg Hitsch +. Now both statement work. + +* Changes in ospfd + +** Last interface is not updated by ospf_if_update(). Reported by +Dave Hartzell . + +* Changes in ospf6d + +** Byte order of ifid is changed. Due to this change, this code will +not work with previous version, sorry. + +** Fix `show ip route' route type mismatch. + +** Fix bug of no network IPV6_NETWORK. + +** Important bug fix about intra-area-prefix-lsa. + +* Changes in zebra-0.64. + +* Changes in lib + +** prefix-list based filtering routine is added. Currently used in +bgpd but it will be in other daemons. + +* Changes in bgpd + +** `no router bgp' works. But network statement is not cleared. This +should be fixed in next beta. + +** Route reflector related statement is added. + + router bgp ASN + bgp cluster-id a.b.c.d + neighbor a.b.c.d route-reflector-client + + is added. + +** Prefix list based filtering is added. + + router bgp ASN + neighbor a.b.c.d prefix-list PREFIX_LIST_NAME + +** Prefix list based routing display works. + + show ip bgp prefix-list PREFIX_LIST_NAME + +* Changes in ripd + +** Fix route metric check bug. Reported from Mr. Carlos Alberto +Barcenilla. + +* Changes in ospf6d + +** 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 + +** `copy running-config stgartup-config' command is added. + +** prefix length check bug is fixed. Thanks Marlos Barcenilla +. + +* Changes in ospfd + +** DR and BDR election works. + +** OSPF Hello simple authentication works. + +* 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. + +* Changes in lib + +** VTY has now access-class to restrict remote connection. +Implemented by Alex Bligh . + +! +line vty + access-class ACCESS-LIST-NAME +! + +** `show version' command added. Implemented by Carlos Alberto +Barcenilla + +* Changes in zebra + +** `ip address' command on *BSD bug is fixed. + +** `no ip address' works now for IPv4 address. + +** Now `write terminal' display `ip address' configuration. + +* Changes in bgpd + +** Redistribute static works now. Please run both zebra and bgpd. +bgpd.conf should be like this: + +! +router zebra +! +router bgp ASN + redisitribute static +! + +* Changes in guile + +** 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. + +** New directory guile is added for linking with guile interpreter. + +* Changes in zebra + +** On GNU/Linux Kernel 2.2.x (with netlink support), zebra detects +asynchronous routing updates. *BSD support is not yet finished. + +* Changes in bgpd + +** `show ip bgp regexp ASPATH_REGEX' uses CISCO like regular expression +instead of RPSL like regular expression. I'm planing to provide RPSL +like regular expression with `show ip bgp rpsl' or something. + +* Changes in lib + +** Press '?' at variable mandatory argument, vty prints nothing. Now +vty outputs description about the argument. Fixed by Alex Bligh + + +** buffer.c has some ugly bugs. Due to the bug, vty interface hangs +when large output date exists. This bug is fixed. Reported by Alex +Bligh . + +* Changes in ospfd + +** DR and BDR information is shown by `show ip ospf interface' command. + +* Changes in zebra-19990408 snapshot + +* Changes in bgpd + +** Old BGP-4+ specification (described in old draft) treatment bug is +fixed. It seems that mrtd uses this format as default. So if you +have problem peering with mrtd and want to use old draft format please +use version statement like this. + +neighbor PEER_ADDRESS remote-as ASN +neighbor PEER_ADDRESS version bgp4+-draft-00 + +** When AS path is epmty (routes generated by bgpd), SEGV is occur +when announce the routes to eBGP peer. Reported by +kad@gibson.skif.net. + +** ip as-path access-list command is added. + +** neighbor PEER_ADDRESS filter-list AS_LIST [in|out] command is added. + +** neighbor PEER_ADDRESS timers holdtimer TIMER command is added. + +* Changes in all daemons + +** With KAME stack, terminal interface is now bind AF_INET socket +instead of AF_INET6 one. + +* Changes in zebra-19990403 snapshot + +* Changes in bgpd + +** When bgpd has 'router zebra', bgpd automatically select it's router +ID as most highest interface's IP Address. + +** When AS path is empty (in case of iBGP), it doesn't include any AS +segment. This change is for announcement to gated under iBGP. + +* Changes in ospfd + +** OSPF hello packet send/receive works. + +* Changes in ospf6d + +** 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 + +** When BGP-4+ connection is done by IPv6 link-local address. One +have to specify interface index for the connection. So I've added +interface statement to the neighbor commmand. Please specify +interface name for getting interface index like below. This statement +only works on GNU/Linux. I'll support BSD ASAP. + +router bgp 7675 + neighbor fe80::200:f8ff:fe01:5fd3 remote-as 2500 + neighbor fe80::200:f8ff:fe01:5fd3 interface sit3 + +** For disable BGP peering `shutdown' command is added. + +router bgp 7675 + neighbor 10.0.0.1 shutdown + +** `description' command is added to neighbor statement. + +router bgp 7675 + neighbor 10.0.0.1 description peering with Norway. + +** `show ip bgp regexp AS-REGEXP' works again. + +show ip bgp regexp AS7675 + +will show routes which include AS7675. + +** When a route which is made from `network' statement is send to +neighbor. Set it's nexthop to self. So 10.0.0.0/8 is announced to +the peer A with source address 192.168.1.1. The routes nexthop is set +to 192.168.1.1. + +* Changes in zebra + +** In zebra/rtread_sysctl.c, function rtm_read() may overrun allocated +buffer when the address family is not supported and the length is big +(i.e link address). Reported Achim Patzner . + +* Changes in ospfd + +** Now ospfd receive OSPF packet. + +* Changes in zebra-19990319 snapshot + +* Changes in configuration and libraries + +** User can disable IPv6 feature and/or pthread feature by configure + option. + + To disable IPv6: configure --disable-ipv6 + To disable pthread: configure --disable-pthread + +** User can disable specified daemon by configure option. + + Don't make zebra: configure --disable-zebra + Don't make bgpd: configure --disable-bgpd + Don't make ripd: configure --disable-ripd + Don't make ripngd: configure --disable-ripngd + Don't make ospfd: configure --disable-ospfd + Don't make ospf6d: configure --disable-ospf6d + +** Sample configuration files are installed as 600 file flag. + Suggested by Jeroen Ruigrok/Asmodai . + +** syslog logging feature is added by Peter Galbavy + + +** Inclusion of standard header files is reworked by Peter Galbavy + + +** Change description from GNU/Linux 2.1.X to GNU/Linux 2.2.X + +** If daemon function exists in standard C library use it. + +** To generate configure script we upgrade autoconf to 2.13. To +generate Makefile.in we upgrade automake to 1.4. + +** doc/texinfo.tex is added to distribution. + +** Update ports/pkg/DESCR description. + +** Update doc/zebra.texi. + +** logfile FILENAME statement deleted. Instead of that please use log +file FILENAME. + +* Changes in zebra + +* Changes in bgpd + +** Communication between zebra and bgpd works now. So if there is + `router zebra' line in bgpd.conf, selected route is installed + into kernel routing table. + +** Delete all routes which inserted by bgpd when bgpd dies. If you +want to retain routes even bgpd dies please specify [-r|--retain] +option to bgpd. + +** BGP announcement code is reworked. Now bgpd announce selected + routes to other peer. + +** All output bgp packet is buffered. It's written to the socket when + it gets ready. + +** Output route-map works now. You can specify output route-map by: + + neighbor IP_ADDR route-map ROUTE_MAP_NAME out + +** New route-map command added. + + set ip nexthop IP_ADDR + set ipv6 nexthop global IP_ADDR + +** Fix bug about unlock of the route_node structure. + +** BGP-4+ support is added. bgpd can listen and speak BGP-4+ packet +specified in RFC2283. You can view IPv6 bgp table by: `show ipv6 bgp'. + +** Meny packet overflow check is added. + +* Changes in ripd + +* Changes in ripngd + +* Changes in ospfd + +** ospfd work is started by Toshiaki Takada . Now +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: "[ ]*$" +end: diff --git a/README b/README new file mode 100644 index 0000000..f1b25d8 --- /dev/null +++ b/README @@ -0,0 +1,11 @@ +Quagga is free software that manages various IPv4 and IPv6 routing +protocols. + +Currently Quagga supports BGP4, BGP4+, OSPFv2, OSPFv3, RIPv1, +RIPv2, and RIPng as well as very early support for IS-IS. + +See the file INSTALL.quagga.txt for building and installation instructions. + +See the file REPORTING-BUGS to report bugs. + +Quagga is free software. See the file COPYING for copying conditions. diff --git a/README.NetBSD b/README.NetBSD new file mode 100755 index 0000000..6bbc680 --- /dev/null +++ b/README.NetBSD @@ -0,0 +1,48 @@ +#!/bin/sh + +# $QuaggaId: Format:%an, %ai, %h$ $ + +# This file is helpful for building quagga from cvs on NetBSD, and +# probably on any system using pkgsrc. +# One should have readline installed already (pkgsrc/devel/readline). + +MAKE=make +# Quagga is currently documented not to require GNU make, but sometimes +# BSD make fails. Enable this if statement as a workaround. +if false; then + MAKE=gmake + echo "WARNING: using gmake to work around nonportable makefiles" +fi + +# Use /usr/quagga to be independent, and /usr/pkg to overwrite pkgsrc. +PREFIX=/usr/pkg + +case $1 in + + build) + # Omitted because it is now default: + # --enable-opaque-lsa + ./bootstrap.sh + LDFLAGS="-L/usr/pkg/lib -R/usr/pkg/lib" CPPFLAGS="-I/usr/pkg/include" \ + ./configure --prefix=${PREFIX} \ + --sysconfdir=/etc/zebra --localstatedir=/var/run/zebra \ + --enable-exampledir=${PREFIX}/share/examples/zebra \ + --enable-pkgsrcrcdir=${PREFIX}/etc/rc.d \ + --enable-vtysh + ${MAKE} + ;; + + install) + ${MAKE} install + ;; + + clean) + ${MAKE} clean + ;; + + *) + echo "Usage: README.NetBSD (build|install|clean)" + exit 1 + ;; + +esac diff --git a/REPORTING-BUGS b/REPORTING-BUGS new file mode 100644 index 0000000..83f4eb9 --- /dev/null +++ b/REPORTING-BUGS @@ -0,0 +1,34 @@ +This file describes the procedure for reporting Quagga bugs. You are not +obliged to follow this format, but it would be great help for Quagga developers +if you report a bug as described below. + +Bugs submitted with woefully incomplete information may be summarily +closed. Submitters of bugs against old versions may be asked to +retest against the latest release. Submitters may be asked for +additional information. Bugs may be closed after 30 days of +non-response to requests to reconfirm or supply additional +information. + +Report bugs http://bugzilla.quagga.net + +Please supply the following information: +1. Your Quagga version or if it is from git then the commit reference. + Please try to report bugs against git master or the latest release. +2. Quagga daemons you run e.g. bgpd or ripd and full name of your OS. Any + specific options you compiled Quagga with. +3. Problem description. Copy and paste relative commands and their output to + describe your network setup e.g. "zebra>show ip route". + Please, also give your simple network layout and output of relative OS + commands (e.g., ifconfig (BSD) or ip (Linux)). +4. All Quagga configuration files you use. If you don't want to publish your + network numbers change 2 middle bytes in IPv4 address to be XXX (e.g. + 192.XXX.XXX.32/24). Similar could be done with IPv6. +5. If any Quagga daemon core dumped, please, supply stack trace using the + following commands: host> gdb exec_file core_file , (gdb) bt . +6. Run all Quagga daemons with full debugging on (see documentation on + debugging) and send _only_ part of logs which are relative to your problem. +7. If the problem is difficult to reproduce please send a shell script to + reproduce it. +8. Patches, workarounds, fixes are always welcome. + +Thank You. diff --git a/SERVICES b/SERVICES new file mode 100644 index 0000000..0322d45 --- /dev/null +++ b/SERVICES @@ -0,0 +1,21 @@ +# As long as this software is in alpha testing it is not yet included +# in /etc/services files. This means that you may need to add the following +# lines into your /etc/services file on your hosts. +# +# --- Please add this to your /etc/services --- + +# +# GNU Zebra services +# + +zebrasrv 2600/tcp +zebra 2601/tcp +ripd 2602/tcp +ripng 2603/tcp +ospfd 2604/tcp +bgpd 2605/tcp +ospf6d 2606/tcp +ospfapi 2607/tcp +isisd 2608/tcp +pimd 2611/tcp +nhrpd 2612/tcp diff --git a/TODO b/TODO new file mode 100644 index 0000000..246cc25 --- /dev/null +++ b/TODO @@ -0,0 +1,209 @@ + + Quagga TODO list + 2013-03-29 + + +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. + +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. + +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 + +"GSoC-Mentors:" listings are preliminary at this point. + + +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: + +[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: + +[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/bgpd/.gitignore b/bgpd/.gitignore new file mode 100644 index 0000000..105be22 --- /dev/null +++ b/bgpd/.gitignore @@ -0,0 +1,18 @@ +Makefile +Makefile.in +*.o +bgpd +bgp_btoa +bgpd.conf +tags +TAGS +.deps +.nfs* +*.lo +*.la +*.a +*.libs +.arch-inventory +.arch-ids +*~ +*.loT diff --git a/bgpd/BGP4-MIB.txt b/bgpd/BGP4-MIB.txt new file mode 100644 index 0000000..c911316 --- /dev/null +++ b/bgpd/BGP4-MIB.txt @@ -0,0 +1,929 @@ + BGP4-MIB DEFINITIONS ::= BEGIN + + IMPORTS + MODULE-IDENTITY, OBJECT-TYPE, NOTIFICATION-TYPE, + IpAddress, Integer32, Counter32, Gauge32, mib-2 + FROM SNMPv2-SMI + MODULE-COMPLIANCE, OBJECT-GROUP, NOTIFICATION-GROUP + FROM SNMPv2-CONF; + + bgp MODULE-IDENTITY + LAST-UPDATED "9902100000Z" + ORGANIZATION "IETF IDR Working Group" + CONTACT-INFO "E-mail: idr@merit.net + + Susan Hares (Editor) + Merit Network + 4251 Plymouth Road + Suite C + Ann Arbor, MI 48105-2785 + Tel: +1 734 936 2095 + Fax: +1 734 647 3185 + E-mail: skh@merit.edu + + Jeff Johnson (Editor) + RedBack Networks, Inc. + 1389 Moffett Park Drive + Sunnyvale, CA 94089-1134 + Tel: +1 408 548 3516 + Fax: +1 408 548 3599 + E-mail: jeff@redback.com" + DESCRIPTION + "The MIB module for BGP-4." + REVISION "9902100000Z" + DESCRIPTION + "Corrected duplicate OBJECT IDENTIFIER + assignment in the conformance information." + REVISION "9601080000Z" + DESCRIPTION + "1) Fixed the definitions of the traps to + make them equivalent to their initial + definition in RFC 1269. + 2) Added compliance and conformance info." + ::= { mib-2 15 } + + bgpVersion OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (1..255)) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Vector of supported BGP protocol version + numbers. Each peer negotiates the version + from this vector. Versions are identified + via the string of bits contained within this + object. The first octet contains bits 0 to + 7, the second octet contains bits 8 to 15, + and so on, with the most significant bit + referring to the lowest bit number in the + octet (e.g., the MSB of the first octet + refers to bit 0). If a bit, i, is present + and set, then the version (i+1) of the BGP + is supported." + ::= { bgp 1 } + + bgpLocalAs OBJECT-TYPE + SYNTAX INTEGER (0..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The local autonomous system number." + ::= { bgp 2 } + + + + -- BGP Peer table. This table contains, one entry per BGP + -- peer, information about the BGP peer. + + bgpPeerTable OBJECT-TYPE + SYNTAX SEQUENCE OF BgpPeerEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "BGP peer table. This table contains, + one entry per BGP peer, information about the + connections with BGP peers." + ::= { bgp 3 } + + bgpPeerEntry OBJECT-TYPE + SYNTAX BgpPeerEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Entry containing information about the + connection with a BGP peer." + INDEX { bgpPeerRemoteAddr } + ::= { bgpPeerTable 1 } + + BgpPeerEntry ::= SEQUENCE { + bgpPeerIdentifier + IpAddress, + bgpPeerState + INTEGER, + bgpPeerAdminStatus + INTEGER, + bgpPeerNegotiatedVersion + Integer32, + bgpPeerLocalAddr + IpAddress, + bgpPeerLocalPort + INTEGER, + bgpPeerRemoteAddr + IpAddress, + bgpPeerRemotePort + INTEGER, + bgpPeerRemoteAs + INTEGER, + bgpPeerInUpdates + Counter32, + bgpPeerOutUpdates + Counter32, + bgpPeerInTotalMessages + Counter32, + bgpPeerOutTotalMessages + Counter32, + bgpPeerLastError + OCTET STRING, + bgpPeerFsmEstablishedTransitions + Counter32, + bgpPeerFsmEstablishedTime + Gauge32, + bgpPeerConnectRetryInterval + INTEGER, + bgpPeerHoldTime + INTEGER, + bgpPeerKeepAlive + INTEGER, + bgpPeerHoldTimeConfigured + INTEGER, + bgpPeerKeepAliveConfigured + INTEGER, + bgpPeerMinASOriginationInterval + INTEGER, + bgpPeerMinRouteAdvertisementInterval + INTEGER, + bgpPeerInUpdateElapsedTime + Gauge32 + } + + bgpPeerIdentifier OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The BGP Identifier of this entry's BGP peer." + ::= { bgpPeerEntry 1 } + + bgpPeerState OBJECT-TYPE + SYNTAX INTEGER { + idle(1), + connect(2), + active(3), + opensent(4), + openconfirm(5), + established(6) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The BGP peer connection state." + ::= { bgpPeerEntry 2 } + + bgpPeerAdminStatus OBJECT-TYPE + SYNTAX INTEGER { + stop(1), + start(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The desired state of the BGP connection. A + transition from 'stop' to 'start' will cause + the BGP Start Event to be generated. A + transition from 'start' to 'stop' will cause + the BGP Stop Event to be generated. This + parameter can be used to restart BGP peer + connections. Care should be used in providing + write access to this object without adequate + authentication." + ::= { bgpPeerEntry 3 } + + bgpPeerNegotiatedVersion OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The negotiated version of BGP running between + the two peers." + ::= { bgpPeerEntry 4 } + + bgpPeerLocalAddr OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The local IP address of this entry's BGP + connection." + ::= { bgpPeerEntry 5 } + + bgpPeerLocalPort OBJECT-TYPE + SYNTAX INTEGER (0..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The local port for the TCP connection between + the BGP peers." + ::= { bgpPeerEntry 6 } + + bgpPeerRemoteAddr OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The remote IP address of this entry's BGP + peer." + ::= { bgpPeerEntry 7 } + + bgpPeerRemotePort OBJECT-TYPE + SYNTAX INTEGER (0..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The remote port for the TCP connection between + the BGP peers. Note that the objects + bgpPeerLocalAddr, bgpPeerLocalPort, + bgpPeerRemoteAddr and bgpPeerRemotePort + provide the appropriate reference to the + standard MIB TCP connection table." + ::= { bgpPeerEntry 8 } + + bgpPeerRemoteAs OBJECT-TYPE + SYNTAX INTEGER (0..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The remote autonomous system number." + ::= { bgpPeerEntry 9 } + + bgpPeerInUpdates OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of BGP UPDATE messages received on + this connection. This object should be + initialized to zero (0) when the connection is + established." + ::= { bgpPeerEntry 10 } + + bgpPeerOutUpdates OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of BGP UPDATE messages transmitted + on this connection. This object should be + initialized to zero (0) when the connection is + established." + ::= { bgpPeerEntry 11 } + + bgpPeerInTotalMessages OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of messages received from the + remote peer on this connection. This object + should be initialized to zero when the + connection is established." + ::= { bgpPeerEntry 12 } + + bgpPeerOutTotalMessages OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of messages transmitted to + the remote peer on this connection. This object + should be initialized to zero when the + connection is established." + ::= { bgpPeerEntry 13 } + + bgpPeerLastError OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (2)) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The last error code and subcode seen by this + peer on this connection. If no error has + occurred, this field is zero. Otherwise, the + first byte of this two byte OCTET STRING + contains the error code, and the second byte + contains the subcode." + ::= { bgpPeerEntry 14 } + + bgpPeerFsmEstablishedTransitions OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of times the BGP FSM + transitioned into the established state." + ::= { bgpPeerEntry 15 } + + bgpPeerFsmEstablishedTime OBJECT-TYPE + SYNTAX Gauge32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "This timer indicates how long (in seconds) this + peer has been in the Established state or how long + since this peer was last in the Established state. + It is set to zero when a new peer is configured or + the router is booted." + ::= { bgpPeerEntry 16 } + + bgpPeerConnectRetryInterval OBJECT-TYPE + SYNTAX INTEGER (1..65535) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Time interval in seconds for the ConnectRetry + timer. The suggested value for this timer is + 120 seconds." + ::= { bgpPeerEntry 17 } + + bgpPeerHoldTime OBJECT-TYPE + SYNTAX INTEGER ( 0 | 3..65535 ) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Time interval in seconds for the Hold Timer + established with the peer. The value of this + object is calculated by this BGP speaker by + using the smaller of the value in + bgpPeerHoldTimeConfigured and the Hold Time + received in the OPEN message. This value + must be at lease three seconds if it is not + zero (0) in which case the Hold Timer has + not been established with the peer, or, the + value of bgpPeerHoldTimeConfigured is zero (0)." + ::= { bgpPeerEntry 18 } + + bgpPeerKeepAlive OBJECT-TYPE + SYNTAX INTEGER ( 0 | 1..21845 ) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Time interval in seconds for the KeepAlive + timer established with the peer. The value of + this object is calculated by this BGP speaker + such that, when compared with bgpPeerHoldTime, + it has the same proportion as what + bgpPeerKeepAliveConfigured has when compared + with bgpPeerHoldTimeConfigured. If the value + of this object is zero (0), it indicates that + the KeepAlive timer has not been established + with the peer, or, the value of + bgpPeerKeepAliveConfigured is zero (0)." + ::= { bgpPeerEntry 19 } + + bgpPeerHoldTimeConfigured OBJECT-TYPE + SYNTAX INTEGER ( 0 | 3..65535 ) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Time interval in seconds for the Hold Time + configured for this BGP speaker with this peer. + This value is placed in an OPEN message sent to + this peer by this BGP speaker, and is compared + with the Hold Time field in an OPEN message + received from the peer when determining the Hold + Time (bgpPeerHoldTime) with the peer. This value + must not be less than three seconds if it is not + zero (0) in which case the Hold Time is NOT to be + established with the peer. The suggested value for + this timer is 90 seconds." + ::= { bgpPeerEntry 20 } + + bgpPeerKeepAliveConfigured OBJECT-TYPE + SYNTAX INTEGER ( 0 | 1..21845 ) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Time interval in seconds for the KeepAlive timer + configured for this BGP speaker with this peer. + The value of this object will only determine the + KEEPALIVE messages' frequency relative to the value + specified in bgpPeerHoldTimeConfigured; the actual + time interval for the KEEPALIVE messages is + indicated by bgpPeerKeepAlive. A reasonable + maximum value for this timer would be configured to + be one third of that of bgpPeerHoldTimeConfigured. + If the value of this object is zero (0), no + periodical KEEPALIVE messages are sent to the peer + after the BGP connection has been established. The + suggested value for this timer is 30 seconds." + ::= { bgpPeerEntry 21 } + + bgpPeerMinASOriginationInterval OBJECT-TYPE + SYNTAX INTEGER (1..65535) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Time interval in seconds for the + MinASOriginationInterval timer. + The suggested value for this timer is 15 seconds." + ::= { bgpPeerEntry 22 } + + bgpPeerMinRouteAdvertisementInterval OBJECT-TYPE + SYNTAX INTEGER (1..65535) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Time interval in seconds for the + MinRouteAdvertisementInterval timer. + The suggested value for this timer is 30 seconds." + ::= { bgpPeerEntry 23 } + + bgpPeerInUpdateElapsedTime OBJECT-TYPE + SYNTAX Gauge32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Elapsed time in seconds since the last BGP + UPDATE message was received from the peer. + Each time bgpPeerInUpdates is incremented, + the value of this object is set to zero (0)." + ::= { bgpPeerEntry 24 } + + + + bgpIdentifier OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The BGP Identifier of local system." + ::= { bgp 4 } + + + + -- Received Path Attribute Table. This table contains, + -- one entry per path to a network, path attributes + -- received from all peers running BGP version 3 or less. + -- This table is obsolete, having been replaced in + -- functionality with the bgp4PathAttrTable. + + bgpRcvdPathAttrTable OBJECT-TYPE + SYNTAX SEQUENCE OF BgpPathAttrEntry + MAX-ACCESS not-accessible + STATUS obsolete + DESCRIPTION + "The BGP Received Path Attribute Table contains + information about paths to destination networks + received from all peers running BGP version 3 or + less." + ::= { bgp 5 } + + bgpPathAttrEntry OBJECT-TYPE + SYNTAX BgpPathAttrEntry + MAX-ACCESS not-accessible + STATUS obsolete + DESCRIPTION + "Information about a path to a network." + INDEX { bgpPathAttrDestNetwork, + bgpPathAttrPeer } + ::= { bgpRcvdPathAttrTable 1 } + + BgpPathAttrEntry ::= SEQUENCE { + bgpPathAttrPeer + IpAddress, + bgpPathAttrDestNetwork + IpAddress, + bgpPathAttrOrigin + INTEGER, + bgpPathAttrASPath + OCTET STRING, + bgpPathAttrNextHop + IpAddress, + bgpPathAttrInterASMetric + Integer32 + } + + bgpPathAttrPeer OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS obsolete + DESCRIPTION + "The IP address of the peer where the path + information was learned." + ::= { bgpPathAttrEntry 1 } + + bgpPathAttrDestNetwork OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS obsolete + DESCRIPTION + "The address of the destination network." + ::= { bgpPathAttrEntry 2 } + + bgpPathAttrOrigin OBJECT-TYPE + SYNTAX INTEGER { + igp(1),-- networks are interior + egp(2),-- networks learned via EGP + incomplete(3) -- undetermined + } + MAX-ACCESS read-only + STATUS obsolete + DESCRIPTION + "The ultimate origin of the path information." + ::= { bgpPathAttrEntry 3 } + + bgpPathAttrASPath OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (2..255)) + MAX-ACCESS read-only + STATUS obsolete + DESCRIPTION + "The set of ASs that must be traversed to reach + the network. This object is probably best + represented as SEQUENCE OF INTEGER. For SMI + compatibility, though, it is represented as + OCTET STRING. Each AS is represented as a pair + of octets according to the following algorithm: + + first-byte-of-pair = ASNumber / 256; + second-byte-of-pair = ASNumber & 255;" + ::= { bgpPathAttrEntry 4 } + + bgpPathAttrNextHop OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS obsolete + DESCRIPTION + "The address of the border router that should + be used for the destination network." + ::= { bgpPathAttrEntry 5 } + + bgpPathAttrInterASMetric OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS obsolete + DESCRIPTION + "The optional inter-AS metric. If this + attribute has not been provided for this route, + the value for this object is 0." + ::= { bgpPathAttrEntry 6 } + + + + -- BGP-4 Received Path Attribute Table. This table contains, + -- one entry per path to a network, path attributes + -- received from all peers running BGP-4. + + bgp4PathAttrTable OBJECT-TYPE + SYNTAX SEQUENCE OF Bgp4PathAttrEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The BGP-4 Received Path Attribute Table contains + information about paths to destination networks + received from all BGP4 peers." + ::= { bgp 6 } + + bgp4PathAttrEntry OBJECT-TYPE + SYNTAX Bgp4PathAttrEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Information about a path to a network." + INDEX { bgp4PathAttrIpAddrPrefix, + bgp4PathAttrIpAddrPrefixLen, + bgp4PathAttrPeer } + ::= { bgp4PathAttrTable 1 } + + Bgp4PathAttrEntry ::= SEQUENCE { + bgp4PathAttrPeer + IpAddress, + bgp4PathAttrIpAddrPrefixLen + INTEGER, + bgp4PathAttrIpAddrPrefix + IpAddress, + bgp4PathAttrOrigin + INTEGER, + bgp4PathAttrASPathSegment + OCTET STRING, + bgp4PathAttrNextHop + IpAddress, + bgp4PathAttrMultiExitDisc + INTEGER, + bgp4PathAttrLocalPref + INTEGER, + bgp4PathAttrAtomicAggregate + INTEGER, + bgp4PathAttrAggregatorAS + INTEGER, + bgp4PathAttrAggregatorAddr + IpAddress, + bgp4PathAttrCalcLocalPref + INTEGER, + bgp4PathAttrBest + INTEGER, + bgp4PathAttrUnknown + OCTET STRING + } + + bgp4PathAttrPeer OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The IP address of the peer where the path + information was learned." + ::= { bgp4PathAttrEntry 1 } + bgp4PathAttrIpAddrPrefixLen OBJECT-TYPE + SYNTAX INTEGER (0..32) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Length in bits of the IP address prefix in the + Network Layer Reachability Information field." + ::= { bgp4PathAttrEntry 2 } + + bgp4PathAttrIpAddrPrefix OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "An IP address prefix in the Network Layer + Reachability Information field. This object + is an IP address containing the prefix with + length specified by bgp4PathAttrIpAddrPrefixLen. + Any bits beyond the length specified by + bgp4PathAttrIpAddrPrefixLen are zeroed." + ::= { bgp4PathAttrEntry 3 } + + bgp4PathAttrOrigin OBJECT-TYPE + SYNTAX INTEGER { + igp(1),-- networks are interior + egp(2),-- networks learned via EGP + incomplete(3) -- undetermined + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The ultimate origin of the path information." + ::= { bgp4PathAttrEntry 4 } + + bgp4PathAttrASPathSegment OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (2..255)) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The sequence of AS path segments. Each AS + path segment is represented by a triple + . + + The type is a 1-octet field which has two + possible values: + 1 AS_SET: unordered set of ASs a + route in the UPDATE message + has traversed + 2 AS_SEQUENCE: ordered set of ASs + a route in the UPDATE message + has traversed. + + The length is a 1-octet field containing the + number of ASs in the value field. + + The value field contains one or more AS + numbers, each AS is represented in the octet + string as a pair of octets according to the + following algorithm: + + first-byte-of-pair = ASNumber / 256; + second-byte-of-pair = ASNumber & 255;" + ::= { bgp4PathAttrEntry 5 } + + bgp4PathAttrNextHop OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The address of the border router that should + be used for the destination network." + ::= { bgp4PathAttrEntry 6 } + + bgp4PathAttrMultiExitDisc OBJECT-TYPE + SYNTAX INTEGER (-1..2147483647) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "This metric is used to discriminate between + multiple exit points to an adjacent autonomous + system. A value of -1 indicates the absence of + this attribute." + ::= { bgp4PathAttrEntry 7 } + + bgp4PathAttrLocalPref OBJECT-TYPE + SYNTAX INTEGER (-1..2147483647) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The originating BGP4 speaker's degree of + preference for an advertised route. A value of + -1 indicates the absence of this attribute." + ::= { bgp4PathAttrEntry 8 } + + bgp4PathAttrAtomicAggregate OBJECT-TYPE + SYNTAX INTEGER { + lessSpecificRrouteNotSelected(1), + lessSpecificRouteSelected(2) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Whether or not a system has selected + a less specific route without selecting a + more specific route." + ::= { bgp4PathAttrEntry 9 } + + bgp4PathAttrAggregatorAS OBJECT-TYPE + SYNTAX INTEGER (0..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The AS number of the last BGP4 speaker that + performed route aggregation. A value of zero (0) + indicates the absence of this attribute." + ::= { bgp4PathAttrEntry 10 } + + bgp4PathAttrAggregatorAddr OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The IP address of the last BGP4 speaker that + performed route aggregation. A value of + 0.0.0.0 indicates the absence of this attribute." + ::= { bgp4PathAttrEntry 11 } + + bgp4PathAttrCalcLocalPref OBJECT-TYPE + SYNTAX INTEGER (-1..2147483647) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The degree of preference calculated by the + receiving BGP4 speaker for an advertised route. + A value of -1 indicates the absence of this + attribute." + ::= { bgp4PathAttrEntry 12 } + + bgp4PathAttrBest OBJECT-TYPE + SYNTAX INTEGER { + false(1),-- not chosen as best route + true(2) -- chosen as best route + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "An indication of whether or not this route + was chosen as the best BGP4 route." + ::= { bgp4PathAttrEntry 13 } + + bgp4PathAttrUnknown OBJECT-TYPE + SYNTAX OCTET STRING (SIZE(0..255)) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "One or more path attributes not understood + by this BGP4 speaker. Size zero (0) indicates + the absence of such attribute(s). Octets + beyond the maximum size, if any, are not + recorded by this object." + ::= { bgp4PathAttrEntry 14 } + + + -- Traps. + + -- note that in RFC 1657, bgpTraps was incorrectly + -- assigned a value of { bgp 7 }, and each of the + -- traps had the bgpPeerRemoteAddr object inappropriately + -- removed from their OBJECTS clause. The following + -- definitions restore the semantics of the traps as + -- they were initially defined in RFC 1269. + + -- { bgp 7 } is unused + + bgpTraps OBJECT IDENTIFIER ::= { bgp 0 } + + bgpEstablished NOTIFICATION-TYPE + OBJECTS { bgpPeerRemoteAddr, + bgpPeerLastError, + bgpPeerState } + STATUS current + DESCRIPTION + "The BGP Established event is generated when + the BGP FSM enters the ESTABLISHED state." + ::= { bgpTraps 1 } + + bgpBackwardTransition NOTIFICATION-TYPE + OBJECTS { bgpPeerRemoteAddr, + bgpPeerLastError, + bgpPeerState } + STATUS current + DESCRIPTION + "The BGPBackwardTransition Event is generated + when the BGP FSM moves from a higher numbered + state to a lower numbered state." + ::= { bgpTraps 2 } + + -- conformance information + + bgpMIBConformance OBJECT IDENTIFIER ::= { bgp 8 } + bgpMIBCompliances OBJECT IDENTIFIER ::= { bgpMIBConformance 1 } + bgpMIBGroups OBJECT IDENTIFIER ::= { bgpMIBConformance 2 } + + -- compliance statements + + bgpMIBCompliance MODULE-COMPLIANCE + STATUS current + DESCRIPTION + "The compliance statement for entities which + implement the BGP4 mib." + MODULE -- this module + MANDATORY-GROUPS { bgp4MIBGlobalsGroup, + bgp4MIBPeerGroup, + bgp4MIBPathAttrGroup, + bgp4MIBNotificationGroup } + ::= { bgpMIBCompliances 1 } + + -- units of conformance + + bgp4MIBGlobalsGroup OBJECT-GROUP + OBJECTS { bgpVersion, + bgpLocalAs, + bgpIdentifier } + STATUS current + DESCRIPTION + "A collection of objects providing information + on global BGP state." + ::= { bgpMIBGroups 1 } + + bgp4MIBPeerGroup OBJECT-GROUP + OBJECTS { bgpPeerIdentifier, + bgpPeerState, + bgpPeerAdminStatus, + bgpPeerNegotiatedVersion, + bgpPeerLocalAddr, + bgpPeerLocalPort, + bgpPeerRemoteAddr, + bgpPeerRemotePort, + bgpPeerRemoteAs, + bgpPeerInUpdates, + bgpPeerOutUpdates, + bgpPeerInTotalMessages, + bgpPeerOutTotalMessages, + bgpPeerLastError, + bgpPeerFsmEstablishedTransitions, + bgpPeerFsmEstablishedTime, + bgpPeerConnectRetryInterval, + bgpPeerHoldTime, + bgpPeerKeepAlive, + bgpPeerHoldTimeConfigured, + bgpPeerKeepAliveConfigured, + bgpPeerMinASOriginationInterval, + bgpPeerMinRouteAdvertisementInterval, + bgpPeerInUpdateElapsedTime } + STATUS current + DESCRIPTION + "A collection of objects for managing + BGP peers." + ::= { bgpMIBGroups 2 } + + bgp4MIBRcvdPathAttrGroup OBJECT-GROUP + OBJECTS { bgpPathAttrPeer, + bgpPathAttrDestNetwork, + bgpPathAttrOrigin, + bgpPathAttrASPath, + bgpPathAttrNextHop, + bgpPathAttrInterASMetric } + STATUS obsolete + DESCRIPTION + "A collection of objects for managing BGP + path entries. + + This conformance group is obsolete, + replaced by bgp4MIBPathAttrGroup." + ::= { bgpMIBGroups 3 } + + bgp4MIBPathAttrGroup OBJECT-GROUP + OBJECTS { bgp4PathAttrPeer, + bgp4PathAttrIpAddrPrefixLen, + bgp4PathAttrIpAddrPrefix, + bgp4PathAttrOrigin, + bgp4PathAttrASPathSegment, + bgp4PathAttrNextHop, + bgp4PathAttrMultiExitDisc, + bgp4PathAttrLocalPref, + bgp4PathAttrAtomicAggregate, + bgp4PathAttrAggregatorAS, + bgp4PathAttrAggregatorAddr, + bgp4PathAttrCalcLocalPref, + bgp4PathAttrBest, + bgp4PathAttrUnknown } + STATUS current + DESCRIPTION + "A collection of objects for managing + BGP path entries." + ::= { bgpMIBGroups 4 } + + bgp4MIBNotificationGroup NOTIFICATION-GROUP + NOTIFICATIONS { bgpEstablished, + bgpBackwardTransition } + STATUS current + DESCRIPTION + "A collection of notifications for signaling + changes in BGP peer relationships." + ::= { bgpMIBGroups 5 } + + END diff --git a/bgpd/IMPLEMENTATION.txt b/bgpd/IMPLEMENTATION.txt new file mode 100644 index 0000000..fff360a --- /dev/null +++ b/bgpd/IMPLEMENTATION.txt @@ -0,0 +1,169 @@ +$Id: IMPLEMENTATION.txt,v 1.2 2005/02/15 17:10:03 gdt Exp $ + +This file contains notes about the internals of the BGP +implementation. The initial impetus is understanding the memory usage +of Quagga'a BGP implementation. There may be some inaccuracies; it is +in the repository in the hopes that it will be significantly more +helpful than not. + +* FILES + +bgp_advertise.[hc]: + data structures: advertised prefixes, attributes + +bgp_aspath.[hc]: + struct aspath: + These are stored in a hash, apparently in wire format. + +bgp_attr.[hc]: + struct attr: contains all attributes + size(ILP32) 26 words/104 bytes (poor packing, v6/multicast is 10) + + bgp_attr_parse: origin, aspath, next hop probably most of interest + bgp_attr_origin: set flag bit + bgp_attr_aspath: put in refcounted hash table, so share pointer + bgp_attr_nexthop: store in attribute structure + +bgp_btoa.c: ? test program + +bgp_clist.[hc]: + data structures: community lists (including permit/deny state) + +bgp_community.[hc]: + data structures: community atttributes (multiple communities per struct) + +bgp_damp.[hc]: + per-route damping data, and damping control information + +bgp_debug.[hc]: + debugging support (vty config, dump of packets) + +bgp_dump.[hc]: + MRT-compatible dump format routines + +bgp_ecommunity.[hc]: + Extended communities attributes (multiple ecommmunities per struct) + +bgp_filter.[hc]: + AS path access list filtering + +bgp_fsm.[hc]: + Per-peer state machine for TCP connection, hold time, etc. + +bgp_main.c: + Daemon startup. + +bgp_mplsvpn.[hc]: + parsing of attribute structures for MPLS VPNs [need better description] + +bgp_network.[hc]: + Opening and binding of sockets, finding addresses for interfaces + +bgp_nexthop.[hc]: + data structures: Nexthop cache [not clear how used, if truly cache + in sense of memoization, or something else] + + importing EGP routes into IGP (thread created) + "scanning" (thread created) + bgp_scan: has useful clues to data structure complexity. Scanning + process iterates over database of received advertisements, and + builds 'cache' structure. + +bgp_open.[ch]: + Open messages, and capability negotiation + +bgp_packet.[hc] + sending and receiving of UPDATE/WITHDRAW + collision resolution for simultanteous opens + bgp_read: top-level read routine: reads whole packet (nonblocking) + and dispatches to per-message-type receive + + bgp_update_receive: + calls bgp_attr_parse + reads nrli into struct bgp_nrli update + + uninterning of aspath, community, ecommmunity, cluster, + transit which were interned in bgp_attr_parse + +bgp_regex.[ch]: + Glue to convert BGP regexps to standard (_ means many things). + +bgp_route.[hc]: + data structures: routes as received, static routes + Application of filters. Lots of route processing. + + bgp_nlri_parse: + sanity checks, then calls bgp_update with peer, prefix, attributes pointer + + bgp_update: bgp_update_main, then RS processing + + bgp_update_main: + find 'struct bgp_node *' for this afi/safi + look for route in table, then 'intern' attributes + ** interning is process of + looking for data in hash table, and putting there if missing, refcnt + using pointer to existing data + many validity checks + get new struct bgp_info (10 words/40 bytes) + call bgp_info_add with rn and bgp_info + call bgp_process + +bgp_routemap.c + implementation of route maps (match and set) + +bgp_snmp.c + SNMP glue. Not particularly interesting except to add variables or + debug SNMP. + +bgp_table.[hc] + data structures: struct bgp_table, struct bgp_node + allocation/lookup/utility operations - not a lot of protocol processin + +bgp_vty.[hc] + protocol-wide vty hooks + +bgp_zebra.[hc] + Processing interface events from zebra, redistribution of routes. + +bgpd.h + struct bgp_master: daemon main data structure + struct bgp: per-instance structure + struct peer_group + struct bgp_notify: (in-core representation of wire format?) + struct bgp_nexthop: (v4 and v6 addresses, *ifp) + struct bgp_rd: router distinguisher: 8 octects + struct bgp_filter: distribute, prefix, aslist, route_maps + struct peer: neighbor structure (very rich/complex) + struct bgp_nlri: reference to wire format + #define of protocol constants + attribute type codes + fsm states/events + timer values + +bgpd.c + instance/peer allocation + configuration + initialization/termination + +* DATA STRUCTURE SIZES + +Question: How much memory does quagga's bgpd use as a function of +state received from peers? + +It seems that a struct bgp_info is kept for each prefix. The "struct +attr *" is interned, and variables within that are interned. So, 40 +bytes are kept per received prefix, plus interned shared values. This +could be 36 if 'int suppress' where changed to a u_char and moved to +be with the other u_chars. Without MPLS, this could be 32 bytes. +Note that 8 bytes of this is linked list overhead, meaning that 24 +bytes are the raw per-prefix storage requirements. + +Also, a struct bgp_damp_info is apparently maintained per route; this +is fairly large (about 44 bytes). + +[TODO: the role of struct bgp_node.] + +* TIME COMPLEXITY + +It appears that received prefixes from each peer are stored in a +linked list. diff --git a/bgpd/Makefile.am b/bgpd/Makefile.am new file mode 100644 index 0000000..753b679 --- /dev/null +++ b/bgpd/Makefile.am @@ -0,0 +1,41 @@ +## Process this file with automake to produce Makefile.in. + +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 = $(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_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_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 + +EXTRA_DIST = BGP4-MIB.txt + diff --git a/bgpd/bgp_advertise.c b/bgpd/bgp_advertise.c new file mode 100644 index 0000000..1b17b66 --- /dev/null +++ b/bgpd/bgp_advertise.c @@ -0,0 +1,423 @@ +/* BGP advertisement and adjacency + Copyright (C) 1996, 97, 98, 99, 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 "memory.h" +#include "prefix.h" +#include "hash.h" +#include "thread.h" +#include "filter.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_table.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_advertise.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_aspath.h" +#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. */ +static struct bgp_advertise_attr * +baa_new (void) +{ + return (struct bgp_advertise_attr *) + XCALLOC (MTYPE_BGP_ADVERTISE_ATTR, sizeof (struct bgp_advertise_attr)); +} + +static void +baa_free (struct bgp_advertise_attr *baa) +{ + XFREE (MTYPE_BGP_ADVERTISE_ATTR, baa); +} + +static void * +baa_hash_alloc (void *p) +{ + struct bgp_advertise_attr * ref = (struct bgp_advertise_attr *) p; + struct bgp_advertise_attr *baa; + + baa = baa_new (); + baa->attr = ref->attr; + return baa; +} + +static unsigned int +baa_hash_key (void *p) +{ + struct bgp_advertise_attr * baa = (struct bgp_advertise_attr *) p; + + return attrhash_key_make (baa->attr); +} + +static int +baa_hash_cmp (const void *p1, const void *p2) +{ + const struct bgp_advertise_attr * baa1 = p1; + const struct bgp_advertise_attr * baa2 = 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. */ +static struct bgp_advertise * +bgp_advertise_new (void) +{ + return (struct bgp_advertise *) + XCALLOC (MTYPE_BGP_ADVERTISE, sizeof (struct bgp_advertise)); +} + +static void +bgp_advertise_free (struct bgp_advertise *adv) +{ + if (adv->binfo) + bgp_info_unlock (adv->binfo); /* bgp_advertise bgp_info reference */ + XFREE (MTYPE_BGP_ADVERTISE, adv); +} + +static void +bgp_advertise_add (struct bgp_advertise_attr *baa, + struct bgp_advertise *adv) +{ + adv->next = baa->adv; + if (baa->adv) + baa->adv->prev = adv; + baa->adv = adv; +} + +static void +bgp_advertise_delete (struct bgp_advertise_attr *baa, + struct bgp_advertise *adv) +{ + if (adv->next) + adv->next->prev = adv->prev; + if (adv->prev) + adv->prev->next = adv->next; + else + baa->adv = adv->next; +} + +static struct bgp_advertise_attr * +bgp_advertise_intern (struct hash *hash, struct attr *attr) +{ + struct bgp_advertise_attr ref; + struct bgp_advertise_attr *baa; + + ref.attr = bgp_attr_intern (attr); + baa = (struct bgp_advertise_attr *) hash_get (hash, &ref, baa_hash_alloc); + baa->refcnt++; + + return baa; +} + +static void +bgp_advertise_unintern (struct hash *hash, struct bgp_advertise_attr *baa) +{ + if (baa->refcnt) + baa->refcnt--; + + if (baa->refcnt && baa->attr) + bgp_attr_unintern (&baa->attr); + else + { + if (baa->attr) + { + hash_release (hash, baa); + bgp_attr_unintern (&baa->attr); + } + baa_free (baa); + } +} + +/* BGP adjacency keeps minimal advertisement information. */ +static void +bgp_adj_out_free (struct bgp_adj_out *adj) +{ + peer_unlock (adj->peer); /* adj_out peer reference */ + XFREE (MTYPE_BGP_ADJ_OUT, adj); +} + +int +bgp_adj_out_lookup (struct peer *peer, struct prefix *p, + afi_t afi, safi_t safi, struct bgp_node *rn) +{ + struct bgp_adj_out *adj; + + for (adj = rn->adj_out; adj; adj = adj->next) + if (adj->peer == peer) + break; + + if (! adj) + return 0; + + return (adj->adv + ? (adj->adv->baa ? 1 : 0) + : (adj->attr ? 1 : 0)); +} + +struct bgp_advertise * +bgp_advertise_clean (struct peer *peer, struct bgp_adj_out *adj, + afi_t afi, safi_t safi) +{ + 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) + { + /* Unlink myself from advertise attribute FIFO. */ + bgp_advertise_delete (baa, adv); + + /* Fetch next advertise candidate. */ + next = baa->adv; + + /* 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. */ + BGP_ADV_FIFO_DEL (fhead, adv); + + /* Free memory. */ + bgp_advertise_free (adj->adv); + adj->adv = NULL; + + return next; +} + +void +bgp_adj_out_set (struct bgp_node *rn, struct peer *peer, struct prefix *p, + struct attr *attr, afi_t afi, safi_t safi, + struct bgp_info *binfo) +{ + struct bgp_adj_out *adj = NULL; + struct bgp_advertise *adv; + + if (DISABLE_BGP_ANNOUNCE) + return; + + /* Look for adjacency information. */ + if (rn) + { + for (adj = rn->adj_out; adj; adj = adj->next) + if (adj->peer == peer) + break; + } + + if (! adj) + { + adj = XCALLOC (MTYPE_BGP_ADJ_OUT, sizeof (struct bgp_adj_out)); + adj->peer = peer_lock (peer); /* adj_out peer reference */ + + if (rn) + { + BGP_ADJ_OUT_ADD (rn, adj); + bgp_lock_node (rn); + } + } + + if (adj->adv) + bgp_advertise_clean (peer, adj, afi, safi); + + adj->adv = bgp_advertise_new (); + + adv = adj->adv; + adv->rn = rn; + + assert (adv->binfo == NULL); + adv->binfo = bgp_info_lock (binfo); /* bgp_info adj_out reference */ + + if (attr) + adv->baa = bgp_advertise_intern (peer->hash[afi][safi], attr); + else + adv->baa = baa_new (); + adv->adj = adj; + + /* Add new advertisement to advertisement attribute list. */ + bgp_advertise_add (adv->baa, adv); + + BGP_ADV_FIFO_ADD (&peer->sync[afi][safi]->update, &adv->fifo); +} + +void +bgp_adj_out_unset (struct bgp_node *rn, struct peer *peer, struct prefix *p, + afi_t afi, safi_t safi) +{ + struct bgp_adj_out *adj; + struct bgp_advertise *adv; + + if (DISABLE_BGP_ANNOUNCE) + return; + + /* Lookup existing adjacency, if it is not there return immediately. */ + for (adj = rn->adj_out; adj; adj = adj->next) + if (adj->peer == peer) + break; + + if (! adj) + return; + + /* Clearn up previous advertisement. */ + if (adj->adv) + bgp_advertise_clean (peer, adj, afi, safi); + + if (adj->attr) + { + /* We need advertisement structure. */ + adj->adv = bgp_advertise_new (); + adv = adj->adv; + adv->rn = rn; + adv->adj = adj; + + /* Add to synchronization entry for withdraw announcement. */ + BGP_ADV_FIFO_ADD (&peer->sync[afi][safi]->withdraw, &adv->fifo); + + /* Schedule packet write. */ + BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); + } + else + { + /* Remove myself from adjacency. */ + BGP_ADJ_OUT_DEL (rn, adj); + + /* Free allocated information. */ + bgp_adj_out_free (adj); + + bgp_unlock_node (rn); + } +} + +void +bgp_adj_out_remove (struct bgp_node *rn, struct bgp_adj_out *adj, + struct peer *peer, afi_t afi, safi_t safi) +{ + if (adj->attr) + bgp_attr_unintern (&adj->attr); + + if (adj->adv) + bgp_advertise_clean (peer, adj, afi, safi); + + 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) +{ + struct bgp_adj_in *adj; + + for (adj = rn->adj_in; adj; adj = adj->next) + { + if (adj->peer == peer) + { + if (adj->attr != attr) + { + bgp_attr_unintern (&adj->attr); + adj->attr = bgp_attr_intern (attr); + } + return; + } + } + adj = XCALLOC (MTYPE_BGP_ADJ_IN, sizeof (struct bgp_adj_in)); + adj->peer = peer_lock (peer); /* adj_in peer reference */ + adj->attr = bgp_attr_intern (attr); + BGP_ADJ_IN_ADD (rn, adj); + bgp_lock_node (rn); +} + +void +bgp_adj_in_remove (struct bgp_node *rn, struct bgp_adj_in *bai) +{ + bgp_attr_unintern (&bai->attr); + BGP_ADJ_IN_DEL (rn, bai); + peer_unlock (bai->peer); /* adj_in peer reference */ + XFREE (MTYPE_BGP_ADJ_IN, bai); +} + +int +bgp_adj_in_unset (struct bgp_node *rn, struct peer *peer) +{ + struct bgp_adj_in *adj; + + for (adj = rn->adj_in; adj; adj = adj->next) + if (adj->peer == peer) + break; + + if (! adj) + return 0; + + bgp_adj_in_remove (rn, adj); + bgp_unlock_node (rn); + return 1; +} + +void +bgp_sync_init (struct peer *peer) +{ + afi_t afi; + safi_t safi; + struct bgp_synchronize *sync; + + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + { + sync = XCALLOC (MTYPE_BGP_SYNCHRONISE, + sizeof (struct bgp_synchronize)); + 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); + } +} + +void +bgp_sync_delete (struct peer *peer) +{ + afi_t afi; + safi_t safi; + + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + { + if (peer->sync[afi][safi]) + XFREE (MTYPE_BGP_SYNCHRONISE, peer->sync[afi][safi]); + peer->sync[afi][safi] = NULL; + + if (peer->hash[afi][safi]) + hash_free (peer->hash[afi][safi]); + peer->hash[afi][safi] = NULL; + } +} diff --git a/bgpd/bgp_advertise.h b/bgpd/bgp_advertise.h new file mode 100644 index 0000000..e2857a3 --- /dev/null +++ b/bgpd/bgp_advertise.h @@ -0,0 +1,173 @@ +/* BGP advertisement and adjacency + Copyright (C) 1996, 97, 98, 99, 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. */ + +#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. */ +struct bgp_advertise_attr +{ + /* Head of advertisement pointer. */ + struct bgp_advertise *adv; + + /* Reference counter. */ + unsigned long refcnt; + + /* Attribute pointer to be announced. */ + struct attr *attr; +}; + +struct bgp_advertise +{ + /* FIFO for advertisement. */ + struct fifo fifo; + + /* Link list for same attribute advertise. */ + struct bgp_advertise *next; + struct bgp_advertise *prev; + + /* Prefix information. */ + struct bgp_node *rn; + + /* Reference pointer. */ + struct bgp_adj_out *adj; + + /* Advertisement attribute. */ + struct bgp_advertise_attr *baa; + + /* BGP info. */ + struct bgp_info *binfo; +}; + +/* BGP adjacency out. */ +struct bgp_adj_out +{ + /* Lined list pointer. */ + struct bgp_adj_out *next; + struct bgp_adj_out *prev; + + /* Advertised peer. */ + struct peer *peer; + + /* Advertised attribute. */ + struct attr *attr; + + /* Advertisement information. */ + struct bgp_advertise *adv; +}; + +/* BGP adjacency in. */ +struct bgp_adj_in +{ + /* Linked list pointer. */ + struct bgp_adj_in *next; + struct bgp_adj_in *prev; + + /* Received peer. */ + struct peer *peer; + + /* Received attribute. */ + struct attr *attr; +}; + +/* BGP advertisement list. */ +struct bgp_synchronize +{ + 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 { \ + (A)->prev = NULL; \ + (A)->next = (N)->TYPE; \ + if ((N)->TYPE) \ + (N)->TYPE->prev = (A); \ + (N)->TYPE = (A); \ + } while (0) + +#define BGP_INFO_DEL(N,A,TYPE) \ + do { \ + if ((A)->next) \ + (A)->next->prev = (A)->prev; \ + if ((A)->prev) \ + (A)->prev->next = (A)->next; \ + else \ + (N)->TYPE = (A)->next; \ + } while (0) + +#define BGP_ADJ_IN_ADD(N,A) BGP_INFO_ADD(N,A,adj_in) +#define BGP_ADJ_IN_DEL(N,A) BGP_INFO_DEL(N,A,adj_in) +#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 *); +extern void bgp_adj_out_unset (struct bgp_node *, struct peer *, struct prefix *, + afi_t, safi_t); +extern void bgp_adj_out_remove (struct bgp_node *, struct bgp_adj_out *, + struct peer *, afi_t, safi_t); +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 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 * +bgp_advertise_clean (struct peer *, struct bgp_adj_out *, afi_t, safi_t); + +extern void bgp_sync_init (struct peer *); +extern void bgp_sync_delete (struct peer *); + +#endif /* _QUAGGA_BGP_ADVERTISE_H */ diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c new file mode 100644 index 0000000..d813bfb --- /dev/null +++ b/bgpd/bgp_aspath.c @@ -0,0 +1,2076 @@ +/* AS path management routines. + Copyright (C) 1996, 97, 98, 99 Kunihiro Ishiguro + Copyright (C) 2005 Sun Microsystems, 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 "hash.h" +#include "memory.h" +#include "vector.h" +#include "vty.h" +#include "str.h" +#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 + +/* Now FOUR octets are used for AS value. */ +#define AS_VALUE_SIZE sizeof (as_t) +/* This is the old one */ +#define AS16_VALUE_SIZE sizeof (as16_t) + +/* Maximum protocol segment length value */ +#define AS_SEGMENT_MAX 255 + +/* The following length and size macros relate specifically to Quagga's + * internal representation of AS-Segments, not per se to the on-wire + * sizes and lengths. At present (200508) they sort of match, however + * the ONLY functions which should now about the on-wire syntax are + * aspath_put, assegment_put and assegment_parse. + * + * aspath_put returns bytes written, the only definitive record of + * size of wire-format attribute.. + */ + +/* Calculated size in bytes of ASN segment data to hold N ASN's */ +#define ASSEGMENT_DATA_SIZE(N,S) \ + ((N) * ( (S) ? AS_VALUE_SIZE : AS16_VALUE_SIZE) ) + +/* Calculated size of segment struct to hold N ASN's */ +#define ASSEGMENT_SIZE(N,S) (AS_HEADER_SIZE + ASSEGMENT_DATA_SIZE (N,S)) + +/* AS segment octet length. */ +#define ASSEGMENT_LEN(X,S) ASSEGMENT_SIZE((X)->length,S) + +/* AS_SEQUENCE segments can be packed together */ +/* Can the types of X and Y be considered for packing? */ +#define ASSEGMENT_TYPES_PACKABLE(X,Y) \ + ( ((X)->type == (Y)->type) \ + && ((X)->type == AS_SEQUENCE)) +/* Types and length of X,Y suitable for packing? */ +#define ASSEGMENTS_PACKABLE(X,Y) \ + ( ASSEGMENT_TYPES_PACKABLE( (X), (Y)) \ + && ( ((X)->length + (Y)->length) <= AS_SEGMENT_MAX ) ) + +/* As segment header - the on-wire representation + * NOT the internal representation! + */ +struct assegment_header +{ + u_char type; + u_char length; +}; + +/* Hash for aspath. This is the top level structure of AS path. */ +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 (XMALLOC (MTYPE_AS_SEG_DATA, ASSEGMENT_DATA_SIZE (num, 1))); +} + +static void +assegment_data_free (as_t *asdata) +{ + XFREE (MTYPE_AS_SEG_DATA, asdata); +} + +/* Get a new segment. Note that 0 is an allowed length, + * and will result in a segment with no allocated data segment. + * the caller should immediately assign data to the segment, as the segment + * otherwise is not generally valid + */ +static struct assegment * +assegment_new (u_char type, u_short length) +{ + struct assegment *new; + + new = XCALLOC (MTYPE_AS_SEG, sizeof (struct assegment)); + + if (length) + new->as = assegment_data_new (length); + + new->length = length; + new->type = type; + + return new; +} + +static void +assegment_free (struct assegment *seg) +{ + if (!seg) + return; + + if (seg->as) + assegment_data_free (seg->as); + memset (seg, 0xfe, sizeof(struct assegment)); + XFREE (MTYPE_AS_SEG, seg); + + return; +} + +/* free entire chain of segments */ +static void +assegment_free_all (struct assegment *seg) +{ + struct assegment *prev; + + while (seg) + { + prev = seg; + seg = seg->next; + assegment_free (prev); + } +} + +/* Duplicate just the given assegment and its data */ +static struct assegment * +assegment_dup (struct assegment *seg) +{ + struct assegment *new; + + new = assegment_new (seg->type, seg->length); + memcpy (new->as, seg->as, ASSEGMENT_DATA_SIZE (new->length, 1) ); + + return new; +} + +/* Duplicate entire chain of assegments, return the head */ +static struct assegment * +assegment_dup_all (struct assegment *seg) +{ + struct assegment *new = NULL; + struct assegment *head = NULL; + + while (seg) + { + if (head) + { + new->next = assegment_dup (seg); + new = new->next; + } + else + head = new = assegment_dup (seg); + + seg = seg->next; + } + return head; +} + +/* prepend the as number to given segment, given num of times */ +static struct assegment * +assegment_prepend_asns (struct assegment *seg, as_t asnum, int num) +{ + as_t *newas; + int i; + + if (!num) + return seg; + + if (num >= AS_SEGMENT_MAX) + return seg; /* we don't do huge prepends */ + + if ((newas = assegment_data_new (seg->length + num)) == NULL) + return seg; + + for (i = 0; i < num; i++) + newas[i] = asnum; + + 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 */ +static struct assegment * +assegment_append_asns (struct assegment *seg, as_t *asnos, int num) +{ + as_t *newas; + + newas = XREALLOC (MTYPE_AS_SEG_DATA, seg->as, + ASSEGMENT_DATA_SIZE (seg->length + num, 1)); + + if (newas) + { + seg->as = newas; + memcpy (seg->as + seg->length, asnos, ASSEGMENT_DATA_SIZE(num, 1)); + seg->length += num; + return seg; + } + + assegment_free_all (seg); + return NULL; +} + +static int +int_cmp (const void *p1, const void *p2) +{ + const as_t *as1 = p1; + const as_t *as2 = p2; + + return (*as1 == *as2) + ? 0 : ( (*as1 > *as2) ? 1 : -1); +} + +/* normalise the segment. + * In particular, merge runs of AS_SEQUENCEs into one segment + * Internally, we do not care about the wire segment length limit, and + * we want each distinct AS_PATHs to have the exact same internal + * representation - eg, so that our hashing actually works.. + */ +static struct assegment * +assegment_normalise (struct assegment *head) +{ + struct assegment *seg = head, *pin; + struct assegment *tmp; + + if (!head) + return head; + + while (seg) + { + pin = seg; + + /* Sort values SET segments, for determinism in paths to aid + * creation of hash values / path comparisons + * and because it helps other lesser implementations ;) + */ + if (seg->type == AS_SET || seg->type == AS_CONFED_SET) + { + int tail = 0; + int i; + + qsort (seg->as, seg->length, sizeof(as_t), int_cmp); + + /* weed out dupes */ + for (i=1; i < seg->length; i++) + { + if (seg->as[tail] == seg->as[i]) + continue; + + tail++; + if (tail < i) + seg->as[tail] = seg->as[i]; + } + /* seg->length can be 0.. */ + if (seg->length) + seg->length = tail + 1; + } + + /* read ahead from the current, pinned segment while the segments + * are packable/mergeable. Append all following packable segments + * to the segment we have pinned and remove these appended + * segments. + */ + while (pin->next && ASSEGMENT_TYPES_PACKABLE(pin, pin->next)) + { + tmp = pin->next; + seg = pin->next; + + /* append the next sequence to the pinned sequence */ + pin = assegment_append_asns (pin, seg->as, seg->length); + + /* bypass the next sequence */ + pin->next = seg->next; + + /* get rid of the now referenceless segment */ + assegment_free (tmp); + + } + + seg = pin->next; + } + return head; +} + +static struct aspath * +aspath_new (void) +{ + return XCALLOC (MTYPE_AS_PATH, sizeof (struct aspath)); +} + +/* Free AS path structure. */ +void +aspath_free (struct aspath *aspath) +{ + if (!aspath) + return; + if (aspath->segments) + assegment_free_all (aspath->segments); + if (aspath->str) + XFREE (MTYPE_AS_STR, aspath->str); + XFREE (MTYPE_AS_PATH, aspath); +} + +/* Unintern aspath from AS path bucket. */ +void +aspath_unintern (struct aspath **aspath) +{ + struct aspath *ret; + struct aspath *asp = *aspath; + + if (asp->refcnt) + asp->refcnt--; + + if (asp->refcnt == 0) + { + /* This aspath must exist in aspath hash table. */ + ret = hash_release (ashash, asp); + assert (ret != NULL); + aspath_free (asp); + *aspath = NULL; + } +} + +/* Return the start or end delimiters for a particular Segment type */ +#define AS_SEG_START 0 +#define AS_SEG_END 1 +static char +aspath_delimiter_char (u_char type, u_char which) +{ + int i; + struct + { + int type; + char start; + char end; + } aspath_delim_char [] = + { + { AS_SET, '{', '}' }, + { AS_CONFED_SET, '[', ']' }, + { AS_CONFED_SEQUENCE, '(', ')' }, + { 0 } + }; + + for (i = 0; aspath_delim_char[i].type != 0; i++) + { + if (aspath_delim_char[i].type == type) + { + if (which == AS_SEG_START) + return aspath_delim_char[i].start; + else if (which == AS_SEG_END) + return aspath_delim_char[i].end; + } + } + return ' '; +} + +/* countup asns from this segment and index onward */ +static int +assegment_count_asns (struct assegment *seg, int from) +{ + int count = 0; + while (seg) + { + if (!from) + count += seg->length; + else + { + count += (seg->length - from); + from = 0; + } + seg = seg->next; + } + return count; +} + +unsigned int +aspath_count_confeds (struct aspath *aspath) +{ + int count = 0; + struct assegment *seg = aspath->segments; + + while (seg) + { + if (seg->type == AS_CONFED_SEQUENCE) + count += seg->length; + else if (seg->type == AS_CONFED_SET) + count++; + + seg = seg->next; + } + return count; +} + +unsigned int +aspath_count_hops (const struct aspath *aspath) +{ + int count = 0; + struct assegment *seg = aspath->segments; + + while (seg) + { + if (seg->type == AS_SEQUENCE) + count += seg->length; + else if (seg->type == AS_SET) + count++; + + seg = seg->next; + } + return count; +} + +/* Estimate size aspath /might/ take if encoded into an + * ASPATH attribute. + * + * This is a quick estimate, not definitive! aspath_put() + * may return a different number!! + */ +unsigned int +aspath_size (struct aspath *aspath) +{ + int size = 0; + struct assegment *seg = aspath->segments; + + while (seg) + { + size += ASSEGMENT_SIZE(seg->length, 1); + seg = seg->next; + } + return size; +} + +/* Return highest public ASN in path */ +as_t +aspath_highest (struct aspath *aspath) +{ + struct assegment *seg = aspath->segments; + as_t highest = 0; + unsigned int i; + + while (seg) + { + for (i = 0; i < seg->length; i++) + 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) +{ + struct assegment *seg = aspath->segments; + unsigned int i; + + while (seg) + { + for (i = 0; i < seg->length; i++) + if (seg->as[i] > BGP_AS_MAX) + return 1; + seg = seg->next; + } + return 0; +} + +/* Convert aspath structure to string expression. */ +static void +aspath_make_str_count (struct aspath *as) +{ + struct assegment *seg; + int str_size; + int len = 0; + char *str_buf; + + /* Empty aspath. */ + if (!as->segments) + { + as->str = XMALLOC (MTYPE_AS_STR, 1); + as->str[0] = '\0'; + as->str_len = 0; + return; + } + + seg = as->segments; + + /* ASN takes 5 to 10 chars plus seperator, see below. + * If there is one differing segment type, we need an additional + * 2 chars for segment delimiters, and the final '\0'. + * Hopefully this is large enough to avoid hitting the realloc + * code below for most common sequences. + * + * This was changed to 10 after the well-known BGP assertion, which + * had hit some parts of the Internet in May of 2009. + */ +#define ASN_STR_LEN (10 + 1) + str_size = MAX (assegment_count_asns (seg, 0) * ASN_STR_LEN + 2 + 1, + ASPATH_STR_DEFAULT_LEN); + str_buf = XMALLOC (MTYPE_AS_STR, str_size); + + while (seg) + { + int i; + char seperator; + + /* Check AS type validity. Set seperator for segment */ + switch (seg->type) + { + case AS_SET: + case AS_CONFED_SET: + seperator = ','; + break; + case AS_SEQUENCE: + case AS_CONFED_SEQUENCE: + seperator = ' '; + break; + default: + XFREE (MTYPE_AS_STR, str_buf); + as->str = NULL; + as->str_len = 0; + return; + } + + /* We might need to increase str_buf, particularly if path has + * differing segments types, our initial guesstimate above will + * have been wrong. Need 10 chars for ASN, a seperator each and + * potentially two segment delimiters, plus a space between each + * segment and trailing zero. + * + * This definitely didn't work with the value of 5 bytes and + * 32-bit ASNs. + */ +#define SEGMENT_STR_LEN(X) (((X)->length * ASN_STR_LEN) + 2 + 1 + 1) + if ( (len + SEGMENT_STR_LEN(seg)) > str_size) + { + str_size = len + SEGMENT_STR_LEN(seg); + str_buf = XREALLOC (MTYPE_AS_STR, str_buf, str_size); + } +#undef ASN_STR_LEN +#undef SEGMENT_STR_LEN + + if (seg->type != AS_SEQUENCE) + len += snprintf (str_buf + len, str_size - len, + "%c", + aspath_delimiter_char (seg->type, AS_SEG_START)); + + /* write out the ASNs, with their seperators, bar the last one*/ + for (i = 0; i < seg->length; i++) + { + len += snprintf (str_buf + len, str_size - len, "%u", seg->as[i]); + + if (i < (seg->length - 1)) + len += snprintf (str_buf + len, str_size - len, "%c", seperator); + } + + if (seg->type != AS_SEQUENCE) + len += snprintf (str_buf + len, str_size - len, "%c", + aspath_delimiter_char (seg->type, AS_SEG_END)); + if (seg->next) + len += snprintf (str_buf + len, str_size - len, " "); + + seg = seg->next; + } + + assert (len < str_size); + + str_buf[len] = '\0'; + as->str = str_buf; + as->str_len = len; + + return; +} + +static void +aspath_str_update (struct aspath *as) +{ + if (as->str) + XFREE (MTYPE_AS_STR, as->str); + aspath_make_str_count (as); +} + +/* Intern allocated AS path. */ +struct aspath * +aspath_intern (struct aspath *aspath) +{ + struct aspath *find; + + /* 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++; + + return find; +} + +/* Duplicate aspath structure. Created same aspath structure but + reference count and AS path string is cleared. */ +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); + + 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; +} + +static void * +aspath_hash_alloc (void *arg) +{ + const struct aspath *aspath = arg; + struct aspath *new; + + /* Malformed AS path value. */ + assert (aspath->str); + if (! aspath->str) + return NULL; + + /* 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 */ +static int +assegments_parse (struct stream *s, size_t length, + struct assegment **result, int use32bit) +{ + struct assegment_header segh; + struct assegment *seg, *prev = NULL, *head = NULL; + size_t bytes = 0; + + /* empty aspath (ie iBGP or somesuch) */ + if (length == 0) + return 0; + + if (BGP_DEBUG (as4, AS4_SEGMENT)) + zlog_debug ("[AS4SEG] Parse aspath segment: got total byte length %lu", + (unsigned long) length); + /* basic checks */ + if ((STREAM_READABLE(s) < length) + || (STREAM_READABLE(s) < AS_HEADER_SIZE) + || (length % AS16_VALUE_SIZE )) + return -1; + + while (bytes < length) + { + int i; + size_t seg_size; + + if ((length - bytes) <= AS_HEADER_SIZE) + { + if (head) + assegment_free_all (head); + return -1; + } + + /* softly softly, get the header first on its own */ + segh.type = stream_getc (s); + segh.length = stream_getc (s); + + seg_size = ASSEGMENT_SIZE(segh.length, use32bit); + + if (BGP_DEBUG (as4, AS4_SEGMENT)) + zlog_debug ("[AS4SEG] Parse aspath segment: got type %d, length %d", + segh.type, segh.length); + + /* check it.. */ + if ( ((bytes + seg_size) > length) + /* 1771bis 4.3b: seg length contains one or more */ + || (segh.length == 0) + /* Paranoia in case someone changes type of segment length. + * Shift both values by 0x10 to make the comparison operate + * on more, than 8 bits (otherwise it's a warning, bug #564). + */ + || ((sizeof segh.length > 1) + && (0x10 + segh.length > 0x10 + AS_SEGMENT_MAX))) + { + if (head) + assegment_free_all (head); + return -1; + } + + switch (segh.type) + { + case AS_SEQUENCE: + case AS_SET: + case AS_CONFED_SEQUENCE: + case AS_CONFED_SET: + break; + default: + if (head) + assegment_free_all (head); + return -1; + } + + /* now its safe to trust lengths */ + seg = assegment_new (segh.type, segh.length); + + if (head) + prev->next = seg; + else /* it's the first segment */ + head = prev = seg; + + for (i = 0; i < segh.length; i++) + seg->as[i] = (use32bit) ? stream_getl (s) : stream_getw (s); + + bytes += seg_size; + + if (BGP_DEBUG (as4, AS4_SEGMENT)) + zlog_debug ("[AS4SEG] Parse aspath segment: Bytes now: %lu", + (unsigned long) bytes); + + prev = seg; + } + + *result = assegment_normalise (head); + return 0; +} + +/* AS path parse function. pnt is a pointer to byte stream and length + is length of byte stream. If there is same AS path in the the AS + path hash then return it else make new AS path structure. + + On error NULL is returned. + */ +struct aspath * +aspath_parse (struct stream *s, size_t length, int use32bit) +{ + struct aspath as; + struct aspath *find; + + /* If length is odd it's malformed AS path. */ + /* Nit-picking: if (use32bit == 0) it is malformed if odd, + * otherwise its malformed when length is larger than 2 and (length-2) + * is not dividable by 4. + * But... this time we're lazy + */ + if (length % AS16_VALUE_SIZE ) + return NULL; + + 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); + + /* 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; +} + +static void +assegment_data_put (struct stream *s, as_t *as, int num, int use32bit) +{ + int i; + assert (num <= AS_SEGMENT_MAX); + + for (i = 0; i < num; i++) + if ( use32bit ) + stream_putl (s, as[i]); + else + { + if ( as[i] <= BGP_AS_MAX ) + stream_putw(s, as[i]); + else + stream_putw(s, BGP_AS_TRANS); + } +} + +static size_t +assegment_header_put (struct stream *s, u_char type, int length) +{ + size_t lenp; + assert (length <= AS_SEGMENT_MAX); + stream_putc (s, type); + lenp = stream_get_endp (s); + stream_putc (s, length); + return lenp; +} + +/* write aspath data to stream */ +size_t +aspath_put (struct stream *s, struct aspath *as, int use32bit ) +{ + struct assegment *seg = as->segments; + size_t bytes = 0; + + if (!seg || seg->length == 0) + return 0; + + if (seg) + { + /* + * Hey, what do we do when we have > STREAM_WRITABLE(s) here? + * At the moment, we would write out a partial aspath, and our peer + * will complain and drop the session :-/ + * + * The general assumption here is that many things tested will + * never happen. And, in real live, up to now, they have not. + */ + while (seg && (ASSEGMENT_LEN(seg, use32bit) <= STREAM_WRITEABLE(s))) + { + struct assegment *next = seg->next; + int written = 0; + int asns_packed = 0; + size_t lenp; + + /* Overlength segments have to be split up */ + while ( (seg->length - written) > AS_SEGMENT_MAX) + { + 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 (AS_SEGMENT_MAX, use32bit); + } + + /* write the final segment, probably is also the first */ + lenp = assegment_header_put (s, seg->type, seg->length - written); + assegment_data_put (s, (seg->as + written), seg->length - written, + use32bit); + + /* Sequence-type segments can be 'packed' together + * Case of a segment which was overlength and split up + * will be missed here, but that doesn't matter. + */ + while (next && ASSEGMENTS_PACKABLE (seg, next)) + { + /* NB: We should never normally get here given we + * normalise aspath data when parse them. However, better + * safe than sorry. We potentially could call + * assegment_normalise here instead, but it's cheaper and + * easier to do it on the fly here rather than go through + * the segment list twice every time we write out + * aspath's. + */ + + /* Next segment's data can fit in this one */ + assegment_data_put (s, next->as, next->length, use32bit); + + /* update the length of the segment header */ + stream_putc_at (s, lenp, seg->length - written + next->length); + asns_packed += next->length; + + next = next->next; + } + + bytes += ASSEGMENT_SIZE (seg->length - written + asns_packed, + use32bit); + seg = next; + } + } + return bytes; +} + +/* This is for SNMP BGP4PATHATTRASPATHSEGMENT + * We have no way to manage the storage, so we use a static stream + * wrapper around aspath_put. + */ +u_char * +aspath_snmp_pathseg (struct aspath *as, size_t *varlen) +{ +#define SNMP_PATHSEG_MAX 1024 + + if (!snmp_stream) + snmp_stream = stream_new (SNMP_PATHSEG_MAX); + else + stream_reset (snmp_stream); + + if (!as) + { + *varlen = 0; + return NULL; + } + aspath_put (snmp_stream, as, 0); /* use 16 bit for now here */ + + *varlen = stream_get_endp (snmp_stream); + return stream_pnt(snmp_stream); +} + +#define min(A,B) ((A) < (B) ? (A) : (B)) + +static struct assegment * +aspath_aggregate_as_set_add (struct aspath *aspath, struct assegment *asset, + as_t as) +{ + int i; + + /* If this is first AS set member, create new as-set segment. */ + if (asset == NULL) + { + asset = assegment_new (AS_SET, 1); + if (! aspath->segments) + aspath->segments = asset; + else + { + struct assegment *seg = aspath->segments; + while (seg->next) + seg = seg->next; + seg->next = asset; + } + asset->type = AS_SET; + asset->length = 1; + asset->as[0] = as; + } + else + { + /* Check this AS value already exists or not. */ + for (i = 0; i < asset->length; i++) + if (asset->as[i] == as) + return asset; + + asset->length++; + asset->as = XREALLOC (MTYPE_AS_SEG_DATA, asset->as, + asset->length * AS_VALUE_SIZE); + asset->as[asset->length - 1] = as; + } + + + return asset; +} + +/* Modify as1 using as2 for aggregation. */ +struct aspath * +aspath_aggregate (struct aspath *as1, struct aspath *as2) +{ + int i; + int minlen; + int match; + int from; + 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; + /* We are moving on to the next segment to reset match */ + else + match = 0; + + seg1 = seg1->next; + seg2 = seg2->next; + } + + if (! aspath) + aspath = aspath_new(); + + /* Make as-set using rest of all information. */ + from = match; + while (seg1) + { + for (i = from; i < seg1->length; i++) + asset = aspath_aggregate_as_set_add (aspath, asset, seg1->as[i]); + + from = 0; + seg1 = seg1->next; + } + + from = match; + while (seg2) + { + for (i = from; i < seg2->length; i++) + asset = aspath_aggregate_as_set_add (aspath, asset, seg2->as[i]); + + from = 0; + seg2 = seg2->next; + } + + assegment_normalise (aspath->segments); + aspath_str_update (aspath); + 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. */ +int +aspath_firstas_check (struct aspath *aspath, as_t asno) +{ + if ( (aspath == NULL) || (aspath->segments == NULL) ) + return 0; + + if (aspath->segments + && (aspath->segments->type == AS_SEQUENCE) + && (aspath->segments->as[0] == asno )) + return 1; + + return 0; +} + +/* AS path loop check. If aspath contains asno then return >= 1. */ +int +aspath_loop_check (struct aspath *aspath, as_t asno) +{ + struct assegment *seg; + int count = 0; + + if ( (aspath == NULL) || (aspath->segments == NULL) ) + return 0; + + seg = aspath->segments; + + while (seg) + { + int i; + + for (i = 0; i < seg->length; i++) + if (seg->as[i] == asno) + count++; + + seg = seg->next; + } + return count; +} + +/* When all of AS path is private AS return 1. */ +int +aspath_private_as_check (struct aspath *aspath) +{ + struct assegment *seg; + + if ( !(aspath && aspath->segments) ) + return 0; + + seg = aspath->segments; + + while (seg) + { + int i; + + for (i = 0; i < seg->length; i++) + { + if (!BGP_AS_IS_PRIVATE(seg->as[i])) + return 0; + } + seg = seg->next; + } + return 1; +} + +/* AS path confed check. If aspath contains confed set or sequence then return 1. */ +int +aspath_confed_check (struct aspath *aspath) +{ + struct assegment *seg; + + if ( !(aspath && aspath->segments) ) + return 0; + + seg = aspath->segments; + + while (seg) + { + if (seg->type == AS_CONFED_SET || seg->type == AS_CONFED_SEQUENCE) + return 1; + seg = seg->next; + } + return 0; +} + +/* Leftmost AS path segment confed check. If leftmost AS segment is of type + AS_CONFED_SEQUENCE or AS_CONFED_SET then return 1. */ +int +aspath_left_confed_check (struct aspath *aspath) +{ + + if ( !(aspath && aspath->segments) ) + return 0; + + if ( (aspath->segments->type == AS_CONFED_SEQUENCE) + || (aspath->segments->type == AS_CONFED_SET) ) + return 1; + + return 0; +} + +/* Merge as1 to as2. as2 should be uninterned aspath. */ +static struct aspath * +aspath_merge (struct aspath *as1, struct aspath *as2) +{ + struct assegment *last, *new; + + if (! as1 || ! as2) + return NULL; + + last = new = assegment_dup_all (as1->segments); + + /* find the last valid segment */ + while (last && last->next) + last = last->next; + + last->next = as2->segments; + as2->segments = new; + aspath_str_update (as2); + return as2; +} + +/* Prepend as1 to as2. as2 should be uninterned aspath. */ +struct aspath * +aspath_prepend (struct aspath *as1, struct aspath *as2) +{ + struct assegment *seg1; + struct assegment *seg2; + + if (! as1 || ! as2) + return NULL; + + seg1 = as1->segments; + seg2 = as2->segments; + + /* If as2 is empty, only need to dupe as1's chain onto as2 */ + if (seg2 == NULL) + { + as2->segments = assegment_dup_all (as1->segments); + aspath_str_update (as2); + return as2; + } + + /* If as1 is empty AS, no prepending to do. */ + if (seg1 == NULL) + return as2; + + /* find the tail as1's segment chain. */ + while (seg1 && seg1->next) + seg1 = seg1->next; + + /* 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); + + if (seg1->type == AS_SEQUENCE) + { + /* We have two chains of segments, as1->segments and seg2, + * and we have to attach them together, merging the attaching + * segments together into one. + * + * 1. dupe as1->segments onto head of as2 + * 2. merge seg2's asns onto last segment of this new chain + * 3. attach chain after seg2 + */ + + /* dupe as1 onto as2's head */ + seg1 = as2->segments = assegment_dup_all (as1->segments); + + /* refind the tail of as2, reusing seg1 */ + while (seg1 && seg1->next) + seg1 = seg1->next; + + /* merge the old head, seg2, into tail, seg1 */ + seg1 = assegment_append_asns (seg1, seg2->as, seg2->length); + + /* bypass the merged seg2, and attach any chain after it to + * chain descending from as2's head + */ + seg1->next = seg2->next; + + /* seg2 is now referenceless and useless*/ + assegment_free (seg2); + + /* we've now prepended as1's segment chain to as2, merging + * the inbetween AS_SEQUENCE of seg2 in the process + */ + aspath_str_update (as2); + return as2; + } + else + { + /* AS_SET merge code is needed at here. */ + return aspath_merge (as1, as2); + } + /* XXX: Ermmm, what if as1 has multiple segments?? */ + + /* Not reached */ +} + +/* Iterate over AS_PATH segments and wipe all occurences of the + * listed AS numbers. Hence some segments may lose some or even + * all data on the way, the operation is implemented as a smarter + * version of aspath_dup(), which allocates memory to hold the new + * data, not the original. The new AS path is returned. + */ +struct aspath * +aspath_filter_exclude (struct aspath * source, struct aspath * exclude_list) +{ + struct assegment * srcseg, * exclseg, * lastseg; + struct aspath * newpath; + + newpath = aspath_new(); + lastseg = NULL; + + for (srcseg = source->segments; srcseg; srcseg = srcseg->next) + { + unsigned i, y, newlen = 0, done = 0, skip_as; + struct assegment * newseg; + + /* Find out, how much ASns are we going to pick from this segment. + * We can't perform filtering right inline, because the size of + * the new segment isn't known at the moment yet. + */ + for (i = 0; i < srcseg->length; i++) + { + skip_as = 0; + for (exclseg = exclude_list->segments; exclseg && !skip_as; exclseg = exclseg->next) + for (y = 0; y < exclseg->length; y++) + if (srcseg->as[i] == exclseg->as[y]) + { + skip_as = 1; + // There's no sense in testing the rest of exclusion list, bail out. + break; + } + if (!skip_as) + newlen++; + } + /* newlen is now the number of ASns to copy */ + if (!newlen) + continue; + + /* Actual copying. Allocate memory and iterate once more, performing filtering. */ + newseg = assegment_new (srcseg->type, newlen); + for (i = 0; i < srcseg->length; i++) + { + skip_as = 0; + for (exclseg = exclude_list->segments; exclseg && !skip_as; exclseg = exclseg->next) + for (y = 0; y < exclseg->length; y++) + if (srcseg->as[i] == exclseg->as[y]) + { + skip_as = 1; + break; + } + if (skip_as) + continue; + newseg->as[done++] = srcseg->as[i]; + } + /* At his point newlen must be equal to done, and both must be positive. Append + * the filtered segment to the gross result. */ + if (!lastseg) + newpath->segments = newseg; + else + lastseg->next = newseg; + lastseg = newseg; + } + aspath_str_update (newpath); + /* We are happy returning even an empty AS_PATH, because the administrator + * might expect this very behaviour. There's a mean to avoid this, if necessary, + * by having a match rule against certain AS_PATH regexps in the route-map index. + */ + aspath_free (source); + return newpath; +} + +/* Add specified AS to the leftmost of aspath. */ +static struct aspath * +aspath_add_asns (struct aspath *aspath, as_t asno, u_char type, unsigned num) +{ + struct assegment *assegment = aspath->segments; + unsigned i; + + if (assegment && assegment->type == type) + { + /* extend existing segment */ + aspath->segments = assegment_prepend_asns (aspath->segments, asno, num); + } + else + { + /* 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_asns (aspath, asno, AS_SEQUENCE, 1); +} + +/* Compare leftmost AS value for MED check. If as1's leftmost AS and + as2's leftmost AS is same return 1. */ +int +aspath_cmp_left (const struct aspath *aspath1, const struct aspath *aspath2) +{ + const struct assegment *seg1; + const struct assegment *seg2; + + if (!(aspath1 && aspath2)) + return 0; + + 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))) + seg1 = seg1->next; + + while (seg2 && ((seg2->type == AS_CONFED_SEQUENCE) + || (seg2->type == AS_CONFED_SET))) + seg2 = seg2->next; + + /* Check as1's */ + if (!(seg1 && seg2 + && (seg1->type == AS_SEQUENCE) && (seg2->type == AS_SEQUENCE))) + return 0; + + if (seg1->as[0] == seg2->as[0]) + return 1; + + return 0; +} + +/* Truncate an aspath after a number of hops, and put the hops remaining + * at the front of another aspath. Needed for AS4 compat. + * + * Returned aspath is a /new/ aspath, which should either by free'd or + * interned by the caller, as desired. + */ +struct aspath * +aspath_reconcile_as4 ( struct aspath *aspath, struct aspath *as4path) +{ + struct assegment *seg, *newseg, *prevseg = NULL; + struct aspath *newpath = NULL, *mergedpath; + int hops, cpasns = 0; + + if (!aspath) + return NULL; + + seg = aspath->segments; + + /* CONFEDs should get reconciled too.. */ + hops = (aspath_count_hops (aspath) + aspath_count_confeds (aspath)) + - aspath_count_hops (as4path); + + if (hops < 0) + { + if (BGP_DEBUG (as4, AS4)) + zlog_warn ("[AS4] Fewer hops in AS_PATH than NEW_AS_PATH"); + /* Something's gone wrong. The RFC says we should now ignore AS4_PATH, + * which is daft behaviour - it contains vital loop-detection + * information which must have been removed from AS_PATH. + */ + hops = aspath_count_hops (aspath); + } + + if (!hops) + return aspath_dup (as4path); + + if ( BGP_DEBUG(as4, AS4)) + zlog_debug("[AS4] got AS_PATH %s and AS4_PATH %s synthesizing now", + aspath->str, as4path->str); + + while (seg && hops > 0) + { + switch (seg->type) + { + case AS_SET: + case AS_CONFED_SET: + hops--; + cpasns = seg->length; + break; + case AS_CONFED_SEQUENCE: + /* Should never split a confed-sequence, if hop-count + * suggests we must then something's gone wrong somewhere. + * + * Most important goal is to preserve AS_PATHs prime function + * as loop-detector, so we fudge the numbers so that the entire + * confed-sequence is merged in. + */ + if (hops < seg->length) + { + if (BGP_DEBUG (as4, AS4)) + zlog_debug ("[AS4] AS4PATHmangle: AS_CONFED_SEQUENCE falls" + " across 2/4 ASN boundary somewhere, broken.."); + hops = seg->length; + } + case AS_SEQUENCE: + cpasns = MIN(seg->length, hops); + hops -= seg->length; + } + + assert (cpasns <= seg->length); + + newseg = assegment_new (seg->type, 0); + newseg = assegment_append_asns (newseg, seg->as, cpasns); + + if (!newpath) + { + newpath = aspath_new (); + newpath->segments = newseg; + } + else + prevseg->next = newseg; + + prevseg = newseg; + seg = seg->next; + } + + /* We may be able to join some segments here, and we must + * do this because... we want normalised aspaths in out hash + * and we do not want to stumble in aspath_put. + */ + mergedpath = aspath_merge (newpath, aspath_dup(as4path)); + aspath_free (newpath); + mergedpath->segments = assegment_normalise (mergedpath->segments); + aspath_str_update (mergedpath); + + if ( BGP_DEBUG(as4, AS4)) + zlog_debug ("[AS4] result of synthesizing is %s", + mergedpath->str); + + return mergedpath; +} + +/* Compare leftmost AS value for MED check. If as1's leftmost AS and + as2's leftmost AS is same return 1. (confederation as-path + only). */ +int +aspath_cmp_left_confed (const struct aspath *aspath1, const struct aspath *aspath2) +{ + if (! (aspath1 && aspath2) ) + return 0; + + if ( !(aspath1->segments && aspath2->segments) ) + return 0; + + if ( (aspath1->segments->type != AS_CONFED_SEQUENCE) + || (aspath2->segments->type != AS_CONFED_SEQUENCE) ) + return 0; + + if (aspath1->segments->as[0] == aspath2->segments->as[0]) + return 1; + + return 0; +} + +/* Delete all leading AS_CONFED_SEQUENCE/SET segments from aspath. + * See RFC3065, 6.1 c1 */ +struct aspath * +aspath_delete_confed_seq (struct aspath *aspath) +{ + struct assegment *seg; + + if (!(aspath && aspath->segments)) + return aspath; + + seg = aspath->segments; + + /* "if the first path segment of the AS_PATH is + * of type AS_CONFED_SEQUENCE," + */ + if (aspath->segments->type != AS_CONFED_SEQUENCE) + return aspath; + + /* "... that segment and any immediately following segments + * of the type AS_CONFED_SET or AS_CONFED_SEQUENCE are removed + * from the AS_PATH attribute," + */ + while (seg && + (seg->type == AS_CONFED_SEQUENCE || seg->type == AS_CONFED_SET)) + { + aspath->segments = seg->next; + assegment_free (seg); + seg = aspath->segments; + } + aspath_str_update (aspath); + return aspath; +} + +/* Add new AS number to the leftmost part of the aspath as + AS_CONFED_SEQUENCE. */ +struct aspath* +aspath_add_confed_seq (struct aspath *aspath, as_t asno) +{ + return aspath_add_asns (aspath, asno, AS_CONFED_SEQUENCE, 1); +} + +/* Add new as value to as path structure. */ +static void +aspath_as_add (struct aspath *as, as_t asno) +{ + struct assegment *seg = as->segments; + + if (!seg) + return; + + /* Last segment search procedure. */ + while (seg->next) + seg = seg->next; + + assegment_append_asns (seg, &asno, 1); +} + +/* Add new as segment to the as path. */ +static void +aspath_segment_add (struct aspath *as, int type) +{ + struct assegment *seg = as->segments; + struct assegment *new = assegment_new (type, 0); + + if (seg) + { + while (seg->next) + seg = seg->next; + seg->next = new; + } + else + as->segments = new; +} + +struct aspath * +aspath_empty (void) +{ + return aspath_parse (NULL, 0, 1); /* 32Bit ;-) */ +} + +struct aspath * +aspath_empty_get (void) +{ + struct aspath *aspath; + + aspath = aspath_new (); + aspath_make_str_count (aspath); + return aspath; +} + +unsigned long +aspath_count (void) +{ + return ashash->count; +} + +/* + Theoretically, one as path can have: + + One BGP packet size should be less than 4096. + One BGP attribute size should be less than 4096 - BGP header size. + One BGP aspath size should be less than 4096 - BGP header size - + BGP mandantry attribute size. +*/ + +/* AS path string lexical token enum. */ +enum as_token +{ + as_token_asval, + as_token_set_start, + as_token_set_end, + as_token_confed_seq_start, + as_token_confed_seq_end, + as_token_confed_set_start, + as_token_confed_set_end, + as_token_unknown +}; + +/* Return next token and point for string parse. */ +static const char * +aspath_gettoken (const char *buf, enum as_token *token, u_long *asno) +{ + const char *p = buf; + + /* Skip seperators (space for sequences, ',' for sets). */ + while (isspace ((int) *p) || *p == ',') + p++; + + /* Check the end of the string and type specify characters + (e.g. {}()). */ + switch (*p) + { + case '\0': + return NULL; + case '{': + *token = as_token_set_start; + p++; + return p; + case '}': + *token = as_token_set_end; + p++; + return p; + case '(': + *token = as_token_confed_seq_start; + p++; + return p; + case ')': + *token = as_token_confed_seq_end; + p++; + return p; + case '[': + *token = as_token_confed_set_start; + p++; + return p; + case ']': + *token = as_token_confed_set_end; + p++; + return p; + } + + /* Check actual AS value. */ + if (isdigit ((int) *p)) + { + as_t asval; + + *token = as_token_asval; + asval = (*p - '0'); + p++; + + while (isdigit ((int) *p)) + { + asval *= 10; + asval += (*p - '0'); + p++; + } + *asno = asval; + return p; + } + + /* There is no match then return unknown token. */ + *token = as_token_unknown; + return p++; +} + +struct aspath * +aspath_str2aspath (const char *str) +{ + enum as_token token = as_token_unknown; + u_short as_type; + u_long asno = 0; + struct aspath *aspath; + int needtype; + + aspath = aspath_new (); + + /* We start default type as AS_SEQUENCE. */ + as_type = AS_SEQUENCE; + needtype = 1; + + while ((str = aspath_gettoken (str, &token, &asno)) != NULL) + { + switch (token) + { + case as_token_asval: + if (needtype) + { + aspath_segment_add (aspath, as_type); + needtype = 0; + } + aspath_as_add (aspath, asno); + break; + case as_token_set_start: + as_type = AS_SET; + aspath_segment_add (aspath, as_type); + needtype = 0; + break; + case as_token_set_end: + as_type = AS_SEQUENCE; + needtype = 1; + break; + case as_token_confed_seq_start: + as_type = AS_CONFED_SEQUENCE; + aspath_segment_add (aspath, as_type); + needtype = 0; + break; + case as_token_confed_seq_end: + as_type = AS_SEQUENCE; + needtype = 1; + break; + case as_token_confed_set_start: + as_type = AS_CONFED_SET; + aspath_segment_add (aspath, as_type); + needtype = 0; + break; + case as_token_confed_set_end: + as_type = AS_SEQUENCE; + needtype = 1; + break; + case as_token_unknown: + default: + aspath_free (aspath); + return NULL; + } + } + + 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; + unsigned int key = 0; + + if (!aspath->str) + aspath_str_update (aspath); + + key = jhash (aspath->str, aspath->str_len, 2334325); + + return key; +} + +/* If two aspath have same value then return 1 else return 0 */ +int +aspath_cmp (const void *arg1, const void *arg2) +{ + const struct assegment *seg1 = ((const struct aspath *)arg1)->segments; + const struct assegment *seg2 = ((const struct aspath *)arg2)->segments; + + while (seg1 || seg2) + { + int i; + if ((!seg1 && seg2) || (seg1 && !seg2)) + return 0; + if (seg1->type != seg2->type) + return 0; + if (seg1->length != seg2->length) + return 0; + for (i = 0; i < seg1->length; i++) + if (seg1->as[i] != seg2->as[i]) + return 0; + seg1 = seg1->next; + seg2 = seg2->next; + } + return 1; +} + +/* AS path hash initialize. */ +void +aspath_init (void) +{ + 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) +{ + return (as ? as->str : NULL); +} + +/* Printing functions */ +/* Feed the AS_PATH to the vty; the suffix string follows it only in case + * AS_PATH wasn't empty. + */ +void +aspath_print_vty (struct vty *vty, const char *format, struct aspath *as, const char * suffix) +{ + assert (format); + vty_out (vty, format, as->str); + if (as->str_len && strlen (suffix)) + vty_out (vty, "%s", suffix); +} + +static void +aspath_show_all_iterator (struct hash_backet *backet, struct vty *vty) +{ + struct aspath *as; + + as = (struct aspath *) backet->data; + + vty_out (vty, "[%p:%u] (%ld) ", (void *)backet, backet->key, as->refcnt); + vty_out (vty, "%s%s", as->str, VTY_NEWLINE); +} + +/* Print all aspath and hash information. This function is used from + `show ip bgp paths' command. */ +void +aspath_print_all_vty (struct vty *vty) +{ + hash_iterate (ashash, + (void (*) (struct hash_backet *, void *)) + aspath_show_all_iterator, + vty); +} diff --git a/bgpd/bgp_aspath.h b/bgpd/bgp_aspath.h new file mode 100644 index 0000000..c8f929f --- /dev/null +++ b/bgpd/bgp_aspath.h @@ -0,0 +1,119 @@ +/* AS path related definitions. + 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 _QUAGGA_BGP_ASPATH_H +#define _QUAGGA_BGP_ASPATH_H + +/* AS path segment type. */ +#define AS_SET 1 +#define AS_SEQUENCE 2 +#define AS_CONFED_SEQUENCE 3 +#define AS_CONFED_SET 4 + +/* Private AS range defined in RFC2270. */ +#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 +{ + struct assegment *next; + as_t *as; + u_short length; + u_char type; +}; + +/* AS path may be include some AsSegments. */ +struct aspath +{ + /* Reference count to this aspath. */ + unsigned long refcnt; + + /* segment data */ + struct assegment *segments; + + /* 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 + +/* Prototypes. */ +extern void aspath_init (void); +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 *); +extern struct aspath *aspath_empty (void); +extern struct aspath *aspath_empty_get (void); +extern struct aspath *aspath_str2aspath (const char *); +extern void aspath_free (struct aspath *); +extern struct aspath *aspath_intern (struct aspath *); +extern void aspath_unintern (struct aspath **); +extern const char *aspath_print (struct aspath *); +extern void aspath_print_vty (struct vty *, const char *, struct aspath *, const char *); +extern void aspath_print_all_vty (struct vty *); +extern unsigned int aspath_key_make (void *); +extern int aspath_loop_check (struct aspath *, as_t); +extern int aspath_private_as_check (struct aspath *); +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 (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 *); +extern unsigned int aspath_has_as4 (struct aspath *); + +/* For SNMP BGP4PATHATTRASPATHSEGMENT, might be useful for debug */ +extern u_char *aspath_snmp_pathseg (struct aspath *, size_t *); + +#endif /* _QUAGGA_BGP_ASPATH_H */ diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c new file mode 100644 index 0000000..a79a03c --- /dev/null +++ b/bgpd/bgp_attr.c @@ -0,0 +1,3260 @@ +/* BGP attributes management routines. + Copyright (C) 1996, 97, 98, 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 "linklist.h" +#include "prefix.h" +#include "memory.h" +#include "vector.h" +#include "vty.h" +#include "stream.h" +#include "log.h" +#include "hash.h" +#include "jhash.h" +#include "filter.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_community.h" +#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 [] = +{ + { BGP_ATTR_ORIGIN, "ORIGIN" }, + { BGP_ATTR_AS_PATH, "AS_PATH" }, + { BGP_ATTR_NEXT_HOP, "NEXT_HOP" }, + { BGP_ATTR_MULTI_EXIT_DISC, "MULTI_EXIT_DISC" }, + { BGP_ATTR_LOCAL_PREF, "LOCAL_PREF" }, + { BGP_ATTR_ATOMIC_AGGREGATE, "ATOMIC_AGGREGATE" }, + { BGP_ATTR_AGGREGATOR, "AGGREGATOR" }, + { BGP_ATTR_COMMUNITIES, "COMMUNITY" }, + { BGP_ATTR_ORIGINATOR_ID, "ORIGINATOR_ID" }, + { BGP_ATTR_CLUSTER_LIST, "CLUSTER_LIST" }, + { BGP_ATTR_DPA, "DPA" }, + { BGP_ATTR_ADVERTISER, "ADVERTISER"} , + { BGP_ATTR_RCID_PATH, "RCID_PATH" }, + { BGP_ATTR_MP_REACH_NLRI, "MP_REACH_NLRI" }, + { BGP_ATTR_MP_UNREACH_NLRI, "MP_UNREACH_NLRI" }, + { BGP_ATTR_EXT_COMMUNITIES, "EXT_COMMUNITIES" }, + { 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 = array_size(attr_str); + +static const struct message attr_flag_str[] = +{ + { BGP_ATTR_FLAG_OPTIONAL, "Optional" }, + { BGP_ATTR_FLAG_TRANS, "Transitive" }, + { BGP_ATTR_FLAG_PARTIAL, "Partial" }, + /* bgp_attr_flags_diagnose() relies on this bit being last in this list */ + { BGP_ATTR_FLAG_EXTLEN, "Extended Length" }, +}; + +static struct hash *cluster_hash; + +static void * +cluster_hash_alloc (void *p) +{ + struct cluster_list * val = (struct cluster_list *) p; + struct cluster_list *cluster; + + cluster = XMALLOC (MTYPE_CLUSTER, sizeof (struct cluster_list)); + cluster->length = val->length; + + if (cluster->length) + { + cluster->list = XMALLOC (MTYPE_CLUSTER_VAL, val->length); + memcpy (cluster->list, val->list, val->length); + } + else + cluster->list = NULL; + + cluster->refcnt = 0; + + return cluster; +} + +/* Cluster list related functions. */ +static struct cluster_list * +cluster_parse (struct in_addr * pnt, int length) +{ + struct cluster_list tmp; + struct cluster_list *cluster; + + tmp.length = length; + tmp.list = pnt; + + cluster = hash_get (cluster_hash, &tmp, cluster_hash_alloc); + cluster->refcnt++; + return cluster; +} + +int +cluster_loop_check (struct cluster_list *cluster, struct in_addr originator) +{ + int i; + + for (i = 0; i < cluster->length / 4; i++) + if (cluster->list[i].s_addr == originator.s_addr) + return 1; + return 0; +} + +static unsigned int +cluster_hash_key_make (void *p) +{ + const struct cluster_list *cluster = p; + + return jhash(cluster->list, cluster->length, 0); +} + +static int +cluster_hash_cmp (const void *p1, const void *p2) +{ + const struct cluster_list * cluster1 = p1; + const struct cluster_list * cluster2 = p2; + + return (cluster1->length == cluster2->length && + memcmp (cluster1->list, cluster2->list, cluster1->length) == 0); +} + +static void +cluster_free (struct cluster_list *cluster) +{ + if (cluster->list) + XFREE (MTYPE_CLUSTER_VAL, cluster->list); + XFREE (MTYPE_CLUSTER, cluster); +} + +#if 0 +static struct cluster_list * +cluster_dup (struct cluster_list *cluster) +{ + struct cluster_list *new; + + new = XCALLOC (MTYPE_CLUSTER, sizeof (struct cluster_list)); + new->length = cluster->length; + + if (cluster->length) + { + new->list = XMALLOC (MTYPE_CLUSTER_VAL, cluster->length); + memcpy (new->list, cluster->list, cluster->length); + } + else + new->list = NULL; + + return new; +} +#endif + +static struct cluster_list * +cluster_intern (struct cluster_list *cluster) +{ + struct cluster_list *find; + + find = hash_get (cluster_hash, cluster, cluster_hash_alloc); + find->refcnt++; + + return find; +} + +void +cluster_unintern (struct cluster_list *cluster) +{ + if (cluster->refcnt) + cluster->refcnt--; + + if (cluster->refcnt == 0) + { + hash_release (cluster_hash, cluster); + cluster_free (cluster); + } +} + +static void +cluster_init (void) +{ + cluster_hash = hash_create (cluster_hash_key_make, cluster_hash_cmp); +} + +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; + +static void +transit_free (struct transit *transit) +{ + if (transit->val) + XFREE (MTYPE_TRANSIT_VAL, transit->val); + XFREE (MTYPE_TRANSIT, transit); +} + + +static void * +transit_hash_alloc (void *p) +{ + /* Transit structure is already allocated. */ + return p; +} + +static struct transit * +transit_intern (struct transit *transit) +{ + struct transit *find; + + find = hash_get (transit_hash, transit, transit_hash_alloc); + if (find != transit) + transit_free (transit); + find->refcnt++; + + return find; +} + +void +transit_unintern (struct transit *transit) +{ + if (transit->refcnt) + transit->refcnt--; + + if (transit->refcnt == 0) + { + hash_release (transit_hash, transit); + transit_free (transit); + } +} + +static unsigned int +transit_hash_key_make (void *p) +{ + const struct transit * transit = p; + + return jhash(transit->val, transit->length, 0); +} + +static int +transit_hash_cmp (const void *p1, const void *p2) +{ + const struct transit * transit1 = p1; + const struct transit * transit2 = p2; + + return (transit1->length == transit2->length && + memcmp (transit1->val, transit2->val, transit1->length) == 0); +} + +static void +transit_init (void) +{ + transit_hash = hash_create (transit_hash_key_make, transit_hash_cmp); +} + +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; + +static struct attr_extra * +bgp_attr_extra_new (void) +{ + return XCALLOC (MTYPE_ATTR_EXTRA, sizeof (struct attr_extra)); +} + +void +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; + } +} + +struct attr_extra * +bgp_attr_extra_get (struct attr *attr) +{ + if (!attr->extra) + attr->extra = bgp_attr_extra_new(); + return attr->extra; +} + +/* Shallow copy of an attribute + * Though, not so shallow that it doesn't copy the contents + * of the attr_extra pointed to by 'extra' + */ +void +bgp_attr_dup (struct attr *new, struct attr *orig) +{ + struct attr_extra *extra = new->extra; + + *new = *orig; + /* 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); + } + } +} + +unsigned long int +attr_count (void) +{ + return attrhash->count; +} + +unsigned long int +attr_unknown_count (void) +{ + return transit_hash->count; +} + +unsigned int +attrhash_key_make (void *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) + + MIX(attr->origin); + MIX(attr->nexthop.s_addr); + MIX(attr->med); + MIX(attr->local_pref); + + key += attr->origin; + key += attr->nexthop.s_addr; + key += attr->med; + key += attr->local_pref; + + if (extra) + { + 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) + MIX(aspath_key_make (attr->aspath)); + if (attr->community) + MIX(community_hash_make (attr->community)); + + if (extra) + { + 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)); + + 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; +} + +int +attrhash_cmp (const void *p1, const void *p2) +{ + const struct attr * attr1 = p1; + const struct attr * attr2 = p2; + + if (attr1->flag == attr2->flag + && attr1->origin == attr2->origin + && attr1->nexthop.s_addr == attr2->nexthop.s_addr + && attr1->aspath == attr2->aspath + && attr1->community == attr2->community + && attr1->med == attr2->med + && attr1->local_pref == attr2->local_pref) + { + const struct attr_extra *ae1 = attr1->extra; + const struct attr_extra *ae2 = attr2->extra; + + if (ae1 && ae2 + && ae1->aggregator_as == ae2->aggregator_as + && ae1->aggregator_addr.s_addr == ae2->aggregator_addr.s_addr + && ae1->weight == ae2->weight + && 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) + && 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->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; + /* neither attribute has extra attributes, so they're same */ + return 1; + } + else + return 0; +} + +static void +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; +} + +static void +attr_show_all_iterator (struct hash_backet *backet, struct vty *vty) +{ + struct attr *attr = backet->data; + + vty_out (vty, "attr[%ld] nexthop %s%s", attr->refcnt, + inet_ntoa (attr->nexthop), VTY_NEWLINE); +} + +void +attr_show_all (struct vty *vty) +{ + hash_iterate (attrhash, + (void (*)(struct hash_backet *, void *)) + attr_show_all_iterator, + vty); +} + +static void * +bgp_attr_hash_alloc (void *p) +{ + struct attr * val = (struct attr *) p; + struct attr *attr; + + attr = XMALLOC (MTYPE_ATTR, sizeof (struct attr)); + *attr = *val; + if (val->extra) + { + 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; +} + +/* Internet argument attribute. */ +struct attr * +bgp_attr_intern (struct attr *attr) +{ + struct attr *find; + + /* Intern referenced strucutre. */ + if (attr->aspath) + { + if (! attr->aspath->refcnt) + attr->aspath = aspath_intern (attr->aspath); + else + attr->aspath->refcnt++; + } + if (attr->community) + { + if (! attr->community->refcnt) + attr->community = community_intern (attr->community); + else + attr->community->refcnt++; + } + if (attr->extra) + { + struct attr_extra *attre = attr->extra; + + if (attre->ecommunity) + { + if (! attre->ecommunity->refcnt) + attre->ecommunity = ecommunity_intern (attre->ecommunity); + else + 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) + attre->cluster = cluster_intern (attre->cluster); + else + attre->cluster->refcnt++; + } + if (attre->transit) + { + if (! attre->transit->refcnt) + attre->transit = transit_intern (attre->transit); + else + attre->transit->refcnt++; + } + } + + find = (struct attr *) hash_get (attrhash, attr, bgp_attr_hash_alloc); + find->refcnt++; + + return find; +} + + +/* Make network statement's attribute. */ +struct attr * +bgp_attr_default_set (struct attr *attr, u_char origin) +{ + memset (attr, 0, sizeof (struct attr)); + bgp_attr_extra_get (attr); + + attr->origin = origin; + attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_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); + attr->extra->mp_nexthop_len = IPV6_MAX_BYTELEN; + + return attr; +} + + +/* Make network statement's attribute. */ +struct attr * +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); + bgp_attr_extra_free (&attr); + + aspath_unintern (&new->aspath); + 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, + u_char atomic_aggregate) +{ + struct attr attr; + struct attr *new; + struct attr_extra attre; + + memset (&attr, 0, sizeof (struct attr)); + memset (&attre, 0, sizeof (struct attr_extra)); + attr.extra = &attre; + + /* Origin attribute. */ + attr.origin = origin; + attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGIN); + + /* AS path attribute. */ + if (aspath) + attr.aspath = aspath_intern (aspath); + else + attr.aspath = aspath_empty (); + attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_AS_PATH); + + /* Next hop attribute. */ + attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP); + + if (community) + { + attr.community = community; + attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES); + } + + 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; + else + attre.aggregator_as = bgp->as; + attre.aggregator_addr = bgp->router_id; + + new = bgp_attr_intern (&attr); + + aspath_unintern (&new->aspath); + return new; +} + +/* Unintern just the sub-components of the attr, but not the attr */ +void +bgp_attr_unintern_sub (struct attr *attr) +{ + /* aspath refcount shoud be decrement. */ + if (attr->aspath) + aspath_unintern (&attr->aspath); + UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AS_PATH)); + + if (attr->community) + community_unintern (&attr->community); + 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, 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, ATTR_FLAG_BIT (BGP_ATTR_CLUSTER_LIST)); + + if (attr->extra->transit) + transit_unintern (attr->extra->transit); + } +} + +/* Free bgp attribute and aspath. */ +void +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--; + + tmp = *attr; + + if (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); + assert (ret != NULL); + bgp_attr_extra_free (attr); + XFREE (MTYPE_ATTR, attr); + *pattr = NULL; + } + + bgp_attr_unintern_sub (&tmp); +} + +void +bgp_attr_flush (struct attr *attr) +{ + if (attr->aspath && ! attr->aspath->refcnt) + { + aspath_free (attr->aspath); + attr->aspath = NULL; + } + if (attr->community && ! attr->community->refcnt) + { + 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); + attre->cluster = NULL; + } + if (attre->transit && ! attre->transit->refcnt) + { + transit_free (attre->transit); + attre->transit = NULL; + } + encap_free(attre->encap_subtlvs); + attre->encap_subtlvs = NULL; + } +} + +/* Implement draft-scudder-idr-optional-transitive behaviour and + * avoid resetting sessions for malformed attributes which are + * are partial/optional and hence where the error likely was not + * introduced by the sending neighbour. + */ +static bgp_attr_parse_ret_t +bgp_attr_malformed (struct bgp_attr_parser_args *args, u_char subcode, + bgp_size_t length) +{ + struct peer *const peer = args->peer; + const u_int8_t flags = args->flags; + /* startp and length must be special-cased, as whether or not to + * send the attribute data with the NOTIFY depends on the error, + * the caller therefore signals this with the seperate length argument + */ + u_char *notify_datap = (length > 0 ? args->startp : NULL); + + /* Only relax error handling for eBGP peers */ + if (peer->sort != BGP_PEER_EBGP) + { + bgp_notify_send_with_data (peer, BGP_NOTIFY_UPDATE_ERR, subcode, + notify_datap, length); + return BGP_ATTR_PARSE_ERROR; + + } + + /* Adjust the stream getp to the end of the attribute, in case we can + * still proceed but the caller hasn't read all the attribute. + */ + stream_set_getp (BGP_INPUT (peer), + (args->startp - STREAM_DATA (BGP_INPUT (peer))) + + args->total); + + switch (args->type) { + /* where an attribute is relatively inconsequential, e.g. it does not + * affect route selection, and can be safely ignored, then any such + * attributes which are malformed should just be ignored and the route + * processed as normal. + */ + case BGP_ATTR_AS4_AGGREGATOR: + case BGP_ATTR_AGGREGATOR: + case BGP_ATTR_ATOMIC_AGGREGATE: + return BGP_ATTR_PARSE_PROCEED; + + /* Core attributes, particularly ones which may influence route + * selection, should always cause session resets + */ + case BGP_ATTR_ORIGIN: + case BGP_ATTR_AS_PATH: + case BGP_ATTR_NEXT_HOP: + case BGP_ATTR_MULTI_EXIT_DISC: + case BGP_ATTR_LOCAL_PREF: + case BGP_ATTR_COMMUNITIES: + case BGP_ATTR_ORIGINATOR_ID: + case BGP_ATTR_CLUSTER_LIST: + case BGP_ATTR_MP_REACH_NLRI: + case BGP_ATTR_MP_UNREACH_NLRI: + case BGP_ATTR_EXT_COMMUNITIES: + bgp_notify_send_with_data (peer, BGP_NOTIFY_UPDATE_ERR, subcode, + notify_datap, length); + return BGP_ATTR_PARSE_ERROR; + } + + /* Partial optional attributes that are malformed should not cause + * the whole session to be reset. Instead treat it as a withdrawal + * of the routes, if possible. + */ + if (CHECK_FLAG (flags, BGP_ATTR_FLAG_TRANS) + && CHECK_FLAG (flags, BGP_ATTR_FLAG_OPTIONAL) + && CHECK_FLAG (flags, BGP_ATTR_FLAG_PARTIAL)) + return BGP_ATTR_PARSE_WITHDRAW; + + /* default to reset */ + return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; +} + +/* Find out what is wrong with the path attribute flag bits and log the error. + "Flag bits" here stand for Optional, Transitive and Partial, but not for + Extended Length. Checking O/T/P bits at once implies, that the attribute + being diagnosed is defined by RFC as either a "well-known" or an "optional, + non-transitive" attribute. */ +static void +bgp_attr_flags_diagnose (struct bgp_attr_parser_args *args, + u_int8_t desired_flags /* how RFC says it must be */ +) +{ + u_char seen = 0, i; + u_char real_flags = args->flags; + const u_int8_t attr_code = args->type; + + desired_flags &= ~BGP_ATTR_FLAG_EXTLEN; + real_flags &= ~BGP_ATTR_FLAG_EXTLEN; + for (i = 0; i <= 2; i++) /* O,T,P, but not E */ + if + ( + CHECK_FLAG (desired_flags, attr_flag_str[i].key) != + CHECK_FLAG (real_flags, attr_flag_str[i].key) + ) + { + zlog (args->peer->log, LOG_ERR, "%s attribute must%s be flagged as \"%s\"", + LOOKUP (attr_str, attr_code), + CHECK_FLAG (desired_flags, attr_flag_str[i].key) ? "" : " not", + attr_flag_str[i].str); + seen = 1; + } + if (!seen) + { + zlog (args->peer->log, LOG_DEBUG, + "Strange, %s called for attr %s, but no problem found with flags" + " (real flags 0x%x, desired 0x%x)", + __func__, LOOKUP (attr_str, attr_code), + real_flags, desired_flags); + } +} + +/* Required flags for attributes. EXTLEN will be masked off when testing, + * as will PARTIAL for optional+transitive attributes. + */ +const u_int8_t attr_flags_values [] = { + [BGP_ATTR_ORIGIN] = BGP_ATTR_FLAG_TRANS, + [BGP_ATTR_AS_PATH] = BGP_ATTR_FLAG_TRANS, + [BGP_ATTR_NEXT_HOP] = BGP_ATTR_FLAG_TRANS, + [BGP_ATTR_MULTI_EXIT_DISC] = BGP_ATTR_FLAG_OPTIONAL, + [BGP_ATTR_LOCAL_PREF] = BGP_ATTR_FLAG_TRANS, + [BGP_ATTR_ATOMIC_AGGREGATE] = BGP_ATTR_FLAG_TRANS, + [BGP_ATTR_AGGREGATOR] = BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL, + [BGP_ATTR_COMMUNITIES] = BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL, + [BGP_ATTR_ORIGINATOR_ID] = BGP_ATTR_FLAG_OPTIONAL, + [BGP_ATTR_CLUSTER_LIST] = BGP_ATTR_FLAG_OPTIONAL, + [BGP_ATTR_MP_REACH_NLRI] = BGP_ATTR_FLAG_OPTIONAL, + [BGP_ATTR_MP_UNREACH_NLRI] = BGP_ATTR_FLAG_OPTIONAL, + [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 = array_size(attr_flags_values) - 1; + +static int +bgp_attr_flag_invalid (struct bgp_attr_parser_args *args) +{ + u_int8_t mask = BGP_ATTR_FLAG_EXTLEN; + const u_int8_t flags = args->flags; + const u_int8_t attr_code = args->type; + struct peer *const peer = args->peer; + + /* there may be attributes we don't know about */ + if (attr_code > attr_flags_values_max) + return 0; + if (attr_flags_values[attr_code] == 0) + return 0; + + /* RFC4271, "For well-known attributes, the Transitive bit MUST be set to + * 1." + */ + if (!CHECK_FLAG (BGP_ATTR_FLAG_OPTIONAL, flags) + && !CHECK_FLAG (BGP_ATTR_FLAG_TRANS, flags)) + { + zlog (peer->log, LOG_ERR, + "%s well-known attributes must have transitive flag set (%x)", + LOOKUP (attr_str, attr_code), flags); + return 1; + } + + /* "For well-known attributes and for optional non-transitive attributes, + * the Partial bit MUST be set to 0." + */ + if (CHECK_FLAG (flags, BGP_ATTR_FLAG_PARTIAL)) + { + if (!CHECK_FLAG (flags, BGP_ATTR_FLAG_OPTIONAL)) + { + zlog (peer->log, LOG_ERR, + "%s well-known attribute " + "must NOT have the partial flag set (%x)", + LOOKUP (attr_str, attr_code), flags); + return 1; + } + if (CHECK_FLAG (flags, BGP_ATTR_FLAG_OPTIONAL) + && !CHECK_FLAG (flags, BGP_ATTR_FLAG_TRANS)) + { + zlog (peer->log, LOG_ERR, + "%s optional + transitive attribute " + "must NOT have the partial flag set (%x)", + LOOKUP (attr_str, attr_code), flags); + return 1; + } + } + + /* Optional transitive attributes may go through speakers that don't + * reocgnise them and set the Partial bit. + */ + if (CHECK_FLAG (flags, BGP_ATTR_FLAG_OPTIONAL) + && CHECK_FLAG (flags, BGP_ATTR_FLAG_TRANS)) + SET_FLAG (mask, BGP_ATTR_FLAG_PARTIAL); + + if ((flags & ~mask) + == attr_flags_values[attr_code]) + return 0; + + bgp_attr_flags_diagnose (args, attr_flags_values[attr_code]); + return 1; +} + +/* Get origin attribute of the update message. */ +static bgp_attr_parse_ret_t +bgp_attr_origin (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 any recognized attribute has Attribute Length that conflicts + with the expected length (based on the attribute type code), then + the Error Subcode is set to Attribute Length Error. The Data + field contains the erroneous attribute (type, length and + value). */ + if (length != 1) + { + zlog (peer->log, LOG_ERR, "Origin attribute length is not one %d", + length); + return bgp_attr_malformed (args, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); + } + + /* Fetch origin attribute. */ + attr->origin = stream_getc (BGP_INPUT (peer)); + + /* If the ORIGIN attribute has an undefined value, then the Error + Subcode is set to Invalid Origin Attribute. The Data field + contains the unrecognized attribute (type, length and value). */ + if ((attr->origin != BGP_ORIGIN_IGP) + && (attr->origin != BGP_ORIGIN_EGP) + && (attr->origin != BGP_ORIGIN_INCOMPLETE)) + { + zlog (peer->log, LOG_ERR, "Origin attribute value is invalid %d", + attr->origin); + return bgp_attr_malformed (args, + BGP_NOTIFY_UPDATE_INVAL_ORIGIN, + args->total); + } + + /* Set oring attribute flag. */ + attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGIN); + + return 0; +} + +/* Parse AS path information. This function is wrapper of + aspath_parse. */ +static int +bgp_attr_aspath (struct bgp_attr_parser_args *args) +{ + struct attr *const attr = args->attr; + struct peer *const peer = args->peer; + const bgp_size_t length = args->length; + + /* + * peer with AS4 => will get 4Byte ASnums + * otherwise, will get 16 Bit + */ + attr->aspath = aspath_parse (peer->ibuf, length, + CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV)); + + /* In case of IBGP, length will be zero. */ + if (! attr->aspath) + { + zlog (peer->log, LOG_ERR, + "Malformed AS path from %s, length is %d", + peer->host, length); + return bgp_attr_malformed (args, BGP_NOTIFY_UPDATE_MAL_AS_PATH, 0); + } + + /* Set aspath attribute flag. */ + attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AS_PATH); + + return BGP_ATTR_PARSE_PROCEED; +} + +static bgp_attr_parse_ret_t +bgp_attr_aspath_check (struct peer *const peer, struct attr *const attr) +{ + /* These checks were part of bgp_attr_aspath, but with + * as4 we should to check aspath things when + * aspath synthesizing with as4_path has already taken place. + * Otherwise we check ASPATH and use the synthesized thing, and that is + * not right. + * So do the checks later, i.e. here + */ + struct bgp *bgp = peer->bgp; + struct aspath *aspath; + + /* Confederation sanity check. */ + 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, + BGP_NOTIFY_UPDATE_MAL_AS_PATH); + return BGP_ATTR_PARSE_ERROR; + } + + /* First AS check for EBGP. */ + if (bgp != NULL && bgp_flag_check (bgp, BGP_FLAG_ENFORCE_FIRST_AS)) + { + if (peer->sort == BGP_PEER_EBGP + && ! aspath_firstas_check (attr->aspath, peer->as)) + { + zlog (peer->log, LOG_ERR, + "%s incorrect first AS (must be %u)", peer->host, peer->as); + bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MAL_AS_PATH); + return BGP_ATTR_PARSE_ERROR; + } + } + + /* local-as prepend */ + if (peer->change_local_as && + ! CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND)) + { + aspath = aspath_dup (attr->aspath); + aspath = aspath_add_seq (aspath, peer->change_local_as); + aspath_unintern (&attr->aspath); + attr->aspath = aspath_intern (aspath); + } + + return BGP_ATTR_PARSE_PROCEED; +} + +/* Parse AS4 path information. This function is another wrapper of + aspath_parse. */ +static int +bgp_attr_as4_path (struct bgp_attr_parser_args *args, struct aspath **as4_path) +{ + struct peer *const peer = args->peer; + struct attr *const attr = args->attr; + const bgp_size_t length = args->length; + + *as4_path = aspath_parse (peer->ibuf, length, 1); + + /* In case of IBGP, length will be zero. */ + if (!*as4_path) + { + zlog (peer->log, LOG_ERR, + "Malformed AS4 path from %s, length is %d", + peer->host, length); + return bgp_attr_malformed (args, + BGP_NOTIFY_UPDATE_MAL_AS_PATH, + 0); + } + + /* Set aspath attribute flag. */ + if (as4_path) + attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AS4_PATH); + + return BGP_ATTR_PARSE_PROCEED; +} + +/* Nexthop attribute. */ +static bgp_attr_parse_ret_t +bgp_attr_nexthop (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; + + in_addr_t nexthop_h, nexthop_n; + + /* Check nexthop attribute length. */ + if (length != 4) + { + zlog (peer->log, LOG_ERR, "Nexthop attribute length isn't four [%d]", + length); + + return bgp_attr_malformed (args, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); + } + + /* According to section 6.3 of RFC4271, syntactically incorrect NEXT_HOP + attribute must result in a NOTIFICATION message (this is implemented below). + At the same time, semantically incorrect NEXT_HOP is more likely to be just + logged locally (this is implemented somewhere else). The UPDATE message + 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)) + && !BGP_DEBUG (allow_martians, ALLOW_MARTIANS)) /* loopbacks may be used in testing */ + { + char 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, + args->total); + } + + attr->nexthop.s_addr = nexthop_n; + attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP); + + return BGP_ATTR_PARSE_PROCEED; +} + +/* MED atrribute. */ +static bgp_attr_parse_ret_t +bgp_attr_med (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; + + /* Length check. */ + if (length != 4) + { + zlog (peer->log, LOG_ERR, + "MED attribute length isn't four [%d]", length); + + return bgp_attr_malformed (args, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); + } + + attr->med = stream_getl (peer->ibuf); + + attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC); + + return BGP_ATTR_PARSE_PROCEED; +} + +/* Local preference attribute. */ +static bgp_attr_parse_ret_t +bgp_attr_local_pref (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; + + /* Length check. */ + if (length != 4) + { + zlog (peer->log, LOG_ERR, "LOCAL_PREF attribute length isn't 4 [%u]", + length); + return bgp_attr_malformed (args, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); + } + + /* 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 == BGP_PEER_EBGP) + { + stream_forward_getp (peer->ibuf, length); + return BGP_ATTR_PARSE_PROCEED; + } + + attr->local_pref = stream_getl (peer->ibuf); + + /* Set atomic aggregate flag. */ + attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF); + + return BGP_ATTR_PARSE_PROCEED; +} + +/* Atomic aggregate. */ +static int +bgp_attr_atomic (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; + + /* Length check. */ + if (length != 0) + { + zlog (peer->log, LOG_ERR, "ATOMIC_AGGREGATE attribute length isn't 0 [%u]", + length); + return bgp_attr_malformed (args, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); + } + + /* Set atomic aggregate flag. */ + attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE); + + return BGP_ATTR_PARSE_PROCEED; +} + +/* Aggregator attribute */ +static int +bgp_attr_aggregator (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; + + int wantedlen = 6; + struct attr_extra *attre = bgp_attr_extra_get (attr); + + /* peer with AS4 will send 4 Byte AS, peer without will send 2 Byte */ + if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV)) + wantedlen = 8; + + if (length != wantedlen) + { + zlog (peer->log, LOG_ERR, "AGGREGATOR attribute length isn't %u [%u]", + wantedlen, length); + return bgp_attr_malformed (args, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); + } + + if ( CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV ) ) + attre->aggregator_as = stream_getl (peer->ibuf); + else + attre->aggregator_as = stream_getw (peer->ibuf); + attre->aggregator_addr.s_addr = stream_get_ipv4 (peer->ibuf); + + /* Set atomic aggregate flag. */ + attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR); + + return BGP_ATTR_PARSE_PROCEED; +} + +/* New Aggregator attribute */ +static bgp_attr_parse_ret_t +bgp_attr_as4_aggregator (struct bgp_attr_parser_args *args, + as_t *as4_aggregator_as, + struct in_addr *as4_aggregator_addr) +{ + struct peer *const peer = args->peer; + struct attr *const attr = args->attr; + const bgp_size_t length = args->length; + + if (length != 8) + { + zlog (peer->log, LOG_ERR, "New Aggregator length is not 8 [%d]", + length); + return bgp_attr_malformed (args, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + 0); + } + + *as4_aggregator_as = stream_getl (peer->ibuf); + as4_aggregator_addr->s_addr = stream_get_ipv4 (peer->ibuf); + + attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AS4_AGGREGATOR); + + return BGP_ATTR_PARSE_PROCEED; +} + +/* Munge Aggregator and New-Aggregator, AS_PATH and NEW_AS_PATH. + */ +static bgp_attr_parse_ret_t +bgp_attr_munge_as4_attrs (struct peer *const peer, + struct attr *const attr, + struct aspath *as4_path, as_t as4_aggregator, + struct in_addr *as4_aggregator_addr) +{ + 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 + * if given. + * It is worth a warning though, because the peer really + * should not send them + */ + if (BGP_DEBUG(as4, AS4)) + { + if (attr->flag & (ATTR_FLAG_BIT(BGP_ATTR_AS4_PATH))) + zlog_debug ("[AS4] %s %s AS4_PATH", + peer->host, "AS4 capable peer, yet it sent"); + + if (attr->flag & (ATTR_FLAG_BIT(BGP_ATTR_AS4_AGGREGATOR))) + zlog_debug ("[AS4] %s %s AS4_AGGREGATOR", + peer->host, "AS4 capable peer, yet it sent"); + } + + return BGP_ATTR_PARSE_PROCEED; + } + + /* We have a asn16 peer. First, look for AS4_AGGREGATOR + * because that may override AS4_PATH + */ + if (attr->flag & (ATTR_FLAG_BIT (BGP_ATTR_AS4_AGGREGATOR) ) ) + { + if (attr->flag & (ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR) ) ) + { + assert (attre); + + /* received both. + * if the as_number in aggregator is not AS_TRANS, + * then AS4_AGGREGATOR and AS4_PATH shall be ignored + * and the Aggregator shall be taken as + * info on the aggregating node, and the AS_PATH + * shall be taken as the AS_PATH + * otherwise + * the Aggregator shall be ignored and the + * AS4_AGGREGATOR shall be taken as the + * Aggregating node and the AS_PATH is to be + * constructed "as in all other cases" + */ + if (attre->aggregator_as != BGP_AS_TRANS) + { + /* ignore */ + if ( BGP_DEBUG(as4, AS4)) + zlog_debug ("[AS4] %s BGP not AS4 capable peer" + " send AGGREGATOR != AS_TRANS and" + " AS4_AGGREGATOR, so ignore" + " AS4_AGGREGATOR and AS4_PATH", peer->host); + ignore_as4_path = 1; + } + else + { + /* "New_aggregator shall be taken as aggregator" */ + attre->aggregator_as = as4_aggregator; + attre->aggregator_addr.s_addr = as4_aggregator_addr->s_addr; + } + } + else + { + /* We received a AS4_AGGREGATOR but no AGGREGATOR. + * That is bogus - but reading the conditions + * we have to handle AS4_AGGREGATOR as if it were + * AGGREGATOR in that case + */ + if ( BGP_DEBUG(as4, AS4)) + zlog_debug ("[AS4] %s BGP not AS4 capable peer send" + " AS4_AGGREGATOR but no AGGREGATOR, will take" + " it as if AGGREGATOR with AS_TRANS had been there", peer->host); + (attre = bgp_attr_extra_get (attr))->aggregator_as = as4_aggregator; + /* sweep it under the carpet and simulate a "good" AGGREGATOR */ + attr->flag |= (ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR)); + } + } + + /* 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); + } + return BGP_ATTR_PARSE_PROCEED; +} + +/* Community attribute. */ +static bgp_attr_parse_ret_t +bgp_attr_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) + { + attr->community = NULL; + return BGP_ATTR_PARSE_PROCEED; + } + + attr->community = + community_parse ((u_int32_t *)stream_pnt (peer->ibuf), length); + + /* XXX: fix community_parse to use stream API and remove this */ + stream_forward_getp (peer->ibuf, length); + + if (!attr->community) + return bgp_attr_malformed (args, + BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, + args->total); + + attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES); + + return BGP_ATTR_PARSE_PROCEED; +} + +/* Originator ID attribute. */ +static bgp_attr_parse_ret_t +bgp_attr_originator_id (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; + + /* Length check. */ + if (length != 4) + { + zlog (peer->log, LOG_ERR, "Bad originator ID length %d", length); + + return bgp_attr_malformed (args, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); + } + + (bgp_attr_extra_get (attr))->originator_id.s_addr + = stream_get_ipv4 (peer->ibuf); + + attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID); + + return BGP_ATTR_PARSE_PROCEED; +} + +/* Cluster list attribute. */ +static bgp_attr_parse_ret_t +bgp_attr_cluster_list (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; + + /* Check length. */ + if (length % 4) + { + zlog (peer->log, LOG_ERR, "Bad cluster list length %d", length); + + return bgp_attr_malformed (args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); + } + + (bgp_attr_extra_get (attr))->cluster + = cluster_parse ((struct in_addr *)stream_pnt (peer->ibuf), length); + + /* XXX: Fix cluster_parse to use stream API and then remove this */ + stream_forward_getp (peer->ibuf, length); + + attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_CLUSTER_LIST); + + return BGP_ATTR_PARSE_PROCEED; +} + +/* Multiprotocol reachability information parse. */ +int +bgp_mp_reach_parse (struct bgp_attr_parser_args *args, + struct bgp_nlri *mp_update) +{ + afi_t afi; + safi_t safi; + bgp_size_t nlri_len; + size_t start; + struct stream *s; + struct peer *const peer = args->peer; + struct attr *const attr = args->attr; + const bgp_size_t length = args->length; + struct attr_extra *attre = bgp_attr_extra_get(attr); + + /* Set end of packet. */ + s = BGP_INPUT(peer); + start = stream_get_getp(s); + + /* safe to read statically sized header? */ +#define BGP_MP_REACH_MIN_SIZE 5 +#define LEN_LEFT (length - (stream_get_getp(s) - start)) + if ((length > STREAM_READABLE(s)) || (length < BGP_MP_REACH_MIN_SIZE)) + { + zlog_info ("%s: %s sent invalid length, %lu", + __func__, peer->host, (unsigned long)length); + return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; + } + + /* Load AFI, SAFI. */ + afi = stream_getw (s); + safi = stream_getc (s); + + /* Get nexthop length. */ + attre->mp_nexthop_len = stream_getc (s); + + if (LEN_LEFT < attre->mp_nexthop_len) + { + 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_NOTIFYPLS; + } + + /* Nexthop length check. */ + switch (attre->mp_nexthop_len) + { + case 4: + stream_get (&attre->mp_nexthop_global_in, s, 4); + /* Probably needed for RFC 2283 */ + if (attr->nexthop.s_addr == 0) + memcpy(&attr->nexthop.s_addr, &attre->mp_nexthop_global_in, 4); + break; + case 12: + stream_getl (s); /* RD high */ + stream_getl (s); /* RD low */ + stream_get (&attre->mp_nexthop_global_in, s, 4); + break; + 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)) + { + char buf1[INET6_ADDRSTRLEN]; + char buf2[INET6_ADDRSTRLEN]; + + if (BGP_DEBUG (update, UPDATE_IN)) + zlog_debug ("%s got two nexthop %s %s but second one is not a link-local nexthop", peer->host, + inet_ntop (AF_INET6, &attre->mp_nexthop_global, + buf1, INET6_ADDRSTRLEN), + inet_ntop (AF_INET6, &attre->mp_nexthop_local, + buf2, INET6_ADDRSTRLEN)); + + attre->mp_nexthop_len = 16; + } + break; + default: + zlog_info ("%s: (%s) Wrong multiprotocol next hop length: %d", + __func__, peer->host, attre->mp_nexthop_len); + 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_NOTIFYPLS; + } + + { + u_char val; + if ((val = stream_getc (s))) + zlog_warn ("%s sent non-zero value, %u, for defunct SNPA-length field", + peer->host, val); + } + + /* must have nrli_len, what is left of the attribute */ + nlri_len = LEN_LEFT; + if ((!nlri_len) || (nlri_len > STREAM_READABLE(s))) + { + zlog_info ("%s: (%s) Failed to read NLRI", + __func__, peer->host); + return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; + } + + mp_update->afi = afi; + mp_update->safi = safi; + mp_update->nlri = stream_pnt (s); + mp_update->length = nlri_len; + + stream_forward_getp (s, nlri_len); + + attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_MP_REACH_NLRI); + + return BGP_ATTR_PARSE_PROCEED; +#undef LEN_LEFT +} + +/* Multiprotocol unreachable parse */ +int +bgp_mp_unreach_parse (struct bgp_attr_parser_args *args, + struct bgp_nlri *mp_withdraw) +{ + struct stream *s; + afi_t afi; + safi_t safi; + u_int16_t withdraw_len; + 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_NOTIFYPLS; + + afi = stream_getw (s); + safi = stream_getc (s); + + withdraw_len = length - BGP_MP_UNREACH_MIN_SIZE; + + mp_withdraw->afi = afi; + mp_withdraw->safi = safi; + mp_withdraw->nlri = stream_pnt (s); + mp_withdraw->length = withdraw_len; + + 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; +} + +/* Extended Community attribute. */ +static bgp_attr_parse_ret_t +bgp_attr_ext_communities (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->ecommunity = NULL; + /* Empty extcomm doesn't seem to be invalid per se */ + return BGP_ATTR_PARSE_PROCEED; + } + + (bgp_attr_extra_get (attr))->ecommunity = + ecommunity_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->ecommunity) + return bgp_attr_malformed (args, + BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, + args->total); + + attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES); + + 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 = args->total; + struct transit *transit; + struct attr_extra *attre; + struct peer *const peer = args->peer; + struct attr *const attr = args->attr; + u_char *const startp = args->startp; + const u_char type = args->type; + const u_char flag = args->flags; + const bgp_size_t length = args->length; + + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s Unknown attribute is received (type %d, length %d)", + peer->host, type, length); + + if (BGP_DEBUG (events, EVENTS)) + zlog (peer->log, LOG_DEBUG, + "Unknown attribute type %d length %d is received", type, length); + + /* Forward read pointer of input stream. */ + stream_forward_getp (peer->ibuf, length); + + /* If any of the mandatory well-known attributes are not recognized, + then the Error Subcode is set to Unrecognized Well-known + Attribute. The Data field contains the unrecognized attribute + (type, length and value). */ + if (!CHECK_FLAG (flag, BGP_ATTR_FLAG_OPTIONAL)) + { + return bgp_attr_malformed (args, + BGP_NOTIFY_UPDATE_UNREC_ATTR, + args->total); + } + + /* Unrecognized non-transitive optional attributes must be quietly + ignored and not passed along to other BGP peers. */ + if (! CHECK_FLAG (flag, BGP_ATTR_FLAG_TRANS)) + return BGP_ATTR_PARSE_PROCEED; + + /* If a path with recognized transitive optional attribute is + accepted and passed along to other BGP peers and the Partial bit + in the Attribute Flags octet is set to 1 by some previous AS, it + is not set back to 0 by the current AS. */ + SET_FLAG (*startp, BGP_ATTR_FLAG_PARTIAL); + + /* Store transitive attribute to the end of attr->transit. */ + if (! ((attre = bgp_attr_extra_get(attr))->transit) ) + attre->transit = XCALLOC (MTYPE_TRANSIT, sizeof (struct transit)); + + transit = attre->transit; + + if (transit->val) + transit->val = XREALLOC (MTYPE_TRANSIT_VAL, transit->val, + transit->length + total); + else + transit->val = XMALLOC (MTYPE_TRANSIT_VAL, total); + + memcpy (transit->val + transit->length, startp, total); + transit->length += total; + + 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_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) +{ + int ret; + u_char flag = 0; + u_char type = 0; + bgp_size_t length; + u_char *startp, *endp; + u_char *attr_endp; + u_char seen[BGP_ATTR_BITMAP_SIZE]; + /* we need the as4_path only until we have synthesized the as_path with it */ + /* same goes for as4_aggregator */ + struct aspath *as4_path = NULL; + as_t as4_aggregator = 0; + struct in_addr as4_aggregator_addr = { .s_addr = 0 }; + + /* Initialize bitmap. */ + memset (seen, 0, BGP_ATTR_BITMAP_SIZE); + + /* End pointer of BGP attribute. */ + endp = BGP_INPUT_PNT (peer) + size; + + /* Get attributes to the end of attribute length. */ + while (BGP_INPUT_PNT (peer) < endp) + { + /* Check remaining length check.*/ + if (endp - BGP_INPUT_PNT (peer) < BGP_ATTR_MIN_LEN) + { + /* XXX warning: long int format, int arg (arg 5) */ + zlog (peer->log, LOG_WARNING, + "%s: error BGP attribute length %lu is smaller than min len", + peer->host, + (unsigned long) (endp - STREAM_PNT (BGP_INPUT (peer)))); + + bgp_notify_send (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); + return BGP_ATTR_PARSE_ERROR; + } + + /* Fetch attribute flag and type. */ + startp = BGP_INPUT_PNT (peer); + /* "The lower-order four bits of the Attribute Flags octet are + unused. They MUST be zero when sent and MUST be ignored when + received." */ + flag = 0xF0 & stream_getc (BGP_INPUT (peer)); + type = stream_getc (BGP_INPUT (peer)); + + /* Check whether Extended-Length applies and is in bounds */ + if (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) + && ((endp - startp) < (BGP_ATTR_MIN_LEN + 1))) + { + zlog (peer->log, LOG_WARNING, + "%s: Extended length set, but just %lu bytes of attr header", + peer->host, + (unsigned long) (endp - STREAM_PNT (BGP_INPUT (peer)))); + + bgp_notify_send (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); + return BGP_ATTR_PARSE_ERROR; + } + + /* Check extended attribue length bit. */ + if (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN)) + length = stream_getw (BGP_INPUT (peer)); + else + length = stream_getc (BGP_INPUT (peer)); + + /* If any attribute appears more than once in the UPDATE + message, then the Error Subcode is set to Malformed Attribute + List. */ + + if (CHECK_BITMAP (seen, type)) + { + zlog (peer->log, LOG_WARNING, + "%s: error BGP attribute type %d appears twice in a message", + peer->host, type); + + bgp_notify_send (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MAL_ATTR); + return BGP_ATTR_PARSE_ERROR; + } + + /* Set type to bitmap to check duplicate attribute. `type' is + unsigned char so it never overflow bitmap range. */ + + SET_BITMAP (seen, type); + + /* Overflow check. */ + attr_endp = BGP_INPUT_PNT (peer) + length; + + if (attr_endp > endp) + { + 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); + 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; + } + + struct bgp_attr_parser_args attr_args = { + .peer = peer, + .length = length, + .attr = attr, + .type = type, + .flags = flag, + .startp = startp, + .total = attr_endp - startp, + }; + + + /* If any recognized attribute has Attribute Flags that conflict + with the Attribute Type Code, then the Error Subcode is set to + Attribute Flags Error. The Data field contains the erroneous + attribute (type, length and value). */ + if (bgp_attr_flag_invalid (&attr_args)) + { + bgp_attr_parse_ret_t ret; + ret = bgp_attr_malformed (&attr_args, + BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR, + attr_args.total); + if (ret == BGP_ATTR_PARSE_PROCEED) + continue; + return ret; + } + + /* OK check attribute and store it's value. */ + switch (type) + { + case BGP_ATTR_ORIGIN: + ret = bgp_attr_origin (&attr_args); + break; + case BGP_ATTR_AS_PATH: + ret = bgp_attr_aspath (&attr_args); + break; + case BGP_ATTR_AS4_PATH: + ret = bgp_attr_as4_path (&attr_args, &as4_path); + break; + case BGP_ATTR_NEXT_HOP: + ret = bgp_attr_nexthop (&attr_args); + break; + case BGP_ATTR_MULTI_EXIT_DISC: + ret = bgp_attr_med (&attr_args); + break; + case BGP_ATTR_LOCAL_PREF: + ret = bgp_attr_local_pref (&attr_args); + break; + case BGP_ATTR_ATOMIC_AGGREGATE: + ret = bgp_attr_atomic (&attr_args); + break; + case BGP_ATTR_AGGREGATOR: + ret = bgp_attr_aggregator (&attr_args); + break; + case BGP_ATTR_AS4_AGGREGATOR: + ret = bgp_attr_as4_aggregator (&attr_args, + &as4_aggregator, + &as4_aggregator_addr); + break; + 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; + case BGP_ATTR_CLUSTER_LIST: + ret = bgp_attr_cluster_list (&attr_args); + break; + case BGP_ATTR_MP_REACH_NLRI: + ret = bgp_mp_reach_parse (&attr_args, mp_update); + break; + case BGP_ATTR_MP_UNREACH_NLRI: + ret = bgp_mp_unreach_parse (&attr_args, mp_withdraw); + break; + 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) + { + zlog (peer->log, LOG_WARNING, + "%s: Attribute %s, parse error", + peer->host, + LOOKUP (attr_str, type)); + if (as4_path) + aspath_unintern (&as4_path); + return ret; + } + if (ret == BGP_ATTR_PARSE_WITHDRAW) + { + + zlog (peer->log, LOG_WARNING, + "%s: Attribute %s, parse error - treating as withdrawal", + peer->host, + LOOKUP (attr_str, type)); + if (as4_path) + aspath_unintern (&as4_path); + return ret; + } + + /* Check the fetched length. */ + if (BGP_INPUT_PNT (peer) != attr_endp) + { + zlog (peer->log, LOG_WARNING, + "%s: BGP attribute %s, fetch error", + peer->host, LOOKUP (attr_str, type)); + bgp_notify_send (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); + if (as4_path) + aspath_unintern (&as4_path); + return BGP_ATTR_PARSE_ERROR; + } + } + /* Check final read pointer is same as end pointer. */ + if (BGP_INPUT_PNT (peer) != endp) + { + zlog (peer->log, LOG_WARNING, + "%s: BGP attribute %s, length mismatch", + peer->host, LOOKUP (attr_str, type)); + bgp_notify_send (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); + if (as4_path) + 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. + * We can not do this before we've read all attributes because + * the as4 handling does not say whether AS4_PATH has to be sent + * after AS_PATH or not - and when AS4_AGGREGATOR will be send + * in relationship to AGGREGATOR. + * 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. + */ + /* 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; + } + + /* At this stage, we have done all fiddling with as4, and the + * resulting info is in attr->aggregator resp. attr->aspath + * so we can chuck as4_aggregator and as4_path alltogether in + * order to save memory + */ + if (as4_path) + { + aspath_unintern (&as4_path); /* unintern - it is in the hash */ + /* The flag that we got this is still there, but that does not + * do any trouble + */ + } + /* + * The "rest" of the code does nothing with as4_aggregator. + * there is no memory attached specifically which is not part + * of the attr. + * so ignoring just means do nothing. + */ + /* + * Finally do the checks on the aspath we did not do yet + * because we waited for a potentially synthesized aspath. + */ + if (attr->flag & (ATTR_FLAG_BIT(BGP_ATTR_AS_PATH))) + { + ret = bgp_attr_aspath_check (peer, attr); + if (ret != BGP_ATTR_PARSE_PROCEED) + return ret; + } + + /* Finally intern unknown attribute. */ + if (attr->extra && attr->extra->transit) + attr->extra->transit = transit_intern (attr->extra->transit); + + return BGP_ATTR_PARSE_PROCEED; +} + +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) +{ + size_t sizep; + + /* Set extended bit always to encode the attribute length as 2 bytes */ + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_EXTLEN); + stream_putc (s, BGP_ATTR_MP_REACH_NLRI); + sizep = stream_get_endp (s); + stream_putw (s, 0); /* Marker: Attribute length. */ + + stream_putw (s, afi); + stream_putc (s, (safi == SAFI_MPLS_VPN) ? SAFI_MPLS_LABELED_VPN : safi); + + /* 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; + + 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) + { + /* 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)); + } + 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); +} + +/* 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) +{ + size_t cp; + size_t aspath_sizep; + struct aspath *aspath; + int send_as4_path = 0; + int send_as4_aggregator = 0; + int use32bit = (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV)) ? 1 : 0; + + if (! bgp) + bgp = bgp_get_default (); + + /* 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); + stream_putc (s, 1); + stream_putc (s, attr->origin); + + /* AS path attribute. */ + + /* If remote-peer is 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))) + { + aspath = aspath_dup (attr->aspath); + + if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION)) + { + /* Strip the confed info, and then stuff our path CONFED_ID + on the front */ + aspath = aspath_delete_confed_seq (aspath); + aspath = aspath_add_seq (aspath, bgp->confed_id); + } + else + { + if (peer->change_local_as) { + /* If replace-as is specified, we only use the change_local_as when + advertising routes. */ + if( ! CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS) ) { + aspath = aspath_add_seq (aspath, peer->local_as); + } + aspath = aspath_add_seq (aspath, peer->change_local_as); + } else { + aspath = aspath_add_seq (aspath, peer->local_as); + } + } + } + else if (peer->sort == BGP_PEER_CONFED) + { + /* A confed member, so we need to do the AS_CONFED_SEQUENCE thing */ + aspath = aspath_dup (attr->aspath); + aspath = aspath_add_confed_seq (aspath, peer->local_as); + } + else + aspath = attr->aspath; + + /* If peer is not AS4 capable, then: + * - send the created AS_PATH out as AS4_PATH (optional, transitive), + * but ensure that no AS_CONFED_SEQUENCE and AS_CONFED_SET path segment + * types are in it (i.e. exclude them if they are there) + * AND do this only if there is at least one asnum > 65535 in the path! + * - send an AS_PATH out, but put 16Bit ASnums in it, not 32bit, and change + * all ASnums > 65535 to BGP_AS_TRANS + */ + + stream_putc (s, BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN); + stream_putc (s, BGP_ATTR_AS_PATH); + aspath_sizep = stream_get_endp (s); + stream_putw (s, 0); + stream_putw_at (s, aspath_sizep, aspath_put (s, aspath, use32bit)); + + /* OLD session may need NEW_AS_PATH sent, if there are 4-byte ASNs + * in the path + */ + if (!use32bit && aspath_has_as4 (aspath)) + 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 && + safi == SAFI_UNICAST) /* only write NH attr for unicast safi */ + { + stream_putc (s, BGP_ATTR_FLAG_TRANS); + stream_putc (s, BGP_ATTR_NEXT_HOP); + stream_putc (s, 4); + if (safi == SAFI_MPLS_VPN) + { + if (attr->nexthop.s_addr == 0) + stream_put_ipv4 (s, peer->nexthop.v4.s_addr); + else + stream_put_ipv4 (s, attr->nexthop.s_addr); + } + else + stream_put_ipv4 (s, attr->nexthop.s_addr); + } + + /* MED attribute. */ + if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC)) + { + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL); + stream_putc (s, BGP_ATTR_MULTI_EXIT_DISC); + stream_putc (s, 4); + stream_putl (s, attr->med); + } + + /* Local preference. */ + 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); + stream_putc (s, 4); + stream_putl (s, attr->local_pref); + } + + /* Atomic aggregate. */ + if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE)) + { + stream_putc (s, BGP_ATTR_FLAG_TRANS); + stream_putc (s, BGP_ATTR_ATOMIC_AGGREGATE); + stream_putc (s, 0); + } + + /* Aggregator. */ + if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR)) + { + assert (attr->extra); + + /* Common to BGP_ATTR_AGGREGATOR, regardless of ASN size */ + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS); + stream_putc (s, BGP_ATTR_AGGREGATOR); + + if (use32bit) + { + /* AS4 capable peer */ + stream_putc (s, 8); + stream_putl (s, attr->extra->aggregator_as); + } + else + { + /* 2-byte AS peer */ + stream_putc (s, 6); + + /* Is ASN representable in 2-bytes? Or must AS_TRANS be used? */ + if ( attr->extra->aggregator_as > 65535 ) + { + stream_putw (s, BGP_AS_TRANS); + + /* we have to send AS4_AGGREGATOR, too. + * we'll do that later in order to send attributes in ascending + * order. + */ + send_as4_aggregator = 1; + } + else + stream_putw (s, (u_int16_t) attr->extra->aggregator_as); + } + stream_put_ipv4 (s, attr->extra->aggregator_addr.s_addr); + } + + /* Community attribute. */ + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY) + && (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES))) + { + if (attr->community->size * 4 > 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->community->size * 4); + } + else + { + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS); + stream_putc (s, BGP_ATTR_COMMUNITIES); + stream_putc (s, attr->community->size * 4); + } + 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 == BGP_PEER_IBGP + && from + && from->sort == BGP_PEER_IBGP) + { + /* Originator ID. */ + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL); + stream_putc (s, BGP_ATTR_ORIGINATOR_ID); + stream_putc (s, 4); + + if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)) + stream_put_in_addr (s, &attr->extra->originator_id); + else + stream_put_in_addr (s, &from->remote_id); + + /* Cluster list. */ + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL); + stream_putc (s, BGP_ATTR_CLUSTER_LIST); + + if (attr->extra && attr->extra->cluster) + { + stream_putc (s, attr->extra->cluster->length + 4); + /* If this peer configuration's parent BGP has cluster_id. */ + if (bgp->config & BGP_CONFIG_CLUSTER_ID) + stream_put_in_addr (s, &bgp->cluster_id); + else + stream_put_in_addr (s, &bgp->router_id); + stream_put (s, attr->extra->cluster->list, + attr->extra->cluster->length); + } + else + { + stream_putc (s, 4); + /* If this peer configuration's parent BGP has cluster_id. */ + if (bgp->config & BGP_CONFIG_CLUSTER_ID) + stream_put_in_addr (s, &bgp->cluster_id); + else + stream_put_in_addr (s, &bgp->router_id); + } + } + + /* 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))) + { + struct attr_extra *attre = attr->extra; + + assert (attre); + + if (peer->sort == BGP_PEER_IBGP + || peer->sort == BGP_PEER_CONFED) + { + if (attre->ecommunity->size * 8 > 255) + { + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN); + stream_putc (s, BGP_ATTR_EXT_COMMUNITIES); + stream_putw (s, attre->ecommunity->size * 8); + } + else + { + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS); + stream_putc (s, BGP_ATTR_EXT_COMMUNITIES); + stream_putc (s, attre->ecommunity->size * 8); + } + stream_put (s, attre->ecommunity->val, attre->ecommunity->size * 8); + } + else + { + u_int8_t *pnt; + int tbit; + int ecom_tr_size = 0; + int i; + + for (i = 0; i < attre->ecommunity->size; i++) + { + pnt = attre->ecommunity->val + (i * 8); + tbit = *pnt; + + if (CHECK_FLAG (tbit, ECOMMUNITY_FLAG_NON_TRANSITIVE)) + continue; + + ecom_tr_size++; + } + + if (ecom_tr_size) + { + if (ecom_tr_size * 8 > 255) + { + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN); + stream_putc (s, BGP_ATTR_EXT_COMMUNITIES); + stream_putw (s, ecom_tr_size * 8); + } + else + { + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS); + stream_putc (s, BGP_ATTR_EXT_COMMUNITIES); + stream_putc (s, ecom_tr_size * 8); + } + + for (i = 0; i < attre->ecommunity->size; i++) + { + pnt = attre->ecommunity->val + (i * 8); + tbit = *pnt; + + if (CHECK_FLAG (tbit, ECOMMUNITY_FLAG_NON_TRANSITIVE)) + continue; + + stream_put (s, pnt, 8); + } + } + } + } + + if ( send_as4_path ) + { + /* If the peer is NOT As4 capable, AND */ + /* there are ASnums > 65535 in path THEN + * give out AS4_PATH */ + + /* Get rid of all AS_CONFED_SEQUENCE and AS_CONFED_SET + * path segments! + * Hm, I wonder... confederation things *should* only be at + * the beginning of an aspath, right? Then we should use + * aspath_delete_confed_seq for this, because it is already + * there! (JK) + * Folks, talk to me: what is reasonable here!? + */ + aspath = aspath_delete_confed_seq (aspath); + + stream_putc (s, BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_EXTLEN); + stream_putc (s, BGP_ATTR_AS4_PATH); + aspath_sizep = stream_get_endp (s); + stream_putw (s, 0); + stream_putw_at (s, aspath_sizep, aspath_put (s, aspath, 1)); + } + + if (aspath != attr->aspath) + aspath_free (aspath); + + if ( send_as4_aggregator ) + { + assert (attr->extra); + + /* send AS4_AGGREGATOR, at this place */ + /* this section of code moved here in order to ensure the correct + * *ascending* order of attributes + */ + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS); + stream_putc (s, BGP_ATTR_AS4_AGGREGATOR); + stream_putc (s, 8); + 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); + + /* Return total size of attribute. */ + return stream_get_endp (s) - cp; +} + +size_t +bgp_packet_mpunreach_start (struct stream *s, afi_t afi, safi_t safi) +{ + unsigned long attrlen_pnt; + + /* 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_putw (s, 0); /* Length of this attribute. */ + + stream_putw (s, afi); + stream_putc (s, (safi == SAFI_MPLS_VPN) ? SAFI_MPLS_LABELED_VPN : safi); + return attrlen_pnt; +} + +void +bgp_packet_mpunreach_prefix (struct stream *s, struct prefix *p, + afi_t afi, safi_t safi, struct prefix_rd *prd, + u_char *tag) +{ + bgp_packet_mpattr_prefix (s, afi, safi, p, prd, tag); +} + +void +bgp_packet_mpunreach_end (struct stream *s, size_t attrlen_pnt) +{ + bgp_packet_mpattr_end (s, attrlen_pnt); +} + +/* Initialization of attribute. */ +void +bgp_attr_init (void) +{ + aspath_init (); + attrhash_init (); + community_init (); + ecommunity_init (); + lcommunity_init (); + cluster_init (); + transit_init (); +} + +void +bgp_attr_finish (void) +{ + aspath_finish (); + attrhash_finish (); + community_finish (); + ecommunity_finish (); + lcommunity_finish (); + cluster_finish (); + transit_finish (); +} + +/* Make attribute packet. */ +void +bgp_dump_routes_attr (struct stream *s, struct attr *attr, + struct prefix *prefix) +{ + unsigned long cp; + unsigned long len; + size_t aspath_lenp; + struct aspath *aspath; + + /* Remember current pointer. */ + cp = stream_get_endp (s); + + /* Place holder of length. */ + stream_putw (s, 0); + + /* Origin attribute. */ + stream_putc (s, BGP_ATTR_FLAG_TRANS); + stream_putc (s, BGP_ATTR_ORIGIN); + stream_putc (s, 1); + stream_putc (s, attr->origin); + + aspath = attr->aspath; + + stream_putc (s, BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN); + stream_putc (s, BGP_ATTR_AS_PATH); + aspath_lenp = stream_get_endp (s); + stream_putw (s, 0); + + stream_putw_at (s, aspath_lenp, aspath_put (s, aspath, 1)); + + /* Nexthop attribute. */ + /* If it's an IPv6 prefix, don't dump the IPv4 nexthop to save space */ + if(prefix != NULL + && prefix->family != AF_INET6 + ) + { + stream_putc (s, BGP_ATTR_FLAG_TRANS); + stream_putc (s, BGP_ATTR_NEXT_HOP); + stream_putc (s, 4); + stream_put_ipv4 (s, attr->nexthop.s_addr); + } + + /* MED attribute. */ + if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC)) + { + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL); + stream_putc (s, BGP_ATTR_MULTI_EXIT_DISC); + stream_putc (s, 4); + stream_putl (s, attr->med); + } + + /* Local preference. */ + if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF)) + { + stream_putc (s, BGP_ATTR_FLAG_TRANS); + stream_putc (s, BGP_ATTR_LOCAL_PREF); + stream_putc (s, 4); + stream_putl (s, attr->local_pref); + } + + /* Atomic aggregate. */ + if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE)) + { + stream_putc (s, BGP_ATTR_FLAG_TRANS); + stream_putc (s, BGP_ATTR_ATOMIC_AGGREGATE); + stream_putc (s, 0); + } + + /* Aggregator. */ + if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR)) + { + assert (attr->extra); + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS); + stream_putc (s, BGP_ATTR_AGGREGATOR); + stream_putc (s, 8); + stream_putl (s, attr->extra->aggregator_as); + stream_put_ipv4 (s, attr->extra->aggregator_addr.s_addr); + } + + /* Community attribute. */ + if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES)) + { + if (attr->community->size * 4 > 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->community->size * 4); + } + else + { + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS); + stream_putc (s, BGP_ATTR_COMMUNITIES); + stream_putc (s, attr->community->size * 4); + } + stream_put (s, attr->community->val, attr->community->size * 4); + } + + /* 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) ) + { + int sizep; + struct attr_extra *attre = attr->extra; + + stream_putc(s, BGP_ATTR_FLAG_OPTIONAL); + stream_putc(s, BGP_ATTR_MP_REACH_NLRI); + sizep = stream_get_endp (s); + + /* MP header */ + stream_putc (s, 0); /* Marker: Attribute length. */ + stream_putw(s, AFI_IP6); /* AFI */ + stream_putc(s, SAFI_UNICAST); /* SAFI */ + + /* Next hop */ + 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); + + /* SNPA */ + stream_putc(s, 0); + + /* Prefix */ + stream_put_prefix(s, prefix); + + /* Set MP attribute length. */ + stream_putc_at (s, sizep, (stream_get_endp (s) - sizep) - 1); + } + + /* Return total size of attribute. */ + len = stream_get_endp (s) - cp - 2; + stream_putw_at (s, cp, len); +} diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h new file mode 100644 index 0000000..9ff074b --- /dev/null +++ b/bgpd/bgp_attr.h @@ -0,0 +1,238 @@ +/* BGP attributes. + Copyright (C) 1996, 97, 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. */ + +#ifndef _QUAGGA_BGP_ATTR_H +#define _QUAGGA_BGP_ATTR_H + +/* Simple bit mapping. */ +#define BITMAP_NBBY 8 + +#define SET_BITMAP(MAP, NUM) \ + SET_FLAG (MAP[(NUM) / BITMAP_NBBY], 1 << ((NUM) % BITMAP_NBBY)) + +#define CHECK_BITMAP(MAP, NUM) \ + CHECK_FLAG (MAP[(NUM) / BITMAP_NBBY], 1 << ((NUM) % BITMAP_NBBY)) + +#define BGP_MED_MAX UINT32_MAX + + +/* BGP Attribute type range. */ +#define BGP_ATTR_TYPE_RANGE 256 +#define BGP_ATTR_BITMAP_SIZE (BGP_ATTR_TYPE_RANGE / BITMAP_NBBY) + +/* BGP Attribute flags. */ +#define BGP_ATTR_FLAG_OPTIONAL 0x80 /* Attribute is optional. */ +#define BGP_ATTR_FLAG_TRANS 0x40 /* Attribute is transitive. */ +#define BGP_ATTR_FLAG_PARTIAL 0x20 /* Attribute is partial. */ +#define BGP_ATTR_FLAG_EXTLEN 0x10 /* Extended length flag. */ + +/* BGP attribute header must bigger than 2. */ +#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. + */ +struct attr_extra +{ + /* Multi-Protocol Nexthop, AFI IPv6 */ + struct in6_addr mp_nexthop_global; + struct in6_addr mp_nexthop_local; + + /* Extended Communities attribute. */ + struct ecommunity *ecommunity; + + /* Large Communities attribute. */ + struct lcommunity *lcommunity; + + /* Route-Reflector Cluster attribute */ + struct cluster_list *cluster; + + /* Unknown transitive attribute. */ + struct transit *transit; + + struct in_addr mp_nexthop_global_in; + + /* Aggregator Router ID attribute */ + struct in_addr aggregator_addr; + + /* Route Reflector Originator attribute */ + struct in_addr originator_id; + + /* Local weight, not actually an attribute */ + u_int32_t weight; + + /* Aggregator ASN */ + as_t aggregator_as; + + /* 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. */ +struct attr +{ + /* AS Path structure */ + struct aspath *aspath; + + /* Community structure */ + struct community *community; + + /* Lazily allocated pointer to extra attributes */ + struct attr_extra *extra; + + /* Reference count of this attribute. */ + unsigned long refcnt; + + /* Flag of attribute is set or not. */ + u_int32_t flag; + + /* Apart from in6_addr, the remaining static attributes */ + struct in_addr nexthop; + u_int32_t med; + u_int32_t local_pref; + + /* Path origin attribute */ + u_char origin; +}; + +/* Router Reflector related structure. */ +struct cluster_list +{ + unsigned long refcnt; + int length; + struct in_addr *list; +}; + +/* Unknown transit attribute. */ +struct transit +{ + unsigned long refcnt; + int length; + u_char *val; +}; + +#define ATTR_FLAG_BIT(X) (1 << ((X) - 1)) + +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. */ +extern void bgp_attr_init (void); +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 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 *); +extern struct attr *bgp_attr_intern (struct attr *attr); +extern void bgp_attr_unintern_sub (struct attr *); +extern void bgp_attr_unintern (struct attr **); +extern void bgp_attr_flush (struct attr *); +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, 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 *); +extern unsigned int attrhash_key_make (void *); +extern void attr_show_all (struct vty *); +extern unsigned long int attr_count (void); +extern unsigned long int attr_unknown_count (void); + +/* Cluster list prototypes. */ +extern int cluster_loop_check (struct cluster_list *, struct in_addr); +extern void cluster_unintern (struct cluster_list *); + +/* Transit attribute prototypes. */ +void transit_unintern (struct transit *); + +/* Below exported for unit-test purposes only */ +struct bgp_attr_parser_args { + struct peer *peer; + bgp_size_t length; /* attribute data length; */ + bgp_size_t total; /* total length, inc header */ + struct attr *attr; + u_int8_t type; + u_int8_t flags; + u_char *startp; +}; +extern int bgp_mp_reach_parse (struct bgp_attr_parser_args *args, + struct bgp_nlri *); +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 new file mode 100644 index 0000000..b408efd --- /dev/null +++ b/bgpd/bgp_btoa.c @@ -0,0 +1,313 @@ +/* BGP dump to ascii converter + 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 "zebra.h" +#include "stream.h" +#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 */ + MSG_DIE, /* receiver should shut down */ + MSG_I_AM_DEAD, /* sender is shutting down */ + MSG_PEER_DOWN, /* sender's peer is down */ + MSG_PROTOCOL_BGP, /* msg is a BGP packet */ + MSG_PROTOCOL_RIP, /* msg is a RIP packet */ + MSG_PROTOCOL_IDRP, /* msg is an IDRP packet */ + MSG_PROTOCOL_RIPNG, /* msg is a RIPNG packet */ + MSG_PROTOCOL_BGP4PLUS, /* msg is a BGP4+ packet */ + MSG_PROTOCOL_BGP4PLUS_01, /* msg is a BGP4+ (draft 01) packet */ + MSG_PROTOCOL_OSPF, /* msg is an OSPF packet */ + MSG_TABLE_DUMP /* routing table dump */ +}; + +static int +attr_parse (struct stream *s, u_int16_t len) +{ + u_int flag; + u_int type; + u_int16_t length; + u_int16_t lim; + + lim = s->getp + len; + + 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 & BGP_ATTR_FLAG_EXTLEN) + length = stream_getw (s); + else + length = stream_getc (s); + + printf ("FLAG: %d\n", flag); + printf ("TYPE: %d\n", type); + printf ("Len: %d\n", length); + + switch (type) + { + case BGP_ATTR_ORIGIN: + { + u_char origin; + origin = stream_getc (s); + printf ("ORIGIN: %d\n", origin); + } + break; + case BGP_ATTR_AS_PATH: + { + struct aspath *aspath; + + aspath = aspath_parse (s, length, 1); + printf ("ASPATH: %s\n", aspath->str); + aspath_free(aspath); + } + break; + case BGP_ATTR_NEXT_HOP: + { + struct in_addr nexthop; + nexthop.s_addr = stream_get_ipv4 (s); + printf ("NEXTHOP: %s\n", inet_ntoa (nexthop)); + } + break; + default: + stream_getw_from (s, length); + break; + } + } + + return 0; +} + +int +main (int argc, char **argv) +{ + int ret; + FILE *fp; + struct stream *s; + time_t now; + int type; + int subtype; + size_t len; + int source_as; + int dest_as; + ifindex_t ifindex; + int family; + struct in_addr sip; + struct in_addr dip; + u_int16_t viewno, seq_num; + struct prefix_ipv4 p; + + s = stream_new (10000); + + if (argc != 2) + { + fprintf (stderr, "Usage: %s FILENAME\n", argv[0]); + exit (1); + } + fp = fopen (argv[1], "r"); + if (!fp) + { + perror ("fopen"); + exit (1); + } + + while (1) + { + stream_reset (s); + + ret = fread (s->data, 12, 1, fp); + if (!ret || feof (fp)) + { + printf ("END OF FILE\n"); + break; + } + if (ferror (fp)) + { + printf ("ERROR OF FREAD\n"); + break; + } + + /* Extract header. */ + now = stream_getl (s); + type = stream_getw (s); + subtype = stream_getw (s); + len = stream_getl (s); + + printf ("TIME: %s", ctime (&now)); + + /* printf ("TYPE: %d/%d\n", type, subtype); */ + + 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 + printf ("TYPE: Unknown %d", type); + + if (type == MSG_TABLE_DUMP) + switch (subtype) + { + case AFI_IP: + printf ("/AFI_IP\n"); + break; + case AFI_IP6: + printf ("/AFI_IP6\n"); + break; + default: + printf ("/UNKNOWN %d", subtype); + break; + } + else + { + switch (subtype) + { + case BGP4MP_STATE_CHANGE: + printf ("/CHANGE\n"); + break; + case BGP4MP_MESSAGE: + printf ("/MESSAGE\n"); + break; + case BGP4MP_ENTRY: + printf ("/ENTRY\n"); + break; + case BGP4MP_SNAPSHOT: + printf ("/SNAPSHOT\n"); + break; + default: + printf ("/UNKNOWN %d", subtype); + break; + } + } + + printf ("len: %zd\n", len); + + ret = fread (s->data + 12, len, 1, fp); + if (feof (fp)) + { + printf ("ENDOF FILE 2\n"); + break; + } + if (ferror (fp)) + { + printf ("ERROR OF FREAD 2\n"); + break; + } + + /* printf ("now read %d\n", len); */ + + if (type == MSG_TABLE_DUMP) + { + u_char status; + time_t originated; + struct in_addr peer; + u_int16_t attrlen; + + viewno = stream_getw (s); + seq_num = stream_getw (s); + printf ("VIEW: %d\n", viewno); + printf ("SEQUENCE: %d\n", seq_num); + + /* start */ + while (s->getp < len - 16) + { + p.prefix.s_addr = stream_get_ipv4 (s); + p.prefixlen = stream_getc (s); + printf ("PREFIX: %s/%d\n", inet_ntoa (p.prefix), p.prefixlen); + + status = stream_getc (s); + originated = stream_getl (s); + peer.s_addr = stream_get_ipv4 (s); + source_as = stream_getw(s); + + printf ("FROM: %s AS%d\n", inet_ntoa (peer), source_as); + printf ("ORIGINATED: %s", ctime (&originated)); + + attrlen = stream_getw (s); + printf ("ATTRLEN: %d\n", attrlen); + + attr_parse (s, attrlen); + + printf ("STATUS: 0x%x\n", status); + } + } + else + { + source_as = stream_getw (s); + dest_as = stream_getw (s); + printf ("source_as: %d\n", source_as); + printf ("dest_as: %d\n", dest_as); + + ifindex = stream_getw (s); + family = stream_getw (s); + + printf ("ifindex: %d\n", ifindex); + printf ("family: %d\n", family); + + sip.s_addr = stream_get_ipv4 (s); + dip.s_addr = stream_get_ipv4 (s); + + printf ("saddr: %s\n", inet_ntoa (sip)); + printf ("daddr: %s\n", inet_ntoa (dip)); + + printf ("\n"); + } + } + fclose (fp); + return 0; +} diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c new file mode 100644 index 0000000..13bdf8e --- /dev/null +++ b/bgpd/bgp_clist.c @@ -0,0 +1,1235 @@ +/* BGP community-list and extcommunity-list. + 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 "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 * +community_list_master_lookup (struct community_list_handler *ch, int master) +{ + if (ch) + switch (master) + { + case COMMUNITY_LIST_MASTER: + return &ch->community_list; + case EXTCOMMUNITY_LIST_MASTER: + return &ch->extcommunity_list; + case LARGE_COMMUNITY_LIST_MASTER: + return &ch->lcommunity_list; + } + return NULL; +} + +/* Allocate a new community list entry. */ +static struct community_entry * +community_entry_new (void) +{ + return XCALLOC (MTYPE_COMMUNITY_LIST_ENTRY, sizeof (struct community_entry)); +} + +/* Free community list entry. */ +static void +community_entry_free (struct community_entry *entry) +{ + switch (entry->style) + { + case COMMUNITY_LIST_STANDARD: + 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(). */ + if (entry->config) + XFREE (MTYPE_ECOMMUNITY_STR, entry->config); + if (entry->u.ecom) + ecommunity_free (&entry->u.ecom); + 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) + bgp_regex_free (entry->reg); + default: + break; + } + XFREE (MTYPE_COMMUNITY_LIST_ENTRY, entry); +} + +/* Allocate a new community-list. */ +static struct community_list * +community_list_new (void) +{ + return XCALLOC (MTYPE_COMMUNITY_LIST, sizeof (struct community_list)); +} + +/* Free community-list. */ +static void +community_list_free (struct community_list *list) +{ + if (list->name) + XFREE (MTYPE_COMMUNITY_LIST_NAME, list->name); + XFREE (MTYPE_COMMUNITY_LIST, list); +} + +static struct community_list * +community_list_insert (struct community_list_handler *ch, + const char *name, int master) +{ + size_t i; + long number; + struct community_list *new; + struct community_list *point; + struct community_list_list *list; + struct community_list_master *cm; + + /* Lookup community-list master. */ + cm = community_list_master_lookup (ch, master); + if (!cm) + return NULL; + + /* Allocate new community_list and copy given name. */ + new = community_list_new (); + new->name = XSTRDUP (MTYPE_COMMUNITY_LIST_NAME, name); + + /* If name is made by all digit character. We treat it as + number. */ + for (number = 0, i = 0; i < strlen (name); i++) + { + if (isdigit ((int) name[i])) + number = (number * 10) + (name[i] - '0'); + else + break; + } + + /* In case of name is all digit character */ + if (i == strlen (name)) + { + new->sort = COMMUNITY_LIST_NUMBER; + + /* Set access_list to number list. */ + list = &cm->num; + + for (point = list->head; point; point = point->next) + if (atol (point->name) >= number) + break; + } + else + { + new->sort = COMMUNITY_LIST_STRING; + + /* Set access_list to string list. */ + list = &cm->str; + + /* Set point to insertion point. */ + for (point = list->head; point; point = point->next) + if (strcmp (point->name, name) >= 0) + break; + } + + /* Link to upper list. */ + new->parent = list; + + /* In case of this is the first element of master. */ + if (list->head == NULL) + { + list->head = list->tail = new; + return new; + } + + /* In case of insertion is made at the tail of access_list. */ + if (point == NULL) + { + new->prev = list->tail; + list->tail->next = new; + list->tail = new; + return new; + } + + /* In case of insertion is made at the head of access_list. */ + if (point == list->head) + { + new->next = list->head; + list->head->prev = new; + list->head = new; + return new; + } + + /* Insertion is made at middle of the access_list. */ + new->next = point; + new->prev = point->prev; + + if (point->prev) + point->prev->next = new; + point->prev = new; + + return new; +} + +struct community_list * +community_list_lookup (struct community_list_handler *ch, + const char *name, int master) +{ + struct community_list *list; + struct community_list_master *cm; + + if (!name) + return NULL; + + cm = community_list_master_lookup (ch, master); + if (!cm) + return NULL; + + for (list = cm->num.head; list; list = list->next) + if (strcmp (list->name, name) == 0) + return list; + for (list = cm->str.head; list; list = list->next) + if (strcmp (list->name, name) == 0) + return list; + + return NULL; +} + +static struct community_list * +community_list_get (struct community_list_handler *ch, + const char *name, int master) +{ + struct community_list *list; + + list = community_list_lookup (ch, name, master); + if (!list) + list = community_list_insert (ch, name, master); + return list; +} + +static void +community_list_delete (struct community_list *list) +{ + struct community_list_list *clist; + struct community_entry *entry, *next; + + for (entry = list->head; entry; entry = next) + { + next = entry->next; + community_entry_free (entry); + } + + clist = list->parent; + + if (list->next) + list->next->prev = list->prev; + else + clist->tail = list->prev; + + if (list->prev) + list->prev->next = list->next; + else + clist->head = list->next; + + community_list_free (list); +} + +static int +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, + struct community_entry *entry) +{ + entry->next = NULL; + entry->prev = list->tail; + + if (list->tail) + list->tail->next = entry; + else + list->head = entry; + list->tail = entry; +} + +/* Delete community-list entry from the list. */ +static void +community_list_entry_delete (struct community_list *list, + struct community_entry *entry, int style) +{ + if (entry->next) + entry->next->prev = entry->prev; + else + list->tail = entry->prev; + + if (entry->prev) + entry->prev->next = entry->next; + else + list->head = entry->next; + + community_entry_free (entry); + + if (community_list_empty_p (list)) + community_list_delete (list); +} + +/* Lookup community-list entry from the list. */ +static struct community_entry * +community_list_entry_lookup (struct community_list *list, const void *arg, + int direct) +{ + struct community_entry *entry; + + for (entry = list->head; entry; entry = entry->next) + { + switch (entry->style) + { + case COMMUNITY_LIST_STANDARD: + 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; + default: + break; + } + } + 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 +community_regexp_match (struct community *com, regex_t * reg) +{ + const char *str; + + /* When there is no communities attribute it is treated as empty + string. */ + if (com == NULL || com->size == 0) + str = ""; + else + str = community_str (com); + + /* Regular expression match. */ + if (regexec (reg, str, 0, NULL, 0) == 0) + return 1; + + /* No match. */ + 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 +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 (lcom == NULL || lcom->size == 0) + str = ""; + else + str = lcommunity_str_get (lcom, i); + + /* Regular expression match. */ + if (regexec (reg, str, 0, NULL, 0) == 0) + return 1; + + /* No match. */ + return 0; +} + +static int +lcommunity_regexp_match (struct lcommunity *com, regex_t * reg) +{ + const char *str; + + /* When there is no communities attribute it is treated as empty + string. */ + if (com == NULL || com->size == 0) + str = ""; + else + str = lcommunity_str (com); + + /* Regular expression match. */ + if (regexec (reg, str, 0, NULL, 0) == 0) + return 1; + + /* No match. */ + return 0; +} + + +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 + 1 else return 0. */ +int +community_list_match (struct community *com, 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 == COMMUNITY_LIST_STANDARD) + { + if (community_include (entry->u.com, COMMUNITY_INTERNET)) + return entry->direct == COMMUNITY_PERMIT ? 1 : 0; + + if (community_match (com, entry->u.com)) + return entry->direct == COMMUNITY_PERMIT ? 1 : 0; + } + else if (entry->style == COMMUNITY_LIST_EXPANDED) + { + if (community_regexp_match (com, entry->reg)) + return entry->direct == COMMUNITY_PERMIT ? 1 : 0; + } + } + 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) +{ + 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 == EXTCOMMUNITY_LIST_STANDARD) + { + if (ecommunity_match (ecom, entry->u.ecom)) + return entry->direct == COMMUNITY_PERMIT ? 1 : 0; + } + else if (entry->style == EXTCOMMUNITY_LIST_EXPANDED) + { + if (ecommunity_regexp_match (ecom, entry->reg)) + return entry->direct == COMMUNITY_PERMIT ? 1 : 0; + } + } + return 0; +} + +/* Perform exact matching. In case of expanded community-list, do + same thing as community_list_match(). */ +int +community_list_exact_match (struct community *com, + 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 == COMMUNITY_LIST_STANDARD) + { + if (community_include (entry->u.com, COMMUNITY_INTERNET)) + return entry->direct == COMMUNITY_PERMIT ? 1 : 0; + + if (community_cmp (com, entry->u.com)) + return entry->direct == COMMUNITY_PERMIT ? 1 : 0; + } + else if (entry->style == COMMUNITY_LIST_EXPANDED) + { + if (community_regexp_match (com, entry->reg)) + return entry->direct == COMMUNITY_PERMIT ? 1 : 0; + } + } + return 0; +} + +/* Delete all permitted communities in the list from com. */ +struct community * +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; + + /* 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++) + { + val = community_val_get (com, i); + + 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 == COMMUNITY_LIST_STANDARD) + && (community_include (entry->u.com, COMMUNITY_INTERNET) + || community_include (entry->u.com, val) )) + { + if (entry->direct == COMMUNITY_PERMIT) + { + 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; +} + +/* To avoid duplicated entry in the community-list, this function + compares specified entry to existing entry. */ +static int +community_list_dup_check (struct community_list *list, + struct community_entry *new) +{ + struct community_entry *entry; + + for (entry = list->head; entry; entry = entry->next) + { + if (entry->style != new->style) + continue; + + if (entry->direct != new->direct) + continue; + + if (entry->any != new->any) + continue; + + if (entry->any) + return 1; + + switch (entry->style) + { + case COMMUNITY_LIST_STANDARD: + if (community_cmp (entry->u.com, new->u.com)) + return 1; + break; + case EXTCOMMUNITY_LIST_STANDARD: + 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: + 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: + break; + } + } + return 0; +} + +/* Set community-list. */ +int +community_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 community *com = NULL; + regex_t *regex = NULL; + + /* Get community list. */ + list = community_list_get (ch, name, 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 == COMMUNITY_LIST_STANDARD) + com = community_str2com (str); + else + regex = bgp_regcomp (str); + + if (! com && ! regex) + return COMMUNITY_LIST_ERR_MALFORMED_VAL; + } + + entry = community_entry_new (); + entry->direct = direct; + entry->style = style; + entry->any = (str ? 0 : 1); + entry->u.com = com; + entry->reg = regex; + entry->config = (regex ? XSTRDUP (MTYPE_COMMUNITY_LIST_CONFIG, str) : 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 +community_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 community *com = NULL; + regex_t *regex = NULL; + + /* Lookup community list. */ + list = community_list_lookup (ch, name, 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 == COMMUNITY_LIST_STANDARD) + com = community_str2com (str); + else + regex = bgp_regcomp (str); + + if (! com && ! regex) + return COMMUNITY_LIST_ERR_MALFORMED_VAL; + + if (com) + entry = community_list_entry_lookup (list, com, direct); + else + entry = community_list_entry_lookup (list, str, direct); + + if (com) + community_free (com); + if (regex) + bgp_regex_free (regex); + + if (!entry) + return COMMUNITY_LIST_ERR_CANT_FIND_LIST; + + community_list_entry_delete (list, entry, style); + + 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, + const char *name, const char *str, + int direct, int style) +{ + struct community_entry *entry = NULL; + struct community_list *list; + struct ecommunity *ecom = NULL; + regex_t *regex = NULL; + + entry = NULL; + + /* Get community list. */ + list = community_list_get (ch, name, EXTCOMMUNITY_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 == EXTCOMMUNITY_LIST_STANDARD + ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT + : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT); + } + } + + if (str) + { + if (style == EXTCOMMUNITY_LIST_STANDARD) + ecom = ecommunity_str2com (str, 0, 1); + else + regex = bgp_regcomp (str); + + if (! ecom && ! regex) + return COMMUNITY_LIST_ERR_MALFORMED_VAL; + } + + if (ecom) + ecom->str = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_DISPLAY); + + entry = community_entry_new (); + entry->direct = direct; + entry->style = style; + entry->any = (str ? 0 : 1); + if (ecom) + entry->config = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_COMMUNITY_LIST); + else if (regex) + entry->config = XSTRDUP (MTYPE_COMMUNITY_LIST_CONFIG, str); + else + entry->config = NULL; + entry->u.ecom = ecom; + entry->reg = regex; + + /* 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 extcommunity-list. When str is NULL, delete all of + extcommunity-list entry belongs to the specified name. */ +int +extcommunity_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 ecommunity *ecom = NULL; + regex_t *regex = NULL; + + /* Lookup extcommunity list. */ + list = community_list_lookup (ch, name, EXTCOMMUNITY_LIST_MASTER); + if (list == NULL) + return COMMUNITY_LIST_ERR_CANT_FIND_LIST; + + /* Delete all of entry belongs to this extcommunity-list. */ + if (!str) + { + community_list_delete (list); + return 0; + } + + if (style == EXTCOMMUNITY_LIST_STANDARD) + ecom = ecommunity_str2com (str, 0, 1); + else + regex = bgp_regcomp (str); + + if (! ecom && ! regex) + return COMMUNITY_LIST_ERR_MALFORMED_VAL; + + if (ecom) + entry = community_list_entry_lookup (list, ecom, direct); + else + entry = community_list_entry_lookup (list, str, direct); + + if (ecom) + ecommunity_free (&ecom); + if (regex) + bgp_regex_free (regex); + + if (!entry) + return COMMUNITY_LIST_ERR_CANT_FIND_LIST; + + community_list_entry_delete (list, entry, style); + + return 0; +} + +/* Initializa community-list. Return community-list handler. */ +struct community_list_handler * +community_list_init (void) +{ + struct community_list_handler *ch; + ch = XCALLOC (MTYPE_COMMUNITY_LIST_HANDLER, + sizeof (struct community_list_handler)); + return ch; +} + +/* Terminate community-list. */ +void +community_list_terminate (struct community_list_handler *ch) +{ + struct community_list_master *cm; + struct community_list *list; + + cm = &ch->community_list; + while ((list = cm->num.head) != NULL) + community_list_delete (list); + 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); + while ((list = cm->str.head) != NULL) + community_list_delete (list); + + XFREE (MTYPE_COMMUNITY_LIST_HANDLER, ch); +} diff --git a/bgpd/bgp_clist.h b/bgpd/bgp_clist.h new file mode 100644 index 0000000..d9db418 --- /dev/null +++ b/bgpd/bgp_clist.h @@ -0,0 +1,172 @@ +/* BGP Community list. + 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_BGP_CLIST_H +#define _QUAGGA_BGP_CLIST_H + +/* 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 +#define COMMUNITY_PERMIT 1 + +/* Number and string based community-list name. */ +#define COMMUNITY_LIST_STRING 0 +#define COMMUNITY_LIST_NUMBER 1 + +/* Community-list entry types. */ +#define COMMUNITY_LIST_STANDARD 0 /* Standard community-list. */ +#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 +{ + /* Name of the community-list. */ + char *name; + + /* String or number. */ + int sort; + + /* Link to upper list. */ + struct community_list_list *parent; + + /* Linked list for other community-list. */ + struct community_list *next; + struct community_list *prev; + + /* Community-list entry in this community-list. */ + struct community_entry *head; + struct community_entry *tail; +}; + +/* Each entry in community-list. */ +struct community_entry +{ + struct community_entry *next; + struct community_entry *prev; + + /* Permit or deny. */ + u_char direct; + + /* Standard or expanded. */ + u_char style; + + /* Any match. */ + u_char any; + + /* Community structure. */ + union + { + struct community *com; + struct ecommunity *ecom; + struct lcommunity *lcom; + } u; + + /* Configuration string. */ + char *config; + + /* Expanded community-list regular expression. */ + regex_t *reg; +}; + +/* Linked list of community-list. */ +struct community_list_list +{ + struct community_list *head; + struct community_list *tail; +}; + +/* Master structure of community-list and extcommunity-list. */ +struct community_list_master +{ + struct community_list_list num; + struct community_list_list str; +}; + +/* Community-list handler. community_list_init() returns this + structure as handler. */ +struct community_list_handler +{ + /* Community-list. */ + struct community_list_master community_list; + + /* Exteded community-list. */ + struct community_list_master extcommunity_list; + + /* Large community-list. */ + struct community_list_master lcommunity_list; +}; + +/* Error code of community-list. */ +#define COMMUNITY_LIST_ERR_CANT_FIND_LIST -1 +#define COMMUNITY_LIST_ERR_MALFORMED_VAL -2 +#define COMMUNITY_LIST_ERR_STANDARD_CONFLICT -3 +#define COMMUNITY_LIST_ERR_EXPANDED_CONFLICT -4 + +/* Handler. */ +extern struct community_list_handler *bgp_clist; + +/* Prototypes. */ +extern struct community_list_handler *community_list_init (void); +extern void community_list_terminate (struct community_list_handler *); + +extern int community_list_set (struct community_list_handler *ch, + const char *name, const char *str, int direct, + int style); +extern int community_list_unset (struct community_list_handler *ch, + const char *name, const char *str, + int direct, int style); +extern int extcommunity_list_set (struct community_list_handler *ch, + const char *name, const char *str, + int direct, int style); +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); + +extern struct community_list * +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 new file mode 100644 index 0000000..f1997bd --- /dev/null +++ b/bgpd/bgp_community.c @@ -0,0 +1,649 @@ +/* Community attribute related functions. + Copyright (C) 1998, 2001 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 "hash.h" +#include "memory.h" + +#include "bgpd/bgp_community.h" + +/* Hash of community attribute. */ +static struct hash *comhash; + +/* Allocate a new communities value. */ +static struct community * +community_new (void) +{ + return (struct community *) XCALLOC (MTYPE_COMMUNITY, + sizeof (struct community)); +} + +/* Free communities value. */ +void +community_free (struct community *com) +{ + if (com->val) + XFREE (MTYPE_COMMUNITY_VAL, com->val); + if (com->str) + XFREE (MTYPE_COMMUNITY_STR, com->str); + XFREE (MTYPE_COMMUNITY, com); +} + +/* Add one community value to the community. */ +static void +community_add_val (struct community *com, u_int32_t val) +{ + com->size++; + if (com->val) + com->val = XREALLOC (MTYPE_COMMUNITY_VAL, com->val, com_length (com)); + else + com->val = XMALLOC (MTYPE_COMMUNITY_VAL, com_length (com)); + + val = htonl (val); + memcpy (com_lastval (com), &val, sizeof (u_int32_t)); +} + +/* Delete one community. */ +void +community_del_val (struct community *com, u_int32_t *val) +{ + int i = 0; + int c = 0; + + if (! com->val) + return; + + while (i < com->size) + { + if (memcmp (com->val + i, val, sizeof (u_int32_t)) == 0) + { + c = com->size -i -1; + + if (c > 0) + memmove (com->val + i, com->val + (i + 1), c * sizeof (*val)); + + com->size--; + + if (com->size > 0) + com->val = XREALLOC (MTYPE_COMMUNITY_VAL, com->val, + com_length (com)); + else + { + XFREE (MTYPE_COMMUNITY_VAL, com->val); + com->val = NULL; + } + return; + } + i++; + } +} + +/* Delete all communities listed in com2 from com1 */ +struct community * +community_delete (struct community *com1, struct community *com2) +{ + int i = 0; + + while(i < com2->size) + { + community_del_val (com1, com2->val + i); + i++; + } + + return com1; +} + +/* Callback function from qsort(). */ +static int +community_compare (const void *a1, const void *a2) +{ + u_int32_t v1; + u_int32_t v2; + + memcpy (&v1, a1, sizeof (u_int32_t)); + memcpy (&v2, a2, sizeof (u_int32_t)); + v1 = ntohl (v1); + v2 = ntohl (v2); + + if (v1 < v2) + return -1; + if (v1 > v2) + return 1; + return 0; +} + +int +community_include (struct community *com, u_int32_t val) +{ + int i; + + val = htonl (val); + + for (i = 0; i < com->size; i++) + if (memcmp (&val, com_nthval (com, i), sizeof (u_int32_t)) == 0) + return 1; + + return 0; +} + +u_int32_t +community_val_get (struct community *com, int i) +{ + u_char *p; + u_int32_t val; + + p = (u_char *) com->val; + p += (i * 4); + + memcpy (&val, p, sizeof (u_int32_t)); + + return ntohl (val); +} + +/* Sort and uniq given community. */ +struct community * +community_uniq_sort (struct community *com) +{ + int i; + struct community *new; + u_int32_t val; + + if (! com) + return NULL; + + new = community_new ();; + + for (i = 0; i < com->size; i++) + { + val = community_val_get (com, i); + + if (! community_include (new, val)) + community_add_val (new, val); + } + + qsort (new->val, new->size, sizeof (u_int32_t), community_compare); + + return new; +} + +/* Convert communities attribute to string. + + For Well-known communities value, below keyword is used. + + 0x0 "internet" + 0xFFFFFF01 "no-export" + 0xFFFFFF02 "no-advertise" + 0xFFFFFF03 "local-AS" + + For other values, "AS:VAL" format is used. */ +static char * +community_com2str (struct community *com) +{ + int i; + char *str; + char *pnt; + int len; + int first; + u_int32_t comval; + u_int16_t as; + u_int16_t val; + + if (!com) + return NULL; + + /* When communities attribute is empty. */ + if (com->size == 0) + { + str = XMALLOC (MTYPE_COMMUNITY_STR, 1); + str[0] = '\0'; + return str; + } + + /* Memory allocation is time consuming work. So we calculate + required string length first. */ + len = 0; + + for (i = 0; i < com->size; i++) + { + 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); + first = 1; + + /* Fill in string. */ + for (i = 0; i < com->size; i++) + { + memcpy (&comval, com_nthval (com, i), sizeof (u_int32_t)); + comval = ntohl (comval); + + if (first) + first = 0; + else + *pnt++ = ' '; + + 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; +} + +/* Intern communities attribute. */ +struct community * +community_intern (struct community *com) +{ + struct community *find; + + /* Assert this community structure is not interned. */ + assert (com->refcnt == 0); + + /* Lookup community hash. */ + find = (struct community *) hash_get (comhash, com, hash_alloc_intern); + + /* Arguemnt com is allocated temporary. So when it is not used in + hash, it should be freed. */ + if (find != com) + community_free (com); + + /* Increment refrence counter. */ + find->refcnt++; + + /* Make string. */ + if (! find->str) + find->str = community_com2str (find); + + return find; +} + +/* Free community attribute. */ +void +community_unintern (struct community **com) +{ + struct community *ret; + + if ((*com)->refcnt) + (*com)->refcnt--; + + /* Pull off from hash. */ + if ((*com)->refcnt == 0) + { + /* Community value com must exist in hash. */ + ret = (struct community *) hash_release (comhash, *com); + assert (ret != NULL); + + community_free (*com); + *com = NULL; + } +} + +/* Create new community attribute. */ +struct community * +community_parse (u_int32_t *pnt, u_short length) +{ + struct community tmp; + struct community *new; + + /* If length is malformed return NULL. */ + if (length % 4) + return NULL; + + /* Make temporary community for hash look up. */ + tmp.size = length / 4; + tmp.val = pnt; + + new = community_uniq_sort (&tmp); + + return community_intern (new); +} + +struct community * +community_dup (struct community *com) +{ + struct community *new; + + new = XCALLOC (MTYPE_COMMUNITY, sizeof (struct community)); + new->size = com->size; + if (new->size) + { + new->val = XMALLOC (MTYPE_COMMUNITY_VAL, com->size * 4); + memcpy (new->val, com->val, com->size * 4); + } + else + new->val = NULL; + return new; +} + +/* Retrun string representation of communities attribute. */ +char * +community_str (struct community *com) +{ + if (!com) + return NULL; + + if (! com->str) + com->str = community_com2str (com); + return com->str; +} + +/* Make hash value of community attribute. This function is used by + hash package.*/ +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; + + for (c = 0; c < size; c += 4) + { + key += pnt[c]; + key += pnt[c + 1]; + key += pnt[c + 2]; + key += pnt[c + 3]; + } + + return key; +} + +int +community_match (const struct community *com1, const struct community *com2) +{ + int i = 0; + int j = 0; + + if (com1 == NULL && com2 == NULL) + return 1; + + if (com1 == NULL || com2 == NULL) + return 0; + + if (com1->size < com2->size) + return 0; + + /* Every community on com2 needs to be on com1 for this to match */ + while (i < com1->size && j < com2->size) + { + if (memcmp (com1->val + i, com2->val + j, sizeof (u_int32_t)) == 0) + j++; + i++; + } + + if (j == com2->size) + return 1; + else + return 0; +} + +/* If two aspath have same value then return 1 else return 0. This + function is used by hash package. */ +int +community_cmp (const struct community *com1, const struct community *com2) +{ + if (com1 == NULL && com2 == NULL) + return 1; + if (com1 == NULL || com2 == NULL) + return 0; + + if (com1->size == com2->size) + if (memcmp (com1->val, com2->val, com1->size * 4) == 0) + return 1; + return 0; +} + +/* Add com2 to the end of com1. */ +struct community * +community_merge (struct community *com1, struct community *com2) +{ + if (com1->val) + com1->val = XREALLOC (MTYPE_COMMUNITY_VAL, com1->val, + (com1->size + com2->size) * 4); + else + com1->val = XMALLOC (MTYPE_COMMUNITY_VAL, (com1->size + com2->size) * 4); + + memcpy (com1->val + com1->size, com2->val, com2->size * 4); + com1->size += com2->size; + + return com1; +} + +/* Community token enum. */ +enum community_token +{ + community_token_val, + community_token_no_export, + community_token_no_advertise, + community_token_local_as, + community_token_unknown +}; + +/* Get next community token from string. */ +static const char * +community_gettoken (const char *buf, enum community_token *token, + u_int32_t *val) +{ + const char *p = buf; + + /* Skip white space. */ + while (isspace ((int) *p)) + p++; + + /* Check the end of the line. */ + if (*p == '\0') + return NULL; + + /* Well known community string check. */ + if (isalpha ((int) *p)) + { + if (strncmp (p, "internet", strlen ("internet")) == 0) + { + *val = COMMUNITY_INTERNET; + *token = community_token_no_export; + p += strlen ("internet"); + return p; + } + if (strncmp (p, "no-export", strlen ("no-export")) == 0) + { + *val = COMMUNITY_NO_EXPORT; + *token = community_token_no_export; + p += strlen ("no-export"); + return p; + } + if (strncmp (p, "no-advertise", strlen ("no-advertise")) == 0) + { + *val = COMMUNITY_NO_ADVERTISE; + *token = community_token_no_advertise; + p += strlen ("no-advertise"); + return p; + } + if (strncmp (p, "local-AS", strlen ("local-AS")) == 0) + { + *val = COMMUNITY_LOCAL_AS; + *token = community_token_local_as; + p += strlen ("local-AS"); + return p; + } + + /* Unknown string. */ + *token = community_token_unknown; + return NULL; + } + + /* Community value. */ + if (isdigit ((int) *p)) + { + int separator = 0; + int digit = 0; + u_int32_t community_low = 0; + u_int32_t community_high = 0; + + while (isdigit ((int) *p) || *p == ':') + { + if (*p == ':') + { + if (separator) + { + *token = community_token_unknown; + return NULL; + } + else + { + separator = 1; + digit = 0; + community_high = community_low << 16; + community_low = 0; + } + } + else + { + digit = 1; + community_low *= 10; + community_low += (*p - '0'); + } + p++; + } + if (! digit) + { + *token = community_token_unknown; + return NULL; + } + *val = community_high + community_low; + *token = community_token_val; + return p; + } + *token = community_token_unknown; + return NULL; +} + +/* convert string to community structure */ +struct community * +community_str2com (const char *str) +{ + struct community *com = NULL; + struct community *com_sort = NULL; + u_int32_t val = 0; + enum community_token token = community_token_unknown; + + do + { + str = community_gettoken (str, &token, &val); + + switch (token) + { + case community_token_val: + case community_token_no_export: + case community_token_no_advertise: + case community_token_local_as: + if (com == NULL) + com = community_new(); + community_add_val (com, val); + break; + case community_token_unknown: + default: + if (com) + community_free (com); + return NULL; + } + } while (str); + + if (! com) + return NULL; + + com_sort = community_uniq_sort (com); + community_free (com); + + return com_sort; +} + +/* Return communities hash entry count. */ +unsigned long +community_count (void) +{ + return comhash->count; +} + +/* Return communities hash. */ +struct hash * +community_hash (void) +{ + return comhash; +} + +/* Initialize comminity related hash. */ +void +community_init (void) +{ + comhash = hash_create ((unsigned int (*) (void *))community_hash_make, + (int (*) (const void *, const void *))community_cmp); +} + +void +community_finish (void) +{ + hash_free (comhash); + comhash = NULL; +} diff --git a/bgpd/bgp_community.h b/bgpd/bgp_community.h new file mode 100644 index 0000000..c73dab3 --- /dev/null +++ b/bgpd/bgp_community.h @@ -0,0 +1,75 @@ +/* Community attribute related functions. + Copyright (C) 1998 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_BGP_COMMUNITY_H +#define _QUAGGA_BGP_COMMUNITY_H + +/* Communities attribute. */ +struct community +{ + /* Reference count of communities value. */ + unsigned long refcnt; + + /* Communities value size. */ + int size; + + /* Communities value. */ + u_int32_t *val; + + /* String of community attribute. This sring is used by vty output + and expanded community-list for regular expression match. */ + char *str; +}; + +/* Well-known communities value. */ +#define COMMUNITY_INTERNET 0x0 +#define COMMUNITY_NO_EXPORT 0xFFFFFF01 +#define COMMUNITY_NO_ADVERTISE 0xFFFFFF02 +#define COMMUNITY_NO_EXPORT_SUBCONFED 0xFFFFFF03 +#define COMMUNITY_LOCAL_AS 0xFFFFFF03 + +/* Macros of community attribute. */ +#define com_length(X) ((X)->size * 4) +#define com_lastval(X) ((X)->val + (X)->size - 1) +#define com_nthval(X,n) ((X)->val + (n)) + +/* Prototypes of communities attribute functions. */ +extern void community_init (void); +extern void community_finish (void); +extern void community_free (struct community *); +extern struct community *community_uniq_sort (struct community *); +extern struct community *community_parse (u_int32_t *, u_short); +extern struct community *community_intern (struct community *); +extern void community_unintern (struct community **); +extern char *community_str (struct community *); +extern unsigned int community_hash_make (struct community *); +extern struct community *community_str2com (const char *); +extern int community_match (const struct community *, const struct community *); +extern int community_cmp (const struct community *, const struct community *); +extern struct community *community_merge (struct community *, struct community *); +extern struct community *community_delete (struct community *, struct community *); +extern struct community *community_dup (struct community *); +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 new file mode 100644 index 0000000..ac64723 --- /dev/null +++ b/bgpd/bgp_damp.c @@ -0,0 +1,675 @@ +/* BGP flap dampening + Copyright (C) 2001 IP Infusion 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 + +#include "prefix.h" +#include "memory.h" +#include "command.h" +#include "log.h" +#include "thread.h" +#include "filter.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_damp.h" +#include "bgpd/bgp_table.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_advertise.h" + +/* Global variable to access damping configuration */ +struct bgp_damp_config bgp_damp_cfg; +static struct bgp_damp_config *damp = &bgp_damp_cfg; + +/* Utility macro to add and delete BGP dampening information to no + 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) +{ + unsigned int i; + int index; + + i = (int)(((double) penalty / damp->reuse_limit - 1.0) * damp->scale_factor); + + if ( i >= damp->reuse_index_size ) + i = damp->reuse_index_size - 1; + + index = damp->reuse_index[i] - damp->reuse_index[0]; + + return (damp->reuse_offset + index) % damp->reuse_list_size; +} + +/* Add BGP dampening information to reuse list. */ +static void +bgp_reuse_list_add (struct bgp_damp_info *bdi) +{ + int index; + + index = bdi->index = bgp_reuse_index (bdi->penalty); + + bdi->prev = NULL; + bdi->next = damp->reuse_list[index]; + if (damp->reuse_list[index]) + damp->reuse_list[index]->prev = bdi; + damp->reuse_list[index] = bdi; +} + +/* Delete BGP dampening information from reuse list. */ +static void +bgp_reuse_list_delete (struct bgp_damp_info *bdi) +{ + if (bdi->next) + bdi->next->prev = bdi->prev; + if (bdi->prev) + bdi->prev->next = bdi->next; + else + damp->reuse_list[bdi->index] = bdi->next; +} + +/* Return decayed penalty value. */ +int +bgp_damp_decay (time_t tdiff, int penalty) +{ + unsigned int i; + + i = (int) ((double) tdiff / DELTA_T); + + if (i == 0) + return penalty; + + if (i >= damp->decay_array_size) + return 0; + + return (int) (penalty * damp->decay_array[i]); +} + +/* Handler of reuse timer event. Each route in the current reuse-list + is evaluated. RFC2439 Section 4.8.7. */ +static int +bgp_reuse_timer (struct thread *t) +{ + struct bgp_damp_info *bdi; + struct bgp_damp_info *next; + time_t t_now, t_diff; + + damp->t_reuse = NULL; + damp->t_reuse = + thread_add_timer (bm->master, bgp_reuse_timer, NULL, DELTA_REUSE); + + t_now = bgp_clock (); + + /* 1. save a pointer to the current zeroth queue head and zero the + list head entry. */ + bdi = damp->reuse_list[damp->reuse_offset]; + damp->reuse_list[damp->reuse_offset] = NULL; + + /* 2. set offset = modulo reuse-list-size ( offset + 1 ), thereby + rotating the circular queue of list-heads. */ + damp->reuse_offset = (damp->reuse_offset + 1) % damp->reuse_list_size; + + /* 3. if ( the saved list head pointer is non-empty ) */ + for (; bdi; bdi = next) + { + struct bgp *bgp = bdi->binfo->peer->bgp; + + next = bdi->next; + + /* Set t-diff = t-now - t-updated. */ + t_diff = t_now - bdi->t_updated; + + /* Set figure-of-merit = figure-of-merit * decay-array-ok [t-diff] */ + bdi->penalty = bgp_damp_decay (t_diff, bdi->penalty); + + /* Set t-updated = t-now. */ + bdi->t_updated = t_now; + + /* if (figure-of-merit < reuse). */ + if (bdi->penalty < damp->reuse_limit) + { + /* Reuse the route. */ + bgp_info_unset_flag (bdi->rn, bdi->binfo, BGP_INFO_DAMPED); + bdi->suppress_time = 0; + + if (bdi->lastrecord == BGP_RECORD_UPDATE) + { + bgp_info_unset_flag (bdi->rn, bdi->binfo, BGP_INFO_HISTORY); + bgp_aggregate_increment (bgp, &bdi->rn->p, bdi->binfo, + bdi->afi, bdi->safi); + bgp_process (bgp, bdi->rn, bdi->afi, bdi->safi); + } + + if (bdi->penalty <= damp->reuse_limit / 2.0) + bgp_damp_info_free (bdi, 1); + else + BGP_DAMP_LIST_ADD (damp, bdi); + } + else + /* Re-insert into another list (See RFC2439 Section 4.8.6). */ + bgp_reuse_list_add (bdi); + } + + return 0; +} + +/* A route becomes unreachable (RFC2439 Section 4.8.2). */ +int +bgp_damp_withdraw (struct bgp_info *binfo, struct bgp_node *rn, + afi_t afi, safi_t safi, int attr_change) +{ + time_t t_now; + struct bgp_damp_info *bdi = NULL; + double last_penalty = 0; + + t_now = bgp_clock (); + + /* Processing Unreachable Messages. */ + if (binfo->extra) + bdi = binfo->extra->damp_info; + + if (bdi == NULL) + { + /* If there is no previous stability history. */ + + /* RFC2439 said: + 1. allocate a damping structure. + 2. set figure-of-merit = 1. + 3. withdraw the route. */ + + bdi = XCALLOC (MTYPE_BGP_DAMP_INFO, sizeof (struct bgp_damp_info)); + bdi->binfo = binfo; + bdi->rn = rn; + bdi->penalty = (attr_change ? DEFAULT_PENALTY / 2 : DEFAULT_PENALTY); + bdi->flap = 1; + bdi->start_time = t_now; + bdi->suppress_time = 0; + bdi->index = -1; + bdi->afi = afi; + bdi->safi = safi; + (bgp_info_extra_get (binfo))->damp_info = bdi; + BGP_DAMP_LIST_ADD (damp, bdi); + } + else + { + last_penalty = bdi->penalty; + + /* 1. Set t-diff = t-now - t-updated. */ + bdi->penalty = + (bgp_damp_decay (t_now - bdi->t_updated, bdi->penalty) + + (attr_change ? DEFAULT_PENALTY / 2 : DEFAULT_PENALTY)); + + if (bdi->penalty > damp->ceiling) + bdi->penalty = damp->ceiling; + + bdi->flap++; + } + + assert ((rn == bdi->rn) && (binfo == bdi->binfo)); + + bdi->lastrecord = BGP_RECORD_WITHDRAW; + bdi->t_updated = t_now; + + /* Make this route as historical status. */ + bgp_info_set_flag (rn, binfo, BGP_INFO_HISTORY); + + /* Remove the route from a reuse list if it is on one. */ + if (CHECK_FLAG (bdi->binfo->flags, BGP_INFO_DAMPED)) + { + /* If decay rate isn't equal to 0, reinsert brn. */ + if (bdi->penalty != last_penalty) + { + bgp_reuse_list_delete (bdi); + bgp_reuse_list_add (bdi); + } + return BGP_DAMP_SUPPRESSED; + } + + /* If not suppressed before, do annonunce this withdraw and + insert into reuse_list. */ + if (bdi->penalty >= damp->suppress_value) + { + bgp_info_set_flag (rn, binfo, BGP_INFO_DAMPED); + bdi->suppress_time = t_now; + BGP_DAMP_LIST_DEL (damp, bdi); + bgp_reuse_list_add (bdi); + } + + return BGP_DAMP_USED; +} + +int +bgp_damp_update (struct bgp_info *binfo, struct bgp_node *rn, + afi_t afi, safi_t safi) +{ + time_t t_now; + struct bgp_damp_info *bdi; + int status; + + if (!binfo->extra || !((bdi = binfo->extra->damp_info))) + return BGP_DAMP_USED; + + t_now = bgp_clock (); + bgp_info_unset_flag (rn, binfo, BGP_INFO_HISTORY); + + bdi->lastrecord = BGP_RECORD_UPDATE; + bdi->penalty = bgp_damp_decay (t_now - bdi->t_updated, bdi->penalty); + + if (! CHECK_FLAG (bdi->binfo->flags, BGP_INFO_DAMPED) + && (bdi->penalty < damp->suppress_value)) + status = BGP_DAMP_USED; + else if (CHECK_FLAG (bdi->binfo->flags, BGP_INFO_DAMPED) + && (bdi->penalty < damp->reuse_limit) ) + { + bgp_info_unset_flag (rn, binfo, BGP_INFO_DAMPED); + bgp_reuse_list_delete (bdi); + BGP_DAMP_LIST_ADD (damp, bdi); + bdi->suppress_time = 0; + status = BGP_DAMP_USED; + } + else + status = BGP_DAMP_SUPPRESSED; + + if (bdi->penalty > damp->reuse_limit / 2.0) + bdi->t_updated = t_now; + else + bgp_damp_info_free (bdi, 0); + + return status; +} + +/* Remove dampening information and history route. */ +int +bgp_damp_scan (struct bgp_info *binfo, afi_t afi, safi_t safi) +{ + time_t t_now, t_diff; + struct bgp_damp_info *bdi; + + assert (binfo->extra && binfo->extra->damp_info); + + t_now = bgp_clock (); + bdi = binfo->extra->damp_info; + + if (CHECK_FLAG (binfo->flags, BGP_INFO_DAMPED)) + { + t_diff = t_now - bdi->suppress_time; + + if (t_diff >= damp->max_suppress_time) + { + bgp_info_unset_flag (bdi->rn, binfo, BGP_INFO_DAMPED); + bgp_reuse_list_delete (bdi); + BGP_DAMP_LIST_ADD (damp, bdi); + bdi->penalty = damp->reuse_limit; + bdi->suppress_time = 0; + bdi->t_updated = t_now; + + /* Need to announce UPDATE once this binfo is usable again. */ + if (bdi->lastrecord == BGP_RECORD_UPDATE) + return 1; + else + return 0; + } + } + else + { + t_diff = t_now - bdi->t_updated; + bdi->penalty = bgp_damp_decay (t_diff, bdi->penalty); + + if (bdi->penalty <= damp->reuse_limit / 2.0) + { + /* release the bdi, bdi->binfo. */ + bgp_damp_info_free (bdi, 1); + return 0; + } + else + bdi->t_updated = t_now; + } + return 0; +} + +void +bgp_damp_info_free (struct bgp_damp_info *bdi, int withdraw) +{ + struct bgp_info *binfo; + + if (! bdi) + return; + + binfo = bdi->binfo; + binfo->extra->damp_info = NULL; + + if (CHECK_FLAG (binfo->flags, BGP_INFO_DAMPED)) + bgp_reuse_list_delete (bdi); + else + BGP_DAMP_LIST_DEL (damp, bdi); + + bgp_info_unset_flag (bdi->rn, binfo, BGP_INFO_HISTORY|BGP_INFO_DAMPED); + + if (bdi->lastrecord == BGP_RECORD_WITHDRAW && withdraw) + bgp_info_delete (bdi->rn, binfo); + + XFREE (MTYPE_BGP_DAMP_INFO, bdi); +} + +static void +bgp_damp_parameter_set (int hlife, int reuse, int sup, int maxsup) +{ + double reuse_max_ratio; + unsigned int i; + double j; + + damp->suppress_value = sup; + damp->half_life = hlife; + damp->reuse_limit = reuse; + damp->max_suppress_time = maxsup; + + /* Initialize params per bgp_damp_config. */ + damp->reuse_index_size = REUSE_ARRAY_SIZE; + + damp->ceiling = (int)(damp->reuse_limit * (pow(2, (double)damp->max_suppress_time/damp->half_life))); + + /* Decay-array computations */ + damp->decay_array_size = ceil ((double) damp->max_suppress_time / DELTA_T); + damp->decay_array = XMALLOC (MTYPE_BGP_DAMP_ARRAY, + sizeof(double) * (damp->decay_array_size)); + damp->decay_array[0] = 1.0; + damp->decay_array[1] = exp ((1.0/((double)damp->half_life/DELTA_T)) * log(0.5)); + + /* Calculate decay values for all possible times */ + for (i = 2; i < damp->decay_array_size; i++) + damp->decay_array[i] = damp->decay_array[i-1] * damp->decay_array[1]; + + /* Reuse-list computations */ + i = ceil ((double)damp->max_suppress_time / DELTA_REUSE) + 1; + if (i > REUSE_LIST_SIZE || i == 0) + i = REUSE_LIST_SIZE; + damp->reuse_list_size = i; + + damp->reuse_list = XCALLOC (MTYPE_BGP_DAMP_ARRAY, + damp->reuse_list_size + * sizeof (struct bgp_reuse_node *)); + + /* Reuse-array computations */ + damp->reuse_index = XCALLOC (MTYPE_BGP_DAMP_ARRAY, + sizeof(int) * damp->reuse_index_size); + + reuse_max_ratio = (double)damp->ceiling/damp->reuse_limit; + j = (exp((double)damp->max_suppress_time/damp->half_life) * log10(2.0)); + if ( reuse_max_ratio > j && j != 0 ) + reuse_max_ratio = j; + + damp->scale_factor = (double)damp->reuse_index_size/(reuse_max_ratio - 1); + + for (i = 0; i < damp->reuse_index_size; i++) + { + damp->reuse_index[i] = + (int)(((double)damp->half_life / DELTA_REUSE) + * log10 (1.0 / (damp->reuse_limit * ( 1.0 + ((double)i/damp->scale_factor)))) / log10(0.5)); + } +} + +int +bgp_damp_enable (struct bgp *bgp, afi_t afi, safi_t safi, time_t half, + unsigned int reuse, unsigned int suppress, time_t max) +{ + if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)) + { + if (damp->half_life == half + && damp->reuse_limit == reuse + && damp->suppress_value == suppress + && damp->max_suppress_time == max) + return 0; + bgp_damp_disable (bgp, afi, safi); + } + + SET_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING); + bgp_damp_parameter_set (half, reuse, suppress, max); + + /* Register reuse timer. */ + if (! damp->t_reuse) + damp->t_reuse = + thread_add_timer (bm->master, bgp_reuse_timer, NULL, DELTA_REUSE); + + return 0; +} + +static void +bgp_damp_config_clean (struct bgp_damp_config *damp) +{ + /* Free decay array */ + XFREE (MTYPE_BGP_DAMP_ARRAY, damp->decay_array); + + /* Free reuse index array */ + XFREE (MTYPE_BGP_DAMP_ARRAY, damp->reuse_index); + + /* Free reuse list array. */ + XFREE (MTYPE_BGP_DAMP_ARRAY, damp->reuse_list); +} + +/* Clean all the bgp_damp_info stored in reuse_list. */ +void +bgp_damp_info_clean (void) +{ + unsigned int i; + struct bgp_damp_info *bdi, *next; + + damp->reuse_offset = 0; + + for (i = 0; i < damp->reuse_list_size; i++) + { + if (! damp->reuse_list[i]) + continue; + + for (bdi = damp->reuse_list[i]; bdi; bdi = next) + { + next = bdi->next; + bgp_damp_info_free (bdi, 1); + } + damp->reuse_list[i] = NULL; + } + + for (bdi = damp->no_reuse_list; bdi; bdi = next) + { + next = bdi->next; + bgp_damp_info_free (bdi, 1); + } + damp->no_reuse_list = NULL; +} + +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); + damp->t_reuse = NULL; + + /* Clean BGP dampening information. */ + bgp_damp_info_clean (); + + /* Clear configuration */ + bgp_damp_config_clean (&bgp_damp_cfg); + + UNSET_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING); + return 0; +} + +void +bgp_config_write_damp (struct vty *vty) +{ + if (bgp_damp_cfg.half_life == DEFAULT_HALF_LIFE*60 + && 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%s", VTY_NEWLINE); + else if (bgp_damp_cfg.half_life != DEFAULT_HALF_LIFE*60 + && 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 %lld%s", + bgp_damp_cfg.half_life/60LL, + VTY_NEWLINE); + else + 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/60LL, + VTY_NEWLINE); +} + +static const char * +bgp_get_reuse_time (unsigned int penalty, char *buf, size_t len) +{ + time_t reuse_time = 0; + struct tm *tm = NULL; + + if (penalty > damp->reuse_limit) + { + reuse_time = (int) (DELTA_T * ((log((double)damp->reuse_limit/penalty))/(log(damp->decay_array[1])))); + + if (reuse_time > damp->max_suppress_time) + reuse_time = damp->max_suppress_time; + + tm = gmtime (&reuse_time); + } + else + reuse_time = 0; + + /* Making formatted timer strings. */ +#define ONE_DAY_SECOND 60*60*24 +#define ONE_WEEK_SECOND 60*60*24*7 + if (reuse_time == 0) + snprintf (buf, len, "00:00:00"); + else if (reuse_time < ONE_DAY_SECOND) + snprintf (buf, len, "%02d:%02d:%02d", + tm->tm_hour, tm->tm_min, tm->tm_sec); + else if (reuse_time < ONE_WEEK_SECOND) + snprintf (buf, len, "%dd%02dh%02dm", + tm->tm_yday, tm->tm_hour, tm->tm_min); + else + snprintf (buf, len, "%02dw%dd%02dh", + tm->tm_yday/7, tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour); + + return buf; +} + +void +bgp_damp_info_vty (struct vty *vty, struct bgp_info *binfo) +{ + struct bgp_damp_info *bdi; + time_t t_now, t_diff; + char timebuf[BGP_UPTIME_LEN]; + int penalty; + + if (!binfo->extra) + return; + + /* BGP dampening information. */ + bdi = binfo->extra->damp_info; + + /* If dampening is not enabled or there is no dampening information, + return immediately. */ + if (! damp || ! bdi) + return; + + /* Calculate new penalty. */ + t_now = bgp_clock (); + t_diff = t_now - bdi->t_updated; + penalty = bgp_damp_decay (t_diff, bdi->penalty); + + vty_out (vty, " Dampinfo: penalty %d, flapped %d times in %s", + penalty, bdi->flap, + peer_uptime (bdi->start_time, timebuf, BGP_UPTIME_LEN)); + + if (CHECK_FLAG (binfo->flags, BGP_INFO_DAMPED) + && ! CHECK_FLAG (binfo->flags, BGP_INFO_HISTORY)) + vty_out (vty, ", reuse in %s", + bgp_get_reuse_time (penalty, timebuf, BGP_UPTIME_LEN)); + + vty_out (vty, "%s", VTY_NEWLINE); +} + +const char * +bgp_damp_reuse_time_vty (struct vty *vty, struct bgp_info *binfo, + char *timebuf, size_t len) +{ + struct bgp_damp_info *bdi; + time_t t_now, t_diff; + int penalty; + + if (!binfo->extra) + return NULL; + + /* BGP dampening information. */ + bdi = binfo->extra->damp_info; + + /* If dampening is not enabled or there is no dampening information, + return immediately. */ + if (! damp || ! bdi) + return NULL; + + /* Calculate new penalty. */ + t_now = bgp_clock (); + t_diff = t_now - bdi->t_updated; + penalty = bgp_damp_decay (t_diff, bdi->penalty); + + 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 new file mode 100644 index 0000000..16fd367 --- /dev/null +++ b/bgpd/bgp_damp.h @@ -0,0 +1,149 @@ +/* BGP flap dampening + Copyright (C) 2001 IP Infusion 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 _QUAGGA_BGP_DAMP_H +#define _QUAGGA_BGP_DAMP_H + +/* Structure maintained on a per-route basis. */ +struct bgp_damp_info +{ + /* Doubly linked list. This information must be linked to + reuse_list or no_reuse_list. */ + struct bgp_damp_info *next; + struct bgp_damp_info *prev; + + /* Figure-of-merit. */ + unsigned int penalty; + + /* Number of flapping. */ + unsigned int flap; + + /* First flap time */ + time_t start_time; + + /* Last time penalty was updated. */ + time_t t_updated; + + /* Time of route start to be suppressed. */ + time_t suppress_time; + + /* Back reference to bgp_info. */ + struct bgp_info *binfo; + + /* Back reference to bgp_node. */ + struct bgp_node *rn; + + /* Current index in the reuse_list. */ + int index; + + /* Last time message type. */ + u_char lastrecord; +#define BGP_RECORD_UPDATE 1U +#define BGP_RECORD_WITHDRAW 2U + + afi_t afi; + safi_t safi; +}; + +/* Specified parameter set configuration. */ +struct bgp_damp_config +{ + /* Value over which routes suppressed. */ + unsigned int suppress_value; + + /* Value below which suppressed routes reused. */ + unsigned int reuse_limit; + + /* Max time a route can be suppressed. */ + time_t max_suppress_time; + + /* Time during which accumulated penalty reduces by half. */ + time_t half_life; + + /* Non-configurable parameters but fixed at implementation time. + * To change this values, init_bgp_damp() should be modified. + */ + time_t tmax; /* Max time previous instability retained */ + unsigned int reuse_list_size; /* Number of reuse lists */ + unsigned int reuse_index_size; /* Size of reuse index array */ + + /* Non-configurable parameters. Most of these are calculated from + * the configurable parameters above. + */ + unsigned int ceiling; /* Max value a penalty can attain */ + unsigned int decay_rate_per_tick; /* Calculated from half-life */ + unsigned int decay_array_size; /* Calculated using config parameters */ + double scale_factor; + unsigned int reuse_scale_factor; + + /* Decay array per-set based. */ + double *decay_array; + + /* Reuse index array per-set based. */ + int *reuse_index; + + /* Reuse list array per-set based. */ + struct bgp_damp_info **reuse_list; + int reuse_offset; + + /* All dampening information which is not on reuse list. */ + struct bgp_damp_info *no_reuse_list; + + /* Reuse timer thread per-set base. */ + struct thread* t_reuse; +}; + +#define BGP_DAMP_NONE 0 +#define BGP_DAMP_USED 1 +#define BGP_DAMP_SUPPRESSED 2 + +/* Time granularity for reuse lists */ +#define DELTA_REUSE 10 + +/* Time granularity for decay arrays */ +#define DELTA_T 5 + +#define DEFAULT_PENALTY 1000 + +#define DEFAULT_HALF_LIFE 15 +#define DEFAULT_REUSE 750 +#define DEFAULT_SUPPRESS 2000 + +#define REUSE_LIST_SIZE 256 +#define REUSE_ARRAY_SIZE 1024 + +extern int bgp_damp_enable (struct bgp *, afi_t, safi_t, time_t, unsigned int, + unsigned int, time_t); +extern int bgp_damp_disable (struct bgp *, afi_t, safi_t); +extern int bgp_damp_withdraw (struct bgp_info *, struct bgp_node *, + afi_t, safi_t, int); +extern int bgp_damp_update (struct bgp_info *, struct bgp_node *, afi_t, safi_t); +extern int bgp_damp_scan (struct bgp_info *, afi_t, safi_t); +extern void bgp_damp_info_free (struct bgp_damp_info *, int); +extern void bgp_damp_info_clean (void); +extern int bgp_damp_decay (time_t, int); +extern void bgp_config_write_damp (struct vty *); +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 new file mode 100644 index 0000000..ba79722 --- /dev/null +++ b/bgpd/bgp_debug.c @@ -0,0 +1,1046 @@ +/* BGP-4, BGP-4+ packet debug routine + Copyright (C) 1996, 97, 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 +#include "prefix.h" +#include "linklist.h" +#include "stream.h" +#include "command.h" +#include "str.h" +#include "log.h" +#include "sockunion.h" +#include "filter.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_debug.h" +#include "bgpd/bgp_community.h" + +unsigned long conf_bgp_debug_as4; +unsigned long conf_bgp_debug_fsm; +unsigned long conf_bgp_debug_events; +unsigned long conf_bgp_debug_packet; +unsigned long conf_bgp_debug_filter; +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; +unsigned long term_bgp_debug_events; +unsigned long term_bgp_debug_packet; +unsigned long term_bgp_debug_filter; +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[] = +{ + { Idle, "Idle" }, + { Connect, "Connect" }, + { Active, "Active" }, + { OpenSent, "OpenSent" }, + { OpenConfirm, "OpenConfirm" }, + { Established, "Established" }, + { Clearing, "Clearing" }, + { Deleted, "Deleted" }, +}; +const int bgp_status_msg_max = BGP_STATUS_MAX; + +/* BGP message type string. */ +const char *bgp_type_str[] = +{ + NULL, + "OPEN", + "UPDATE", + "NOTIFICATION", + "KEEPALIVE", + "ROUTE-REFRESH", + "CAPABILITY" +}; + +/* message for BGP-4 Notify */ +static const struct message bgp_notify_msg[] = +{ + { BGP_NOTIFY_HEADER_ERR, "Message Header Error"}, + { BGP_NOTIFY_OPEN_ERR, "OPEN Message Error"}, + { BGP_NOTIFY_UPDATE_ERR, "UPDATE Message Error"}, + { BGP_NOTIFY_HOLD_ERR, "Hold Timer Expired"}, + { BGP_NOTIFY_FSM_ERR, "Finite State Machine Error"}, + { BGP_NOTIFY_CEASE, "Cease"}, + { BGP_NOTIFY_CAPABILITY_ERR, "CAPABILITY Message Error"}, +}; +static const int bgp_notify_msg_max = BGP_NOTIFY_MAX; + +static const struct message bgp_notify_head_msg[] = +{ + { BGP_NOTIFY_HEADER_NOT_SYNC, "/Connection Not Synchronized"}, + { BGP_NOTIFY_HEADER_BAD_MESLEN, "/Bad Message Length"}, + { BGP_NOTIFY_HEADER_BAD_MESTYPE, "/Bad Message Type"} +}; +static const int bgp_notify_head_msg_max = BGP_NOTIFY_HEADER_MAX; + +static const struct message bgp_notify_open_msg[] = +{ + { BGP_NOTIFY_SUBCODE_UNSPECIFIC, "/Unspecific"}, + { BGP_NOTIFY_OPEN_UNSUP_VERSION, "/Unsupported Version Number" }, + { BGP_NOTIFY_OPEN_BAD_PEER_AS, "/Bad Peer AS"}, + { BGP_NOTIFY_OPEN_BAD_BGP_IDENT, "/Bad BGP Identifier"}, + { BGP_NOTIFY_OPEN_UNSUP_PARAM, "/Unsupported Optional Parameter"}, + { BGP_NOTIFY_OPEN_AUTH_FAILURE, "/Authentication Failure"}, + { BGP_NOTIFY_OPEN_UNACEP_HOLDTIME, "/Unacceptable Hold Time"}, + { BGP_NOTIFY_OPEN_UNSUP_CAPBL, "/Unsupported Capability"}, +}; +static const int bgp_notify_open_msg_max = BGP_NOTIFY_OPEN_MAX; + +static const struct message bgp_notify_update_msg[] = +{ + { BGP_NOTIFY_SUBCODE_UNSPECIFIC, "/Unspecific"}, + { BGP_NOTIFY_UPDATE_MAL_ATTR, "/Malformed Attribute List"}, + { BGP_NOTIFY_UPDATE_UNREC_ATTR, "/Unrecognized Well-known Attribute"}, + { BGP_NOTIFY_UPDATE_MISS_ATTR, "/Missing Well-known Attribute"}, + { BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR, "/Attribute Flags Error"}, + { BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, "/Attribute Length Error"}, + { BGP_NOTIFY_UPDATE_INVAL_ORIGIN, "/Invalid ORIGIN Attribute"}, + { BGP_NOTIFY_UPDATE_AS_ROUTE_LOOP, "/AS Routing Loop"}, + { BGP_NOTIFY_UPDATE_INVAL_NEXT_HOP, "/Invalid NEXT_HOP Attribute"}, + { BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, "/Optional Attribute Error"}, + { BGP_NOTIFY_UPDATE_INVAL_NETWORK, "/Invalid Network Field"}, + { BGP_NOTIFY_UPDATE_MAL_AS_PATH, "/Malformed AS_PATH"}, +}; +static const int bgp_notify_update_msg_max = BGP_NOTIFY_UPDATE_MAX; + +static const struct message bgp_notify_cease_msg[] = +{ + { BGP_NOTIFY_SUBCODE_UNSPECIFIC, "/Unspecific"}, + { BGP_NOTIFY_CEASE_MAX_PREFIX, "/Maximum Number of Prefixes Reached"}, + { BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN, "/Administratively Shutdown"}, + { BGP_NOTIFY_CEASE_PEER_UNCONFIG, "/Peer Unconfigured"}, + { BGP_NOTIFY_CEASE_ADMIN_RESET, "/Administratively Reset"}, + { BGP_NOTIFY_CEASE_CONNECT_REJECT, "/Connection Rejected"}, + { BGP_NOTIFY_CEASE_CONFIG_CHANGE, "/Other Configuration Change"}, + { BGP_NOTIFY_CEASE_COLLISION_RESOLUTION, "/Connection collision resolution"}, + { BGP_NOTIFY_CEASE_OUT_OF_RESOURCE, "/Out of Resource"}, +}; +static const int bgp_notify_cease_msg_max = BGP_NOTIFY_CEASE_MAX; + +static const struct message bgp_notify_capability_msg[] = +{ + { BGP_NOTIFY_SUBCODE_UNSPECIFIC, "/Unspecific"}, + { BGP_NOTIFY_CAPABILITY_INVALID_ACTION, "/Invalid Action Value" }, + { BGP_NOTIFY_CAPABILITY_INVALID_LENGTH, "/Invalid Capability Length"}, + { BGP_NOTIFY_CAPABILITY_MALFORMED_CODE, "/Malformed Capability Value"}, +}; +static const int bgp_notify_capability_msg_max = BGP_NOTIFY_CAPABILITY_MAX; + +/* Origin strings. */ +const char *bgp_origin_str[] = {"i","e","?"}; +const char *bgp_origin_long_str[] = {"IGP","EGP","incomplete"}; + +/* Dump attribute. */ +int +bgp_dump_attr (struct peer *peer, struct attr *attr, char *buf, size_t size) +{ + if (! attr) + return 0; + + if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP))) + snprintf (buf, size, "nexthop %s", inet_ntoa (attr->nexthop)); + + if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_ORIGIN))) + snprintf (buf + strlen (buf), size - strlen (buf), ", origin %s", + bgp_origin_str[attr->origin]); + + if (attr->extra) + { + char addrbuf[BUFSIZ]; + + /* Add MP case. */ + if (attr->extra->mp_nexthop_len == 16 + || attr->extra->mp_nexthop_len == 32) + snprintf (buf + strlen (buf), size - strlen (buf), ", mp_nexthop %s", + inet_ntop (AF_INET6, &attr->extra->mp_nexthop_global, + addrbuf, BUFSIZ)); + + if (attr->extra->mp_nexthop_len == 32) + snprintf (buf + strlen (buf), size - strlen (buf), "(%s)", + inet_ntop (AF_INET6, &attr->extra->mp_nexthop_local, + addrbuf, BUFSIZ)); + } + + if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF))) + 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 %u", + attr->med); + + if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES))) + snprintf (buf + strlen (buf), size - strlen (buf), ", community %s", + community_str (attr->community)); + + if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE))) + snprintf (buf + strlen (buf), size - strlen (buf), ", atomic-aggregate"); + + if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR))) + snprintf (buf + strlen (buf), size - strlen (buf), ", aggregated by %u %s", + attr->extra->aggregator_as, + inet_ntoa (attr->extra->aggregator_addr)); + + if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID))) + snprintf (buf + strlen (buf), size - strlen (buf), ", originator %s", + inet_ntoa (attr->extra->originator_id)); + + if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_CLUSTER_LIST))) + { + int i; + + snprintf (buf + strlen (buf), size - strlen (buf), ", clusterlist"); + for (i = 0; i < attr->extra->cluster->length / 4; i++) + snprintf (buf + strlen (buf), size - strlen (buf), " %s", + inet_ntoa (attr->extra->cluster->list[i])); + } + + if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AS_PATH))) + snprintf (buf + strlen (buf), size - strlen (buf), ", path %s", + aspath_print (attr->aspath)); + + if (strlen (buf) > 1) + return 1; + else + return 0; +} + +/* dump notify packet */ +void +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) + { + case BGP_NOTIFY_HEADER_ERR: + subcode_str = LOOKUP_DEF (bgp_notify_head_msg, bgp_notify->subcode, + "Unrecognized Error Subcode"); + break; + case BGP_NOTIFY_OPEN_ERR: + subcode_str = LOOKUP_DEF (bgp_notify_open_msg, bgp_notify->subcode, + "Unrecognized Error Subcode"); + break; + case BGP_NOTIFY_UPDATE_ERR: + subcode_str = LOOKUP_DEF (bgp_notify_update_msg, bgp_notify->subcode, + "Unrecognized Error Subcode"); + break; + case BGP_NOTIFY_HOLD_ERR: + break; + case BGP_NOTIFY_FSM_ERR: + break; + case BGP_NOTIFY_CEASE: + subcode_str = LOOKUP_DEF (bgp_notify_cease_msg, bgp_notify->subcode, + "Unrecognized Error Subcode"); + break; + case BGP_NOTIFY_CAPABILITY_ERR: + subcode_str = LOOKUP_DEF (bgp_notify_capability_msg, bgp_notify->subcode, + "Unrecognized Error Subcode"); + break; + } + + if (bgp_flag_check (peer->bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES)) + 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, + 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, + code_str, subcode_str, bgp_notify->length, + bgp_notify->data ? bgp_notify->data : ""); +} + +/* Debug option setting interface. */ +unsigned long bgp_debug_option = 0; + +int +debug (unsigned int option) +{ + return bgp_debug_option & option; +} + +DEFUN (debug_bgp_as4, + debug_bgp_as4_cmd, + "debug bgp as4", + DEBUG_STR + BGP_STR + "BGP AS4 actions\n") +{ + if (vty->node == CONFIG_NODE) + DEBUG_ON (as4, AS4); + else + { + TERM_DEBUG_ON (as4, AS4); + vty_out (vty, "BGP as4 debugging is on%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +DEFUN (no_debug_bgp_as4, + no_debug_bgp_as4_cmd, + "no debug bgp as4", + NO_STR + DEBUG_STR + BGP_STR + "BGP AS4 actions\n") +{ + if (vty->node == CONFIG_NODE) + DEBUG_OFF (as4, AS4); + else + { + TERM_DEBUG_OFF (as4, AS4); + vty_out (vty, "BGP as4 debugging is off%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +ALIAS (no_debug_bgp_as4, + undebug_bgp_as4_cmd, + "undebug bgp as4", + UNDEBUG_STR + BGP_STR + "BGP AS4 actions\n") + +DEFUN (debug_bgp_as4_segment, + debug_bgp_as4_segment_cmd, + "debug bgp as4 segment", + DEBUG_STR + BGP_STR + "BGP AS4 actions\n" + "BGP AS4 aspath segment handling\n") +{ + if (vty->node == CONFIG_NODE) + DEBUG_ON (as4, AS4_SEGMENT); + else + { + TERM_DEBUG_ON (as4, AS4_SEGMENT); + vty_out (vty, "BGP as4 segment debugging is on%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +DEFUN (no_debug_bgp_as4_segment, + no_debug_bgp_as4_segment_cmd, + "no debug bgp as4 segment", + NO_STR + DEBUG_STR + BGP_STR + "BGP AS4 actions\n" + "BGP AS4 aspath segment handling\n") +{ + if (vty->node == CONFIG_NODE) + DEBUG_OFF (as4, AS4_SEGMENT); + else + { + TERM_DEBUG_OFF (as4, AS4_SEGMENT); + vty_out (vty, "BGP as4 segment debugging is off%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +ALIAS (no_debug_bgp_as4_segment, + undebug_bgp_as4_segment_cmd, + "undebug bgp as4 segment", + UNDEBUG_STR + BGP_STR + "BGP AS4 actions\n" + "BGP AS4 aspath segment handling\n") + +DEFUN (debug_bgp_fsm, + debug_bgp_fsm_cmd, + "debug bgp fsm", + DEBUG_STR + BGP_STR + "BGP Finite State Machine\n") +{ + if (vty->node == CONFIG_NODE) + DEBUG_ON (fsm, FSM); + else + { + TERM_DEBUG_ON (fsm, FSM); + vty_out (vty, "BGP fsm debugging is on%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +DEFUN (no_debug_bgp_fsm, + no_debug_bgp_fsm_cmd, + "no debug bgp fsm", + NO_STR + DEBUG_STR + BGP_STR + "Finite State Machine\n") +{ + if (vty->node == CONFIG_NODE) + DEBUG_OFF (fsm, FSM); + else + { + TERM_DEBUG_OFF (fsm, FSM); + vty_out (vty, "BGP fsm debugging is off%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +ALIAS (no_debug_bgp_fsm, + undebug_bgp_fsm_cmd, + "undebug bgp fsm", + UNDEBUG_STR + BGP_STR + "Finite State Machine\n") + +DEFUN (debug_bgp_events, + debug_bgp_events_cmd, + "debug bgp events", + DEBUG_STR + BGP_STR + "BGP events\n") +{ + if (vty->node == CONFIG_NODE) + DEBUG_ON (events, EVENTS); + else + { + TERM_DEBUG_ON (events, EVENTS); + vty_out (vty, "BGP events debugging is on%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +DEFUN (no_debug_bgp_events, + no_debug_bgp_events_cmd, + "no debug bgp events", + NO_STR + DEBUG_STR + BGP_STR + "BGP events\n") +{ + if (vty->node == CONFIG_NODE) + DEBUG_OFF (events, EVENTS); + else + { + TERM_DEBUG_OFF (events, EVENTS); + vty_out (vty, "BGP events debugging is off%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +ALIAS (no_debug_bgp_events, + undebug_bgp_events_cmd, + "undebug bgp events", + UNDEBUG_STR + 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", + DEBUG_STR + BGP_STR + "BGP filters\n") +{ + if (vty->node == CONFIG_NODE) + DEBUG_ON (filter, FILTER); + else + { + TERM_DEBUG_ON (filter, FILTER); + vty_out (vty, "BGP filters debugging is on%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +DEFUN (no_debug_bgp_filter, + no_debug_bgp_filter_cmd, + "no debug bgp filters", + NO_STR + DEBUG_STR + BGP_STR + "BGP filters\n") +{ + if (vty->node == CONFIG_NODE) + DEBUG_OFF (filter, FILTER); + else + { + TERM_DEBUG_OFF (filter, FILTER); + vty_out (vty, "BGP filters debugging is off%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +ALIAS (no_debug_bgp_filter, + undebug_bgp_filter_cmd, + "undebug bgp filters", + UNDEBUG_STR + BGP_STR + "BGP filters\n") + +DEFUN (debug_bgp_keepalive, + debug_bgp_keepalive_cmd, + "debug bgp keepalives", + DEBUG_STR + BGP_STR + "BGP keepalives\n") +{ + if (vty->node == CONFIG_NODE) + DEBUG_ON (keepalive, KEEPALIVE); + else + { + TERM_DEBUG_ON (keepalive, KEEPALIVE); + vty_out (vty, "BGP keepalives debugging is on%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +DEFUN (no_debug_bgp_keepalive, + no_debug_bgp_keepalive_cmd, + "no debug bgp keepalives", + NO_STR + DEBUG_STR + BGP_STR + "BGP keepalives\n") +{ + if (vty->node == CONFIG_NODE) + DEBUG_OFF (keepalive, KEEPALIVE); + else + { + TERM_DEBUG_OFF (keepalive, KEEPALIVE); + vty_out (vty, "BGP keepalives debugging is off%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +ALIAS (no_debug_bgp_keepalive, + undebug_bgp_keepalive_cmd, + "undebug bgp keepalives", + UNDEBUG_STR + BGP_STR + "BGP keepalives\n") + +DEFUN (debug_bgp_update, + debug_bgp_update_cmd, + "debug bgp updates", + DEBUG_STR + BGP_STR + "BGP updates\n") +{ + if (vty->node == CONFIG_NODE) + { + DEBUG_ON (update, UPDATE_IN); + DEBUG_ON (update, UPDATE_OUT); + } + else + { + TERM_DEBUG_ON (update, UPDATE_IN); + TERM_DEBUG_ON (update, UPDATE_OUT); + vty_out (vty, "BGP updates debugging is on%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +DEFUN (debug_bgp_update_direct, + debug_bgp_update_direct_cmd, + "debug bgp updates (in|out)", + DEBUG_STR + BGP_STR + "BGP updates\n" + "Inbound updates\n" + "Outbound updates\n") +{ + if (vty->node == CONFIG_NODE) + { + if (strncmp ("i", argv[0], 1) == 0) + { + DEBUG_OFF (update, UPDATE_OUT); + DEBUG_ON (update, UPDATE_IN); + } + else + { + DEBUG_OFF (update, UPDATE_IN); + DEBUG_ON (update, UPDATE_OUT); + } + } + else + { + if (strncmp ("i", argv[0], 1) == 0) + { + TERM_DEBUG_OFF (update, UPDATE_OUT); + TERM_DEBUG_ON (update, UPDATE_IN); + vty_out (vty, "BGP updates debugging is on (inbound)%s", VTY_NEWLINE); + } + else + { + TERM_DEBUG_OFF (update, UPDATE_IN); + TERM_DEBUG_ON (update, UPDATE_OUT); + vty_out (vty, "BGP updates debugging is on (outbound)%s", VTY_NEWLINE); + } + } + return CMD_SUCCESS; +} + +DEFUN (no_debug_bgp_update, + no_debug_bgp_update_cmd, + "no debug bgp updates", + NO_STR + DEBUG_STR + BGP_STR + "BGP updates\n") +{ + if (vty->node == CONFIG_NODE) + { + DEBUG_OFF (update, UPDATE_IN); + DEBUG_OFF (update, UPDATE_OUT); + } + else + { + TERM_DEBUG_OFF (update, UPDATE_IN); + TERM_DEBUG_OFF (update, UPDATE_OUT); + vty_out (vty, "BGP updates debugging is off%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +ALIAS (no_debug_bgp_update, + undebug_bgp_update_cmd, + "undebug bgp updates", + UNDEBUG_STR + BGP_STR + "BGP updates\n") + +DEFUN (debug_bgp_normal, + debug_bgp_normal_cmd, + "debug bgp", + DEBUG_STR + BGP_STR) +{ + if (vty->node == CONFIG_NODE) + DEBUG_ON (normal, NORMAL); + else + { + TERM_DEBUG_ON (normal, NORMAL); + vty_out (vty, "BGP debugging is on%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +DEFUN (no_debug_bgp_normal, + no_debug_bgp_normal_cmd, + "no debug bgp", + NO_STR + DEBUG_STR + BGP_STR) +{ + if (vty->node == CONFIG_NODE) + DEBUG_OFF (normal, NORMAL); + else + { + TERM_DEBUG_OFF (normal, NORMAL); + vty_out (vty, "BGP debugging is off%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +ALIAS (no_debug_bgp_normal, + undebug_bgp_normal_cmd, + "undebug bgp", + UNDEBUG_STR + BGP_STR) + +DEFUN (debug_bgp_zebra, + debug_bgp_zebra_cmd, + "debug bgp zebra", + DEBUG_STR + BGP_STR + "BGP Zebra messages\n") +{ + if (vty->node == CONFIG_NODE) + DEBUG_ON (zebra, ZEBRA); + else + { + TERM_DEBUG_ON (zebra, ZEBRA); + vty_out (vty, "BGP zebra debugging is on%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +DEFUN (no_debug_bgp_zebra, + no_debug_bgp_zebra_cmd, + "no debug bgp zebra", + NO_STR + DEBUG_STR + BGP_STR + "BGP Zebra messages\n") +{ + if (vty->node == CONFIG_NODE) + DEBUG_OFF (zebra, ZEBRA); + else + { + TERM_DEBUG_OFF (zebra, ZEBRA); + vty_out (vty, "BGP zebra debugging is off%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +ALIAS (no_debug_bgp_zebra, + undebug_bgp_zebra_cmd, + "undebug bgp zebra", + UNDEBUG_STR + 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", + NO_STR + DEBUG_STR + "Enable all debugging\n" + BGP_STR) +{ + TERM_DEBUG_OFF (normal, NORMAL); + TERM_DEBUG_OFF (events, EVENTS); + TERM_DEBUG_OFF (keepalive, KEEPALIVE); + TERM_DEBUG_OFF (update, UPDATE_IN); + TERM_DEBUG_OFF (update, UPDATE_OUT); + TERM_DEBUG_OFF (as4, AS4); + TERM_DEBUG_OFF (as4, AS4_SEGMENT); + 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; +} + +ALIAS (no_debug_bgp_all, + undebug_bgp_all_cmd, + "undebug all bgp", + UNDEBUG_STR + "Enable all debugging\n" + BGP_STR) + +DEFUN (show_debugging_bgp, + show_debugging_bgp_cmd, + "show debugging bgp", + SHOW_STR + DEBUG_STR + BGP_STR) +{ + vty_out (vty, "BGP debugging status:%s", VTY_NEWLINE); + + if (BGP_DEBUG (normal, NORMAL)) + vty_out (vty, " BGP debugging is on%s", VTY_NEWLINE); + if (BGP_DEBUG (events, EVENTS)) + vty_out (vty, " BGP events debugging is on%s", VTY_NEWLINE); + if (BGP_DEBUG (keepalive, KEEPALIVE)) + vty_out (vty, " BGP keepalives debugging is on%s", VTY_NEWLINE); + if (BGP_DEBUG (update, UPDATE_IN) && BGP_DEBUG (update, UPDATE_OUT)) + vty_out (vty, " BGP updates debugging is on%s", VTY_NEWLINE); + else if (BGP_DEBUG (update, UPDATE_IN)) + vty_out (vty, " BGP updates debugging is on (inbound)%s", VTY_NEWLINE); + else if (BGP_DEBUG (update, UPDATE_OUT)) + vty_out (vty, " BGP updates debugging is on (outbound)%s", VTY_NEWLINE); + if (BGP_DEBUG (fsm, FSM)) + vty_out (vty, " BGP fsm debugging is on%s", VTY_NEWLINE); + if (BGP_DEBUG (filter, FILTER)) + vty_out (vty, " BGP filter debugging is on%s", VTY_NEWLINE); + if (BGP_DEBUG (zebra, ZEBRA)) + vty_out (vty, " BGP zebra debugging is on%s", VTY_NEWLINE); + if (BGP_DEBUG (as4, AS4)) + 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; +} + +static int +bgp_config_write_debug (struct vty *vty) +{ + int write = 0; + + if (CONF_BGP_DEBUG (normal, NORMAL)) + { + vty_out (vty, "debug bgp%s", VTY_NEWLINE); + write++; + } + + if (CONF_BGP_DEBUG (as4, AS4)) + { + vty_out (vty, "debug bgp as4%s", VTY_NEWLINE); + write++; + } + + if (CONF_BGP_DEBUG (as4, AS4_SEGMENT)) + { + vty_out (vty, "debug bgp as4 segment%s", VTY_NEWLINE); + write++; + } + + if (CONF_BGP_DEBUG (events, EVENTS)) + { + vty_out (vty, "debug bgp events%s", VTY_NEWLINE); + write++; + } + + if (CONF_BGP_DEBUG (keepalive, KEEPALIVE)) + { + vty_out (vty, "debug bgp keepalives%s", VTY_NEWLINE); + write++; + } + + if (CONF_BGP_DEBUG (update, UPDATE_IN) && CONF_BGP_DEBUG (update, UPDATE_OUT)) + { + vty_out (vty, "debug bgp updates%s", VTY_NEWLINE); + write++; + } + else if (CONF_BGP_DEBUG (update, UPDATE_IN)) + { + vty_out (vty, "debug bgp updates in%s", VTY_NEWLINE); + write++; + } + else if (CONF_BGP_DEBUG (update, UPDATE_OUT)) + { + vty_out (vty, "debug bgp updates out%s", VTY_NEWLINE); + write++; + } + + if (CONF_BGP_DEBUG (fsm, FSM)) + { + vty_out (vty, "debug bgp fsm%s", VTY_NEWLINE); + write++; + } + + if (CONF_BGP_DEBUG (filter, FILTER)) + { + vty_out (vty, "debug bgp filters%s", VTY_NEWLINE); + write++; + } + + if (CONF_BGP_DEBUG (zebra, ZEBRA)) + { + vty_out (vty, "debug bgp zebra%s", VTY_NEWLINE); + 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; +} + +static struct cmd_node debug_node = +{ + DEBUG_NODE, + "", + 1 +}; + +void +bgp_debug_init (void) +{ + install_node (&debug_node, bgp_config_write_debug); + + install_element (ENABLE_NODE, &show_debugging_bgp_cmd); + + install_element (ENABLE_NODE, &debug_bgp_as4_cmd); + install_element (CONFIG_NODE, &debug_bgp_as4_cmd); + install_element (ENABLE_NODE, &debug_bgp_as4_segment_cmd); + install_element (CONFIG_NODE, &debug_bgp_as4_segment_cmd); + + install_element (ENABLE_NODE, &debug_bgp_fsm_cmd); + 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); + install_element (CONFIG_NODE, &debug_bgp_keepalive_cmd); + install_element (ENABLE_NODE, &debug_bgp_update_cmd); + install_element (CONFIG_NODE, &debug_bgp_update_cmd); + install_element (ENABLE_NODE, &debug_bgp_update_direct_cmd); + install_element (CONFIG_NODE, &debug_bgp_update_direct_cmd); + install_element (ENABLE_NODE, &debug_bgp_normal_cmd); + 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); + install_element (CONFIG_NODE, &no_debug_bgp_as4_cmd); + install_element (ENABLE_NODE, &no_debug_bgp_as4_segment_cmd); + install_element (ENABLE_NODE, &undebug_bgp_as4_segment_cmd); + install_element (CONFIG_NODE, &no_debug_bgp_as4_segment_cmd); + + install_element (ENABLE_NODE, &no_debug_bgp_fsm_cmd); + install_element (ENABLE_NODE, &undebug_bgp_fsm_cmd); + install_element (CONFIG_NODE, &no_debug_bgp_fsm_cmd); + 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); + install_element (ENABLE_NODE, &no_debug_bgp_keepalive_cmd); + install_element (ENABLE_NODE, &undebug_bgp_keepalive_cmd); + install_element (CONFIG_NODE, &no_debug_bgp_keepalive_cmd); + install_element (ENABLE_NODE, &no_debug_bgp_update_cmd); + install_element (ENABLE_NODE, &undebug_bgp_update_cmd); + install_element (CONFIG_NODE, &no_debug_bgp_update_cmd); + install_element (ENABLE_NODE, &no_debug_bgp_normal_cmd); + install_element (ENABLE_NODE, &undebug_bgp_normal_cmd); + install_element (CONFIG_NODE, &no_debug_bgp_normal_cmd); + 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 new file mode 100644 index 0000000..253bd7f --- /dev/null +++ b/bgpd/bgp_debug.h @@ -0,0 +1,134 @@ +/* BGP message debug header. + Copyright (C) 1996, 97, 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. */ + +#ifndef _QUAGGA_BGP_DEBUG_H +#define _QUAGGA_BGP_DEBUG_H + +#include "bgp_attr.h" + +/* sort of packet direction */ +#define DUMP_ON 1 +#define DUMP_SEND 2 +#define DUMP_RECV 4 + +/* for dump_update */ +#define DUMP_WITHDRAW 8 +#define DUMP_NLRI 16 + +/* dump detail */ +#define DUMP_DETAIL 32 + +extern int dump_open; +extern int dump_update; +extern int dump_keepalive; +extern int dump_notify; + +extern int Debug_Event; +extern int Debug_Keepalive; +extern int Debug_Update; +extern int Debug_Radix; + +#define NLRI 1 +#define WITHDRAW 2 +#define NO_OPT 3 +#define SEND 4 +#define RECV 5 +#define DETAIL 6 + +/* Prototypes. */ +extern void bgp_debug_init (void); +extern void bgp_packet_dump (struct stream *); + +extern int debug (unsigned int option); + +extern unsigned long conf_bgp_debug_as4; +extern unsigned long conf_bgp_debug_fsm; +extern unsigned long conf_bgp_debug_events; +extern unsigned long conf_bgp_debug_packet; +extern unsigned long conf_bgp_debug_filter; +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; +extern unsigned long term_bgp_debug_events; +extern unsigned long term_bgp_debug_packet; +extern unsigned long term_bgp_debug_filter; +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 + +#define BGP_DEBUG_FSM 0x01 +#define BGP_DEBUG_EVENTS 0x01 +#define BGP_DEBUG_PACKET 0x01 +#define BGP_DEBUG_FILTER 0x01 +#define BGP_DEBUG_KEEPALIVE 0x01 +#define BGP_DEBUG_UPDATE_IN 0x01 +#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 + +#define BGP_DEBUG_PACKET_RECV 0x01 +#define BGP_DEBUG_PACKET_RECV_DETAIL 0x02 + +#define CONF_DEBUG_ON(a, b) (conf_bgp_debug_ ## a |= (BGP_DEBUG_ ## b)) +#define CONF_DEBUG_OFF(a, b) (conf_bgp_debug_ ## a &= ~(BGP_DEBUG_ ## b)) + +#define TERM_DEBUG_ON(a, b) (term_bgp_debug_ ## a |= (BGP_DEBUG_ ## b)) +#define TERM_DEBUG_OFF(a, b) (term_bgp_debug_ ## a &= ~(BGP_DEBUG_ ## b)) + +#define DEBUG_ON(a, b) \ + do { \ + CONF_DEBUG_ON(a, b); \ + TERM_DEBUG_ON(a, b); \ + } while (0) +#define DEBUG_OFF(a, b) \ + do { \ + CONF_DEBUG_OFF(a, b); \ + TERM_DEBUG_OFF(a, b); \ + } while (0) + +#define BGP_DEBUG(a, b) (term_bgp_debug_ ## a & BGP_DEBUG_ ## b) +#define CONF_BGP_DEBUG(a, b) (conf_bgp_debug_ ## a & BGP_DEBUG_ ## b) + +extern const char *bgp_type_str[]; + +extern int bgp_dump_attr (struct peer *, struct attr *, char *, size_t); +extern void bgp_notify_print (struct peer *, struct bgp_notify *, const char *); + +extern const struct message bgp_status_msg[]; +extern const int bgp_status_msg_max; + +#endif /* _QUAGGA_BGP_DEBUG_H */ diff --git a/bgpd/bgp_dump.c b/bgpd/bgp_dump.c new file mode 100644 index 0000000..01f9b41 --- /dev/null +++ b/bgpd/bgp_dump.c @@ -0,0 +1,893 @@ +/* BGP-4 dump routine + 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 "log.h" +#include "stream.h" +#include "sockunion.h" +#include "command.h" +#include "prefix.h" +#include "thread.h" +#include "linklist.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 */ + MSG_DIE, /* receiver should shut down */ + MSG_I_AM_DEAD, /* sender is shutting down */ + MSG_PEER_DOWN, /* sender's peer is down */ + MSG_PROTOCOL_BGP, /* msg is a BGP packet */ + MSG_PROTOCOL_RIP, /* msg is a RIP packet */ + MSG_PROTOCOL_IDRP, /* msg is an IDRP packet */ + MSG_PROTOCOL_RIPNG, /* msg is a RIPNG packet */ + MSG_PROTOCOL_BGP4PLUS, /* msg is a BGP4+ packet */ + MSG_PROTOCOL_BGP4PLUS_01, /* msg is a BGP4+ (draft 01) packet */ + MSG_PROTOCOL_OSPF, /* msg is an OSPF packet */ + MSG_TABLE_DUMP, /* routing table dump */ + MSG_TABLE_DUMP_V2 /* routing table dump, version 2 */ +}; + +struct bgp_dump +{ + enum bgp_dump_type type; + + char *filename; + + FILE *fp; + + unsigned int interval; + + char *interval_str; + + 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; + +/* BGP dump strucuture for 'dump bgp all' */ +struct bgp_dump bgp_dump_all; + +/* BGP dump structure for 'dump bgp updates' */ +struct bgp_dump bgp_dump_updates; + +/* BGP dump structure for 'dump bgp routes' */ +struct bgp_dump bgp_dump_routes; + +static FILE * +bgp_dump_open_file (struct bgp_dump *bgp_dump) +{ + int ret; + time_t clock; + struct tm *tm; + char fullpath[MAXPATHLEN]; + char realpath[MAXPATHLEN]; + mode_t oldumask; + + time (&clock); + tm = localtime (&clock); + + if (bgp_dump->filename[0] != DIRECTORY_SEP) + { + sprintf (fullpath, "%s/%s", vty_get_cwd (), bgp_dump->filename); + ret = strftime (realpath, MAXPATHLEN, fullpath, tm); + } + else + ret = strftime (realpath, MAXPATHLEN, bgp_dump->filename, tm); + + if (ret == 0) + { + zlog_warn ("bgp_dump_open_file: strftime error"); + return NULL; + } + + if (bgp_dump->fp) + fclose (bgp_dump->fp); + + + oldumask = umask(0777 & ~LOGFILE_MASK); + bgp_dump->fp = fopen (realpath, "w"); + + if (bgp_dump->fp == NULL) + { + zlog_warn ("bgp_dump_open_file: %s: %s", realpath, strerror (errno)); + umask(oldumask); + return NULL; + } + umask(oldumask); + + return bgp_dump->fp; +} + +static int +bgp_dump_interval_add (struct bgp_dump *bgp_dump, int interval) +{ + int secs_into_day; + time_t t; + struct tm *tm; + + if (interval > 0) + { + /* Periodic dump every interval seconds */ + if ((interval < 86400) && ((86400 % interval) == 0)) + { + /* Dump at predictable times: if a day has a whole number of + * intervals, dump every interval seconds starting from midnight + */ + (void) time(&t); + tm = localtime(&t); + 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 (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 (bm->master, bgp_dump_interval_func, + bgp_dump, 0); + } + + return 0; +} + +/* Dump common header. */ +static void +bgp_dump_header (struct stream *obuf, int type, int subtype, int dump_type) +{ + 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); + + secs = clock.tv_sec; + msecs = clock.tv_usec; + + /* Put dump packet header. */ + 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); +} + +static void +bgp_dump_routes_index_table(struct bgp *bgp) +{ + struct peer *peer; + struct listnode *node; + 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_ROUTES); + + /* Collector BGP ID */ + stream_put_in_addr (obuf, &bgp->router_id); + + /* View name */ + if(bgp->name) + { + stream_putw (obuf, strlen(bgp->name)); + stream_put(obuf, bgp->name, strlen(bgp->name)); + } + else + { + stream_putw(obuf, 0); + } + + /* 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)) + { + + /* Peer's type */ + if (sockunion_family(&peer->su) == AF_INET) + { + stream_putc (obuf, TABLE_DUMP_V2_PEER_INDEX_TABLE_AS4+TABLE_DUMP_V2_PEER_INDEX_TABLE_IP); + } + 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); + } + + /* Peer's BGP ID */ + stream_put_in_addr (obuf, &peer->remote_id); + + /* Peer's IP address */ + if (sockunion_family(&peer->su) == AF_INET) + { + stream_put_in_addr (obuf, &peer->su.sin.sin_addr); + } + else if (sockunion_family(&peer->su) == AF_INET6) + { + stream_write (obuf, (u_char *)&peer->su.sin6.sin6_addr, + IPV6_MAX_BYTELEN); + } + + /* Peer's AS number. */ + /* Note that, as this is an AS4 compliant quagga, the RIB is always AS4 */ + stream_putl (obuf, peer->as); + + /* Store the peer number for this peer */ + peer->table_dump_index = peerno; + peerno++; + } + + bgp_dump_set_size(obuf, MSG_TABLE_DUMP_V2); + + fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp); + fflush (bgp_dump_routes.fp); +} + + +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 bgp_info *info; + struct bgp_node *rn; + struct bgp *bgp; + struct bgp_table *table; + + bgp = bgp_get_default (); + if (!bgp) + return seq; + + if (bgp_dump_routes.fp == NULL) + return seq; + + /* Note that bgp_dump_routes_index_table will do ipv4 and ipv6 peers, + so this should only be done on the first call to bgp_dump_routes_func. + ( this function will be called once for ipv4 and once for ipv6 ) */ + if(first_run) + bgp_dump_routes_index_table(bgp); + + /* Walk down each BGP route. */ + table = bgp->rib[afi][SAFI_UNICAST]; + + for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) + { + info = rn->info; + while (info) + { + info = bgp_dump_route_node_record(afi, rn, info, seq); + seq++; + } + } + + fflush (bgp_dump_routes.fp); + + return seq; +} + +static int +bgp_dump_interval_func (struct thread *t) +{ + struct bgp_dump *bgp_dump; + bgp_dump = THREAD_ARG (t); + bgp_dump->t_interval = NULL; + + /* Reschedule dump even if file couldn't be opened this time... */ + if (bgp_dump_open_file (bgp_dump) != NULL) + { + /* In case of bgp_dump_routes, we need special route dump function. */ + if (bgp_dump->type == BGP_DUMP_ROUTES) + { + unsigned int seq = bgp_dump_routes_func (AFI_IP, 1, 0); + bgp_dump_routes_func (AFI_IP6, 0, seq); + /* 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; + } + } + + /* if interval is set reschedule */ + if (bgp_dump->interval > 0) + bgp_dump_interval_add (bgp_dump, bgp_dump->interval); + + return 0; +} + +/* Dump common information. */ +static void +bgp_dump_common (struct stream *obuf, struct peer *peer, int forceas4) +{ + char empty[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + + /* Source AS number and Destination AS number. */ + if (forceas4 || CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV) ) + { + stream_putl (obuf, peer->as); + stream_putl (obuf, peer->local_as); + } + else + { + stream_putw (obuf, peer->as); + stream_putw (obuf, peer->local_as); + } + + if (peer->su.sa.sa_family == AF_INET) + { + stream_putw (obuf, peer->ifindex); + stream_putw (obuf, AFI_IP); + + stream_put (obuf, &peer->su.sin.sin_addr, IPV4_MAX_BYTELEN); + + if (peer->su_local) + stream_put (obuf, &peer->su_local->sin.sin_addr, IPV4_MAX_BYTELEN); + else + stream_put (obuf, empty, IPV4_MAX_BYTELEN); + } + else if (peer->su.sa.sa_family == AF_INET6) + { + /* Interface Index and Address family. */ + stream_putw (obuf, peer->ifindex); + stream_putw (obuf, AFI_IP6); + + /* Source IP Address and Destination IP Address. */ + stream_put (obuf, &peer->su.sin6.sin6_addr, IPV6_MAX_BYTELEN); + + if (peer->su_local) + stream_put (obuf, &peer->su_local->sin6.sin6_addr, IPV6_MAX_BYTELEN); + else + stream_put (obuf, empty, IPV6_MAX_BYTELEN); + } +} + +/* Dump BGP status change. */ +void +bgp_dump_state (struct peer *peer, int status_old, int status_new) +{ + struct stream *obuf; + + /* If dump file pointer is disabled return immediately. */ + if (bgp_dump_all.fp == NULL) + return; + + /* Make dump stream. */ + obuf = bgp_dump_obuf; + stream_reset (obuf); + + 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); + stream_putw (obuf, status_new); + + /* Set length. */ + bgp_dump_set_size (obuf, MSG_PROTOCOL_BGP4MP); + + /* Write to the stream. */ + fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_all.fp); + fflush (bgp_dump_all.fp); +} + +static void +bgp_dump_packet_func (struct bgp_dump *bgp_dump, struct peer *peer, + struct stream *packet) +{ + struct stream *obuf; + + /* If dump file pointer is disabled return immediately. */ + if (bgp_dump->fp == NULL) + return; + + /* Make dump stream. */ + obuf = bgp_dump_obuf; + stream_reset (obuf); + + /* 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->type); + } + else + { + bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_MESSAGE, + bgp_dump->type); + } + bgp_dump_common (obuf, peer, 0); + + /* Packet contents. */ + stream_put (obuf, STREAM_DATA (packet), stream_get_endp (packet)); + + /* Set length. */ + bgp_dump_set_size (obuf, MSG_PROTOCOL_BGP4MP); + + /* Write to the stream. */ + fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump->fp); + fflush (bgp_dump->fp); +} + +/* Called from bgp_packet.c when BGP packet is received. */ +void +bgp_dump_packet (struct peer *peer, int type, struct stream *packet) +{ + /* bgp_dump_all. */ + bgp_dump_packet_func (&bgp_dump_all, peer, packet); + + /* bgp_dump_updates. */ + if (type == BGP_MSG_UPDATE) + bgp_dump_packet_func (&bgp_dump_updates, peer, packet); +} + +static unsigned int +bgp_dump_parse_time (const char *str) +{ + int i; + int len; + int seen_h; + int seen_m; + int time; + unsigned int total; + + time = 0; + total = 0; + seen_h = 0; + seen_m = 0; + len = strlen (str); + + for (i = 0; i < len; i++) + { + if (isdigit ((int) str[i])) + { + time *= 10; + time += str[i] - '0'; + } + else if (str[i] == 'H' || str[i] == 'h') + { + if (seen_h) + return 0; + if (seen_m) + return 0; + total += time * 60 *60; + time = 0; + seen_h = 1; + } + else if (str[i] == 'M' || str[i] == 'm') + { + if (seen_m) + return 0; + total += time * 60; + time = 0; + seen_h = 1; + } + else + return 0; + } + return total + time; +} + +static int +bgp_dump_set (struct vty *vty, struct bgp_dump *bgp_dump, + enum bgp_dump_type type, const char *path, + const char *interval_str) +{ + 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) + { + vty_out (vty, "Malformed interval string%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Setting interval string */ + bgp_dump->interval_str = strdup (interval_str); + } + else + { + interval = 0; + } + + /* Set type. */ + bgp_dump->type = type; + + /* Set interval */ + bgp_dump->interval = interval; + + /* Set file name. */ + 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); + + return CMD_SUCCESS; +} + +static int +bgp_dump_unset (struct vty *vty, struct bgp_dump *bgp_dump) +{ + /* Removing file name. */ + if (bgp_dump->filename) + { + free (bgp_dump->filename); + bgp_dump->filename = NULL; + } + + /* Closing file. */ + if (bgp_dump->fp) + { + fclose (bgp_dump->fp); + bgp_dump->fp = NULL; + } + + /* Removing interval thread. */ + if (bgp_dump->t_interval) + { + thread_cancel (bgp_dump->t_interval); + bgp_dump->t_interval = NULL; + } + + 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|all-et|updates|updates-et|routes-mrt) PATH [INTERVAL]", + "Dump packet\n" + "BGP packet dump\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") +{ + int bgp_dump_type = 0; + const char *interval = NULL; + struct bgp_dump *bgp_dump_struct = NULL; + const struct bgp_dump_type_map *map = NULL; + + for (map = bgp_dump_type_map; map->str; map++) + if (strcmp(argv[0], map->str) == 0) + bgp_dump_type = map->type; + + 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; + } + + /* When an interval is given */ + if (argc == 3) + interval = argv[2]; + + return bgp_dump_set (vty, bgp_dump_struct, bgp_dump_type, + argv[1], interval); +} + +DEFUN (no_dump_bgp_all, + no_dump_bgp_all_cmd, + "no dump bgp (all|updates|routes-mrt) [PATH] [INTERVAL]", + NO_STR + "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_all); +} + +/* BGP node structure. */ +static struct cmd_node bgp_dump_node = +{ + DUMP_NODE, + "", + 1 +}; + +#if 0 +char * +config_time2str (unsigned int interval) +{ + static char buf[BUFSIZ]; + + buf[0] = '\0'; + + if (interval / 3600) + { + sprintf (buf, "%dh", interval / 3600); + interval %= 3600; + } + if (interval / 60) + { + sprintf (buf + strlen (buf), "%dm", interval /60); + interval %= 60; + } + if (interval) + { + sprintf (buf + strlen (buf), "%d", interval); + } + return buf; +} +#endif + +static int +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 %s %s %s%s", type_str, + bgp_dump_all.filename, bgp_dump_all.interval_str, + VTY_NEWLINE); + else + 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 %s %s %s%s", type_str, + bgp_dump_updates.filename, bgp_dump_updates.interval_str, + VTY_NEWLINE); + else + vty_out (vty, "dump bgp updates %s%s", + bgp_dump_updates.filename, VTY_NEWLINE); + } + if (bgp_dump_routes.filename) + { + if (bgp_dump_routes.interval_str) + vty_out (vty, "dump bgp routes-mrt %s %s%s", + bgp_dump_routes.filename, bgp_dump_routes.interval_str, + VTY_NEWLINE); + } + return 0; +} + +/* Initialize BGP packet dump functionality. */ +void +bgp_dump_init (void) +{ + memset (&bgp_dump_all, 0, sizeof (struct bgp_dump)); + 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 << 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, &no_dump_bgp_all_cmd); +} + +void +bgp_dump_finish (void) +{ + stream_free (bgp_dump_obuf); + bgp_dump_obuf = NULL; +} diff --git a/bgpd/bgp_dump.h b/bgpd/bgp_dump.h new file mode 100644 index 0000000..d1e66d9 --- /dev/null +++ b/bgpd/bgp_dump.h @@ -0,0 +1,57 @@ +/* BGP dump routine. + 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_BGP_DUMP_H +#define _QUAGGA_BGP_DUMP_H + +/* MRT compatible packet dump values. */ +/* type value */ +#define MSG_PROTOCOL_BGP4MP 16 +#define MSG_PROTOCOL_BGP4MP_ET 17 + +/* subtype value */ +#define BGP4MP_STATE_CHANGE 0 +#define BGP4MP_MESSAGE 1 +#define BGP4MP_ENTRY 2 +#define BGP4MP_SNAPSHOT 3 +#define BGP4MP_MESSAGE_AS4 4 +#define BGP4MP_STATE_CHANGE_AS4 5 + +#define BGP_DUMP_HEADER_SIZE 12 +#define BGP_DUMP_MSG_HEADER 40 + +#define TABLE_DUMP_V2_PEER_INDEX_TABLE 1 +#define TABLE_DUMP_V2_RIB_IPV4_UNICAST 2 +#define TABLE_DUMP_V2_RIB_IPV4_MULTICAST 3 +#define TABLE_DUMP_V2_RIB_IPV6_UNICAST 4 +#define TABLE_DUMP_V2_RIB_IPV6_MULTICAST 5 +#define TABLE_DUMP_V2_RIB_GENERIC 6 + +#define TABLE_DUMP_V2_PEER_INDEX_TABLE_IP 0 +#define TABLE_DUMP_V2_PEER_INDEX_TABLE_IP6 1 +#define TABLE_DUMP_V2_PEER_INDEX_TABLE_AS2 0 +#define TABLE_DUMP_V2_PEER_INDEX_TABLE_AS4 2 + +extern void bgp_dump_init (void); +extern void bgp_dump_finish (void); +extern void bgp_dump_state (struct peer *, int, int); +extern void bgp_dump_packet (struct peer *, int, struct stream *); + +#endif /* _QUAGGA_BGP_DUMP_H */ diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c new file mode 100644 index 0000000..5d69b42 --- /dev/null +++ b/bgpd/bgp_ecommunity.c @@ -0,0 +1,776 @@ +/* BGP Extended Communities Attribute + 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 "hash.h" +#include "memory.h" +#include "prefix.h" +#include "command.h" +#include "filter.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_aspath.h" + +/* Hash of community attribute. */ +static struct hash *ecomhash; + +/* Allocate a new ecommunities. */ +static struct ecommunity * +ecommunity_new (void) +{ + return (struct ecommunity *) XCALLOC (MTYPE_ECOMMUNITY, + sizeof (struct ecommunity)); +} + +/* Allocate ecommunities. */ +void +ecommunity_free (struct ecommunity **ecom) +{ + if ((*ecom)->val) + XFREE (MTYPE_ECOMMUNITY_VAL, (*ecom)->val); + if ((*ecom)->str) + XFREE (MTYPE_ECOMMUNITY_STR, (*ecom)->str); + XFREE (MTYPE_ECOMMUNITY, *ecom); + ecom = NULL; +} + +/* Add a new Extended Communities value to Extended 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 +ecommunity_add_val (struct ecommunity *ecom, struct ecommunity_val *eval) +{ + u_int8_t *p; + int ret; + int c; + + /* When this is fist value, just add it. */ + if (ecom->val == NULL) + { + ecom->size++; + ecom->val = XMALLOC (MTYPE_ECOMMUNITY_VAL, ecom_length (ecom)); + memcpy (ecom->val, eval->val, ECOMMUNITY_SIZE); + return 1; + } + + /* If the value already exists in the structure return 0. */ + c = 0; + for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) + { + ret = memcmp (p, eval->val, ECOMMUNITY_SIZE); + if (ret == 0) + return 0; + if (ret > 0) + break; + } + + /* Add the value to the structure with numerical sorting. */ + ecom->size++; + ecom->val = XREALLOC (MTYPE_ECOMMUNITY_VAL, ecom->val, ecom_length (ecom)); + + memmove (ecom->val + (c + 1) * ECOMMUNITY_SIZE, + ecom->val + c * ECOMMUNITY_SIZE, + (ecom->size - 1 - c) * ECOMMUNITY_SIZE); + memcpy (ecom->val + c * ECOMMUNITY_SIZE, eval->val, ECOMMUNITY_SIZE); + + return 1; +} + +/* This function takes pointer to Extended Communites strucutre then + create a new Extended Communities structure by uniq and sort each + Extended Communities value. */ +struct ecommunity * +ecommunity_uniq_sort (struct ecommunity *ecom) +{ + int i; + struct ecommunity *new; + struct ecommunity_val *eval; + + if (! ecom) + return NULL; + + new = ecommunity_new (); + + for (i = 0; i < ecom->size; i++) + { + eval = (struct ecommunity_val *) (ecom->val + (i * ECOMMUNITY_SIZE)); + ecommunity_add_val (new, eval); + } + return new; +} + +/* Parse Extended Communites Attribute in BGP packet. */ +struct ecommunity * +ecommunity_parse (u_int8_t *pnt, u_short length) +{ + struct ecommunity tmp; + struct ecommunity *new; + + /* Length check. */ + if (length % ECOMMUNITY_SIZE) + return NULL; + + /* Prepare tmporary structure for making a new Extended Communities + Attribute. */ + tmp.size = length / ECOMMUNITY_SIZE; + tmp.val = pnt; + + /* Create a new Extended Communities Attribute by uniq and sort each + Extended Communities value */ + new = ecommunity_uniq_sort (&tmp); + + return ecommunity_intern (new); +} + +/* Duplicate the Extended Communities Attribute structure. */ +struct ecommunity * +ecommunity_dup (struct ecommunity *ecom) +{ + struct ecommunity *new; + + new = XCALLOC (MTYPE_ECOMMUNITY, sizeof (struct ecommunity)); + new->size = ecom->size; + if (new->size) + { + new->val = XMALLOC (MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE); + memcpy (new->val, ecom->val, ecom->size * ECOMMUNITY_SIZE); + } + else + new->val = NULL; + return new; +} + +/* Retrun string representation of communities attribute. */ +char * +ecommunity_str (struct ecommunity *ecom) +{ + if (! ecom->str) + ecom->str = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_DISPLAY); + return ecom->str; +} + +/* Merge two Extended Communities Attribute structure. */ +struct ecommunity * +ecommunity_merge (struct ecommunity *ecom1, struct ecommunity *ecom2) +{ + if (ecom1->val) + ecom1->val = XREALLOC (MTYPE_ECOMMUNITY_VAL, ecom1->val, + (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE); + else + ecom1->val = XMALLOC (MTYPE_ECOMMUNITY_VAL, + (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE); + + memcpy (ecom1->val + (ecom1->size * ECOMMUNITY_SIZE), + ecom2->val, ecom2->size * ECOMMUNITY_SIZE); + ecom1->size += ecom2->size; + + return ecom1; +} + +/* Intern Extended Communities Attribute. */ +struct ecommunity * +ecommunity_intern (struct ecommunity *ecom) +{ + struct ecommunity *find; + + assert (ecom->refcnt == 0); + + find = (struct ecommunity *) hash_get (ecomhash, ecom, hash_alloc_intern); + + if (find != ecom) + ecommunity_free (&ecom); + + find->refcnt++; + + if (! find->str) + find->str = ecommunity_ecom2str (find, ECOMMUNITY_FORMAT_DISPLAY); + + return find; +} + +/* Unintern Extended Communities Attribute. */ +void +ecommunity_unintern (struct ecommunity **ecom) +{ + struct ecommunity *ret; + + if ((*ecom)->refcnt) + (*ecom)->refcnt--; + + /* Pull off from hash. */ + if ((*ecom)->refcnt == 0) + { + /* Extended community must be in the hash. */ + ret = (struct ecommunity *) hash_release (ecomhash, *ecom); + assert (ret != NULL); + + ecommunity_free (ecom); + } +} + +/* Utinity function to make hash key. */ +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; + + 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; +} + +/* Compare two Extended Communities Attribute structure. */ +int +ecommunity_cmp (const void *arg1, const void *arg2) +{ + const struct ecommunity *ecom1 = arg1; + const struct ecommunity *ecom2 = arg2; + + return (ecom1->size == ecom2->size + && memcmp (ecom1->val, ecom2->val, ecom1->size * ECOMMUNITY_SIZE) == 0); +} + +/* Initialize Extended Comminities related hash. */ +void +ecommunity_init (void) +{ + ecomhash = hash_create (ecommunity_hash_make, ecommunity_cmp); +} + +void +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, +}; + +/* Get next Extended Communities token from the string. */ +static const char * +ecommunity_gettoken (const char *str, struct ecommunity_val *eval, + enum ecommunity_token *token) +{ + int ret; + int dot = 0; + int digit = 0; + int separator = 0; + const char *p = str; + char *endptr; + struct in_addr ip; + as_t as = 0; + u_int32_t val = 0; + char buf[INET_ADDRSTRLEN + 1]; + + /* Skip white space. */ + while (isspace ((int) *p)) + { + p++; + str++; + } + + /* Check the end of the line. */ + if (*p == '\0') + return NULL; + + /* "rt" and "soo" keyword parse. */ + if (! isdigit ((int) *p)) + { + /* "rt" match check. */ + if (tolower ((int) *p) == 'r') + { + p++; + if (tolower ((int) *p) == 't') + { + p++; + *token = ecommunity_token_rt; + return p; + } + if (isspace ((int) *p) || *p == '\0') + { + *token = ecommunity_token_rt; + return p; + } + goto error; + } + /* "soo" match check. */ + else if (tolower ((int) *p) == 's') + { + p++; + if (tolower ((int) *p) == 'o') + { + p++; + if (tolower ((int) *p) == 'o') + { + p++; + *token = ecommunity_token_soo; + return p; + } + if (isspace ((int) *p) || *p == '\0') + { + *token = ecommunity_token_soo; + return p; + } + goto error; + } + if (isspace ((int) *p) || *p == '\0') + { + *token = ecommunity_token_soo; + return p; + } + goto error; + } + goto error; + } + + /* What a mess, there are several possibilities: + * + * a) A.B.C.D:MN + * b) EF:OPQR + * c) GHJK:MN + * + * A.B.C.D: Four Byte IP + * EF: Two byte ASN + * GHJK: Four-byte ASN + * MN: Two byte value + * OPQR: Four byte value + * + */ + while (isdigit ((int) *p) || *p == ':' || *p == '.') + { + if (*p == ':') + { + if (separator) + goto error; + + separator = 1; + digit = 0; + + if ((p - str) > INET_ADDRSTRLEN) + goto error; + memset (buf, 0, INET_ADDRSTRLEN + 1); + memcpy (buf, str, p - str); + + if (dot) + { + /* Parsing A.B.C.D in: + * A.B.C.D:MN + */ + ret = inet_aton (buf, &ip); + if (ret == 0) + goto error; + } + else + { + /* ASN */ + as = strtoul (buf, &endptr, 10); + if (*endptr != '\0' || as == BGP_AS4_MAX) + goto error; + } + } + else if (*p == '.') + { + if (separator) + goto error; + dot++; + if (dot > 4) + goto error; + } + else + { + digit = 1; + + /* We're past the IP/ASN part */ + if (separator) + { + val *= 10; + val += (*p - '0'); + } + } + p++; + } + + /* Low digit part must be there. */ + if (!digit || !separator) + goto error; + + /* Encode result into routing distinguisher. */ + if (dot) + { + if (val > UINT16_MAX) + goto error; + + eval->val[0] = ECOMMUNITY_ENCODE_IP; + eval->val[1] = 0; + memcpy (&eval->val[2], &ip, sizeof (struct in_addr)); + eval->val[6] = (val >> 8) & 0xff; + eval->val[7] = val & 0xff; + } + else if (as > BGP_AS_MAX) + { + if (val > UINT16_MAX) + goto error; + + eval->val[0] = ECOMMUNITY_ENCODE_AS4; + eval->val[1] = 0; + eval->val[2] = (as >>24) & 0xff; + eval->val[3] = (as >>16) & 0xff; + eval->val[4] = (as >>8) & 0xff; + eval->val[5] = as & 0xff; + eval->val[6] = (val >> 8) & 0xff; + eval->val[7] = val & 0xff; + } + else + { + eval->val[0] = ECOMMUNITY_ENCODE_AS; + eval->val[1] = 0; + + eval->val[2] = (as >>8) & 0xff; + eval->val[3] = as & 0xff; + eval->val[4] = (val >>24) & 0xff; + eval->val[5] = (val >>16) & 0xff; + eval->val[6] = (val >>8) & 0xff; + eval->val[7] = val & 0xff; + } + *token = ecommunity_token_val; + return p; + + error: + *token = ecommunity_token_unknown; + return p; +} + +/* Convert string to extended community attribute. + + When type is already known, please specify both str and type. str + should not include keyword such as "rt" and "soo". Type is + ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN. + keyword_included should be zero. + + For example route-map's "set extcommunity" command case: + + "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3" + type = ECOMMUNITY_ROUTE_TARGET + keyword_included = 0 + + "soo 100:1" -> str = "100:1" + type = ECOMMUNITY_SITE_ORIGIN + keyword_included = 0 + + When string includes keyword for each extended community value. + Please specify keyword_included as non-zero value. + + For example standard extcommunity-list case: + + "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1" + type = 0 + keyword_include = 1 +*/ +struct ecommunity * +ecommunity_str2com (const char *str, int type, int keyword_included) +{ + struct ecommunity *ecom = NULL; + enum ecommunity_token token = ecommunity_token_unknown; + struct ecommunity_val eval; + int keyword = 0; + + while ((str = ecommunity_gettoken (str, &eval, &token))) + { + switch (token) + { + case ecommunity_token_rt: + case ecommunity_token_soo: + if (! keyword_included || keyword) + { + if (ecom) + ecommunity_free (&ecom); + return NULL; + } + keyword = 1; + + if (token == ecommunity_token_rt) + { + type = ECOMMUNITY_ROUTE_TARGET; + } + if (token == ecommunity_token_soo) + { + type = ECOMMUNITY_SITE_ORIGIN; + } + break; + case ecommunity_token_val: + if (keyword_included) + { + if (! keyword) + { + if (ecom) + ecommunity_free (&ecom); + return NULL; + } + keyword = 0; + } + if (ecom == NULL) + ecom = ecommunity_new (); + eval.val[1] = type; + ecommunity_add_val (ecom, &eval); + break; + case ecommunity_token_unknown: + default: + if (ecom) + ecommunity_free (&ecom); + return NULL; + } + } + return ecom; +} + +/* Convert extended community attribute to string. + + Due to historical reason of industry standard implementation, there + are three types of format. + + route-map set extcommunity format + "rt 100:1 100:2" + "soo 100:3" + + extcommunity-list + "rt 100:1 rt 100:2 soo 100:3" + + "show ip bgp" and extcommunity-list regular expression matching + "RT:100:1 RT:100:2 SoO:100:3" + + For each formath please use below definition for format: + + ECOMMUNITY_FORMAT_ROUTE_MAP + ECOMMUNITY_FORMAT_COMMUNITY_LIST + ECOMMUNITY_FORMAT_DISPLAY +*/ +char * +ecommunity_ecom2str (struct ecommunity *ecom, int format) +{ + int i; + u_int8_t *pnt; + int encode = 0; + int type = 0; +#define ECOMMUNITY_STR_DEFAULT_LEN 27 + int str_size; + int str_pnt; + char *str_buf; + const char *prefix; + int len = 0; + int first = 1; + + /* For parse Extended Community attribute tupple. */ + struct ecommunity_as + { + as_t as; + u_int32_t val; + } eas; + + struct ecommunity_ip + { + struct in_addr ip; + u_int16_t val; + } eip; + + if (ecom->size == 0) + { + str_buf = XMALLOC (MTYPE_ECOMMUNITY_STR, 1); + str_buf[0] = '\0'; + return str_buf; + } + + /* Prepare buffer. */ + str_buf = XMALLOC (MTYPE_ECOMMUNITY_STR, ECOMMUNITY_STR_DEFAULT_LEN + 1); + str_size = ECOMMUNITY_STR_DEFAULT_LEN + 1; + str_pnt = 0; + + for (i = 0; i < ecom->size; i++) + { + /* Make it sure size is enough. */ + while (str_pnt + ECOMMUNITY_STR_DEFAULT_LEN >= str_size) + { + str_size *= 2; + str_buf = XREALLOC (MTYPE_ECOMMUNITY_STR, str_buf, str_size); + } + + /* Space between each value. */ + if (! first) + str_buf[str_pnt++] = ' '; + + pnt = ecom->val + (i * 8); + + /* High-order octet of type. */ + encode = *pnt++; + + 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) + { + len = sprintf (str_buf + str_pnt, "?"); + str_pnt += len; + first = 0; + continue; + } + + switch (format) + { + case ECOMMUNITY_FORMAT_COMMUNITY_LIST: + prefix = (type == ECOMMUNITY_ROUTE_TARGET ? "rt " : "soo "); + break; + case ECOMMUNITY_FORMAT_DISPLAY: + prefix = (type == ECOMMUNITY_ROUTE_TARGET ? "RT:" : "SoO:"); + break; + case ECOMMUNITY_FORMAT_ROUTE_MAP: + prefix = ""; + break; + default: + prefix = ""; + break; + } + + /* Put string into buffer. */ + if (encode == ECOMMUNITY_ENCODE_AS4) + { + eas.as = (*pnt++ << 24); + eas.as |= (*pnt++ << 16); + eas.as |= (*pnt++ << 8); + eas.as |= (*pnt++); + + eas.val = (*pnt++ << 8); + eas.val |= (*pnt++); + + len = sprintf( str_buf + str_pnt, "%s%u:%u", prefix, + eas.as, eas.val ); + str_pnt += len; + first = 0; + } + if (encode == ECOMMUNITY_ENCODE_AS) + { + eas.as = (*pnt++ << 8); + eas.as |= (*pnt++); + + eas.val = (*pnt++ << 24); + eas.val |= (*pnt++ << 16); + eas.val |= (*pnt++ << 8); + eas.val |= (*pnt++); + + len = sprintf (str_buf + str_pnt, "%s%u:%u", prefix, + eas.as, eas.val); + str_pnt += len; + first = 0; + } + else if (encode == ECOMMUNITY_ENCODE_IP) + { + memcpy (&eip.ip, pnt, 4); + pnt += 4; + eip.val = (*pnt++ << 8); + eip.val |= (*pnt++); + + len = sprintf (str_buf + str_pnt, "%s%s:%u", prefix, + inet_ntoa (eip.ip), eip.val); + str_pnt += len; + first = 0; + } + } + return str_buf; +} + +int +ecommunity_match (const struct ecommunity *ecom1, + const struct ecommunity *ecom2) +{ + int i = 0; + int j = 0; + + if (ecom1 == NULL && ecom2 == NULL) + return 1; + + if (ecom1 == NULL || ecom2 == NULL) + return 0; + + if (ecom1->size < ecom2->size) + return 0; + + /* Every community on com2 needs to be on com1 for this to match */ + while (i < ecom1->size && j < ecom2->size) + { + if (memcmp (ecom1->val + i, ecom2->val + j, ECOMMUNITY_SIZE) == 0) + j++; + i++; + } + + if (j == ecom2->size) + return 1; + else + return 0; +} + diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h new file mode 100644 index 0000000..993fd5a --- /dev/null +++ b/bgpd/bgp_ecommunity.h @@ -0,0 +1,88 @@ +/* BGP Extended Communities Attribute. + 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. */ + +#ifndef _QUAGGA_BGP_ECOMMUNITY_H +#define _QUAGGA_BGP_ECOMMUNITY_H + +/* High-order octet of the Extended Communities type field. */ +#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 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 +#define ECOMMUNITY_FORMAT_DISPLAY 2 + +/* Extended Communities value is eight octet long. */ +#define ECOMMUNITY_SIZE 8 + +/* Extended Communities type flag. */ +#define ECOMMUNITY_FLAG_NON_TRANSITIVE 0x40 + +/* Extended Communities attribute. */ +struct ecommunity +{ + /* 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 ecommunity_val +{ + char val[ECOMMUNITY_SIZE]; +}; + +#define ecom_length(X) ((X)->size * ECOMMUNITY_SIZE) + +extern void ecommunity_init (void); +extern void ecommunity_finish (void); +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 **); +extern unsigned int ecommunity_hash_make (void *); +extern struct ecommunity *ecommunity_str2com (const char *, int, int); +extern char *ecommunity_ecom2str (struct ecommunity *, int); +extern int ecommunity_match (const struct ecommunity *, const struct ecommunity *); +extern char *ecommunity_str (struct ecommunity *); + +#endif /* _QUAGGA_BGP_ECOMMUNITY_H */ diff --git a/bgpd/bgp_encap.c b/bgpd/bgp_encap.c new file mode 100644 index 0000000..cd58ac2 --- /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 0000000..7442c73 --- /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 0000000..347b4b3 --- /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 0000000..d94d544 --- /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 0000000..603ff9d --- /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 new file mode 100644 index 0000000..ab1e504 --- /dev/null +++ b/bgpd/bgp_filter.c @@ -0,0 +1,719 @@ +/* AS path filter list. + 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 "command.h" +#include "log.h" +#include "memory.h" +#include "buffer.h" +#include "filter.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_regex.h" +#include "bgpd/bgp_filter.h" + +/* List of AS filter list. */ +struct as_list_list +{ + struct as_list *head; + struct as_list *tail; +}; + +/* AS path filter master. */ +struct as_list_master +{ + /* List of access_list which name is number. */ + struct as_list_list num; + + /* List of access_list which name is string. */ + struct as_list_list str; + + /* Hook function which is executed when new access_list is added. */ + void (*add_hook) (void); + + /* Hook function which is executed when access_list is deleted. */ + void (*delete_hook) (void); +}; + +/* Element of AS path filter. */ +struct as_filter +{ + struct as_filter *next; + struct as_filter *prev; + + enum as_filter_type type; + + regex_t *reg; + char *reg_str; +}; + +/* AS path filter list. */ +struct as_list +{ + char *name; + + enum access_type type; + + struct as_list *next; + struct as_list *prev; + + struct as_filter *head; + struct as_filter *tail; +}; + +/* ip as-path access-list 10 permit AS1. */ + +static struct as_list_master as_list_master = +{ + {NULL, NULL}, + {NULL, NULL}, + NULL, + NULL +}; + +/* Allocate new AS filter. */ +static struct as_filter * +as_filter_new (void) +{ + return XCALLOC (MTYPE_AS_FILTER, sizeof (struct as_filter)); +} + +/* Free allocated AS filter. */ +static void +as_filter_free (struct as_filter *asfilter) +{ + if (asfilter->reg) + bgp_regex_free (asfilter->reg); + if (asfilter->reg_str) + XFREE (MTYPE_AS_FILTER_STR, asfilter->reg_str); + XFREE (MTYPE_AS_FILTER, asfilter); +} + +/* Make new AS filter. */ +static struct as_filter * +as_filter_make (regex_t *reg, const char *reg_str, enum as_filter_type type) +{ + struct as_filter *asfilter; + + asfilter = as_filter_new (); + asfilter->reg = reg; + asfilter->type = type; + asfilter->reg_str = XSTRDUP (MTYPE_AS_FILTER_STR, reg_str); + + return asfilter; +} + +static struct as_filter * +as_filter_lookup (struct as_list *aslist, const char *reg_str, + enum as_filter_type type) +{ + struct as_filter *asfilter; + + for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) + if (strcmp (reg_str, asfilter->reg_str) == 0) + return asfilter; + return NULL; +} + +static void +as_list_filter_add (struct as_list *aslist, struct as_filter *asfilter) +{ + asfilter->next = NULL; + asfilter->prev = aslist->tail; + + if (aslist->tail) + aslist->tail->next = asfilter; + else + aslist->head = asfilter; + aslist->tail = asfilter; +} + +/* Lookup as_list from list of as_list by name. */ +struct as_list * +as_list_lookup (const char *name) +{ + struct as_list *aslist; + + if (name == NULL) + return NULL; + + for (aslist = as_list_master.num.head; aslist; aslist = aslist->next) + if (strcmp (aslist->name, name) == 0) + return aslist; + + for (aslist = as_list_master.str.head; aslist; aslist = aslist->next) + if (strcmp (aslist->name, name) == 0) + return aslist; + + return NULL; +} + +static struct as_list * +as_list_new (void) +{ + return XCALLOC (MTYPE_AS_LIST, sizeof (struct as_list)); +} + +static void +as_list_free (struct as_list *aslist) +{ + if (aslist->name) + { + free (aslist->name); + aslist->name = NULL; + } + XFREE (MTYPE_AS_LIST, aslist); +} + +/* Insert new AS list to list of as_list. Each as_list is sorted by + the name. */ +static struct as_list * +as_list_insert (const char *name) +{ + size_t i; + long number; + struct as_list *aslist; + struct as_list *point; + struct as_list_list *list; + + /* Allocate new access_list and copy given name. */ + aslist = as_list_new (); + aslist->name = strdup (name); + assert (aslist->name); + + /* If name is made by all digit character. We treat it as + number. */ + for (number = 0, i = 0; i < strlen (name); i++) + { + if (isdigit ((int) name[i])) + number = (number * 10) + (name[i] - '0'); + else + break; + } + + /* In case of name is all digit character */ + if (i == strlen (name)) + { + aslist->type = ACCESS_TYPE_NUMBER; + + /* Set access_list to number list. */ + list = &as_list_master.num; + + for (point = list->head; point; point = point->next) + if (atol (point->name) >= number) + break; + } + else + { + aslist->type = ACCESS_TYPE_STRING; + + /* Set access_list to string list. */ + list = &as_list_master.str; + + /* Set point to insertion point. */ + for (point = list->head; point; point = point->next) + if (strcmp (point->name, name) >= 0) + break; + } + + /* In case of this is the first element of master. */ + if (list->head == NULL) + { + list->head = list->tail = aslist; + return aslist; + } + + /* In case of insertion is made at the tail of access_list. */ + if (point == NULL) + { + aslist->prev = list->tail; + list->tail->next = aslist; + list->tail = aslist; + return aslist; + } + + /* In case of insertion is made at the head of access_list. */ + if (point == list->head) + { + aslist->next = list->head; + list->head->prev = aslist; + list->head = aslist; + return aslist; + } + + /* Insertion is made at middle of the access_list. */ + aslist->next = point; + aslist->prev = point->prev; + + if (point->prev) + point->prev->next = aslist; + point->prev = aslist; + + return aslist; +} + +static struct as_list * +as_list_get (const char *name) +{ + struct as_list *aslist; + + aslist = as_list_lookup (name); + if (aslist == NULL) + { + aslist = as_list_insert (name); + + /* Run hook function. */ + if (as_list_master.add_hook) + (*as_list_master.add_hook) (); + } + + return aslist; +} + +static const char * +filter_type_str (enum as_filter_type type) +{ + switch (type) + { + case AS_FILTER_PERMIT: + return "permit"; + case AS_FILTER_DENY: + return "deny"; + default: + return ""; + } +} + +static void +as_list_delete (struct as_list *aslist) +{ + struct as_list_list *list; + struct as_filter *filter, *next; + + for (filter = aslist->head; filter; filter = next) + { + next = filter->next; + as_filter_free (filter); + } + + if (aslist->type == ACCESS_TYPE_NUMBER) + list = &as_list_master.num; + else + list = &as_list_master.str; + + if (aslist->next) + aslist->next->prev = aslist->prev; + else + list->tail = aslist->prev; + + if (aslist->prev) + aslist->prev->next = aslist->next; + else + list->head = aslist->next; + + as_list_free (aslist); +} + +static int +as_list_empty (struct as_list *aslist) +{ + if (aslist->head == NULL && aslist->tail == NULL) + return 1; + else + return 0; +} + +static void +as_list_filter_delete (struct as_list *aslist, struct as_filter *asfilter) +{ + if (asfilter->next) + asfilter->next->prev = asfilter->prev; + else + aslist->tail = asfilter->prev; + + if (asfilter->prev) + asfilter->prev->next = asfilter->next; + else + aslist->head = asfilter->next; + + as_filter_free (asfilter); + + /* If access_list becomes empty delete it from access_master. */ + if (as_list_empty (aslist)) + as_list_delete (aslist); + + /* Run hook function. */ + if (as_list_master.delete_hook) + (*as_list_master.delete_hook) (); +} + +static int +as_filter_match (struct as_filter *asfilter, struct aspath *aspath) +{ + if (bgp_regexec (asfilter->reg, aspath) != REG_NOMATCH) + return 1; + return 0; +} + +/* Apply AS path filter to AS. */ +enum as_filter_type +as_list_apply (struct as_list *aslist, void *object) +{ + struct as_filter *asfilter; + struct aspath *aspath; + + aspath = (struct aspath *) object; + + if (aslist == NULL) + return AS_FILTER_DENY; + + for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) + { + if (as_filter_match (asfilter, aspath)) + return asfilter->type; + } + return AS_FILTER_DENY; +} + +/* Add hook function. */ +void +as_list_add_hook (void (*func) (void)) +{ + as_list_master.add_hook = func; +} + +/* Delete hook function. */ +void +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) +{ + struct as_filter *asfilter; + + for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) + { + if (asfilter->type == new->type + && strcmp (asfilter->reg_str, new->reg_str) == 0) + return 1; + } + return 0; +} + +DEFUN (ip_as_path, ip_as_path_cmd, + "ip as-path access-list WORD (deny|permit) .LINE", + IP_STR + "BGP autonomous system path filter\n" + "Specify an access list name\n" + "Regular expression access list name\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "A regular-expression to match the BGP AS paths\n") +{ + enum as_filter_type type; + struct as_filter *asfilter; + struct as_list *aslist; + regex_t *regex; + char *regstr; + + /* Check the filter type. */ + if (strncmp (argv[1], "p", 1) == 0) + type = AS_FILTER_PERMIT; + else if (strncmp (argv[1], "d", 1) == 0) + type = AS_FILTER_DENY; + else + { + vty_out (vty, "filter type must be [permit|deny]%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Check AS path regex. */ + regstr = argv_concat(argv, argc, 2); + + regex = bgp_regcomp (regstr); + if (!regex) + { + XFREE (MTYPE_TMP, regstr); + vty_out (vty, "can't compile regexp %s%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + + asfilter = as_filter_make (regex, regstr, type); + + XFREE (MTYPE_TMP, regstr); + + /* Install new filter to the access_list. */ + aslist = as_list_get (argv[0]); + + /* Duplicate insertion check. */; + if (as_list_dup_check (aslist, asfilter)) + as_filter_free (asfilter); + else + as_list_filter_add (aslist, asfilter); + + return CMD_SUCCESS; +} + +DEFUN (no_ip_as_path, + no_ip_as_path_cmd, + "no ip as-path access-list WORD (deny|permit) .LINE", + NO_STR + IP_STR + "BGP autonomous system path filter\n" + "Specify an access list name\n" + "Regular expression access list name\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "A regular-expression to match the BGP AS paths\n") +{ + enum as_filter_type type; + struct as_filter *asfilter; + struct as_list *aslist; + char *regstr; + regex_t *regex; + + /* Lookup AS list from AS path list. */ + aslist = as_list_lookup (argv[0]); + if (aslist == NULL) + { + vty_out (vty, "ip as-path access-list %s doesn't exist%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Check the filter type. */ + if (strncmp (argv[1], "p", 1) == 0) + type = AS_FILTER_PERMIT; + else if (strncmp (argv[1], "d", 1) == 0) + type = AS_FILTER_DENY; + else + { + vty_out (vty, "filter type must be [permit|deny]%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Compile AS path. */ + regstr = argv_concat(argv, argc, 2); + + regex = bgp_regcomp (regstr); + if (!regex) + { + XFREE (MTYPE_TMP, regstr); + vty_out (vty, "can't compile regexp %s%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Lookup asfilter. */ + asfilter = as_filter_lookup (aslist, regstr, type); + + XFREE (MTYPE_TMP, regstr); + bgp_regex_free (regex); + + if (asfilter == NULL) + { + vty_out (vty, "%s", VTY_NEWLINE); + return CMD_WARNING; + } + + as_list_filter_delete (aslist, asfilter); + + return CMD_SUCCESS; +} + +DEFUN (no_ip_as_path_all, + no_ip_as_path_all_cmd, + "no ip as-path access-list WORD", + NO_STR + IP_STR + "BGP autonomous system path filter\n" + "Specify an access list name\n" + "Regular expression access list name\n") +{ + struct as_list *aslist; + + aslist = as_list_lookup (argv[0]); + if (aslist == NULL) + { + vty_out (vty, "ip as-path access-list %s doesn't exist%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + + as_list_delete (aslist); + + /* Run hook function. */ + if (as_list_master.delete_hook) + (*as_list_master.delete_hook) (); + + return CMD_SUCCESS; +} + +static void +as_list_show (struct vty *vty, struct as_list *aslist) +{ + struct as_filter *asfilter; + + vty_out (vty, "AS path access list %s%s", aslist->name, VTY_NEWLINE); + + for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) + { + vty_out (vty, " %s %s%s", filter_type_str (asfilter->type), + asfilter->reg_str, VTY_NEWLINE); + } +} + +static void +as_list_show_all (struct vty *vty) +{ + struct as_list *aslist; + struct as_filter *asfilter; + + for (aslist = as_list_master.num.head; aslist; aslist = aslist->next) + { + vty_out (vty, "AS path access list %s%s", aslist->name, VTY_NEWLINE); + + for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) + { + vty_out (vty, " %s %s%s", filter_type_str (asfilter->type), + asfilter->reg_str, VTY_NEWLINE); + } + } + + for (aslist = as_list_master.str.head; aslist; aslist = aslist->next) + { + vty_out (vty, "AS path access list %s%s", aslist->name, VTY_NEWLINE); + + for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) + { + vty_out (vty, " %s %s%s", filter_type_str (asfilter->type), + asfilter->reg_str, VTY_NEWLINE); + } + } +} + +DEFUN (show_ip_as_path_access_list, + show_ip_as_path_access_list_cmd, + "show ip as-path-access-list WORD", + SHOW_STR + IP_STR + "List AS path access lists\n" + "AS path access list name\n") +{ + struct as_list *aslist; + + aslist = as_list_lookup (argv[0]); + if (aslist) + as_list_show (vty, aslist); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_as_path_access_list_all, + show_ip_as_path_access_list_all_cmd, + "show ip as-path-access-list", + SHOW_STR + IP_STR + "List AS path access lists\n") +{ + as_list_show_all (vty); + return CMD_SUCCESS; +} + +static int +config_write_as_list (struct vty *vty) +{ + struct as_list *aslist; + struct as_filter *asfilter; + int write = 0; + + for (aslist = as_list_master.num.head; aslist; aslist = aslist->next) + for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) + { + vty_out (vty, "ip as-path access-list %s %s %s%s", + aslist->name, filter_type_str (asfilter->type), + asfilter->reg_str, + VTY_NEWLINE); + write++; + } + + for (aslist = as_list_master.str.head; aslist; aslist = aslist->next) + for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) + { + vty_out (vty, "ip as-path access-list %s %s %s%s", + aslist->name, filter_type_str (asfilter->type), + asfilter->reg_str, + VTY_NEWLINE); + write++; + } + return write; +} + +static struct cmd_node as_list_node = +{ + AS_LIST_NODE, + "", + 1 +}; + +/* Register functions. */ +void +bgp_filter_init (void) +{ + install_node (&as_list_node, config_write_as_list); + + install_element (CONFIG_NODE, &ip_as_path_cmd); + install_element (CONFIG_NODE, &no_ip_as_path_cmd); + install_element (CONFIG_NODE, &no_ip_as_path_all_cmd); + + install_element (VIEW_NODE, &show_ip_as_path_access_list_cmd); + install_element (VIEW_NODE, &show_ip_as_path_access_list_all_cmd); +} + +void +bgp_filter_reset (void) +{ + struct as_list *aslist; + struct as_list *next; + + for (aslist = as_list_master.num.head; aslist; aslist = next) + { + next = aslist->next; + as_list_delete (aslist); + } + + for (aslist = as_list_master.str.head; aslist; aslist = next) + { + next = aslist->next; + as_list_delete (aslist); + } + + assert (as_list_master.num.head == NULL); + assert (as_list_master.num.tail == NULL); + + assert (as_list_master.str.head == NULL); + assert (as_list_master.str.tail == NULL); +} diff --git a/bgpd/bgp_filter.h b/bgpd/bgp_filter.h new file mode 100644 index 0000000..c1da904 --- /dev/null +++ b/bgpd/bgp_filter.h @@ -0,0 +1,39 @@ +/* AS path filter list. + 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_BGP_FILTER_H +#define _QUAGGA_BGP_FILTER_H + +enum as_filter_type +{ + AS_FILTER_DENY, + AS_FILTER_PERMIT +}; + +extern void bgp_filter_init (void); +extern void bgp_filter_reset (void); + +extern enum as_filter_type as_list_apply (struct as_list *, void *); + +extern struct as_list *as_list_lookup (const char *); +extern void as_list_add_hook (void (*func) (void)); +extern void as_list_delete_hook (void (*func) (void)); + +#endif /* _QUAGGA_BGP_FILTER_H */ diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c new file mode 100644 index 0000000..4198a8e --- /dev/null +++ b/bgpd/bgp_fsm.c @@ -0,0 +1,1205 @@ +/* BGP-4 Finite State Machine + From RFC1771 [A Border Gateway Protocol 4 (BGP-4)] + Copyright (C) 1996, 97, 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 "linklist.h" +#include "prefix.h" +#include "vty.h" +#include "sockunion.h" +#include "thread.h" +#include "log.h" +#include "stream.h" +#include "memory.h" +#include "plist.h" +#include "workqueue.h" +#include "filter.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_debug.h" +#include "bgpd/bgp_fsm.h" +#include "bgpd/bgp_packet.h" +#include "bgpd/bgp_network.h" +#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 + function. */ + +/* BGP event function. */ +int bgp_event (struct thread *); + +/* BGP thread functions. */ +static int bgp_start_timer (struct thread *); +static int bgp_connect_timer (struct thread *); +static int bgp_holdtime_timer (struct thread *); +static int bgp_keepalive_timer (struct thread *); + +/* BGP FSM functions. */ +static int bgp_start (struct peer *); + +/* BGP start timer jitter. */ +static int +bgp_start_jitter (int time) +{ + return ((random () % (time + 1)) - (time / 2)); +} + +/* Check if suppress start/restart of sessions to peer. */ +#define BGP_PEER_START_SUPPRESSED(P) \ + (CHECK_FLAG ((P)->flags, PEER_FLAG_SHUTDOWN) \ + || CHECK_FLAG ((P)->sflags, PEER_STATUS_PREFIX_OVERFLOW)) + +/* Hook function called after bgp event is occered. And vty's + neighbor command invoke this function after making neighbor + structure. */ +void +bgp_timer_set (struct peer *peer) +{ + int jitter = 0; + + switch (peer->status) + { + case Idle: + /* First entry point of peer's finite state machine. In Idle + status start timer is on unless peer is shutdown or peer is + inactive. All other timer must be turned off */ + if (BGP_PEER_START_SUPPRESSED (peer) || ! peer_active (peer)) + { + BGP_TIMER_OFF (peer->t_start); + } + else + { + jitter = bgp_start_jitter (peer->v_start); + BGP_TIMER_ON (peer->t_start, bgp_start_timer, + peer->v_start + jitter); + } + BGP_TIMER_OFF (peer->t_connect); + BGP_TIMER_OFF (peer->t_holdtime); + BGP_TIMER_OFF (peer->t_keepalive); + BGP_TIMER_OFF (peer->t_routeadv); + break; + + case Connect: + /* 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_routeadv); + break; + + case Active: + /* Active is waiting connection from remote peer. And if + connect timer is expired, change status to Connect. */ + BGP_TIMER_OFF (peer->t_start); + /* If peer is passive mode, do not set connect timer. */ + if (CHECK_FLAG (peer->flags, PEER_FLAG_PASSIVE) + || CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT)) + { + BGP_TIMER_OFF (peer->t_connect); + } + else + { + 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_routeadv); + break; + + case OpenSent: + /* OpenSent status. */ + BGP_TIMER_OFF (peer->t_start); + BGP_TIMER_OFF (peer->t_connect); + if (peer->v_holdtime != 0) + { + BGP_TIMER_ON (peer->t_holdtime, bgp_holdtime_timer, + peer->v_holdtime); + } + else + { + BGP_TIMER_OFF (peer->t_holdtime); + } + BGP_TIMER_OFF (peer->t_keepalive); + BGP_TIMER_OFF (peer->t_routeadv); + break; + + case OpenConfirm: + /* OpenConfirm status. */ + BGP_TIMER_OFF (peer->t_start); + BGP_TIMER_OFF (peer->t_connect); + + /* If the negotiated Hold Time value is zero, then the Hold Time + timer and KeepAlive timers are not started. */ + if (peer->v_holdtime == 0) + { + BGP_TIMER_OFF (peer->t_holdtime); + BGP_TIMER_OFF (peer->t_keepalive); + } + else + { + BGP_TIMER_ON (peer->t_holdtime, bgp_holdtime_timer, + peer->v_holdtime); + BGP_TIMER_ON (peer->t_keepalive, bgp_keepalive_timer, + peer->v_keepalive); + } + BGP_TIMER_OFF (peer->t_routeadv); + break; + + case Established: + /* In Established status start and connect timer is turned + off. */ + BGP_TIMER_OFF (peer->t_start); + BGP_TIMER_OFF (peer->t_connect); + + /* Same as OpenConfirm, if holdtime is zero then both holdtime + and keepalive must be turned off. */ + if (peer->v_holdtime == 0) + { + BGP_TIMER_OFF (peer->t_holdtime); + BGP_TIMER_OFF (peer->t_keepalive); + } + else + { + BGP_TIMER_ON (peer->t_holdtime, bgp_holdtime_timer, + peer->v_holdtime); + BGP_TIMER_ON (peer->t_keepalive, bgp_keepalive_timer, + peer->v_keepalive); + } + break; + case Deleted: + BGP_TIMER_OFF (peer->t_gr_restart); + BGP_TIMER_OFF (peer->t_gr_stale); + BGP_TIMER_OFF (peer->t_pmax_restart); + case Clearing: + BGP_TIMER_OFF (peer->t_start); + BGP_TIMER_OFF (peer->t_connect); + BGP_TIMER_OFF (peer->t_holdtime); + BGP_TIMER_OFF (peer->t_keepalive); + BGP_TIMER_OFF (peer->t_routeadv); + } +} + +/* BGP start timer. This function set BGP_Start event to thread value + and process event. */ +static int +bgp_start_timer (struct thread *thread) +{ + struct peer *peer; + + peer = THREAD_ARG (thread); + peer->t_start = NULL; + + if (BGP_DEBUG (fsm, FSM)) + zlog (peer->log, LOG_DEBUG, + "%s [FSM] Timer (start timer expire).", peer->host); + + THREAD_VAL (thread) = BGP_Start; + bgp_event (thread); /* bgp_event unlocks peer */ + + return 0; +} + +/* BGP connect retry timer. */ +static int +bgp_connect_timer (struct thread *thread) +{ + struct peer *peer; + + peer = THREAD_ARG (thread); + peer->t_connect = NULL; + + if (BGP_DEBUG (fsm, FSM)) + zlog (peer->log, LOG_DEBUG, "%s [FSM] Timer (connect timer expire)", + peer->host); + + THREAD_VAL (thread) = ConnectRetry_timer_expired; + bgp_event (thread); /* bgp_event unlocks peer */ + + return 0; +} + +/* BGP holdtime timer. */ +static int +bgp_holdtime_timer (struct thread *thread) +{ + struct peer *peer; + + peer = THREAD_ARG (thread); + peer->t_holdtime = NULL; + + if (BGP_DEBUG (fsm, FSM)) + zlog (peer->log, LOG_DEBUG, + "%s [FSM] Timer (holdtime timer expire)", + peer->host); + + THREAD_VAL (thread) = Hold_Timer_expired; + bgp_event (thread); /* bgp_event unlocks peer */ + + return 0; +} + +/* BGP keepalive fire ! */ +static int +bgp_keepalive_timer (struct thread *thread) +{ + struct peer *peer; + + peer = THREAD_ARG (thread); + peer->t_keepalive = NULL; + + if (BGP_DEBUG (fsm, FSM)) + zlog (peer->log, LOG_DEBUG, + "%s [FSM] Timer (keepalive timer expire)", + peer->host); + + THREAD_VAL (thread) = KeepAlive_timer_expired; + bgp_event (thread); /* bgp_event unlocks peer */ + + return 0; +} + +static int +bgp_routeadv_timer (struct thread *thread) +{ + struct peer *peer; + + peer = THREAD_ARG (thread); + peer->t_routeadv = NULL; + + if (BGP_DEBUG (fsm, FSM)) + zlog (peer->log, LOG_DEBUG, + "%s [FSM] Timer (routeadv timer expire)", + peer->host); + + peer->synctime = bgp_clock (); + + BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); + + BGP_TIMER_ON (peer->t_routeadv, bgp_routeadv_timer, + peer->v_routeadv); + + return 0; +} + +/* BGP Peer Down Cause */ +const char *peer_down_str[] = +{ + "", + "Router ID changed", + "Remote AS changed", + "Local AS change", + "Cluster ID changed", + "Confederation identifier changed", + "Confederation peer changed", + "RR client config change", + "RS client config change", + "Update source change", + "Address family activated", + "Admin. shutdown", + "User reset", + "BGP Notification received", + "BGP Notification send", + "Peer closed the session", + "Neighbor deleted", + "Peer-group add member", + "Peer-group delete member", + "Capability changed", + "Passive config change", + "Multihop config change", + "NSF peer closed the session" +}; + +static int +bgp_graceful_restart_timer_expire (struct thread *thread) +{ + struct peer *peer; + afi_t afi; + safi_t safi; + + peer = THREAD_ARG (thread); + peer->t_gr_restart = NULL; + + /* NSF delete stale route */ + for (afi = AFI_IP ; afi < AFI_MAX ; afi++) + for (safi = SAFI_UNICAST ; safi < SAFI_RESERVED_3 ; safi++) + if (peer->nsf[afi][safi]) + bgp_clear_stale_route (peer, afi, safi); + + UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT); + BGP_TIMER_OFF (peer->t_gr_stale); + + if (BGP_DEBUG (events, EVENTS)) + { + zlog_debug ("%s graceful restart timer expired", peer->host); + zlog_debug ("%s graceful restart stalepath timer stopped", peer->host); + } + + bgp_timer_set (peer); + + return 0; +} + +static int +bgp_graceful_stale_timer_expire (struct thread *thread) +{ + struct peer *peer; + afi_t afi; + safi_t safi; + + peer = THREAD_ARG (thread); + peer->t_gr_stale = NULL; + + if (BGP_DEBUG (events, EVENTS)) + zlog_debug ("%s graceful restart stalepath timer expired", peer->host); + + /* NSF delete stale route */ + for (afi = AFI_IP ; afi < AFI_MAX ; afi++) + for (safi = SAFI_UNICAST ; safi < SAFI_RESERVED_3 ; safi++) + if (peer->nsf[afi][safi]) + bgp_clear_stale_route (peer, afi, safi); + + return 0; +} + +/* Called after event occured, this function change status and reset + read/write and timer thread. */ +void +bgp_fsm_change_status (struct peer *peer, int status) +{ + bgp_dump_state (peer, peer->status, status); + + /* Transition into Clearing or Deleted must /always/ clear all routes.. + * (and must do so before actually changing into Deleted.. + */ + if (status >= Clearing) + { + 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; + peer->status = status; + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s went from %s to %s", + peer->host, + LOOKUP (bgp_status_msg, peer->ostatus), + LOOKUP (bgp_status_msg, peer->status)); +} + +/* Flush the event queue and ensure the peer is shut down */ +static int +bgp_clearing_completed (struct peer *peer) +{ + int rc = bgp_stop(peer); + BGP_EVENT_FLUSH (peer); + + return rc; +} + +/* Administrative BGP peer stop event. */ +/* May be called multiple times for the same peer */ +int +bgp_stop (struct peer *peer) +{ + afi_t afi; + safi_t safi; + char orf_name[BUFSIZ]; + + /* Can't do this in Clearing; events are used for state transitions */ + if (peer->status != Clearing) + { + /* Delete all existing events of the peer */ + BGP_EVENT_FLUSH (peer); + } + + /* Increment Dropped count. */ + if (peer->status == Established) + { + peer->dropped++; + + /* bgp log-neighbor-changes of neighbor Down */ + if (bgp_flag_check (peer->bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES)) + zlog_info ("%%ADJCHANGE: neighbor %s Down %s", peer->host, + peer_down_str [(int) peer->last_reset]); + + /* graceful restart */ + if (peer->t_gr_stale) + { + BGP_TIMER_OFF (peer->t_gr_stale); + if (BGP_DEBUG (events, EVENTS)) + zlog_debug ("%s graceful restart stalepath timer stopped", peer->host); + } + if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT)) + { + if (BGP_DEBUG (events, EVENTS)) + { + zlog_debug ("%s graceful restart timer started for %d sec", + peer->host, peer->v_gr_restart); + zlog_debug ("%s graceful restart stalepath timer started for %d sec", + peer->host, peer->bgp->stalepath_time); + } + BGP_TIMER_ON (peer->t_gr_restart, bgp_graceful_restart_timer_expire, + peer->v_gr_restart); + BGP_TIMER_ON (peer->t_gr_stale, bgp_graceful_stale_timer_expire, + peer->bgp->stalepath_time); + } + else + { + UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_MODE); + + for (afi = AFI_IP ; afi < AFI_MAX ; afi++) + for (safi = SAFI_UNICAST ; safi < SAFI_RESERVED_3 ; safi++) + peer->nsf[afi][safi] = 0; + } + + /* set last reset time */ + peer->resettime = peer->uptime = bgp_clock (); + +#ifdef HAVE_SNMP + bgpTrapBackwardTransition (peer); +#endif /* HAVE_SNMP */ + + /* 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); + + /* Stop all timers. */ + BGP_TIMER_OFF (peer->t_start); + BGP_TIMER_OFF (peer->t_connect); + BGP_TIMER_OFF (peer->t_holdtime); + BGP_TIMER_OFF (peer->t_keepalive); + BGP_TIMER_OFF (peer->t_routeadv); + + /* Stream reset. */ + peer->packet_size = 0; + + /* Clear input and output buffer. */ + if (peer->ibuf) + stream_reset (peer->ibuf); + if (peer->work) + stream_reset (peer->work); + if (peer->obuf) + stream_fifo_clean (peer->obuf); + + /* Close of file descriptor. */ + if (peer->fd >= 0) + { + close (peer->fd); + peer->fd = -1; + } + + for (afi = AFI_IP ; afi < AFI_MAX ; afi++) + for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++) + { + /* Reset all negotiated variables */ + peer->afc_nego[afi][safi] = 0; + peer->afc_adv[afi][safi] = 0; + peer->afc_recv[afi][safi] = 0; + + /* peer address family capability flags*/ + peer->af_cap[afi][safi] = 0; + + /* peer address family status flags*/ + peer->af_sflags[afi][safi] = 0; + + /* Received ORF prefix-filter */ + peer->orf_plist[afi][safi] = NULL; + + /* ORF received prefix-filter pnt */ + sprintf (orf_name, "%s.%d.%d", peer->host, afi, safi); + prefix_bgp_orf_remove_all (afi, orf_name); + } + + /* Reset keepalive and holdtime */ + if (CHECK_FLAG (peer->config, PEER_CONFIG_TIMER)) + { + peer->v_keepalive = peer->keepalive; + peer->v_holdtime = peer->holdtime; + } + else + { + peer->v_keepalive = peer->bgp->default_keepalive; + peer->v_holdtime = peer->bgp->default_holdtime; + } + + peer->update_time = 0; + + /* Until we are sure that there is no problem about prefix count + this should be commented out.*/ +#if 0 + /* Reset prefix count */ + peer->pcount[AFI_IP][SAFI_UNICAST] = 0; + peer->pcount[AFI_IP][SAFI_MULTICAST] = 0; + peer->pcount[AFI_IP][SAFI_MPLS_VPN] = 0; + peer->pcount[AFI_IP6][SAFI_UNICAST] = 0; + peer->pcount[AFI_IP6][SAFI_MULTICAST] = 0; +#endif /* 0 */ + + 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) +{ + peer->v_start + = back_off_exp2_bias (BGP_INIT_START_TIMER, peer->v_start, 60); + bgp_stop (peer); + return 0; +} + + +/* something went wrong, send notify and tear down */ +static int +bgp_stop_with_notify (struct peer *peer, u_char code, u_char sub_code) +{ + /* Send notify to remote peer */ + bgp_notify_send (peer, code, sub_code); + + /* Sweep if it is temporary peer. */ + if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) + { + zlog_info ("%s [Event] Accepting BGP peer is deleted", peer->host); + peer_delete (peer); + return -1; + } + + /* Clear start timer value to default. */ + peer->v_start = BGP_INIT_START_TIMER; + + /* bgp_stop needs to be invoked while in Established state */ + bgp_stop(peer); + + return 0; +} + + +/* TCP connection open. Next we send open message to remote peer. And + add read thread for reading open message. */ +static int +bgp_connect_success (struct peer *peer) +{ + struct peer *realpeer; + + if (peer->fd < 0) + { + zlog_err ("bgp_connect_success peer's fd is negative value %d", + peer->fd); + return -1; + } + BGP_READ_ON (peer->t_read, bgp_read, peer->fd); + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) + bgp_getsockname (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); + } + + /* 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; +} + +/* TCP connect fail */ +static int +bgp_connect_fail (struct peer *peer) +{ + bgp_stop (peer); + return 0; +} + +/* This function is the first starting point of all BGP connection. It + try to connect to remote peer with non-blocking IO. */ +int +bgp_start (struct peer *peer) +{ + int status; + int connected = 0; + + if (BGP_PEER_START_SUPPRESSED (peer)) + { + if (BGP_DEBUG (fsm, FSM)) + plog_err (peer->log, "%s [FSM] Trying to start suppressed peer" + " - this is never supposed to happen!", peer->host); + return -1; + } + + /* Scrub some information that might be left over from a previous, + * session + */ + /* Connection information. */ + if (peer->su_local) + { + sockunion_free (peer->su_local); + peer->su_local = NULL; + } + + if (peer->su_remote) + { + sockunion_free (peer->su_remote); + peer->su_remote = NULL; + } + + /* Clear remote router-id. */ + peer->remote_id.s_addr = 0; + + /* Clear peer capability flag. */ + peer->cap = 0; + + /* If the peer is passive mode, force to move to Active mode. */ + if (CHECK_FLAG (peer->flags, PEER_FLAG_PASSIVE)) + { + BGP_EVENT_ADD (peer, TCP_connection_open_failed); + 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) + { + case connect_error: + if (BGP_DEBUG (fsm, FSM)) + plog_debug (peer->log, "%s [FSM] Connect error", peer->host); + BGP_EVENT_ADD (peer, TCP_connection_open_failed); + break; + case connect_success: + if (BGP_DEBUG (fsm, FSM)) + plog_debug (peer->log, "%s [FSM] Connect immediately success", + peer->host); + BGP_EVENT_ADD (peer, TCP_connection_open); + break; + case connect_in_progress: + /* To check nonblocking connect, we wait until socket is + readable or writable. */ + if (BGP_DEBUG (fsm, FSM)) + plog_debug (peer->log, "%s [FSM] Non blocking connect waiting result", + peer->host); + if (peer->fd < 0) + { + zlog_err ("bgp_start peer's fd is negative value %d", + peer->fd); + return -1; + } + BGP_READ_ON (peer->t_read, bgp_read, peer->fd); + BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); + break; + } + return 0; +} + +/* Connect retry timer is expired when the peer status is Connect. */ +static int +bgp_reconnect (struct peer *peer) +{ + bgp_stop (peer); + bgp_start (peer); + return 0; +} + +static int +bgp_fsm_open (struct peer *peer) +{ + /* Send keepalive and make keepalive timer */ + bgp_keepalive_send (peer); + + /* Reset holdtimer value. */ + BGP_TIMER_OFF (peer->t_holdtime); + + return 0; +} + +/* Keepalive send to peer. */ +static int +bgp_fsm_keepalive_expire (struct peer *peer) +{ + bgp_keepalive_send (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)) + plog_debug (peer->log, "%s [FSM] Hold timer expire", peer->host); + + return bgp_stop_with_notify (peer, BGP_NOTIFY_HOLD_ERR, 0); +} + +/* Status goes to Established. Send keepalive packet then make first + update information. */ +static int +bgp_establish (struct peer *peer) +{ + struct bgp_notify *notify; + afi_t afi; + safi_t safi; + int nsf_af_count = 0; + + /* Reset capability open status flag. */ + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN)) + SET_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN); + + /* Clear last notification data. */ + notify = &peer->notify; + if (notify->data) + XFREE (MTYPE_TMP, notify->data); + memset (notify, 0, sizeof (struct bgp_notify)); + + /* Clear start timer value to default. */ + peer->v_start = BGP_INIT_START_TIMER; + + /* Increment established count. */ + peer->established++; + bgp_fsm_change_status (peer, Established); + + /* bgp log-neighbor-changes of neighbor Up */ + if (bgp_flag_check (peer->bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES)) + zlog_info ("%%ADJCHANGE: neighbor %s Up", peer->host); + + /* graceful restart */ + UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT); + for (afi = AFI_IP ; afi < AFI_MAX ; afi++) + for (safi = SAFI_UNICAST ; safi < SAFI_RESERVED_3 ; safi++) + { + if (peer->afc_nego[afi][safi] + && CHECK_FLAG (peer->cap, PEER_CAP_RESTART_ADV) + && CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_RESTART_AF_RCV)) + { + if (peer->nsf[afi][safi] + && ! CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_RESTART_AF_PRESERVE_RCV)) + bgp_clear_stale_route (peer, afi, safi); + + peer->nsf[afi][safi] = 1; + nsf_af_count++; + } + else + { + if (peer->nsf[afi][safi]) + bgp_clear_stale_route (peer, afi, safi); + peer->nsf[afi][safi] = 0; + } + } + + if (nsf_af_count) + SET_FLAG (peer->sflags, PEER_STATUS_NSF_MODE); + else + { + UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_MODE); + if (peer->t_gr_stale) + { + BGP_TIMER_OFF (peer->t_gr_stale); + if (BGP_DEBUG (events, EVENTS)) + zlog_debug ("%s graceful restart stalepath timer stopped", peer->host); + } + } + + if (peer->t_gr_restart) + { + BGP_TIMER_OFF (peer->t_gr_restart); + if (BGP_DEBUG (events, EVENTS)) + zlog_debug ("%s graceful restart timer stopped", peer->host); + } + +#ifdef HAVE_SNMP + bgpTrapEstablished (peer); +#endif /* HAVE_SNMP */ + + /* Reset uptime, send keepalive, send current table. */ + peer->uptime = bgp_clock (); + + /* Send route-refresh when ORF is enabled */ + for (afi = AFI_IP ; afi < AFI_MAX ; afi++) + for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++) + if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV)) + { + if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_RCV)) + bgp_route_refresh_send (peer, afi, safi, ORF_TYPE_PREFIX, + REFRESH_IMMEDIATE, 0); + else if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_OLD_RCV)) + bgp_route_refresh_send (peer, afi, safi, ORF_TYPE_PREFIX_OLD, + REFRESH_IMMEDIATE, 0); + } + + if (peer->v_keepalive) + bgp_keepalive_send (peer); + + /* First update is deferred until ORF or ROUTE-REFRESH is received */ + for (afi = AFI_IP ; afi < AFI_MAX ; afi++) + for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++) + if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_ADV)) + if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_RCV) + || CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_OLD_RCV)) + SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_WAIT_REFRESH); + + bgp_announce_route_all (peer); + + BGP_TIMER_ON (peer->t_routeadv, bgp_routeadv_timer, 1); + + return 0; +} + +/* Keepalive packet is received. */ +static int +bgp_fsm_keepalive (struct peer *peer) +{ + /* peer count update */ + peer->keepalive_in++; + + BGP_TIMER_OFF (peer->t_holdtime); + return 0; +} + +/* Update packet is received. */ +static int +bgp_fsm_update (struct peer *peer) +{ + BGP_TIMER_OFF (peer->t_holdtime); + return 0; +} + +/* This is empty event. */ +static int +bgp_ignore (struct peer *peer) +{ + if (BGP_DEBUG (fsm, FSM)) + 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 *); + int next_state; +} FSM [BGP_STATUS_MAX - 1][BGP_EVENTS_MAX - 1] = +{ + { + /* Idle state: In Idle state, all events other than BGP_Start is + ignored. With BGP_Start event, finite state machine calls + bgp_start(). */ + {bgp_start, Connect}, /* BGP_Start */ + {bgp_stop, Idle}, /* BGP_Stop */ + {bgp_stop, Idle}, /* TCP_connection_open */ + {bgp_stop, Idle}, /* TCP_connection_closed */ + {bgp_ignore, Idle}, /* TCP_connection_open_failed */ + {bgp_stop, Idle}, /* TCP_fatal_error */ + {bgp_ignore, Idle}, /* ConnectRetry_timer_expired */ + {bgp_ignore, Idle}, /* Hold_Timer_expired */ + {bgp_ignore, Idle}, /* KeepAlive_timer_expired */ + {bgp_ignore, Idle}, /* Receive_OPEN_message */ + {bgp_ignore, Idle}, /* Receive_KEEPALIVE_message */ + {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 */ + {bgp_ignore, Connect}, /* BGP_Start */ + {bgp_stop, Idle}, /* BGP_Stop */ + {bgp_connect_success, OpenSent}, /* TCP_connection_open */ + {bgp_stop, Idle}, /* TCP_connection_closed */ + {bgp_connect_fail, Active}, /* TCP_connection_open_failed */ + {bgp_connect_fail, Idle}, /* TCP_fatal_error */ + {bgp_reconnect, Connect}, /* ConnectRetry_timer_expired */ + {bgp_ignore, Idle}, /* Hold_Timer_expired */ + {bgp_ignore, Idle}, /* KeepAlive_timer_expired */ + {bgp_ignore, Idle}, /* Receive_OPEN_message */ + {bgp_ignore, Idle}, /* Receive_KEEPALIVE_message */ + {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, */ + {bgp_ignore, Active}, /* BGP_Start */ + {bgp_stop, Idle}, /* BGP_Stop */ + {bgp_connect_success, OpenSent}, /* TCP_connection_open */ + {bgp_stop, Idle}, /* TCP_connection_closed */ + {bgp_ignore, Active}, /* TCP_connection_open_failed */ + {bgp_ignore, Idle}, /* TCP_fatal_error */ + {bgp_start, Connect}, /* ConnectRetry_timer_expired */ + {bgp_ignore, Idle}, /* Hold_Timer_expired */ + {bgp_ignore, Idle}, /* KeepAlive_timer_expired */ + {bgp_ignore, Idle}, /* Receive_OPEN_message */ + {bgp_ignore, Idle}, /* Receive_KEEPALIVE_message */ + {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, */ + {bgp_ignore, OpenSent}, /* BGP_Start */ + {bgp_stop, Idle}, /* BGP_Stop */ + {bgp_stop, Active}, /* TCP_connection_open */ + {bgp_stop, Active}, /* TCP_connection_closed */ + {bgp_stop, Active}, /* TCP_connection_open_failed */ + {bgp_stop, Active}, /* TCP_fatal_error */ + {bgp_ignore, Idle}, /* ConnectRetry_timer_expired */ + {bgp_fsm_holdtime_expire, Idle}, /* Hold_Timer_expired */ + {bgp_ignore, Idle}, /* KeepAlive_timer_expired */ + {bgp_fsm_open, OpenConfirm}, /* Receive_OPEN_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, */ + {bgp_ignore, OpenConfirm}, /* BGP_Start */ + {bgp_stop, Idle}, /* BGP_Stop */ + {bgp_stop, Idle}, /* TCP_connection_open */ + {bgp_stop, Idle}, /* TCP_connection_closed */ + {bgp_stop, Idle}, /* TCP_connection_open_failed */ + {bgp_stop, Idle}, /* TCP_fatal_error */ + {bgp_ignore, Idle}, /* ConnectRetry_timer_expired */ + {bgp_fsm_holdtime_expire, Idle}, /* Hold_Timer_expired */ + {bgp_ignore, OpenConfirm}, /* KeepAlive_timer_expired */ + {bgp_ignore, Idle}, /* Receive_OPEN_message */ + {bgp_establish, Established}, /* Receive_KEEPALIVE_message */ + {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, */ + {bgp_ignore, Established}, /* BGP_Start */ + {bgp_stop, Clearing}, /* BGP_Stop */ + {bgp_stop, Clearing}, /* TCP_connection_open */ + {bgp_stop, Clearing}, /* TCP_connection_closed */ + {bgp_stop, Clearing}, /* TCP_connection_open_failed */ + {bgp_stop, Clearing}, /* TCP_fatal_error */ + {bgp_stop, Clearing}, /* ConnectRetry_timer_expired */ + {bgp_fsm_holdtime_expire, Clearing}, /* Hold_Timer_expired */ + {bgp_fsm_keepalive_expire, Established}, /* KeepAlive_timer_expired */ + {bgp_stop, Clearing}, /* Receive_OPEN_message */ + {bgp_fsm_keepalive, Established}, /* Receive_KEEPALIVE_message */ + {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, */ + {bgp_ignore, Clearing}, /* BGP_Start */ + {bgp_stop, Clearing}, /* BGP_Stop */ + {bgp_stop, Clearing}, /* TCP_connection_open */ + {bgp_stop, Clearing}, /* TCP_connection_closed */ + {bgp_stop, Clearing}, /* TCP_connection_open_failed */ + {bgp_stop, Clearing}, /* TCP_fatal_error */ + {bgp_stop, Clearing}, /* ConnectRetry_timer_expired */ + {bgp_stop, Clearing}, /* Hold_Timer_expired */ + {bgp_stop, Clearing}, /* KeepAlive_timer_expired */ + {bgp_stop, Clearing}, /* Receive_OPEN_message */ + {bgp_stop, Clearing}, /* Receive_KEEPALIVE_message */ + {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, */ + {bgp_ignore, Deleted}, /* BGP_Start */ + {bgp_ignore, Deleted}, /* BGP_Stop */ + {bgp_ignore, Deleted}, /* TCP_connection_open */ + {bgp_ignore, Deleted}, /* TCP_connection_closed */ + {bgp_ignore, Deleted}, /* TCP_connection_open_failed */ + {bgp_ignore, Deleted}, /* TCP_fatal_error */ + {bgp_ignore, Deleted}, /* ConnectRetry_timer_expired */ + {bgp_ignore, Deleted}, /* Hold_Timer_expired */ + {bgp_ignore, Deleted}, /* KeepAlive_timer_expired */ + {bgp_ignore, Deleted}, /* Receive_OPEN_message */ + {bgp_ignore, Deleted}, /* Receive_KEEPALIVE_message */ + {bgp_ignore, Deleted}, /* Receive_UPDATE_message */ + {bgp_ignore, Deleted}, /* Receive_NOTIFICATION_message */ + {bgp_ignore, Deleted}, /* Clearing_Completed */ + {bgp_ignore, Deleted}, /* BGP_Stop_with_error */ + }, +}; + +static const char *bgp_event_str[] = +{ + NULL, + "BGP_Start", + "BGP_Stop", + "TCP_connection_open", + "TCP_connection_closed", + "TCP_connection_open_failed", + "TCP_fatal_error", + "ConnectRetry_timer_expired", + "Hold_Timer_expired", + "KeepAlive_timer_expired", + "Receive_OPEN_message", + "Receive_KEEPALIVE_message", + "Receive_UPDATE_message", + "Receive_NOTIFICATION_message", + "Clearing_Completed", + "BGP_Stop_with_error", +}; + +/* Execute event process. */ +int +bgp_event (struct thread *thread) +{ + int ret = 0; + int event; + int next; + struct peer *peer; + + peer = THREAD_ARG (thread); + event = THREAD_VAL (thread); + + /* Logging this event. */ + next = FSM [peer->status -1][event - 1].next_state; + + if (BGP_DEBUG (fsm, FSM) && peer->status != next) + plog_debug (peer->log, "%s [FSM] %s (%s->%s)", peer->host, + bgp_event_str[event], + LOOKUP (bgp_status_msg, peer->status), + LOOKUP (bgp_status_msg, next)); + + /* Call function. */ + if (FSM [peer->status -1][event - 1].func) + ret = (*(FSM [peer->status - 1][event - 1].func))(peer); + + /* When function do not want proceed next job return -1. */ + if (ret >= 0) + { + /* If status is changed. */ + if (next != peer->status) + bgp_fsm_change_status (peer, next); + + /* Make sure timer is set. */ + bgp_timer_set (peer); + } + + return ret; +} diff --git a/bgpd/bgp_fsm.h b/bgpd/bgp_fsm.h new file mode 100644 index 0000000..752d6e2 --- /dev/null +++ b/bgpd/bgp_fsm.h @@ -0,0 +1,81 @@ +/* BGP-4 Finite State Machine + From RFC1771 [A Border Gateway Protocol 4 (BGP-4)] + Copyright (C) 1998 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_BGP_FSM_H +#define _QUAGGA_BGP_FSM_H + +/* Macro for BGP read, write and timer thread. */ +#define BGP_READ_ON(T,F,V) \ + do { \ + if (!(T) && (peer->status != Deleted)) \ + THREAD_READ_ON(bm->master,T,F,peer,V); \ + } while (0) + +#define BGP_READ_OFF(T) \ + do { \ + if (T) \ + THREAD_READ_OFF(T); \ + } while (0) + +#define BGP_WRITE_ON(T,F,V) \ + do { \ + if (!(T) && (peer->status != Deleted)) \ + THREAD_WRITE_ON(bm->master,(T),(F),peer,(V)); \ + } while (0) + +#define BGP_WRITE_OFF(T) \ + do { \ + if (T) \ + THREAD_WRITE_OFF(T); \ + } while (0) + +#define BGP_TIMER_ON(T,F,V) \ + do { \ + if (!(T) && (peer->status != Deleted)) \ + THREAD_TIMER_ON(bm->master,(T),(F),peer,(V)); \ + } while (0) + +#define BGP_TIMER_OFF(T) \ + do { \ + if (T) \ + THREAD_TIMER_OFF(T); \ + } while (0) + +#define BGP_EVENT_ADD(P,E) \ + do { \ + if ((P)->status != Deleted) \ + thread_add_event (bm->master, bgp_event, (P), (E)); \ + } while (0) + +#define BGP_EVENT_FLUSH(P) \ + do { \ + assert (peer); \ + thread_cancel_event (bm->master, (P)); \ + } while (0) + +/* Prototypes. */ +extern int bgp_event (struct thread *); +extern int bgp_stop (struct peer *peer); +extern void bgp_timer_set (struct peer *); +extern void bgp_fsm_change_status (struct peer *peer, int status); +extern const char *peer_down_str[]; + +#endif /* _QUAGGA_BGP_FSM_H */ diff --git a/bgpd/bgp_fsm_4271.dot b/bgpd/bgp_fsm_4271.dot new file mode 100644 index 0000000..c03939f --- /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 0000000..2b9bee8 --- /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 0000000..cc67e12 --- /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 0000000..7841b4b --- /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 new file mode 100644 index 0000000..af9c030 --- /dev/null +++ b/bgpd/bgp_main.c @@ -0,0 +1,493 @@ +/* Main routine of bgpd. + Copyright (C) 1996, 97, 98, 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 "vector.h" +#include "vty.h" +#include "command.h" +#include "getopt.h" +#include "thread.h" +#include +#include "memory.h" +#include "prefix.h" +#include "log.h" +#include "privs.h" +#include "sigevent.h" +#include "zclient.h" +#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" +#include "bgpd/bgp_mplsvpn.h" +#include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_dump.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_nexthop.h" +#include "bgpd/bgp_regex.h" +#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[] = +{ + { "daemon", no_argument, NULL, 'd'}, + { "config_file", required_argument, NULL, 'f'}, + { "pid_file", required_argument, NULL, 'i'}, + { "socket", required_argument, NULL, 'z'}, + { "bgp_port", required_argument, NULL, 'p'}, + { "listenon", required_argument, NULL, 'l'}, + { "vty_addr", required_argument, NULL, 'A'}, + { "vty_port", required_argument, NULL, 'P'}, + { "retain", no_argument, NULL, 'r'}, + { "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'}, + { 0 } +}; + +/* signal definitions */ +void sighup (void); +void sigint (void); +void sigusr1 (void); + +static void bgp_exit (int); + +static struct quagga_signal_t bgp_signals[] = +{ + { + .signal = SIGHUP, + .handler = &sighup, + }, + { + .signal = SIGUSR1, + .handler = &sigusr1, + }, + { + .signal = SIGINT, + .handler = &sigint, + }, + { + .signal = SIGTERM, + .handler = &sigint, + }, +}; + +/* Configuration file and directory. */ +char config_default[] = SYSCONFDIR BGP_DEFAULT_CONFIG; + +/* Route retain mode flag. */ +static int retain_mode = 0; + +/* Manually specified configuration file name. */ +char *config_file = NULL; + +/* Process ID saved for use by init system */ +static const char *pid_file = PATH_BGPD_PID; + +/* VTY port number and address. */ +int vty_port = BGP_VTY_PORT; +char *vty_addr = NULL; + +/* 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, +}; + +/* Help information display. */ +static void +usage (char *progname, int status) +{ + if (status != 0) + fprintf (stderr, "Try `%s --help' for more information.\n", progname); + else + { + printf ("Usage : %s [OPTION...]\n\n\ +Daemon which manages kernel routing table management and \ +redistribution between different routing protocols.\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\ +-p, --bgp_port Set bgp protocol's port number\n\ +-l, --listenon Listen on specified address (implies -n)\n\ +-A, --vty_addr Set vty's bind address\n\ +-P, --vty_port Set vty's port number\n\ +-r, --retain When program terminates, retain added route by bgpd.\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\ +\n\ +Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); + } + + exit (status); +} + +/* SIGHUP handler. */ +void +sighup (void) +{ + zlog (NULL, LOG_INFO, "SIGHUP received"); + + /* Terminate all thread. */ + bgp_terminate (); + bgp_reset (); + zlog_info ("bgpd restarting!"); + + /* Reload config file. */ + vty_read_config (config_file, config_default); + + /* Create VTY's socket */ + vty_serv_sock (vty_addr, vty_port, BGP_VTYSH_PATH); + + /* Try to return to normal operation. */ +} + +/* SIGINT handler. */ +void +sigint (void) +{ + zlog_notice ("Terminating on signal"); + + if (! retain_mode) + { + bgp_terminate (); + if (bgpd_privs.user) /* NULL if skip_runas flag set */ + zprivs_terminate (&bgpd_privs); + } + + bgp_exit (0); +} + +/* SIGUSR1 handler. */ +void +sigusr1 (void) +{ + zlog_rotate (NULL); +} + +/* + Try to free up allocations we know about so that diagnostic tools such as + valgrind are able to better illuminate leaks. + + Zebra route removal and protocol teardown are not meant to be done here. + For example, "retain_mode" may be set. +*/ +static void +bgp_exit (int status) +{ + struct bgp *bgp; + struct listnode *node, *nnode; + int *socket; + struct interface *ifp; + + /* it only makes sense for this to be called on a clean exit */ + assert (status == 0); + + /* reverse bgp_master_init */ + 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)) + { + if (close ((int)(long)socket) == -1) + zlog_err ("close (%d): %s", (int)(long)socket, safe_strerror (errno)); + } + list_delete (bm->listen_sockets); + + /* reverse bgp_zebra_init/if_init */ + if (retain_mode) + if_add_hook (IF_DELETE_HOOK, NULL); + 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); + } + + /* reverse bgp_attr_init */ + bgp_attr_finish (); + + /* reverse bgp_dump_init */ + bgp_dump_finish (); + + /* reverse bgp_route_init */ + bgp_route_finish (); + + /* reverse bgp_route_map_init/route_map_init */ + route_map_finish (); + + /* reverse access_list_init */ + access_list_add_hook (NULL); + access_list_delete_hook (NULL); + access_list_reset (); + + /* reverse bgp_filter_init */ + as_list_add_hook (NULL); + as_list_delete_hook (NULL); + bgp_filter_reset (); + + /* reverse prefix_list_init */ + prefix_list_add_hook (NULL); + prefix_list_delete_hook (NULL); + prefix_list_reset (); + + /* reverse community_list_init */ + community_list_terminate (bgp_clist); + + vrf_terminate (); + cmd_terminate (); + vty_terminate (); + 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 (bm->master) + thread_master_free (bm->master); + + if (zlog_default) + closezlog (zlog_default); + + if (CONF_BGP_DEBUG (normal, NORMAL)) + log_memstats_stderr ("bgpd"); + + exit (status); +} + +/* Main routine of bgpd. Treatment of argument and start bgp finite + state machine is handled at here. */ +int +main (int argc, char **argv) +{ + char *p; + int opt; + int daemon_mode = 0; + int dryrun = 0; + char *progname; + struct thread thread; + int tmp_port; + int skip_runas = 0; + + /* Set umask before anything for security */ + umask (0027); + + /* Preserve name of myself. */ + progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]); + + zlog_default = openzlog (progname, ZLOG_BGP, + LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON); + + /* BGP master init. */ + bgp_master_init (); + + /* Command line argument treatment. */ + while (1) + { + opt = getopt_long (argc, argv, "df:i:z:hp:l:A:P:rnu:g:vCS", 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': + zclient_serv_path_set (optarg); + break; + case 'p': + tmp_port = atoi (optarg); + if (tmp_port <= 0 || tmp_port > 0xffff) + bm->port = BGP_PORT_DEFAULT; + else + bm->port = tmp_port; + break; + case 'A': + vty_addr = optarg; + break; + case 'P': + /* Deal with atoi() returning 0 on failure, and bgpd not + listening on bgp port... */ + if (strcmp(optarg, "0") == 0) + { + vty_port = 0; + break; + } + vty_port = atoi (optarg); + if (vty_port <= 0 || vty_port > 0xffff) + vty_port = BGP_VTY_PORT; + break; + case 'r': + retain_mode = 1; + break; + case 'l': + bm->address = optarg; + /* listenon implies -n */ + case 'n': + bgp_option_set (BGP_OPT_NO_FIB); + break; + case 'u': + bgpd_privs.user = optarg; + break; + 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); + break; + case 'C': + dryrun = 1; + break; + case 'h': + usage (progname, 0); + break; + default: + usage (progname, 1); + break; + } + } + + /* Initializations. */ + 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 (bm->master); + memory_init (); + vrf_init (); + + /* BGP related initialization. */ + bgp_init (); + + /* Parse config file. */ + vty_read_config (config_file, config_default); + + /* Start execution only if not in dry-run mode */ + if(dryrun) + return(0); + + /* Turn into daemon if daemon_mode is set. */ + if (daemon_mode && daemon (0, 0) < 0) + { + zlog_err("BGPd daemon failed: %s", strerror(errno)); + return (1); + } + + + /* Process ID file creation. */ + pid_output (pid_file); + + /* Make bgp vty socket. */ + vty_serv_sock (vty_addr, vty_port, BGP_VTYSH_PATH); + + /* Print banner. */ + zlog_notice ("BGPd %s starting: vty@%d, bgp@%s:%d pid %d", QUAGGA_VERSION, + vty_port, + (bm->address ? bm->address : ""), + bm->port, + getpid ()); + + /* Start finite state machine, here we go! */ + while (thread_fetch (bm->master, &thread)) + thread_call (&thread); + + /* Not reached. */ + return (0); +} diff --git a/bgpd/bgp_mpath.c b/bgpd/bgp_mpath.c new file mode 100644 index 0000000..8195e47 --- /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 0000000..2a84d5e --- /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 new file mode 100644 index 0000000..ac3ee08 --- /dev/null +++ b/bgpd/bgp_mplsvpn.c @@ -0,0 +1,1063 @@ +/* MPLS-VPN + 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_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) +{ + u_int16_t v; + + v = ((u_int16_t) *pnt++ << 8); + v |= (u_int16_t) *pnt; + return v; +} + +u_int32_t +decode_label (u_char *pnt) +{ + u_int32_t l; + + l = ((u_int32_t) *pnt++ << 12); + l |= (u_int32_t) *pnt++ << 4; + l |= (u_int32_t) ((*pnt & 0xf0) >> 4); + return l; +} + +/* type == RD_TYPE_AS */ +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; +} + +/* 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) +{ + memcpy (&rd_ip->ip, pnt, 4); + pnt += 4; + + rd_ip->val = ((u_int16_t) *pnt++ << 8); + rd_ip->val |= (u_int16_t) *pnt; +} + +int +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 = 0; + int prefixlen; + u_int16_t type; + struct rd_as rd_as; + struct rd_ip rd_ip; + struct prefix_rd prd; + u_char *tagpnt; + + /* Check peer status. */ + if (peer->status != Established) + return 0; + + /* Make prefix_rd */ + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + + pnt = packet->nlri; + lim = pnt + packet->length; + +#define VPN_PREFIXLEN_MIN_BYTES (3 + 8) /* label + RD */ + for (; pnt < lim; pnt += psize) + { + /* Clear prefix structure. */ + memset (&p, 0, sizeof (struct prefix)); + + /* Fetch prefix length. */ + prefixlen = *pnt++; + p.family = afi2family (packet->afi); + psize = PSIZE (prefixlen); + + /* 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; + + /* Copy routing distinguisher to rd. */ + memcpy (&prd.val, pnt + 3, 8); + + /* Decode RD type. */ + type = decode_rd_type (pnt + 3); + + switch (type) + { + case RD_TYPE_AS: + decode_rd_as (pnt + 5, &rd_as); + break; + + case RD_TYPE_AS4: + decode_rd_as4 (pnt + 5, &rd_as); + break; + + case RD_TYPE_IP: + decode_rd_ip (pnt + 5, &rd_ip); + break; + + 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, packet->afi, SAFI_MPLS_VPN, + ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, tagpnt, 0); + else + 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) + { + 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; /* ret of called functions */ + int lret; /* local ret, of this func */ + char *p; + char *p2; + struct stream *s = NULL; + char *half = NULL; + struct in_addr addr; + + s = stream_new (8); + + prd->family = AF_UNSPEC; + prd->prefixlen = 64; + + lret = 0; + p = strchr (str, ':'); + if (! p) + goto out; + + if (! all_digit (p + 1)) + goto out; + + half = XMALLOC (MTYPE_TMP, (p - str) + 1); + memcpy (half, str, (p - str)); + half[p - str] = '\0'; + + p2 = strchr (str, '.'); + + if (! p2) + { + if (! all_digit (half)) + goto out; + + stream_putw (s, RD_TYPE_AS); + stream_putw (s, atoi (half)); + stream_putl (s, atol (p + 1)); + } + else + { + ret = inet_aton (half, &addr); + if (! ret) + 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); + lret = 1; + +out: + if (s) + stream_free (s); + if (half) + XFREE(MTYPE_TMP, half); + return lret; +} + +int +str2tag (const char *str, u_char *tag) +{ + unsigned long l; + char *endptr; + u_int32_t t; + + if (*str == '-') + return 0; + + errno = 0; + l = strtoul (str, &endptr, 10); + + if (*endptr != '\0' || errno || l > UINT32_MAX) + return 0; + + t = (u_int32_t) l; + + tag[0] = (u_char)(t >> 12); + tag[1] = (u_char)(t >> 4); + tag[2] = (u_char)(t << 4); + + return 1; +} + +char * +prefix_rd2str (struct prefix_rd *prd, char *buf, size_t size) +{ + u_char *pnt; + u_int16_t type; + struct rd_as rd_as; + struct rd_ip rd_ip; + + if (size < RD_ADDRSTRLEN) + return NULL; + + pnt = prd->val; + + type = decode_rd_type (pnt); + + if (type == RD_TYPE_AS) + { + decode_rd_as (pnt + 2, &rd_as); + 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; +} + +/* For testing purpose, static route of MPLS-VPN. */ +DEFUN (vpnv4_network, + vpnv4_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" + "VPN Route Distinguisher\n" + "BGP tag\n" + "tag value\n") +{ + 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. */ +DEFUN (no_vpnv4_network, + no_vpnv4_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" + "VPN Route Distinguisher\n" + "BGP tag\n" + "tag value\n") +{ + return bgp_static_unset_safi (SAFI_MPLS_VPN, vty, argv[0], argv[1], argv[2]); +} + +static int +show_adj_route_vpn (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_MPLS_VPN]); 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; + + /* Decode RD type. */ + type = decode_rd_type (pnt); + /* 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); + 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); + + vty_out (vty, "%s", VTY_NEWLINE); + rd_header = 0; + } + route_vty_out_tmp (vty, &rm->p, attr, SAFI_MPLS_VPN); + } + } + } + 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_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; + 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_MPLS_VPN]); 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); + /* 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, "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, "ip %s:%d", inet_ntoa (rd_ip.ip), rd_ip.val); + + vty_out (vty, "%s", VTY_NEWLINE); + rd_header = 0; + } + if (tags) + 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_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 + BGP_STR + "Address Family\n" + "Display VPN NLRI specific information\n" + "Display information for a route distinguisher\n" + "VPN 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_mpls_vpn (vty, AFI_IP, &prd, bgp_show_type_normal, NULL, 0); +} + +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 + BGP_STR + "Address Family\n" + "Display VPN NLRI specific information\n" + "Display information for a route distinguisher\n" + "VPN 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_mpls_vpn (vty, AFI_IP6, &prd, bgp_show_type_normal, NULL, 0); +} + + +DEFUN (show_bgp_ipv4_vpn_tags, + show_bgp_ipv4_vpn_tags_cmd, + "show bgp ipv4 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_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_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 + 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; + + 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, 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; + + 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 + 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" + "Neighbor to display information about\n" + "Display routes learned from neighbor\n") +{ + union sockunion su; + struct peer *peer; + int ret; + + 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, AFI_IP, NULL, bgp_show_type_neighbor, &su, 0); +} + +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 + 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" + "Neighbor to display information about\n" + "Display routes learned from neighbor\n") +{ + union sockunion su; + struct peer *peer; + + int ret; + + 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_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, 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 show_adj_route_vpn (vty, peer, NULL); +} +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 + 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_IP6][SAFI_MPLS_VPN]) + { + vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return show_adj_route_vpn (vty, peer, NULL); +} + +DEFUN (show_ip_bgp_vpnv4_rd_neighbor_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 + 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_IP][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_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_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 new file mode 100644 index 0000000..3299b9c --- /dev/null +++ b/bgpd/bgp_mplsvpn.h @@ -0,0 +1,51 @@ +/* MPLS-VPN + 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. */ + +#ifndef _QUAGGA_BGP_MPLSVPN_H +#define _QUAGGA_BGP_MPLSVPN_H + +#define RD_TYPE_AS 0 +#define RD_TYPE_IP 1 +#define RD_TYPE_AS4 2 + +#define RD_ADDRSTRLEN 28 + +struct rd_as +{ + u_int16_t type; + as_t as; + u_int32_t val; +}; + +struct rd_ip +{ + u_int16_t type; + struct in_addr ip; + u_int16_t val; +}; + +extern void bgp_mplsvpn_init (void); +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 *); +extern char *prefix_rd2str (struct prefix_rd *, char *, size_t); + +#endif /* _QUAGGA_BGP_MPLSVPN_H */ diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c new file mode 100644 index 0000000..1604239 --- /dev/null +++ b/bgpd/bgp_network.c @@ -0,0 +1,567 @@ +/* BGP network related fucntions + 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 "thread.h" +#include "sockunion.h" +#include "sockopt.h" +#include "memory.h" +#include "log.h" +#include "if.h" +#include "prefix.h" +#include "command.h" +#include "privs.h" +#include "linklist.h" +#include "network.h" +#include "filter.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_fsm.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_debug.h" +#include "bgpd/bgp_network.h" + +extern struct zebra_privs_t bgpd_privs; + +/* BGP listening socket. */ +struct bgp_listener +{ + int fd; + 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. + */ +static int +bgp_md5_set_socket (int socket, union sockunion *su, const char *password) +{ + int ret = -1; + int en = ENOSYS; + + assert (socket >= 0); + +#if HAVE_DECL_TCP_MD5SIG + ret = sockopt_tcp_signature (socket, su, password); + en = errno; +#endif /* HAVE_TCP_MD5SIG */ + + if (ret < 0) + zlog (NULL, LOG_WARNING, "can't set TCP_MD5SIG option on socket %d: %s", + socket, safe_strerror (en)); + + return ret; +} + +/* Helper for bgp_connect */ +static int +bgp_md5_set_connect (int socket, union sockunion *su, const char *password) +{ + int ret = -1; + +#if HAVE_DECL_TCP_MD5SIG + if ( bgpd_privs.change (ZPRIVS_RAISE) ) + { + zlog_err ("%s: could not raise privs", __func__); + return ret; + } + + ret = bgp_md5_set_socket (socket, su, password); + + if (bgpd_privs.change (ZPRIVS_LOWER) ) + zlog_err ("%s: could not lower privs", __func__); +#endif /* HAVE_TCP_MD5SIG */ + + return ret; +} + +int +bgp_md5_set (struct peer *peer) +{ + struct listnode *node; + int ret = 0; + struct bgp_listener *listener; + + if ( bgpd_privs.change (ZPRIVS_RAISE) ) + { + zlog_err ("%s: could not raise privs", __func__); + return -1; + } + + /* Just set the password on the listen socket(s). Outbound connections + * are taken care of in bgp_connect() below. + */ + for (ALL_LIST_ELEMENTS_RO(bm->listen_sockets, node, listener)) + if (listener->su.sa.sa_family == peer->su.sa.sa_family) + { + ret = bgp_md5_set_socket (listener->fd, &peer->su, peer->password); + break; + } + + if (bgpd_privs.change (ZPRIVS_LOWER) ) + zlog_err ("%s: could not lower privs", __func__); + + 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) +{ + int bgp_sock; + int accept_sock; + union sockunion su; + struct bgp_listener *listener = THREAD_ARG(thread); + struct peer *peer; + struct peer *peer1; + char buf[SU_ADDRSTRLEN]; + + /* Register accept thread. */ + accept_sock = THREAD_FD (thread); + if (accept_sock < 0) + { + zlog_err ("accept_sock is nevative value %d", accept_sock); + return -1; + } + listener->thread = thread_add_read (bm->master, bgp_accept, listener, accept_sock); + + /* Accept client connection. */ + bgp_sock = sockunion_accept (accept_sock, &su); + if (bgp_sock < 0) + { + zlog_err ("[Error] BGP socket accept failed (%s)", safe_strerror (errno)); + return -1; + } + 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:%d", + inet_sutop (&su, buf), sockunion_get_port (&su)); + + /* Check remote IP address */ + peer1 = peer_lookup (NULL, &su); + /* 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)) + { + if (! peer1) + 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 %s state", + inet_sutop (&su, buf), + LOOKUP (bgp_status_msg, peer1->status)); + } + close (bgp_sock); + return -1; + } + + 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]; + + peer = peer_create_accept (peer1->bgp); + 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); + + return 0; +} + +/* BGP socket bind. */ +static int +bgp_bind (struct peer *peer) +{ +#ifdef SO_BINDTODEVICE + int ret; + struct ifreq ifreq; + int myerrno; + + if (! peer->ifname) + return 0; + + strncpy ((char *)&ifreq.ifr_name, peer->ifname, sizeof (ifreq.ifr_name)); + + if ( bgpd_privs.change (ZPRIVS_RAISE) ) + zlog_err ("bgp_bind: could not raise privs"); + + 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, errno=%d", + peer->ifname, myerrno); + return ret; + } +#endif /* SO_BINDTODEVICE */ + return 0; +} + +static int +bgp_update_address (struct interface *ifp, const union sockunion *dst, + union sockunion *addr) +{ + struct prefix *p, *sel, d; + struct connected *connected; + struct listnode *node; + int common; + + sockunion2hostprefix (dst, &d); + sel = NULL; + common = -1; + + for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, connected)) + { + p = connected->address; + if (p->family != d.family) + continue; + if (prefix_common_bits (p, &d) > common) + { + sel = p; + common = prefix_common_bits (sel, &d); + } + } + + if (!sel) + return 1; + + prefix2sockunion (sel, addr); + return 0; +} + +/* Update source selection. */ +static void +bgp_update_source (struct peer *peer) +{ + struct interface *ifp; + union sockunion addr; + + /* Source is specified with interface name. */ + if (peer->update_if) + { + ifp = if_lookup_by_name (peer->update_if); + if (! ifp) + return; + + if (bgp_update_address (ifp, &peer->su, &addr)) + return; + + sockunion_bind (peer->fd, &addr, 0, &addr); + } + + /* Source is specified with IP address. */ + if (peer->update_source) + sockunion_bind (peer->fd, peer->update_source, 0, peer->update_source); +} + +/* BGP try to connect to the peer. */ +int +bgp_connect (struct peer *peer) +{ + ifindex_t ifindex = 0; + + /* Make socket for the peer. */ + peer->fd = sockunion_socket (&peer->su); + if (peer->fd < 0) + return -1; + + 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); + +#ifdef IPTOS_PREC_INTERNETCONTROL + if (bgpd_privs.change (ZPRIVS_RAISE)) + zlog_err ("%s: could not raise privs", __func__); + if (sockunion_family (&peer->su) == AF_INET) + setsockopt_ipv4_tos (peer->fd, IPTOS_PREC_INTERNETCONTROL); + else if (sockunion_family (&peer->su) == AF_INET6) + setsockopt_ipv6_tclass (peer->fd, IPTOS_PREC_INTERNETCONTROL); + if (bgpd_privs.change (ZPRIVS_LOWER)) + zlog_err ("%s: could not lower privs", __func__); +#endif + + if (peer->password) + bgp_md5_set_connect (peer->fd, &peer->su, peer->password); + + /* Bind socket. */ + bgp_bind (peer); + + /* Update source bind. */ + bgp_update_source (peer); + + if (peer->ifname) + ifindex = ifname2ifindex (peer->ifname); + + if (BGP_DEBUG (events, EVENTS)) + plog_debug (peer->log, "%s [Event] Connect start to %s fd %d", + peer->host, peer->host, peer->fd); + + /* Connect to the remote peer. */ + return sockunion_connect (peer->fd, &peer->su, htons (peer->port), ifindex); +} + +/* After TCP connection is established. Get local address and port. */ +void +bgp_getsockname (struct peer *peer) +{ + if (peer->su_local) + { + sockunion_free (peer->su_local); + peer->su_local = NULL; + } + + if (peer->su_remote) + { + sockunion_free (peer->su_remote); + peer->su_remote = NULL; + } + + peer->su_local = sockunion_getsockname (peer->fd); + peer->su_remote = sockunion_getpeername (peer->fd); + + bgp_nexthop_set (peer->su_local, peer->su_remote, &peer->nexthop, peer); +} + + +static int +bgp_listener (int sock, struct sockaddr *sa, socklen_t salen) +{ + struct bgp_listener *listener; + int ret, en; + + sockopt_reuseaddr (sock); + sockopt_reuseport (sock); + + if (bgpd_privs.change (ZPRIVS_RAISE)) + zlog_err ("%s: could not raise privs", __func__); + +#ifdef IPTOS_PREC_INTERNETCONTROL + if (sa->sa_family == AF_INET) + setsockopt_ipv4_tos (sock, IPTOS_PREC_INTERNETCONTROL); + else if (sa->sa_family == AF_INET6) + setsockopt_ipv6_tclass (sock, IPTOS_PREC_INTERNETCONTROL); +#endif + + sockopt_v6only (sa->sa_family, sock); + + ret = bind (sock, sa, salen); + en = errno; + if (bgpd_privs.change (ZPRIVS_LOWER)) + zlog_err ("%s: could not lower privs", __func__); + + if (ret < 0) + { + zlog_err ("bind: %s", safe_strerror (en)); + return ret; + } + + ret = listen (sock, 3); + if (ret < 0) + { + zlog_err ("listen: %s", safe_strerror (errno)); + return ret; + } + + listener = XMALLOC (MTYPE_BGP_LISTENER, sizeof(*listener)); + listener->fd = sock; + memcpy(&listener->su, sa, salen); + 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. */ +int +bgp_socket (unsigned short port, const char *address) +{ + struct addrinfo *ainfo; + struct addrinfo *ainfo_save; + static const struct addrinfo req = { + .ai_family = AF_UNSPEC, + .ai_flags = AI_PASSIVE, + .ai_socktype = SOCK_STREAM, + }; + int ret, count; + char port_str[BUFSIZ]; + + snprintf (port_str, sizeof(port_str), "%d", port); + port_str[sizeof (port_str) - 1] = '\0'; + + ret = getaddrinfo (address, port_str, &req, &ainfo_save); + if (ret != 0) + { + zlog_err ("getaddrinfo: %s", gai_strerror (ret)); + return -1; + } + + count = 0; + for (ainfo = ainfo_save; ainfo; ainfo = ainfo->ai_next) + { + int sock; + + if (ainfo->ai_family != AF_INET && ainfo->ai_family != AF_INET6) + continue; + + sock = socket (ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol); + if (sock < 0) + { + zlog_err ("socket: %s", safe_strerror (errno)); + continue; + } + + /* if we intend to implement ttl-security, this socket needs ttl=255 */ + sockopt_ttl (ainfo->ai_family, sock, MAXTTL); + + ret = bgp_listener (sock, ainfo->ai_addr, ainfo->ai_addrlen); + if (ret == 0) + ++count; + else + close(sock); + } + freeaddrinfo (ainfo_save); + if (count == 0) + { + zlog_err ("%s: no usable addresses", __func__); + return -1; + } + + return 0; +} + +void +bgp_close (void) +{ + struct listnode *node, *next; + struct bgp_listener *listener; + + for (ALL_LIST_ELEMENTS (bm->listen_sockets, node, next, listener)) + { + thread_cancel (listener->thread); + close (listener->fd); + listnode_delete (bm->listen_sockets, listener); + XFREE (MTYPE_BGP_LISTENER, listener); + } +} diff --git a/bgpd/bgp_network.h b/bgpd/bgp_network.h new file mode 100644 index 0000000..31995ca --- /dev/null +++ b/bgpd/bgp_network.h @@ -0,0 +1,34 @@ +/* BGP network related 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. */ + +#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 new file mode 100644 index 0000000..479ef94 --- /dev/null +++ b/bgpd/bgp_nexthop.c @@ -0,0 +1,567 @@ +/* BGP nexthop scan + 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 "thread.h" +#include "prefix.h" +#include "zclient.h" +#include "stream.h" +#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. */ + + +/* Route table for next-hop lookup cache. */ +struct bgp_table *bgp_nexthop_cache_table[AFI_MAX]; +static struct bgp_table *cache1_table[AFI_MAX]; + +/* Route table for connected route. */ +static struct bgp_table *bgp_connected_table[AFI_MAX]; + +char * +bnc_str (struct bgp_nexthop_cache *bnc, char *buf, int size) +{ + prefix2str(&(bnc->node->p), buf, size); + return buf; +} + +void +bnc_nexthop_free (struct bgp_nexthop_cache *bnc) +{ + struct nexthop *nexthop; + struct nexthop *next = NULL; + + for (nexthop = bnc->nexthop; nexthop; nexthop = next) + { + next = nexthop->next; + XFREE (MTYPE_NEXTHOP, nexthop); + } +} + +struct bgp_nexthop_cache * +bnc_new (void) +{ + struct bgp_nexthop_cache *bnc; + + bnc = XCALLOC (MTYPE_BGP_NEXTHOP_CACHE, sizeof (struct bgp_nexthop_cache)); + LIST_INIT(&(bnc->paths)); + return bnc; +} + +void +bnc_free (struct bgp_nexthop_cache *bnc) +{ + bnc_nexthop_free (bnc); + XFREE (MTYPE_BGP_NEXTHOP_CACHE, bnc); +} + +/* If nexthop exists on connected network return 1. */ +int +bgp_nexthop_onlink (afi_t afi, struct attr *attr) +{ + struct bgp_node *rn; + + /* Lookup the address is onlink or not. */ + if (afi == AFI_IP) + { + rn = bgp_node_match_ipv4 (bgp_connected_table[AFI_IP], &attr->nexthop); + if (rn) + { + bgp_unlock_node (rn); + return 1; + } + } + else if (afi == AFI_IP6) + { + if (attr->extra->mp_nexthop_len == 32) + return 1; + else if (attr->extra->mp_nexthop_len == 16) + { + if (IN6_IS_ADDR_LINKLOCAL (&attr->extra->mp_nexthop_global)) + return 1; + + rn = bgp_node_match_ipv6 (bgp_connected_table[AFI_IP6], + &attr->extra->mp_nexthop_global); + if (rn) + { + bgp_unlock_node (rn); + return 1; + } + } + } + return 0; +} + +/* BGP own address structure */ +struct bgp_addr +{ + struct in_addr addr; + int refcnt; +}; + +static struct hash *bgp_address_hash; + +static void * +bgp_address_hash_alloc (void *p) +{ + struct in_addr *val = p; + struct bgp_addr *addr; + + addr = XMALLOC (MTYPE_BGP_ADDR, sizeof (struct bgp_addr)); + addr->refcnt = 0; + addr->addr.s_addr = val->s_addr; + + return addr; +} + +static unsigned int +bgp_address_hash_key_make (void *p) +{ + const struct bgp_addr *addr = p; + + return jhash_1word(addr->addr.s_addr, 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 addr1->addr.s_addr == addr2->addr.s_addr; +} + +void +bgp_address_init (void) +{ + bgp_address_hash = hash_create (bgp_address_hash_key_make, + bgp_address_hash_cmp); +} + +void +bgp_address_destroy (void) +{ + if (bgp_address_hash == NULL) + return; + + hash_clean(bgp_address_hash, NULL); + hash_free(bgp_address_hash); + bgp_address_hash = NULL; +} + +static void +bgp_address_add (struct prefix *p) +{ + struct bgp_addr tmp; + struct bgp_addr *addr; + + tmp.addr = p->u.prefix4; + + addr = hash_get (bgp_address_hash, &tmp, bgp_address_hash_alloc); + if (!addr) + return; + + addr->refcnt++; +} + +static void +bgp_address_del (struct prefix *p) +{ + struct bgp_addr tmp; + struct bgp_addr *addr; + + tmp.addr = p->u.prefix4; + + addr = hash_lookup (bgp_address_hash, &tmp); + /* may have been deleted earlier by bgp_interface_down() */ + if (addr == NULL) + return; + + addr->refcnt--; + + if (addr->refcnt == 0) + { + hash_release (bgp_address_hash, addr); + XFREE (MTYPE_BGP_ADDR, addr); + } +} + + +struct bgp_connected_ref +{ + unsigned int refcnt; +}; + +void +bgp_connected_add (struct connected *ifc) +{ + struct prefix p; + struct prefix *addr; + struct interface *ifp; + struct bgp_node *rn; + struct bgp_connected_ref *bc; + + ifp = ifc->ifp; + + if (! ifp) + return; + + if (if_is_loopback (ifp)) + return; + + addr = ifc->address; + + p = *(CONNECTED_PREFIX(ifc)); + if (addr->family == AF_INET) + { + 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) + { + bc = rn->info; + bc->refcnt++; + } + else + { + bc = XCALLOC (MTYPE_BGP_CONN, sizeof (struct bgp_connected_ref)); + bc->refcnt = 1; + rn->info = bc; + } + } + else if (addr->family == AF_INET6) + { + apply_mask_ipv6 ((struct prefix_ipv6 *) &p); + + if (IN6_IS_ADDR_UNSPECIFIED (&p.u.prefix6)) + return; + + if (IN6_IS_ADDR_LINKLOCAL (&p.u.prefix6)) + return; + + rn = bgp_node_get (bgp_connected_table[AFI_IP6], (struct prefix *) &p); + if (rn->info) + { + bc = rn->info; + bc->refcnt++; + } + else + { + bc = XCALLOC (MTYPE_BGP_CONN, sizeof (struct bgp_connected_ref)); + bc->refcnt = 1; + rn->info = bc; + } + } +} + +void +bgp_connected_delete (struct connected *ifc) +{ + struct prefix p; + struct prefix *addr; + struct interface *ifp; + struct bgp_node *rn; + struct bgp_connected_ref *bc; + + ifp = ifc->ifp; + + if (if_is_loopback (ifp)) + return; + + addr = ifc->address; + + p = *(CONNECTED_PREFIX(ifc)); + if (addr->family == AF_INET) + { + 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; + + bc = rn->info; + bc->refcnt--; + if (bc->refcnt == 0) + { + XFREE (MTYPE_BGP_CONN, bc); + rn->info = NULL; + } + bgp_unlock_node (rn); + bgp_unlock_node (rn); + } + else if (addr->family == AF_INET6) + { + apply_mask_ipv6 ((struct prefix_ipv6 *) &p); + + if (IN6_IS_ADDR_UNSPECIFIED (&p.u.prefix6)) + return; + + if (IN6_IS_ADDR_LINKLOCAL (&p.u.prefix6)) + return; + + rn = bgp_node_lookup (bgp_connected_table[AFI_IP6], (struct prefix *) &p); + if (! rn) + return; + + bc = rn->info; + bc->refcnt--; + if (bc->refcnt == 0) + { + XFREE (MTYPE_BGP_CONN, bc); + rn->info = NULL; + } + bgp_unlock_node (rn); + bgp_unlock_node (rn); + } +} + +int +bgp_nexthop_self (struct attr *attr) +{ + struct bgp_addr tmp, *addr; + + tmp.addr = attr->nexthop; + + addr = hash_lookup (bgp_address_hash, &tmp); + if (addr) + return 1; + + return 0; +} + +int +bgp_multiaccess_check_v4 (struct in_addr nexthop, struct peer *peer) +{ + struct bgp_node *rn1; + struct bgp_node *rn2; + struct prefix p; + int ret; + + p.family = AF_INET; + p.prefixlen = IPV4_MAX_BITLEN; + p.u.prefix4 = nexthop; + + rn1 = bgp_node_match (bgp_connected_table[AFI_IP], &p); + if (!rn1) + return 0; + + p.family = AF_INET; + p.prefixlen = IPV4_MAX_BITLEN; + p.u.prefix4 = peer->su.sin.sin_addr; + + rn2 = bgp_node_match (bgp_connected_table[AFI_IP], &p); + if (!rn2) + { + bgp_unlock_node(rn1); + return 0; + } + + ret = (rn1 == rn2) ? 1 : 0; + + bgp_unlock_node(rn1); + bgp_unlock_node(rn2); + + return (ret); +} + +static int +show_ip_bgp_nexthop_table (struct vty *vty, int detail) +{ + struct bgp_node *rn; + struct bgp_nexthop_cache *bnc; + char buf[INET6_ADDRSTRLEN]; + struct nexthop *nexthop; + time_t tbuf; + afi_t afi; + + vty_out (vty, "Current BGP nexthop cache:%s", VTY_NEWLINE); + 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 = rn->info) != NULL) + { + if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID)) + { + 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_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); + } + } + } + return CMD_SUCCESS; +} + +DEFUN (show_ip_bgp_nexthop, + show_ip_bgp_nexthop_cmd, + "show ip bgp nexthop", + SHOW_STR + IP_STR + BGP_STR + "BGP nexthop table\n") +{ + return show_ip_bgp_nexthop_table (vty, 0); +} + +DEFUN (show_ip_bgp_nexthop_detail, + show_ip_bgp_nexthop_detail_cmd, + "show ip bgp nexthop detail", + SHOW_STR + IP_STR + BGP_STR + "BGP nexthop table\n") +{ + return show_ip_bgp_nexthop_table (vty, 1); +} + +void +bgp_scan_init (void) +{ + cache1_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); + + cache1_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); + + 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_vty_init() +{ + install_element (VIEW_NODE, &show_ip_bgp_nexthop_cmd); + install_element (VIEW_NODE, &show_ip_bgp_nexthop_detail_cmd); +} + +void +bgp_scan_finish (void) +{ + if (cache1_table[AFI_IP]) + bgp_table_unlock (cache1_table[AFI_IP]); + cache1_table[AFI_IP] = NULL; + + if (bgp_connected_table[AFI_IP]) + bgp_table_unlock (bgp_connected_table[AFI_IP]); + bgp_connected_table[AFI_IP] = NULL; + + if (cache1_table[AFI_IP6]) + bgp_table_unlock (cache1_table[AFI_IP6]); + cache1_table[AFI_IP6] = NULL; + + if (bgp_connected_table[AFI_IP6]) + bgp_table_unlock (bgp_connected_table[AFI_IP6]); + bgp_connected_table[AFI_IP6] = NULL; + + 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 new file mode 100644 index 0000000..fe4f5ad --- /dev/null +++ b/bgpd/bgp_nexthop.h @@ -0,0 +1,84 @@ +/* BGP nexthop scan + 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. */ + +#ifndef _QUAGGA_BGP_NEXTHOP_H +#define _QUAGGA_BGP_NEXTHOP_H + +#include "if.h" +#include "queue.h" +#include "prefix.h" + +#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 +{ + /* 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 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, 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 (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 0000000..1158ab1 --- /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 0000000..dd6300e --- /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 new file mode 100644 index 0000000..2800423 --- /dev/null +++ b/bgpd/bgp_open.c @@ -0,0 +1,1144 @@ +/* BGP open message handling + Copyright (C) 1998, 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 "linklist.h" +#include "prefix.h" +#include "stream.h" +#include "thread.h" +#include "log.h" +#include "command.h" +#include "memory.h" +#include "filter.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_debug.h" +#include "bgpd/bgp_fsm.h" +#include "bgpd/bgp_packet.h" +#include "bgpd/bgp_open.h" +#include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_vty.h" + +/* BGP-4 Multiprotocol Extentions lead us to the complex world. We can + negotiate remote peer supports extentions or not. But if + remote-peer doesn't supports negotiation process itself. We would + like to do manual configuration. + + So there is many configurable point. First of all we want set each + peer whether we send capability negotiation to the peer or not. + Next, if we send capability to the peer we want to set my capabilty + inforation at each peer. */ + +void +bgp_capability_vty_out (struct vty *vty, struct peer *peer) +{ + char *pnt; + char *end; + struct capability_mp_data mpc; + struct capability_header *hdr; + + pnt = peer->notify.data; + end = pnt + peer->notify.length; + + while (pnt < end) + { + if (pnt + sizeof (struct capability_mp_data) + 2 > end) + return; + + hdr = (struct capability_header *)pnt; + if (pnt + hdr->length + 2 > end) + return; + + memcpy (&mpc, pnt + 2, sizeof(struct capability_mp_data)); + + if (hdr->code == CAPABILITY_CODE_MP) + { + vty_out (vty, " Capability error for: Multi protocol "); + + switch (ntohs (mpc.afi)) + { + case AFI_IP: + vty_out (vty, "AFI IPv4, "); + break; + case AFI_IP6: + vty_out (vty, "AFI IPv6, "); + break; + default: + vty_out (vty, "AFI Unknown %d, ", ntohs (mpc.afi)); + break; + } + switch (mpc.safi) + { + case SAFI_UNICAST: + vty_out (vty, "SAFI Unicast"); + break; + case SAFI_MULTICAST: + vty_out (vty, "SAFI Multicast"); + break; + 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; + } + vty_out (vty, "%s", VTY_NEWLINE); + } + else if (hdr->code >= 128) + vty_out (vty, " Capability error: vendor specific capability code %d", + hdr->code); + else + vty_out (vty, " Capability error: unknown capability code %d", + hdr->code); + + pnt += hdr->length + 2; + } +} + +static void +bgp_capability_mp_data (struct stream *s, struct capability_mp_data *mpc) +{ + mpc->afi = stream_getw (s); + mpc->reserved = stream_getc (s); + mpc->safi = stream_getc (s); +} + +int +bgp_afi_safi_valid_indices (afi_t afi, safi_t *safi) +{ + switch (afi) + { + 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; +} + +/* Set negotiated capability value. */ +static int +bgp_capability_mp (struct peer *peer, struct capability_header *hdr) +{ + struct capability_mp_data mpc; + struct stream *s = BGP_INPUT (peer); + + bgp_capability_mp_data (s, &mpc); + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s OPEN has MP_EXT CAP for afi/safi: %u/%u", + peer->host, mpc.afi, mpc.safi); + + if (!bgp_afi_safi_valid_indices (mpc.afi, &mpc.safi)) + return -1; + + /* Now safi remapped, and afi/safi are valid array indices */ + peer->afc_recv[mpc.afi][mpc.safi] = 1; + + if (peer->afc[mpc.afi][mpc.safi]) + peer->afc_nego[mpc.afi][mpc.safi] = 1; + else + return -1; + + return 0; +} + +static void +bgp_capability_orf_not_support (struct peer *peer, afi_t afi, safi_t safi, + u_char type, u_char mode) +{ + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s Addr-family %d/%d has ORF type/mode %d/%d not supported", + peer->host, afi, safi, type, mode); +} + +static const struct message orf_type_str[] = +{ + { ORF_TYPE_PREFIX, "Prefixlist" }, + { ORF_TYPE_PREFIX_OLD, "Prefixlist (old)" }, +}; +static const int orf_type_str_max = array_size(orf_type_str); + +static const struct message orf_mode_str[] = +{ + { ORF_MODE_RECEIVE, "Receive" }, + { ORF_MODE_SEND, "Send" }, + { ORF_MODE_BOTH, "Both" }, +}; +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) +{ + struct stream *s = BGP_INPUT (peer); + struct capability_orf_entry entry; + afi_t afi; + safi_t safi; + u_char type; + u_char mode; + u_int16_t sm_cap = 0; /* capability send-mode receive */ + u_int16_t rm_cap = 0; /* capability receive-mode receive */ + int i; + + /* ORF Entry header */ + bgp_capability_mp_data (s, &entry.mpc); + entry.num = stream_getc (s); + afi = entry.mpc.afi; + safi = entry.mpc.safi; + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s ORF Cap entry for afi/safi: %u/%u", + peer->host, entry.mpc.afi, entry.mpc.safi); + + /* Check AFI and SAFI. */ + if (!bgp_afi_safi_valid_indices (entry.mpc.afi, &safi)) + { + zlog_info ("%s Addr-family %d/%d not supported." + " Ignoring the ORF capability", + peer->host, entry.mpc.afi, entry.mpc.safi); + return 0; + } + + /* validate number field */ + 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_OPEN_ERR, BGP_NOTIFY_OPEN_UNSPECIFIC); + return -1; + } + + for (i = 0 ; i < entry.num ; i++) + { + type = stream_getc(s); + mode = stream_getc(s); + + /* ORF Mode error check */ + switch (mode) + { + case ORF_MODE_BOTH: + case ORF_MODE_SEND: + case ORF_MODE_RECEIVE: + break; + default: + bgp_capability_orf_not_support (peer, afi, safi, type, mode); + continue; + } + /* ORF Type and afi/safi error checks */ + /* capcode versus type */ + switch (hdr->code) + { + case CAPABILITY_CODE_ORF: + switch (type) + { + case ORF_TYPE_PREFIX: + break; + default: + bgp_capability_orf_not_support (peer, afi, safi, type, mode); + continue; + } + break; + case CAPABILITY_CODE_ORF_OLD: + switch (type) + { + case ORF_TYPE_PREFIX_OLD: + break; + default: + bgp_capability_orf_not_support (peer, afi, safi, type, mode); + continue; + } + break; + default: + bgp_capability_orf_not_support (peer, afi, safi, type, mode); + continue; + } + + /* AFI vs SAFI */ + if (!((afi == AFI_IP && safi == SAFI_UNICAST) + || (afi == AFI_IP && safi == SAFI_MULTICAST) + || (afi == AFI_IP6 && safi == SAFI_UNICAST))) + { + bgp_capability_orf_not_support (peer, afi, safi, type, mode); + continue; + } + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s OPEN has %s ORF capability" + " as %s for afi/safi: %d/%d", + peer->host, LOOKUP (orf_type_str, type), + LOOKUP (orf_mode_str, mode), + entry.mpc.afi, safi); + + if (hdr->code == CAPABILITY_CODE_ORF) + { + sm_cap = PEER_CAP_ORF_PREFIX_SM_RCV; + rm_cap = PEER_CAP_ORF_PREFIX_RM_RCV; + } + else if (hdr->code == CAPABILITY_CODE_ORF_OLD) + { + sm_cap = PEER_CAP_ORF_PREFIX_SM_OLD_RCV; + rm_cap = PEER_CAP_ORF_PREFIX_RM_OLD_RCV; + } + else + { + bgp_capability_orf_not_support (peer, afi, safi, type, mode); + continue; + } + + switch (mode) + { + case ORF_MODE_BOTH: + SET_FLAG (peer->af_cap[afi][safi], sm_cap); + SET_FLAG (peer->af_cap[afi][safi], rm_cap); + break; + case ORF_MODE_SEND: + SET_FLAG (peer->af_cap[afi][safi], sm_cap); + break; + case ORF_MODE_RECEIVE: + SET_FLAG (peer->af_cap[afi][safi], rm_cap); + break; + } + } + 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; + 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)) + SET_FLAG (peer->cap, PEER_CAP_RESTART_BIT_RCV); + + UNSET_FLAG (restart_flag_time, 0xF000); + peer->v_gr_restart = restart_flag_time; + + if (BGP_DEBUG (normal, NORMAL)) + { + zlog_debug ("%s OPEN has Graceful Restart capability", peer->host); + zlog_debug ("%s Peer has%srestarted. Restart Time : %d", + peer->host, + CHECK_FLAG (peer->cap, PEER_CAP_RESTART_BIT_RCV) ? " " + : " not ", + peer->v_gr_restart); + } + + while (stream_get_getp (s) + 4 <= end) + { + afi_t afi = stream_getw (s); + safi_t safi = stream_getc (s); + u_char flag = stream_getc (s); + + if (!bgp_afi_safi_valid_indices (afi, &safi)) + { + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s Addr-family %d/%d(afi/safi) not supported." + " Ignore the Graceful Restart capability", + peer->host, afi, safi); + } + else if (!peer->afc[afi][safi]) + { + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s Addr-family %d/%d(afi/safi) not enabled." + " Ignore the Graceful Restart capability", + peer->host, afi, safi); + } + else + { + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s Address family %s is%spreserved", peer->host, + afi_safi_print (afi, safi), + CHECK_FLAG (peer->af_cap[afi][safi], + PEER_CAP_RESTART_AF_PRESERVE_RCV) + ? " " : " not "); + + SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_RESTART_AF_RCV); + if (CHECK_FLAG (flag, RESTART_F_BIT)) + SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_RESTART_AF_PRESERVE_RCV); + + } + } + return 0; +} + +static as_t +bgp_capability_as4 (struct peer *peer, struct capability_header *hdr) +{ + SET_FLAG (peer->cap, PEER_CAP_AS4_RCV); + + if (hdr->length != CAPABILITY_CODE_AS4_LEN) + { + zlog_err ("%s AS4 capability has incorrect data length %d", + peer->host, hdr->length); + return 0; + } + + as_t as4 = stream_getl (BGP_INPUT(peer)); + + if (BGP_DEBUG (as4, AS4)) + zlog_debug ("%s [AS4] about to set cap PEER_CAP_AS4_RCV, got as4 %u", + peer->host, as4); + return as4; +} + +static const struct message capcode_str[] = +{ + { CAPABILITY_CODE_MP, "MultiProtocol Extensions" }, + { CAPABILITY_CODE_REFRESH, "Route Refresh" }, + { CAPABILITY_CODE_ORF, "Cooperative Route Filtering" }, + { CAPABILITY_CODE_RESTART, "Graceful Restart" }, + { CAPABILITY_CODE_AS4, "4-octet AS number" }, + { CAPABILITY_CODE_DYNAMIC, "Dynamic" }, + { CAPABILITY_CODE_REFRESH_OLD, "Route Refresh (Old)" }, + { CAPABILITY_CODE_ORF_OLD, "ORF (Old)" }, +}; +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] = CAPABILITY_CODE_MP_LEN, + [CAPABILITY_CODE_REFRESH] = CAPABILITY_CODE_REFRESH_LEN, + [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] = 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. + * 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, int *mp_capability, + u_char **error) +{ + int ret; + struct stream *s = BGP_INPUT (peer); + size_t end = stream_get_getp (s) + length; + + assert (STREAM_READABLE (s) >= length); + + while (stream_get_getp (s) < end) + { + size_t start; + u_char *sp = stream_pnt (s); + struct capability_header caphdr; + + /* We need at least capability code and capability length. */ + if (stream_get_getp(s) + 2 > end) + { + zlog_info ("%s Capability length error (< header)", peer->host); + bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_UNSPECIFIC); + return -1; + } + + caphdr.code = stream_getc (s); + caphdr.length = stream_getc (s); + start = stream_get_getp (s); + + /* Capability length check sanity check. */ + if (start + caphdr.length > end) + { + zlog_info ("%s Capability length error (< length)", peer->host); + bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_UNSPECIFIC); + return -1; + } + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s OPEN has %s capability (%u), length %u", + peer->host, + LOOKUP (capcode_str, caphdr.code), + caphdr.code, caphdr.length); + + /* Length sanity check, type-specific, for known capabilities */ + switch (caphdr.code) + { + case CAPABILITY_CODE_MP: + case CAPABILITY_CODE_REFRESH: + case CAPABILITY_CODE_REFRESH_OLD: + case CAPABILITY_CODE_ORF: + case CAPABILITY_CODE_ORF_OLD: + case CAPABILITY_CODE_RESTART: + case CAPABILITY_CODE_AS4: + case CAPABILITY_CODE_DYNAMIC: + /* Check length. */ + if (caphdr.length < cap_minsizes[caphdr.code]) + { + zlog_info ("%s %s Capability length error: got %u," + " expected at least %u", + peer->host, + LOOKUP (capcode_str, caphdr.code), + caphdr.length, + (unsigned) cap_minsizes[caphdr.code]); + 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 */ + default: + break; + } + + switch (caphdr.code) + { + case CAPABILITY_CODE_MP: + { + *mp_capability = 1; + + /* Ignore capability when override-capability is set. */ + if (! CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY)) + { + /* Set negotiated value. */ + ret = bgp_capability_mp (peer, &caphdr); + + /* Unsupported Capability. */ + if (ret < 0) + { + /* Store return data. */ + memcpy (*error, sp, caphdr.length + 2); + *error += caphdr.length + 2; + } + } + } + break; + case CAPABILITY_CODE_REFRESH: + case CAPABILITY_CODE_REFRESH_OLD: + { + /* BGP refresh capability */ + if (caphdr.code == CAPABILITY_CODE_REFRESH_OLD) + SET_FLAG (peer->cap, PEER_CAP_REFRESH_OLD_RCV); + else + SET_FLAG (peer->cap, PEER_CAP_REFRESH_NEW_RCV); + } + break; + case CAPABILITY_CODE_ORF: + case CAPABILITY_CODE_ORF_OLD: + if (bgp_capability_orf_entry (peer, &caphdr)) + return -1; + break; + case CAPABILITY_CODE_RESTART: + if (bgp_capability_restart (peer, &caphdr)) + return -1; + break; + case CAPABILITY_CODE_DYNAMIC: + SET_FLAG (peer->cap, PEER_CAP_DYNAMIC_RCV); + break; + case CAPABILITY_CODE_AS4: + /* Already handled as a special-case parsing of the capabilities + * at the beginning of OPEN processing. So we care not a jot + * for the value really, only error case. + */ + if (!bgp_capability_as4 (peer, &caphdr)) + return -1; + break; + default: + if (caphdr.code > 128) + { + /* We don't send Notification for unknown vendor specific + capabilities. It seems reasonable for now... */ + zlog_warn ("%s Vendor specific capability %d", + peer->host, caphdr.code); + } + else + { + zlog_warn ("%s unrecognized capability code: %d - ignored", + peer->host, caphdr.code); + memcpy (*error, sp, caphdr.length + 2); + *error += caphdr.length + 2; + } + } + if (stream_get_getp(s) != (start + caphdr.length)) + { + if (stream_get_getp(s) > (start + caphdr.length)) + zlog_warn ("%s Cap-parser for %s read past cap-length, %u!", + peer->host, LOOKUP (capcode_str, caphdr.code), + caphdr.length); + stream_set_getp (s, start + caphdr.length); + } + } + return 0; +} + +static int +bgp_auth_parse (struct peer *peer, size_t length) +{ + bgp_notify_send (peer, + BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_AUTH_FAILURE); + return -1; +} + +static int +strict_capability_same (struct peer *peer) +{ + int i, j; + + for (i = AFI_IP; i < AFI_MAX; i++) + for (j = SAFI_UNICAST; j < SAFI_MAX; j++) + if (peer->afc[i][j] != peer->afc_nego[i][j]) + return 0; + return 1; +} + +/* peek into option, stores ASN to *as4 if the AS4 capability was found. + * Returns 0 if no as4 found, as4cap value otherwise. + */ +as_t +peek_for_as4_capability (struct peer *peer, u_char length) +{ + struct stream *s = BGP_INPUT (peer); + size_t orig_getp = stream_get_getp (s); + size_t end = orig_getp + length; + as_t as4 = 0; + + /* The full capability parser will better flag the error.. */ + if (STREAM_READABLE(s) < length) + return 0; + + if (BGP_DEBUG (as4, AS4)) + zlog_info ("%s [AS4] rcv OPEN w/ OPTION parameter len: %u," + " peeking for as4", + peer->host, length); + /* the error cases we DONT handle, we ONLY try to read as4 out of + * correctly formatted options. + */ + while (stream_get_getp(s) < end) + { + u_char opt_type; + u_char opt_length; + + /* Check the length. */ + if (stream_get_getp (s) + 2 > end) + goto end; + + /* Fetch option type and length. */ + opt_type = stream_getc (s); + opt_length = stream_getc (s); + + /* Option length check. */ + if (stream_get_getp (s) + opt_length > end) + goto end; + + if (opt_type == BGP_OPEN_OPT_CAP) + { + unsigned long capd_start = stream_get_getp (s); + unsigned long capd_end = capd_start + opt_length; + + assert (capd_end <= end); + + while (stream_get_getp (s) < capd_end) + { + struct capability_header hdr; + + if (stream_get_getp (s) + 2 > capd_end) + goto end; + + hdr.code = stream_getc (s); + hdr.length = stream_getc (s); + + if ((stream_get_getp(s) + hdr.length) > capd_end) + goto end; + + if (hdr.code == CAPABILITY_CODE_AS4) + { + if (BGP_DEBUG (as4, AS4)) + zlog_info ("[AS4] found AS4 capability, about to parse"); + as4 = bgp_capability_as4 (peer, &hdr); + + goto end; + } + stream_forward_getp (s, hdr.length); + } + } + } + +end: + stream_set_getp (s, orig_getp); + return as4; +} + +/** + * 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 *mp_capability) +{ + int ret; + u_char *error; + u_char error_data[BGP_MAX_PACKET_SIZE]; + struct stream *s = BGP_INPUT(peer); + size_t end = stream_get_getp (s) + length; + + ret = 0; + error = error_data; + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s rcv OPEN w/ OPTION parameter len: %u", + peer->host, length); + + while (stream_get_getp(s) < end) + { + u_char opt_type; + u_char opt_length; + + /* Must have at least an OPEN option header */ + if (STREAM_READABLE(s) < 2) + { + zlog_info ("%s Option length error", peer->host); + bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_UNSPECIFIC); + return -1; + } + + /* Fetch option type and length. */ + opt_type = stream_getc (s); + opt_length = stream_getc (s); + + /* Option length check. */ + if (STREAM_READABLE (s) < opt_length) + { + zlog_info ("%s Option length error", peer->host); + bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_UNSPECIFIC); + return -1; + } + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s rcvd OPEN w/ optional parameter type %u (%s) len %u", + peer->host, opt_type, + opt_type == BGP_OPEN_OPT_AUTH ? "Authentication" : + opt_type == BGP_OPEN_OPT_CAP ? "Capability" : "Unknown", + opt_length); + + switch (opt_type) + { + case BGP_OPEN_OPT_AUTH: + ret = bgp_auth_parse (peer, opt_length); + break; + case BGP_OPEN_OPT_CAP: + ret = bgp_capability_parse (peer, opt_length, mp_capability, &error); + break; + default: + bgp_notify_send (peer, + BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_UNSUP_PARAM); + ret = -1; + break; + } + + /* Parse error. To accumulate all unsupported capability codes, + bgp_capability_parse does not return -1 when encounter + unsupported capability code. To detect that, please check + error and erro_data pointer, like below. */ + if (ret < 0) + return -1; + } + + /* All OPEN option is parsed. Check capability when strict compare + flag is enabled.*/ + if (CHECK_FLAG (peer->flags, PEER_FLAG_STRICT_CAP_MATCH)) + { + /* If Unsupported Capability exists. */ + if (error != error_data) + { + bgp_notify_send_with_data (peer, + BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_UNSUP_CAPBL, + error_data, error - error_data); + return -1; + } + + /* Check local capability does not negotiated with remote + peer. */ + if (! strict_capability_same (peer)) + { + bgp_notify_send (peer, + BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_UNSUP_CAPBL); + return -1; + } + } + + /* Check there are no common AFI/SAFIs and send Unsupported Capability + error. */ + 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_MPLS_VPN] + && ! peer->afc_nego[AFI_IP6][SAFI_ENCAP]) + { + plog_err (peer->log, "%s [Error] Configured AFI/SAFIs do not " + "overlap with received MP capabilities", + peer->host); + + if (error != error_data) + + bgp_notify_send_with_data (peer, + BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_UNSUP_CAPBL, + error_data, error - error_data); + else + bgp_notify_send (peer, + BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_UNSUP_CAPBL); + return -1; + } + } + return 0; +} + +static void +bgp_open_capability_orf (struct stream *s, struct peer *peer, + afi_t afi, safi_t safi, u_char code) +{ + u_char cap_len; + u_char orf_len; + unsigned long capp; + unsigned long orfp; + unsigned long numberp; + int number_of_orfs = 0; + + if (safi == SAFI_MPLS_VPN) + safi = SAFI_MPLS_LABELED_VPN; + + 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, code); /* Capability Code */ + orfp = stream_get_endp (s); /* Set ORF Len Pointer */ + stream_putc (s, 0); /* ORF Length */ + stream_putw (s, afi); + stream_putc (s, 0); + stream_putc (s, safi); + numberp = stream_get_endp (s); /* Set Number Pointer */ + stream_putc (s, 0); /* Number of ORFs */ + + /* Address Prefix ORF */ + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_SM) + || CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_RM)) + { + stream_putc (s, (code == CAPABILITY_CODE_ORF ? + ORF_TYPE_PREFIX : ORF_TYPE_PREFIX_OLD)); + + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_SM) + && CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_RM)) + { + SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV); + SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_ADV); + stream_putc (s, ORF_MODE_BOTH); + } + else if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_SM)) + { + SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV); + stream_putc (s, ORF_MODE_SEND); + } + else + { + SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_ADV); + stream_putc (s, ORF_MODE_RECEIVE); + } + number_of_orfs++; + } + + /* Total Number of ORFs. */ + stream_putc_at (s, numberp, number_of_orfs); + + /* Total ORF Len. */ + orf_len = stream_get_endp (s) - orfp - 1; + stream_putc_at (s, orfp, orf_len); + + /* Total Capability Len. */ + cap_len = stream_get_endp (s) - capp - 1; + stream_putc_at (s, capp, cap_len); +} + +/* Fill in capability open option to the packet. */ +void +bgp_open_capability (struct stream *s, struct peer *peer) +{ + u_char len; + 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); + + /* Opt Parm Len. */ + stream_putc (s, 0); + + /* Do not send capability. */ + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN) + || CHECK_FLAG (peer->flags, PEER_FLAG_DONT_CAPABILITY)) + return; + + /* IPv4 unicast. */ + if (peer->afc[AFI_IP][SAFI_UNICAST]) + { + peer->afc_adv[AFI_IP][SAFI_UNICAST] = 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_UNICAST); + } + /* IPv4 multicast. */ + if (peer->afc[AFI_IP][SAFI_MULTICAST]) + { + peer->afc_adv[AFI_IP][SAFI_MULTICAST] = 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_MULTICAST); + } + /* IPv4 VPN */ + if (peer->afc[AFI_IP][SAFI_MPLS_VPN]) + { + peer->afc_adv[AFI_IP][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_IP); + stream_putc (s, 0); + stream_putc (s, SAFI_MPLS_LABELED_VPN); + } + /* 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]) + { + peer->afc_adv[AFI_IP6][SAFI_UNICAST] = 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_UNICAST); + } + /* IPv6 multicast. */ + if (peer->afc[AFI_IP6][SAFI_MULTICAST]) + { + peer->afc_adv[AFI_IP6][SAFI_MULTICAST] = 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_MULTICAST); + } + /* 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); + stream_putc (s, BGP_OPEN_OPT_CAP); + stream_putc (s, CAPABILITY_CODE_REFRESH_LEN + 2); + stream_putc (s, CAPABILITY_CODE_REFRESH_OLD); + stream_putc (s, CAPABILITY_CODE_REFRESH_LEN); + stream_putc (s, BGP_OPEN_OPT_CAP); + stream_putc (s, CAPABILITY_CODE_REFRESH_LEN + 2); + stream_putc (s, CAPABILITY_CODE_REFRESH); + stream_putc (s, CAPABILITY_CODE_REFRESH_LEN); + + /* AS4 */ + SET_FLAG (peer->cap, PEER_CAP_AS4_ADV); + stream_putc (s, BGP_OPEN_OPT_CAP); + stream_putc (s, CAPABILITY_CODE_AS4_LEN + 2); + stream_putc (s, CAPABILITY_CODE_AS4); + stream_putc (s, CAPABILITY_CODE_AS4_LEN); + if ( peer->change_local_as ) + local_as = peer->change_local_as; + else + local_as = peer->local_as; + stream_putl (s, local_as ); + + /* ORF capability. */ + 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_ORF_PREFIX_SM) + || CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_RM)) + { + bgp_open_capability_orf (s, peer, afi, safi, CAPABILITY_CODE_ORF_OLD); + bgp_open_capability_orf (s, peer, afi, safi, CAPABILITY_CODE_ORF); + } + + /* Dynamic capability. */ + if (CHECK_FLAG (peer->flags, PEER_FLAG_DYNAMIC_CAPABILITY)) + { + SET_FLAG (peer->cap, PEER_CAP_DYNAMIC_ADV); + stream_putc (s, BGP_OPEN_OPT_CAP); + stream_putc (s, CAPABILITY_CODE_DYNAMIC_LEN + 2); + stream_putc (s, CAPABILITY_CODE_DYNAMIC); + stream_putc (s, CAPABILITY_CODE_DYNAMIC_LEN); + } + + /* 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)) + { + 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; + stream_putc_at (s, cp, len); +} diff --git a/bgpd/bgp_open.h b/bgpd/bgp_open.h new file mode 100644 index 0000000..6233375 --- /dev/null +++ b/bgpd/bgp_open.h @@ -0,0 +1,112 @@ +/* BGP open message handling + 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_BGP_OPEN_H +#define _QUAGGA_BGP_OPEN_H + +/* Standard header for capability TLV */ +struct capability_header +{ + u_char code; + u_char length; +}; + +/* Generic MP capability data */ +struct capability_mp_data +{ + afi_t afi; + u_char reserved; + safi_t safi; +}; + +#pragma pack(1) +struct capability_orf_entry +{ + struct capability_mp_data mpc; + u_char num; + struct { + u_char type; + u_char mode; + } orfs[]; +} __attribute__ ((packed)); +#pragma pack() + +struct capability_as4 +{ + uint32_t as4; +}; + +struct graceful_restart_af +{ + afi_t afi; + safi_t safi; + u_char flag; +}; + +struct capability_gr +{ + u_int16_t restart_flag_time; + struct graceful_restart_af gr[]; +}; + +/* Capability Code */ +#define CAPABILITY_CODE_MP 1 /* Multiprotocol Extensions */ +#define CAPABILITY_CODE_REFRESH 2 /* Route Refresh Capability */ +#define CAPABILITY_CODE_ORF 3 /* Cooperative Route Filtering Capability */ +#define CAPABILITY_CODE_RESTART 64 /* Graceful Restart Capability */ +#define CAPABILITY_CODE_AS4 65 /* 4-octet AS number Capability */ +#define CAPABILITY_CODE_DYNAMIC 66 /* Dynamic Capability */ +#define CAPABILITY_CODE_REFRESH_OLD 128 /* Route Refresh Capability(cisco) */ +#define CAPABILITY_CODE_ORF_OLD 130 /* Cooperative Route Filtering Capability(cisco) */ + +/* Capability Length */ +#define CAPABILITY_CODE_MP_LEN 4 +#define CAPABILITY_CODE_REFRESH_LEN 0 +#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. */ + +/* ORF Type */ +#define ORF_TYPE_PREFIX 64 +#define ORF_TYPE_PREFIX_OLD 128 + +/* ORF Mode */ +#define ORF_MODE_RECEIVE 1 +#define ORF_MODE_SEND 2 +#define ORF_MODE_BOTH 3 + +/* Capability Message Action. */ +#define CAPABILITY_ACTION_SET 0 +#define CAPABILITY_ACTION_UNSET 1 + +/* Graceful Restart */ +#define RESTART_R_BIT 0x8000 +#define RESTART_F_BIT 0x80 + +extern int bgp_open_option_parse (struct peer *, u_char, int *); +extern void bgp_open_capability (struct stream *, struct peer *); +extern void bgp_capability_vty_out (struct vty *, struct peer *); +extern as_t peek_for_as4_capability (struct peer *, u_char); +extern int bgp_afi_safi_valid_indices (afi_t, safi_t *); + +#endif /* _QUAGGA_BGP_OPEN_H */ diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c new file mode 100644 index 0000000..b497e45 --- /dev/null +++ b/bgpd/bgp_packet.c @@ -0,0 +1,2717 @@ +/* BGP packet management routine. + 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 "thread.h" +#include "stream.h" +#include "network.h" +#include "prefix.h" +#include "command.h" +#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" +#include "bgpd/bgp_dump.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_debug.h" +#include "bgpd/bgp_fsm.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_packet.h" +#include "bgpd/bgp_open.h" +#include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_community.h" +#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) +{ + int i; + + /* Fill in marker. */ + for (i = 0; i < BGP_MARKER_SIZE; i++) + stream_putc (s, 0xff); + + /* Dummy total length. This field is should be filled in later on. */ + stream_putw (s, 0); + + /* BGP packet type. */ + stream_putc (s, type); + + /* Return current stream size. */ + return stream_get_endp (s); +} + +/* Set BGP packet header size entry. If size is zero then use current + stream size. */ +static int +bgp_packet_set_size (struct stream *s) +{ + int cp; + + /* Preserve current pointer. */ + cp = stream_get_endp (s); + stream_putw_at (s, BGP_MARKER_SIZE, cp); + + return cp; +} + +/* Add new packet to the peer. */ +static void +bgp_packet_add (struct peer *peer, struct stream *s) +{ + /* Add packet to the end of list. */ + stream_fifo_push (peer->obuf, s); +} + +/* Free first packet. */ +static void +bgp_packet_delete (struct peer *peer) +{ + stream_free (stream_fifo_pop (peer->obuf)); +} + +/* Check file descriptor whether connect is established. */ +static void +bgp_connect_check (struct peer *peer) +{ + int status; + socklen_t slen; + int ret; + + /* Anyway I have to reset read and write thread. */ + BGP_READ_OFF (peer->t_read); + BGP_WRITE_OFF (peer->t_write); + + /* Check file descriptor. */ + slen = sizeof (status); + ret = getsockopt(peer->fd, SOL_SOCKET, SO_ERROR, (void *) &status, &slen); + + /* If getsockopt is fail, this is fatal error. */ + if (ret < 0) + { + zlog (peer->log, LOG_INFO, "can't get sockopt for nonblocking connect"); + BGP_EVENT_ADD (peer, TCP_fatal_error); + return; + } + + /* When status is 0 then TCP connection is established. */ + if (status == 0) + { + BGP_EVENT_ADD (peer, TCP_connection_open); + } + else + { + if (BGP_DEBUG (events, EVENTS)) + plog_debug (peer->log, "%s [Event] Connect failed (%s)", + peer->host, safe_strerror (errno)); + BGP_EVENT_ADD (peer, TCP_connection_open_failed); + } +} + +/* Make BGP update packet. */ +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 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 = BGP_ADV_FIFO_HEAD (&peer->sync[afi][safi]->update); + + while (adv) + { + assert (adv->rn); + rn = adv->rn; + adj = adv->adj; + 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 (space_remaining < space_needed) + break; + + /* If packet is empty, set attribute. */ + if (stream_empty (s)) + { + struct prefix_rd *prd = NULL; + u_char *tag = NULL; + struct peer *from = NULL; + + if (rn->prn) + prd = (struct prefix_rd *) &rn->prn->p; + if (binfo) + { + from = binfo->peer; + if (binfo->extra) + tag = binfo->extra->tag; + } + + /* 1: Write the BGP message header - 16 bytes marker, 2 bytes length, + * one byte message type. + */ + bgp_packet_set_marker (s, BGP_MSG_UPDATE); + + /* 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); + + /* 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, + ((afi == AFI_IP && safi == SAFI_UNICAST) ? + &rn->p : NULL), + afi, safi, + from, prd, tag); + 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)) + { + 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) + bgp_attr_unintern (&adj->attr); + else + peer->scount[afi][safi]++; + + adj->attr = bgp_attr_intern (adv->baa->attr); + + adv = bgp_advertise_clean (peer, adj, afi, safi); + } + + if (! stream_empty (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; +} + +static struct stream * +bgp_update_packet_eor (struct peer *peer, afi_t afi, safi_t safi) +{ + struct stream *s; + + if (DISABLE_BGP_ANNOUNCE) + return NULL; + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("send End-of-RIB for %s to %s", afi_safi_print (afi, safi), peer->host); + + s = stream_new (BGP_MAX_PACKET_SIZE); + + /* Make BGP update packet. */ + bgp_packet_set_marker (s, BGP_MSG_UPDATE); + + /* Unfeasible Routes Length */ + stream_putw (s, 0); + + if (afi == AFI_IP && safi == SAFI_UNICAST) + { + /* Total Path Attribute Length */ + stream_putw (s, 0); + } + else + { + /* Total Path Attribute Length */ + stream_putw (s, 6); + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL); + stream_putc (s, BGP_ATTR_MP_UNREACH_NLRI); + stream_putc (s, 3); + stream_putw (s, afi); + stream_putc (s, safi); + } + + bgp_packet_set_size (s); + 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) +{ + struct stream *s; + struct stream *packet; + struct bgp_adj_out *adj; + struct bgp_advertise *adv; + struct bgp_node *rn; + bgp_size_t unfeasible_len; + bgp_size_t total_attr_len; + size_t mp_start = 0; + size_t attrlen_pos = 0; + size_t mplen_pos = 0; + u_char first_time = 1; + int space_remaining = 0; + int space_needed = 0; + + s = peer->work; + stream_reset (s); + + while ((adv = BGP_ADV_FIFO_HEAD (&peer->sync[afi][safi]->withdraw)) != NULL) + { + assert (adv->rn); + adj = adv->adj; + rn = adv->rn; + + 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); /* 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; + + /* 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)) + { + 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 (! stream_empty (s)) + { + if (afi == AFI_IP && safi == SAFI_UNICAST) + { + 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); + stream_reset (s); + return packet; + } + + return NULL; +} + +void +bgp_default_update_send (struct peer *peer, struct attr *attr, + afi_t afi, safi_t safi, struct peer *from) +{ + struct stream *s; + struct prefix p; + unsigned long pos; + bgp_size_t total_attr_len; + + if (DISABLE_BGP_ANNOUNCE) + return; + + if (afi == AFI_IP) + str2prefix ("0.0.0.0/0", &p); + else + str2prefix ("::/0", &p); + + /* 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, INET6_BUFSIZ), + p.prefixlen, attrstr); + } + + s = stream_new (BGP_MAX_PACKET_SIZE); + + /* Make BGP update packet. */ + bgp_packet_set_marker (s, BGP_MSG_UPDATE); + + /* Unfeasible Routes Length. */ + stream_putw (s, 0); + + /* Make place for total attribute length. */ + pos = stream_get_endp (s); + stream_putw (s, 0); + total_attr_len = bgp_packet_attribute (NULL, peer, s, attr, &p, afi, safi, from, NULL, NULL); + + /* Set Total Path Attribute Length. */ + stream_putw_at (s, pos, total_attr_len); + + /* NLRI set. */ + if (p.family == AF_INET && safi == SAFI_UNICAST) + stream_put_prefix (s, &p); + + /* Set size. */ + bgp_packet_set_size (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, s); + + BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); +} + +void +bgp_default_withdraw_send (struct peer *peer, afi_t afi, safi_t safi) +{ + struct stream *s; + struct prefix p; + unsigned long attrlen_pos = 0; + unsigned long cp; + bgp_size_t unfeasible_len; + bgp_size_t total_attr_len; + size_t mp_start = 0; + size_t mplen_pos = 0; + + if (DISABLE_BGP_ANNOUNCE) + return; + + if (afi == AFI_IP) + str2prefix ("0.0.0.0/0", &p); + else + str2prefix ("::/0", &p); + + total_attr_len = 0; + + if (BGP_DEBUG (update, UPDATE_OUT)) + { + 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); + + /* Make BGP update packet. */ + bgp_packet_set_marker (s, BGP_MSG_UPDATE); + + /* Unfeasible Routes Length. */; + cp = stream_get_endp (s); + stream_putw (s, 0); + + /* Withdrawn Routes. */ + if (p.family == AF_INET && safi == SAFI_UNICAST) + { + stream_put_prefix (s, &p); + + unfeasible_len = stream_get_endp (s) - cp - 2; + + /* Set unfeasible len. */ + stream_putw_at (s, cp, unfeasible_len); + + /* Set total path attribute length. */ + stream_putw (s, 0); + } + else + { + attrlen_pos = stream_get_endp (s); + stream_putw (s, 0); + 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. */ + total_attr_len = stream_get_endp(s) - mp_start; + stream_putw_at (s, attrlen_pos, total_attr_len); + } + + bgp_packet_set_size (s); + + /* Add packet to the peer. */ + bgp_packet_add (peer, s); + + BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); +} + +/* Get next packet to be written. */ +static struct stream * +bgp_write_packet (struct peer *peer) +{ + afi_t afi; + safi_t safi; + struct stream *s = NULL; + struct bgp_advertise *adv; + + s = stream_fifo_head (peer->obuf); + if (s) + return s; + + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + { + adv = BGP_ADV_FIFO_HEAD (&peer->sync[afi][safi]->withdraw); + if (adv) + { + s = bgp_withdraw_packet (peer, afi, safi); + if (s) + return s; + } + } + + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + { + 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) + { + if (CHECK_FLAG (adv->binfo->peer->af_sflags[afi][safi], + PEER_STATUS_EOR_RECEIVED)) + s = bgp_update_packet (peer, afi, safi); + } + else + s = bgp_update_packet (peer, afi, safi); + } + + if (s) + return s; + } + + if (CHECK_FLAG (peer->cap, PEER_CAP_RESTART_RCV)) + { + if (peer->afc_nego[afi][safi] && peer->synctime + && ! CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_EOR_SEND) + && safi != SAFI_MPLS_VPN) + { + SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_EOR_SEND); + return bgp_update_packet_eor (peer, afi, safi); + } + } + } + + return NULL; +} + +/* Is there partially written packet or updates we can send right + now. */ +static int +bgp_write_proceed (struct peer *peer) +{ + afi_t afi; + safi_t safi; + struct bgp_advertise *adv; + + if (stream_fifo_head (peer->obuf)) + return 1; + + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + if (FIFO_HEAD (&peer->sync[afi][safi]->withdraw)) + return 1; + + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + if ((adv = BGP_ADV_FIFO_HEAD (&peer->sync[afi][safi]->update)) != NULL) + if (adv->binfo->uptime < peer->synctime) + return 1; + + return 0; +} + +/* Write packet to the peer. */ +int +bgp_write (struct thread *thread) +{ + struct peer *peer; + u_char type; + struct stream *s; + int num; + unsigned int count = 0; + + /* Yes first of all get peer pointer. */ + peer = THREAD_ARG (thread); + peer->t_write = NULL; + + /* For non-blocking IO check. */ + if (peer->status == Connect) + { + bgp_connect_check (peer); + return 0; + } + + s = bgp_write_packet (peer); + if (!s) + return 0; /* nothing to send */ + + sockopt_cork (peer->fd, 1); + + /* Nonblocking write until TCP output buffer is full. */ + do + { + int writenum; + + /* Number of bytes to be sent. */ + writenum = stream_get_endp (s) - stream_get_getp (s); + + /* Call write() system call. */ + num = write (peer->fd, STREAM_PNT (s), writenum); + if (num < 0) + { + /* write failed either retry needed or error */ + if (ERRNO_IO_RETRY(errno)) + break; + + BGP_EVENT_ADD (peer, TCP_fatal_error); + return 0; + } + + if (num != writenum) + { + /* Partial write */ + stream_forward_getp (s, num); + break; + } + + /* Retrieve BGP packet type. */ + stream_set_getp (s, BGP_MARKER_SIZE + 2); + type = stream_getc (s); + + switch (type) + { + case BGP_MSG_OPEN: + peer->open_out++; + break; + case BGP_MSG_UPDATE: + peer->update_out++; + break; + case BGP_MSG_NOTIFY: + peer->notify_out++; + + /* Flush any existing events */ + BGP_EVENT_ADD (peer, BGP_Stop_with_error); + goto done; + + case BGP_MSG_KEEPALIVE: + peer->keepalive_out++; + break; + case BGP_MSG_ROUTE_REFRESH_NEW: + case BGP_MSG_ROUTE_REFRESH_OLD: + peer->refresh_out++; + break; + case BGP_MSG_CAPABILITY: + peer->dynamic_cap_out++; + break; + } + + /* OK we send packet so delete it. */ + bgp_packet_delete (peer); + } + while (++count < BGP_WRITE_PACKET_MAX && + (s = bgp_write_packet (peer)) != NULL); + + if (bgp_write_proceed (peer)) + BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); + + done: + sockopt_cork (peer->fd, 0); + return 0; +} + +/* This is only for sending NOTIFICATION message to neighbor. */ +static int +bgp_write_notify (struct peer *peer) +{ + int ret, val; + u_char type; + struct stream *s; + + /* There should be at least one packet. */ + s = stream_fifo_head (peer->obuf); + if (!s) + return 0; + assert (stream_get_endp (s) >= BGP_HEADER_SIZE); + + /* 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)); + + /* 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); + + assert (type == BGP_MSG_NOTIFY); + + /* Type should be notify. */ + peer->notify_out++; + + BGP_EVENT_ADD (peer, BGP_Stop_with_error); + + return 0; +} + +/* Make keepalive packet and send it to the peer. */ +void +bgp_keepalive_send (struct peer *peer) +{ + struct stream *s; + int length; + + s = stream_new (BGP_MAX_PACKET_SIZE); + + /* Make keepalive packet. */ + bgp_packet_set_marker (s, BGP_MSG_KEEPALIVE); + + /* Set packet size. */ + length = bgp_packet_set_size (s); + + /* Dump packet if debug option is set. */ + /* bgp_packet_dump (s); */ + + if (BGP_DEBUG (keepalive, KEEPALIVE)) + zlog_debug ("%s sending KEEPALIVE", peer->host); + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s send message type %d, length (incl. header) %d", + peer->host, BGP_MSG_KEEPALIVE, length); + + /* Add packet to the peer. */ + bgp_packet_add (peer, s); + + BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); +} + +/* Make open packet and send it to the peer. */ +void +bgp_open_send (struct peer *peer) +{ + struct stream *s; + int length; + u_int16_t send_holdtime; + as_t local_as; + + if (CHECK_FLAG (peer->config, PEER_CONFIG_TIMER)) + send_holdtime = peer->holdtime; + else + send_holdtime = peer->bgp->default_holdtime; + + /* local-as Change */ + if (peer->change_local_as) + local_as = peer->change_local_as; + else + local_as = peer->local_as; + + s = stream_new (BGP_MAX_PACKET_SIZE); + + /* Make open packet. */ + bgp_packet_set_marker (s, BGP_MSG_OPEN); + + /* Set open packet values. */ + stream_putc (s, BGP_VERSION_4); /* BGP version */ + stream_putw (s, (local_as <= BGP_AS_MAX) ? (u_int16_t) local_as + : BGP_AS_TRANS); + stream_putw (s, send_holdtime); /* Hold Time */ + stream_put_in_addr (s, &peer->local_id); /* BGP Identifier */ + + /* Set capability code. */ + bgp_open_capability (s, peer); + + /* Set BGP packet length. */ + length = bgp_packet_set_size (s); + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s sending OPEN, version %d, my as %u, holdtime %d, id %s", + peer->host, BGP_VERSION_4, local_as, + send_holdtime, inet_ntoa (peer->local_id)); + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s send message type %d, length (incl. header) %d", + peer->host, BGP_MSG_OPEN, length); + + /* Dump packet if debug option is set. */ + /* bgp_packet_dump (s); */ + + /* Add packet to the peer. */ + bgp_packet_add (peer, s); + + BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); +} + +/* Send BGP notify packet with data potion. */ +void +bgp_notify_send_with_data (struct peer *peer, u_char code, u_char sub_code, + u_char *data, size_t datalen) +{ + struct stream *s; + int length; + + /* Allocate new stream. */ + s = stream_new (BGP_MAX_PACKET_SIZE); + + /* Make nitify packet. */ + bgp_packet_set_marker (s, BGP_MSG_NOTIFY); + + /* Set notify packet values. */ + stream_putc (s, code); /* BGP notify code */ + stream_putc (s, sub_code); /* BGP notify sub_code */ + + /* If notify data is present. */ + if (data) + stream_write (s, data, datalen); + + /* Set BGP packet length. */ + length = bgp_packet_set_size (s); + + /* Add packet to the peer. */ + stream_fifo_clean (peer->obuf); + bgp_packet_add (peer, s); + + /* For debug */ + { + struct bgp_notify bgp_notify; + int first = 0; + int i; + char c[4]; + + bgp_notify.code = code; + bgp_notify.subcode = sub_code; + bgp_notify.data = NULL; + bgp_notify.length = length - BGP_MSG_NOTIFY_MIN_SIZE; + + if (bgp_notify.length) + { + bgp_notify.data = XMALLOC (MTYPE_TMP, bgp_notify.length * 3); + for (i = 0; i < bgp_notify.length; i++) + if (first) + { + sprintf (c, " %02x", data[i]); + strcat (bgp_notify.data, c); + } + else + { + first = 1; + sprintf (c, "%02x", data[i]); + strcpy (bgp_notify.data, c); + } + } + bgp_notify_print (peer, &bgp_notify, "sending"); + + if (bgp_notify.data) + { + XFREE (MTYPE_TMP, bgp_notify.data); + bgp_notify.data = NULL; + bgp_notify.length = 0; + } + } + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s send message type %d, length (incl. header) %d", + peer->host, BGP_MSG_NOTIFY, length); + + /* peer reset cause */ + if (sub_code != BGP_NOTIFY_CEASE_CONFIG_CHANGE) + { + if (sub_code == BGP_NOTIFY_CEASE_ADMIN_RESET) + { + peer->last_reset = PEER_DOWN_USER_RESET; + 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:%u shutdown", + peer->host, sockunion_get_port (&peer->su)); + } + else + { + peer->last_reset = PEER_DOWN_NOTIFY_SEND; + 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:%u: configuration change", + peer->host, sockunion_get_port (&peer->su)); + + /* Call immediately. */ + BGP_WRITE_OFF (peer->t_write); + + bgp_write_notify (peer); +} + +/* Send BGP notify packet. */ +void +bgp_notify_send (struct peer *peer, u_char code, u_char sub_code) +{ + bgp_notify_send_with_data (peer, code, sub_code, NULL, 0); +} + +/* Send route refresh message to the peer. */ +void +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; + int length; + struct bgp_filter *filter; + int orf_refresh = 0; + + if (DISABLE_BGP_ANNOUNCE) + return; + + filter = &peer->filter[afi][safi]; + + /* Adjust safi code. */ + if (safi == SAFI_MPLS_VPN) + safi = SAFI_MPLS_LABELED_VPN; + + s = stream_new (BGP_MAX_PACKET_SIZE); + + /* Make BGP update packet. */ + if (CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_NEW_RCV)) + bgp_packet_set_marker (s, BGP_MSG_ROUTE_REFRESH_NEW); + else + bgp_packet_set_marker (s, BGP_MSG_ROUTE_REFRESH_OLD); + + /* Encode Route Refresh message. */ + stream_putw (s, afi); + stream_putc (s, 0); + stream_putc (s, safi); + + if (orf_type == ORF_TYPE_PREFIX + || orf_type == ORF_TYPE_PREFIX_OLD) + if (remove || filter->plist[FILTER_IN].plist) + { + u_int16_t orf_len; + unsigned long orfp; + + orf_refresh = 1; + stream_putc (s, when_to_refresh); + stream_putc (s, orf_type); + orfp = stream_get_endp (s); + stream_putw (s, 0); + + if (remove) + { + UNSET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_PREFIX_SEND); + stream_putc (s, ORF_COMMON_PART_REMOVE_ALL); + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s sending REFRESH_REQ to remove ORF(%d) (%s) for afi/safi: %d/%d", + peer->host, orf_type, + (when_to_refresh == REFRESH_DEFER ? "defer" : "immediate"), + afi, safi); + } + else + { + SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_PREFIX_SEND); + prefix_bgp_orf_entry (s, filter->plist[FILTER_IN].plist, + ORF_COMMON_PART_ADD, ORF_COMMON_PART_PERMIT, + ORF_COMMON_PART_DENY); + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s sending REFRESH_REQ with pfxlist ORF(%d) (%s) for afi/safi: %d/%d", + peer->host, orf_type, + (when_to_refresh == REFRESH_DEFER ? "defer" : "immediate"), + afi, safi); + } + + /* Total ORF Entry Len. */ + orf_len = stream_get_endp (s) - orfp - 2; + stream_putw_at (s, orfp, orf_len); + } + + /* Set packet size. */ + length = bgp_packet_set_size (s); + + if (BGP_DEBUG (normal, NORMAL)) + { + if (! orf_refresh) + zlog_debug ("%s sending REFRESH_REQ for afi/safi: %d/%d", + peer->host, afi, safi); + zlog_debug ("%s send message type %d, length (incl. header) %d", + peer->host, CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_NEW_RCV) ? + BGP_MSG_ROUTE_REFRESH_NEW : BGP_MSG_ROUTE_REFRESH_OLD, length); + } + + /* Add packet to the peer. */ + bgp_packet_add (peer, s); + + BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); +} + +/* Send capability message to the peer. */ +void +bgp_capability_send (struct peer *peer, afi_t afi, safi_t safi, + int capability_code, int action) +{ + struct stream *s; + int length; + + /* Adjust safi code. */ + if (safi == SAFI_MPLS_VPN) + safi = SAFI_MPLS_LABELED_VPN; + + s = stream_new (BGP_MAX_PACKET_SIZE); + + /* Make BGP update packet. */ + bgp_packet_set_marker (s, BGP_MSG_CAPABILITY); + + /* Encode MP_EXT capability. */ + if (capability_code == CAPABILITY_CODE_MP) + { + stream_putc (s, action); + stream_putc (s, CAPABILITY_CODE_MP); + stream_putc (s, CAPABILITY_CODE_MP_LEN); + stream_putw (s, afi); + stream_putc (s, 0); + stream_putc (s, safi); + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s sending CAPABILITY has %s MP_EXT CAP for afi/safi: %d/%d", + peer->host, action == CAPABILITY_ACTION_SET ? + "Advertising" : "Removing", afi, safi); + } + + /* Set packet size. */ + length = bgp_packet_set_size (s); + + + /* Add packet to the peer. */ + bgp_packet_add (peer, s); + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s send message type %d, length (incl. header) %d", + peer->host, BGP_MSG_CAPABILITY, length); + + 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) +{ + struct peer *peer; + struct listnode *node, *nnode; + struct bgp *bgp; + + bgp = bgp_get_default (); + if (! bgp) + return 0; + + /* Upon receipt of an OPEN message, the local system must examine + all of its connections that are in the OpenConfirm state. A BGP + speaker may also examine connections in an OpenSent state if it + knows the BGP Identifier of the peer by means outside of the + protocol. If among these connections there is a connection to a + remote BGP speaker whose BGP Identifier equals the one in the + OPEN message, then the local system performs the following + collision resolution procedure: */ + + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + 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). */ + + if (ntohl (peer->local_id.s_addr) < ntohl (remote_id.s_addr)) + { + /* 2. If the value of the local BGP Identifier is less + than the remote one, the local system closes BGP + connection that already exists (the one that is + already in the OpenConfirm state), and accepts BGP + connection initiated by the remote system. */ + + 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 + { + /* 3. Otherwise, the local system closes newly created + BGP connection (the one associated with the newly + received OPEN message), and continues to use the + existing one (the one that is already in the + OpenConfirm state). */ + + 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; + } + } + } + return 0; +} + +static int +bgp_open_receive (struct peer *peer, bgp_size_t size) +{ + int ret; + u_char version; + u_char optlen; + u_int16_t holdtime; + u_int16_t send_holdtime; + as_t remote_as; + as_t as4 = 0; + struct peer *realpeer; + struct in_addr remote_id; + 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; + + /* Parse open packet. */ + 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); + + /* 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, %sbound connection", + peer->host, version, remote_as, holdtime, + 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 */ + mp_capability = 0; + optlen = stream_getc (peer->ibuf); + + if (optlen != 0) + { + /* We need the as4 capability value *right now* because + * if it is there, we have not got the remote_as yet, and without + * that we do not know which peer is connecting to us now. + */ + as4 = peek_for_as4_capability (peer, optlen); + } + + /* Just in case we have a silly peer who sends AS4 capability set to 0 */ + if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV) && !as4) + { + zlog_err ("%s bad OPEN, got AS4 capability, but AS4 set to 0", + peer->host); + bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_BAD_PEER_AS); + return -1; + } + + if (remote_as == BGP_AS_TRANS) + { + /* Take the AS4 from the capability. We must have received the + * capability now! Otherwise we have a asn16 peer who uses + * BGP_AS_TRANS, for some unknown reason. + */ + if (as4 == BGP_AS_TRANS) + { + zlog_err ("%s [AS4] NEW speaker using AS_TRANS for AS4, not allowed", + peer->host); + bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_BAD_PEER_AS); + return -1; + } + + if (!as4 && BGP_DEBUG (as4, AS4)) + zlog_debug ("%s [AS4] OPEN remote_as is AS_TRANS, but no AS4." + " Odd, but proceeding.", peer->host); + else if (as4 < BGP_AS_MAX && BGP_DEBUG (as4, AS4)) + zlog_debug ("%s [AS4] OPEN remote_as is AS_TRANS, but AS4 (%u) fits " + "in 2-bytes, very odd peer.", peer->host, as4); + if (as4) + remote_as = as4; + } + else + { + /* We may have a partner with AS4 who has an asno < BGP_AS_MAX */ + /* If we have got the capability, peer->as4cap must match remote_as */ + if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV) + && as4 != remote_as) + { + /* raise error, log this, close session */ + zlog_err ("%s bad OPEN, got AS4 capability, but remote_as %u" + " mismatch with 16bit 'myasn' %u in open", + peer->host, as4, remote_as); + bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_BAD_PEER_AS); + return -1; + } + } + + /* Lookup peer from Open packet. */ + if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) + { + int as = 0; + + realpeer = peer_lookup_with_open (&peer->su, remote_as, &remote_id, &as); + + if (! realpeer) + { + /* Peer's source IP address is check in bgp_accept(), so this + must be AS number mismatch or remote-id configuration + mismatch. */ + if (as) + { + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s bad OPEN, wrong router identifier %s", + peer->host, inet_ntoa (remote_id)); + bgp_notify_send_with_data (peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_BAD_BGP_IDENT, + notify_data_remote_id, 4); + } + else + { + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s bad OPEN, remote AS is %u, expected %u", + peer->host, remote_as, peer->as); + bgp_notify_send_with_data (peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_BAD_PEER_AS, + notify_data_remote_as, 2); + } + return -1; + } + } + + /* When collision is detected and this peer is closed. Retrun + immidiately. */ + ret = bgp_collision_detect (peer, remote_id); + if (ret < 0) + return ret; + + /* Bit hacky */ + if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) + { + /* 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) + { + /* 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 + * onnection (the 'realpeer') is in a more larval state, and + * reconcile them when OPEN is sent on the 'realpeer'. + * + * 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. + * + * 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 (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:%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); + + /* Transfer file descriptor. */ + realpeer->fd = peer->fd; + peer->fd = -1; + + /* Transfer input buffer. */ + stream_free (realpeer->ibuf); + 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 */ + peer = realpeer; + + if (peer->fd < 0) + { + zlog_err ("bgp_open_receive peer's fd is negative value %d", + peer->fd); + 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. */ + if (remote_id.s_addr == 0 + || IPV4_CLASS_DE (ntohl (remote_id.s_addr)) + || ntohl (peer->local_id.s_addr) == ntohl (remote_id.s_addr)) + { + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s bad OPEN, wrong router identifier %s", + peer->host, inet_ntoa (remote_id)); + bgp_notify_send_with_data (peer, + BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_BAD_BGP_IDENT, + notify_data_remote_id, 4); + return -1; + } + + /* Set remote router-id */ + peer->remote_id = remote_id; + + /* Peer BGP version check. */ + if (version != 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, + (u_int8_t *) &maxver, 2); + return -1; + } + + /* Check neighbor as number. */ + if (remote_as != peer->as) + { + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s bad OPEN, remote AS is %u, expected %u", + peer->host, remote_as, peer->as); + bgp_notify_send_with_data (peer, + BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_BAD_PEER_AS, + notify_data_remote_as, 2); + return -1; + } + + /* From the rfc: Upon receipt of an OPEN message, a BGP speaker MUST + calculate the value of the Hold Timer by using the smaller of its + configured Hold Time and the Hold Time received in the OPEN message. + The Hold Time MUST be either zero or at least three seconds. An + implementation may reject connections on the basis of the Hold Time. */ + + if (holdtime < 3 && holdtime != 0) + { + bgp_notify_send_with_data (peer, + BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_UNACEP_HOLDTIME, + (u_int8_t *)holdtime_ptr, 2); + return -1; + } + + /* From the rfc: A reasonable maximum time between KEEPALIVE messages + would be one third of the Hold Time interval. KEEPALIVE messages + MUST NOT be sent more frequently than one per second. An + implementation MAY adjust the rate at which it sends KEEPALIVE + messages as a function of the Hold Time interval. */ + + if (CHECK_FLAG (peer->config, PEER_CONFIG_TIMER)) + send_holdtime = peer->holdtime; + else + send_holdtime = peer->bgp->default_holdtime; + + if (holdtime < send_holdtime) + peer->v_holdtime = holdtime; + else + peer->v_holdtime = send_holdtime; + + peer->v_keepalive = peer->v_holdtime / 3; + + /* Open option part parse. */ + if (optlen != 0) + { + if ((ret = bgp_open_option_parse (peer, optlen, &mp_capability)) < 0) + { + bgp_notify_send (peer, + BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_UNSPECIFIC); + return ret; + } + } + else + { + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s rcvd OPEN w/ OPTION parameter len: 0", + peer->host); + } + + /* + * 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]; + peer->afc_nego[AFI_IP6][SAFI_UNICAST] = peer->afc[AFI_IP6][SAFI_UNICAST]; + peer->afc_nego[AFI_IP6][SAFI_MULTICAST] = peer->afc[AFI_IP6][SAFI_MULTICAST]; + } + + /* Get sockname. */ + bgp_getsockname (peer); + peer->rtt = sockopt_tcp_rtt (peer->fd); + + BGP_EVENT_ADD (peer, Receive_OPEN_message); + + peer->packet_size = 0; + if (peer->ibuf) + stream_reset (peer->ibuf); + + 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, 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; + 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) + { + zlog_err ("%s [FSM] Update packet received under status %s", + peer->host, LOOKUP (bgp_status_msg, peer->status)); + bgp_notify_send (peer, BGP_NOTIFY_FSM_ERR, 0); + return -1; + } + + /* Set initial values. */ + memset (&attr, 0, sizeof (struct attr)); + memset (&extra, 0, sizeof (struct attr_extra)); + memset (&nlris, 0, sizeof nlris); + + attr.extra = &extra; + + s = peer->ibuf; + end = stream_pnt (s) + size; + + /* RFC1771 6.3 If the Unfeasible Routes Length or Total Attribute + Length is too large (i.e., if Unfeasible Routes Length + Total + Attribute Length + 23 exceeds the message Length), then the Error + Subcode is set to Malformed Attribute List. */ + if (stream_pnt (s) + 2 > end) + { + zlog_err ("%s [Error] Update packet error" + " (packet length is short for unfeasible length)", + peer->host); + bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MAL_ATTR); + return -1; + } + + /* Unfeasible Route Length. */ + withdraw_len = stream_getw (s); + + /* Unfeasible Route Length check. */ + if (stream_pnt (s) + withdraw_len > end) + { + zlog_err ("%s [Error] Update packet error" + " (packet unfeasible length overflow %d)", + peer->host, withdraw_len); + bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MAL_ATTR); + return -1; + } + + /* Unfeasible Route packet format check. */ + if (withdraw_len > 0) + { + 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); + + stream_forward_getp (s, withdraw_len); + } + + /* Attribute total length check. */ + if (stream_pnt (s) + 2 > end) + { + zlog_warn ("%s [Error] Packet Error" + " (update packet is short for attribute length)", + peer->host); + bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MAL_ATTR); + return -1; + } + + /* Fetch attribute total length. */ + attribute_len = stream_getw (s); + + /* Attribute length check. */ + if (stream_pnt (s) + attribute_len > end) + { + zlog_warn ("%s [Error] Packet Error" + " (update packet attribute length overflow %d)", + peer->host, attribute_len); + bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MAL_ATTR); + return -1; + } + + /* Certain attribute parsing errors should not be considered bad enough + * to reset the session for, most particularly any partial/optional + * attributes that have 'tunneled' over speakers that don't understand + * them. Instead we withdraw only the prefix concerned. + * + * Complicates the flow a little though.. + */ + bgp_attr_parse_ret_t attr_parse_ret = BGP_ATTR_PARSE_PROCEED; + /* This define morphs the update case into a withdraw when lower levels + * have signalled an error condition where this is best. + */ +#define NLRI_ATTR_ARG (attr_parse_ret != BGP_ATTR_PARSE_WITHDRAW ? &attr : NULL) + + /* Parse attribute when it exists. */ + if (attribute_len) + { + attr_parse_ret = bgp_attr_parse (peer, &attr, attribute_len, + &nlris[NLRI_MP_UPDATE], &nlris[NLRI_MP_WITHDRAW]); + if (attr_parse_ret == BGP_ATTR_PARSE_ERROR) + { + 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; + + if (attr_parse_ret == BGP_ATTR_PARSE_WITHDRAW) + zlog (peer->log, LOG_ERR, + "%s rcvd UPDATE with errors in attr(s)!! Withdrawing route.", + peer->host); + + if (ret) + zlog (peer->log, lvl, "%s rcvd UPDATE w/ attr: %s", + peer->host, attrstr); + } + + /* Network Layer Reachability Information. */ + update_len = end - stream_pnt (s); + + if (update_len) + { + /* Set NLRI portion to structure. */ + 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); + } + + /* Parse any given NLRIs */ + for (i = NLRI_UPDATE; i < NLRI_TYPE_MAX; i++) + { + 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; + } + } + + /* 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) + { + 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][safi], + PEER_STATUS_EOR_RECEIVED); + + /* NSF delete stale route */ + 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 %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); + bgp_attr_flush (&attr); + + /* If peering is stopped due to some reason, do not generate BGP + event. */ + if (peer->status != Established) + return 0; + + /* Increment packet counter. */ + peer->update_in++; + peer->update_time = bgp_clock (); + + /* Rearm holdtime timer */ + BGP_TIMER_OFF (peer->t_holdtime); + bgp_timer_set (peer); + + return 0; +} + +/* Notify message treatment function. */ +static void +bgp_notify_receive (struct peer *peer, bgp_size_t size) +{ + struct bgp_notify bgp_notify; + + if (peer->notify.data) + { + XFREE (MTYPE_TMP, peer->notify.data); + peer->notify.data = NULL; + peer->notify.length = 0; + } + + bgp_notify.code = stream_getc (peer->ibuf); + bgp_notify.subcode = stream_getc (peer->ibuf); + bgp_notify.length = size - 2; + bgp_notify.data = NULL; + + /* Preserv notify code and sub code. */ + peer->notify.code = bgp_notify.code; + peer->notify.subcode = bgp_notify.subcode; + /* For further diagnostic record returned Data. */ + if (bgp_notify.length) + { + peer->notify.length = size - 2; + peer->notify.data = XMALLOC (MTYPE_TMP, size - 2); + memcpy (peer->notify.data, stream_pnt (peer->ibuf), size - 2); + } + + /* For debug */ + { + int i; + int first = 0; + char c[4]; + + if (bgp_notify.length) + { + bgp_notify.data = XMALLOC (MTYPE_TMP, bgp_notify.length * 3); + for (i = 0; i < bgp_notify.length; i++) + if (first) + { + sprintf (c, " %02x", stream_getc (peer->ibuf)); + strcat (bgp_notify.data, c); + } + else + { + first = 1; + sprintf (c, "%02x", stream_getc (peer->ibuf)); + strcpy (bgp_notify.data, c); + } + } + + bgp_notify_print(peer, &bgp_notify, "received"); + if (bgp_notify.data) + { + XFREE (MTYPE_TMP, bgp_notify.data); + bgp_notify.data = NULL; + bgp_notify.length = 0; + } + } + + /* peer count update */ + peer->notify_in++; + + if (peer->status == Established) + peer->last_reset = PEER_DOWN_NOTIFY_RECEIVED; + + /* We have to check for Notify with Unsupported Optional Parameter. + in that case we fallback to open without the capability option. + But this done in bgp_stop. We just mark it here to avoid changing + the fsm tables. */ + if (bgp_notify.code == BGP_NOTIFY_OPEN_ERR && + bgp_notify.subcode == BGP_NOTIFY_OPEN_UNSUP_PARAM ) + UNSET_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN); + + BGP_EVENT_ADD (peer, Receive_NOTIFICATION_message); +} + +/* Keepalive treatment function -- get keepalive send keepalive */ +static void +bgp_keepalive_receive (struct peer *peer, bgp_size_t size) +{ + if (BGP_DEBUG (keepalive, KEEPALIVE)) + zlog_debug ("%s KEEPALIVE rcvd", peer->host); + + BGP_EVENT_ADD (peer, Receive_KEEPALIVE_message); +} + +/* Route refresh message is received. */ +static void +bgp_route_refresh_receive (struct peer *peer, bgp_size_t size) +{ + afi_t afi; + safi_t safi; + struct stream *s; + + /* If peer does not have the capability, send notification. */ + if (! CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_ADV)) + { + plog_err (peer->log, "%s [Error] BGP route refresh is not enabled", + peer->host); + bgp_notify_send (peer, + BGP_NOTIFY_HEADER_ERR, + BGP_NOTIFY_HEADER_BAD_MESTYPE); + return; + } + + /* Status must be Established. */ + if (peer->status != Established) + { + plog_err (peer->log, + "%s [Error] Route refresh packet received under status %s", + peer->host, LOOKUP (bgp_status_msg, peer->status)); + bgp_notify_send (peer, BGP_NOTIFY_FSM_ERR, 0); + return; + } + + s = peer->ibuf; + + /* Parse packet. */ + afi = stream_getw (s); + /* reserved byte */ + stream_getc (s); + safi = stream_getc (s); + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s rcvd REFRESH_REQ for afi/safi: %d/%d", + peer->host, afi, safi); + + /* Check AFI and SAFI. */ + if ((afi != AFI_IP && afi != AFI_IP6) + || (safi != SAFI_UNICAST && safi != SAFI_MULTICAST + && safi != SAFI_MPLS_LABELED_VPN)) + { + if (BGP_DEBUG (normal, NORMAL)) + { + zlog_debug ("%s REFRESH_REQ for unrecognized afi/safi: %d/%d - ignored", + peer->host, afi, safi); + } + return; + } + + /* Adjust safi code. */ + if (safi == SAFI_MPLS_LABELED_VPN) + safi = SAFI_MPLS_VPN; + + if (size != BGP_MSG_ROUTE_REFRESH_MIN_SIZE - BGP_HEADER_SIZE) + { + u_char *end; + u_char when_to_refresh; + u_char orf_type; + u_int16_t orf_len; + + if (size - (BGP_MSG_ROUTE_REFRESH_MIN_SIZE - BGP_HEADER_SIZE) < 5) + { + zlog_info ("%s ORF route refresh length error", peer->host); + bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); + return; + } + + when_to_refresh = stream_getc (s); + end = stream_pnt (s) + (size - 5); + + while ((stream_pnt (s) + 2) < end) + { + orf_type = stream_getc (s); + orf_len = stream_getw (s); + + /* orf_len in bounds? */ + if ((stream_pnt (s) + orf_len) > end) + break; /* XXX: Notify instead?? */ + if (orf_type == ORF_TYPE_PREFIX + || orf_type == ORF_TYPE_PREFIX_OLD) + { + 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]; + int ret; + + if (BGP_DEBUG (normal, NORMAL)) + { + zlog_debug ("%s rcvd Prefixlist ORF(%d) length %d", + peer->host, orf_type, orf_len); + } + + /* we're going to read at least 1 byte of common ORF header, + * and 7 bytes of ORF Address-filter entry from the stream + */ + if (orf_len < 7) + break; + + /* ORF prefix-list name */ + sprintf (name, "%s.%d.%d", peer->host, afi, safi); + + while (p_pnt < p_end) + { + /* If the ORF entry is malformed, want to read as much of it + * as possible without going beyond the bounds of the entry, + * to maximise debug information. + */ + int ok; + memset (&orfp, 0, sizeof (struct orf_prefix)); + common = *p_pnt++; + /* after ++: p_pnt <= p_end */ + if (common & ORF_COMMON_PART_REMOVE_ALL) + { + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s rcvd Remove-All pfxlist ORF request", peer->host); + prefix_bgp_orf_remove_all (afi, name); + break; + } + ok = ((size_t)(p_end - p_pnt) >= sizeof(u_int32_t)) ; + if (ok) + { + memcpy (&seq, p_pnt, sizeof (u_int32_t)); + p_pnt += sizeof (u_int32_t); + orfp.seq = ntohl (seq); + } + else + p_pnt = p_end ; + + if ((ok = (p_pnt < p_end))) + orfp.ge = *p_pnt++ ; /* value checked in prefix_bgp_orf_set() */ + if ((ok = (p_pnt < p_end))) + orfp.le = *p_pnt++ ; /* value checked in prefix_bgp_orf_set() */ + if ((ok = (p_pnt < p_end))) + orfp.p.prefixlen = *p_pnt++ ; + orfp.p.family = afi2family (afi); /* afi checked already */ + + psize = PSIZE (orfp.p.prefixlen); /* 0 if not ok */ + if (psize > prefix_blen(&orfp.p)) /* valid for family ? */ + { + ok = 0 ; + psize = prefix_blen(&orfp.p) ; + } + if (psize > (p_end - p_pnt)) /* valid for packet ? */ + { + ok = 0 ; + psize = p_end - p_pnt ; + } + + if (psize > 0) + memcpy (&orfp.p.u.prefix, p_pnt, psize); + p_pnt += psize; + + if (BGP_DEBUG (normal, NORMAL)) + { + 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 || (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 (afi, name); + break; + } + } + peer->orf_plist[afi][safi] = + prefix_bgp_orf_lookup (afi, name); + } + stream_forward_getp (s, orf_len); + } + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s rcvd Refresh %s ORF request", peer->host, + when_to_refresh == REFRESH_DEFER ? "Defer" : "Immediate"); + if (when_to_refresh == REFRESH_DEFER) + return; + } + + /* First update is deferred until ORF or ROUTE-REFRESH is received */ + if (CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_WAIT_REFRESH)) + UNSET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_WAIT_REFRESH); + + /* Perform route refreshment to the peer */ + bgp_announce_route (peer, afi, safi); +} + +static int +bgp_capability_msg_parse (struct peer *peer, u_char *pnt, bgp_size_t length) +{ + u_char *end; + struct capability_mp_data mpc; + struct capability_header *hdr; + u_char action; + afi_t afi; + safi_t safi; + + end = pnt + length; + + while (pnt < end) + { + /* We need at least action, capability code and capability length. */ + if (pnt + 3 > end) + { + zlog_info ("%s Capability length error", peer->host); + bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); + return -1; + } + action = *pnt; + hdr = (struct capability_header *)(pnt + 1); + + /* Action value check. */ + if (action != CAPABILITY_ACTION_SET + && action != CAPABILITY_ACTION_UNSET) + { + zlog_info ("%s Capability Action Value error %d", + peer->host, action); + bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); + return -1; + } + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s CAPABILITY has action: %d, code: %u, length %u", + peer->host, action, hdr->code, hdr->length); + + /* Capability length check. */ + if ((pnt + hdr->length + 3) > end) + { + zlog_info ("%s Capability length error", peer->host); + bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); + return -1; + } + + /* Fetch structure to the byte stream. */ + memcpy (&mpc, pnt + 3, sizeof (struct capability_mp_data)); + + /* We know MP Capability Code. */ + if (hdr->code == CAPABILITY_CODE_MP) + { + afi = ntohs (mpc.afi); + safi = mpc.safi; + + /* Ignore capability when override-capability is set. */ + if (CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY)) + continue; + + if (!bgp_afi_safi_valid_indices (afi, &safi)) + { + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s Dynamic Capability MP_EXT afi/safi invalid " + "(%u/%u)", peer->host, afi, safi); + continue; + } + + /* Address family check. */ + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s CAPABILITY has %s MP_EXT CAP for afi/safi: %u/%u", + peer->host, + action == CAPABILITY_ACTION_SET + ? "Advertising" : "Removing", + ntohs(mpc.afi) , mpc.safi); + + if (action == CAPABILITY_ACTION_SET) + { + peer->afc_recv[afi][safi] = 1; + if (peer->afc[afi][safi]) + { + peer->afc_nego[afi][safi] = 1; + bgp_announce_route (peer, afi, safi); + } + } + else + { + peer->afc_recv[afi][safi] = 0; + peer->afc_nego[afi][safi] = 0; + + if (peer_active_nego (peer)) + bgp_clear_route (peer, afi, safi, BGP_CLEAR_ROUTE_NORMAL); + else + BGP_EVENT_ADD (peer, BGP_Stop); + } + } + else + { + zlog_warn ("%s unrecognized capability code: %d - ignored", + peer->host, hdr->code); + } + pnt += hdr->length + 3; + } + return 0; +} + +/* Dynamic Capability is received. + * + * This is exported for unit-test purposes + */ +int +bgp_capability_receive (struct peer *peer, bgp_size_t size) +{ + u_char *pnt; + + /* Fetch pointer. */ + pnt = stream_pnt (peer->ibuf); + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s rcv CAPABILITY", peer->host); + + /* If peer does not have the capability, send notification. */ + if (! CHECK_FLAG (peer->cap, PEER_CAP_DYNAMIC_ADV)) + { + plog_err (peer->log, "%s [Error] BGP dynamic capability is not enabled", + peer->host); + bgp_notify_send (peer, + BGP_NOTIFY_HEADER_ERR, + BGP_NOTIFY_HEADER_BAD_MESTYPE); + return -1; + } + + /* Status must be Established. */ + if (peer->status != Established) + { + plog_err (peer->log, + "%s [Error] Dynamic capability packet received under status %s", peer->host, LOOKUP (bgp_status_msg, peer->status)); + bgp_notify_send (peer, BGP_NOTIFY_FSM_ERR, 0); + return -1; + } + + /* Parse packet. */ + return bgp_capability_msg_parse (peer, pnt, size); +} + +/* BGP read utility function. */ +static int +bgp_read_packet (struct peer *peer) +{ + int nbytes; + int readsize; + + readsize = peer->packet_size - stream_get_endp (peer->ibuf); + + /* If size is zero then return. */ + if (! readsize) + return 0; + + /* Read packet from fd. */ + nbytes = stream_read_try (peer->ibuf, peer->fd, readsize); + + /* If read byte is smaller than zero then error occured. */ + if (nbytes < 0) + { + /* Transient error should retry */ + if (nbytes == -2) + return -1; + + plog_err (peer->log, "%s [Error] bgp_read_packet error: %s", + peer->host, safe_strerror (errno)); + + if (peer->status == Established) + { + if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_MODE)) + { + peer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION; + SET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT); + } + else + peer->last_reset = PEER_DOWN_CLOSE_SESSION; + } + + BGP_EVENT_ADD (peer, TCP_fatal_error); + return -1; + } + + /* When read byte is zero : clear bgp peer and return */ + if (nbytes == 0) + { + if (BGP_DEBUG (events, EVENTS)) + plog_debug (peer->log, "%s [Event] BGP connection closed fd %d", + peer->host, peer->fd); + + if (peer->status == Established) + { + if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_MODE)) + { + peer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION; + SET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT); + } + else + peer->last_reset = PEER_DOWN_CLOSE_SESSION; + } + + BGP_EVENT_ADD (peer, TCP_connection_closed); + return -1; + } + + /* We read partial packet. */ + if (stream_get_endp (peer->ibuf) != peer->packet_size) + return -1; + + return 0; +} + +/* Marker check. */ +static int +bgp_marker_all_one (struct stream *s, int length) +{ + int i; + + for (i = 0; i < length; i++) + if (s->data[i] != 0xff) + return 0; + + 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) +{ + int ret; + u_char type = 0; + struct peer *peer; + bgp_size_t size; + char notify_data_length[2]; + + /* Yes first of all get peer pointer. */ + peer = THREAD_ARG (thread); + peer->t_read = NULL; + + /* For non-blocking IO check. */ + if (peer->status == Connect) + { + bgp_connect_check (peer); + goto done; + } + else + { + if (peer->fd < 0) + { + zlog_err ("bgp_read peer's fd is negative value %d", peer->fd); + return -1; + } + BGP_READ_ON (peer->t_read, bgp_read, peer->fd); + } + + /* Read packet header to determine type of the packet */ + if (peer->packet_size == 0) + peer->packet_size = BGP_HEADER_SIZE; + + if (stream_get_endp (peer->ibuf) < BGP_HEADER_SIZE) + { + ret = bgp_read_packet (peer); + + /* Header read error or partial read packet. */ + if (ret < 0) + goto done; + + /* Get size and type. */ + stream_forward_getp (peer->ibuf, BGP_MARKER_SIZE); + memcpy (notify_data_length, stream_pnt (peer->ibuf), 2); + size = stream_getw (peer->ibuf); + type = stream_getc (peer->ibuf); + + if (BGP_DEBUG (normal, NORMAL) && type != 2 && type != 0) + zlog_debug ("%s rcv message type %d, length (excl. header) %d", + peer->host, type, size - BGP_HEADER_SIZE); + + /* Marker check */ + if (((type == BGP_MSG_OPEN) || (type == BGP_MSG_KEEPALIVE)) + && ! bgp_marker_all_one (peer->ibuf, BGP_MARKER_SIZE)) + { + bgp_notify_send (peer, + BGP_NOTIFY_HEADER_ERR, + BGP_NOTIFY_HEADER_NOT_SYNC); + goto done; + } + + /* BGP type check. */ + if (type != BGP_MSG_OPEN && type != BGP_MSG_UPDATE + && type != BGP_MSG_NOTIFY && type != BGP_MSG_KEEPALIVE + && type != BGP_MSG_ROUTE_REFRESH_NEW + && type != BGP_MSG_ROUTE_REFRESH_OLD + && type != BGP_MSG_CAPABILITY) + { + if (BGP_DEBUG (normal, NORMAL)) + plog_debug (peer->log, + "%s unknown message type 0x%02x", + peer->host, type); + bgp_notify_send_with_data (peer, + BGP_NOTIFY_HEADER_ERR, + BGP_NOTIFY_HEADER_BAD_MESTYPE, + &type, 1); + goto done; + } + /* Mimimum packet length check. */ + if ((size < BGP_HEADER_SIZE) + || (size > BGP_MAX_PACKET_SIZE) + || (type == BGP_MSG_OPEN && size < BGP_MSG_OPEN_MIN_SIZE) + || (type == BGP_MSG_UPDATE && size < BGP_MSG_UPDATE_MIN_SIZE) + || (type == BGP_MSG_NOTIFY && size < BGP_MSG_NOTIFY_MIN_SIZE) + || (type == BGP_MSG_KEEPALIVE && size != BGP_MSG_KEEPALIVE_MIN_SIZE) + || (type == BGP_MSG_ROUTE_REFRESH_NEW && size < BGP_MSG_ROUTE_REFRESH_MIN_SIZE) + || (type == BGP_MSG_ROUTE_REFRESH_OLD && size < BGP_MSG_ROUTE_REFRESH_MIN_SIZE) + || (type == BGP_MSG_CAPABILITY && size < BGP_MSG_CAPABILITY_MIN_SIZE)) + { + if (BGP_DEBUG (normal, NORMAL)) + plog_debug (peer->log, + "%s bad message length - %d for %s", + peer->host, size, + type == 128 ? "ROUTE-REFRESH" : + bgp_type_str[(int) type]); + bgp_notify_send_with_data (peer, + BGP_NOTIFY_HEADER_ERR, + BGP_NOTIFY_HEADER_BAD_MESLEN, + (u_char *) notify_data_length, 2); + goto done; + } + + /* Adjust size to message length. */ + peer->packet_size = size; + } + + ret = bgp_read_packet (peer); + if (ret < 0) + goto done; + + /* Get size and type again. */ + size = stream_getw_from (peer->ibuf, BGP_MARKER_SIZE); + type = stream_getc_from (peer->ibuf, BGP_MARKER_SIZE + 2); + + /* BGP packet dump function. */ + bgp_dump_packet (peer, type, peer->ibuf); + + size = (peer->packet_size - BGP_HEADER_SIZE); + + /* Read rest of the packet and call each sort of packet routine */ + switch (type) + { + case BGP_MSG_OPEN: + peer->open_in++; + bgp_open_receive (peer, size); /* XXX return value ignored! */ + break; + case BGP_MSG_UPDATE: + 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 = bgp_recent_clock (); + bgp_keepalive_receive (peer, size); + break; + case BGP_MSG_ROUTE_REFRESH_NEW: + case BGP_MSG_ROUTE_REFRESH_OLD: + peer->refresh_in++; + bgp_route_refresh_receive (peer, size); + break; + case BGP_MSG_CAPABILITY: + peer->dynamic_cap_in++; + bgp_capability_receive (peer, size); + break; + } + + /* Clear input buffer. */ + peer->packet_size = 0; + if (peer->ibuf) + stream_reset (peer->ibuf); + + done: + if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) + { + if (BGP_DEBUG (events, EVENTS)) + zlog_debug ("%s [Event] Accepting BGP peer delete", peer->host); + peer_delete (peer); + } + return 0; +} diff --git a/bgpd/bgp_packet.h b/bgpd/bgp_packet.h new file mode 100644 index 0000000..6b0b7f4 --- /dev/null +++ b/bgpd/bgp_packet.h @@ -0,0 +1,59 @@ +/* BGP packet management 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. */ + +#ifndef _QUAGGA_BGP_PACKET_H +#define _QUAGGA_BGP_PACKET_H + +#define BGP_NLRI_LENGTH 1U +#define BGP_TOTAL_ATTR_LEN 2U +#define BGP_UNFEASIBLE_LEN 2U +#define BGP_WRITE_PACKET_MAX 10U + +/* When to refresh */ +#define REFRESH_IMMEDIATE 1 +#define REFRESH_DEFER 2 + +/* ORF Common part flag */ +#define ORF_COMMON_PART_ADD 0x00 +#define ORF_COMMON_PART_REMOVE 0x80 +#define ORF_COMMON_PART_REMOVE_ALL 0xC0 +#define ORF_COMMON_PART_PERMIT 0x00 +#define ORF_COMMON_PART_DENY 0x20 + +/* Packet send and receive function prototypes. */ +extern int bgp_read (struct thread *); +extern int bgp_write (struct thread *); + +extern void bgp_keepalive_send (struct peer *); +extern void bgp_open_send (struct peer *); +extern void bgp_notify_send (struct peer *, u_int8_t, u_int8_t); +extern void bgp_notify_send_with_data (struct peer *, u_int8_t, u_int8_t, + u_int8_t *, size_t); +extern void bgp_route_refresh_send (struct peer *, afi_t, safi_t, u_char, u_char, int); +extern void bgp_capability_send (struct peer *, afi_t, safi_t, int, int); +extern void bgp_default_update_send (struct peer *, struct attr *, + afi_t, safi_t, struct peer *); +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 new file mode 100644 index 0000000..13fa829 --- /dev/null +++ b/bgpd/bgp_regex.c @@ -0,0 +1,94 @@ +/* AS regular expression routine + 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 "log.h" +#include "command.h" +#include "memory.h" +#include "filter.h" + +#include "bgpd.h" +#include "bgp_aspath.h" +#include "bgp_regex.h" + +/* Character `_' has special mean. It represents [,{}() ] and the + beginning of the line(^) and the end of the line ($). + + (^|[,{}() ]|$) */ + +regex_t * +bgp_regcomp (const char *regstr) +{ + /* Convert _ character to generic regular expression. */ + int i, j; + int len; + int magic = 0; + char *magic_str; + char magic_regexp[] = "(^|[,{}() ]|$)"; + int ret; + regex_t *regex; + + len = strlen (regstr); + for (i = 0; i < len; i++) + if (regstr[i] == '_') + magic++; + + magic_str = XMALLOC (MTYPE_TMP, len + (14 * magic) + 1); + + for (i = 0, j = 0; i < len; i++) + { + if (regstr[i] == '_') + { + memcpy (magic_str + j, magic_regexp, strlen (magic_regexp)); + j += strlen (magic_regexp); + } + else + magic_str[j++] = regstr[i]; + } + magic_str[j] = '\0'; + + regex = XMALLOC (MTYPE_BGP_REGEXP, sizeof (regex_t)); + + ret = regcomp (regex, magic_str, REG_EXTENDED|REG_NOSUB); + + XFREE (MTYPE_TMP, magic_str); + + if (ret != 0) + { + XFREE (MTYPE_BGP_REGEXP, regex); + return NULL; + } + + return regex; +} + +int +bgp_regexec (regex_t *regex, struct aspath *aspath) +{ + return regexec (regex, aspath->str, 0, NULL, 0); +} + +void +bgp_regex_free (regex_t *regex) +{ + regfree (regex); + XFREE (MTYPE_BGP_REGEXP, regex); +} diff --git a/bgpd/bgp_regex.h b/bgpd/bgp_regex.h new file mode 100644 index 0000000..9fc8322 --- /dev/null +++ b/bgpd/bgp_regex.h @@ -0,0 +1,40 @@ +/* AS regular expression routine + 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_BGP_REGEX_H +#define _QUAGGA_BGP_REGEX_H + +#include + +#ifdef HAVE_LIBPCREPOSIX +# include +#else +# ifdef HAVE_GNU_REGEX +# include +# else +# include "regex-gnu.h" +# endif /* HAVE_GNU_REGEX */ +#endif /* HAVE_LIBPCREPOSIX */ + +extern void bgp_regex_free (regex_t *regex); +extern regex_t *bgp_regcomp (const char *str); +extern int bgp_regexec (regex_t *regex, struct aspath *aspath); + +#endif /* _QUAGGA_BGP_REGEX_H */ diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c new file mode 100644 index 0000000..13596fb --- /dev/null +++ b/bgpd/bgp_route.c @@ -0,0 +1,17989 @@ +/* BGP routing information + Copyright (C) 1996, 97, 98, 99 Kunihiro Ishiguro + Copyright (C) 2016 Job Snijders + +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 "linklist.h" +#include "memory.h" +#include "command.h" +#include "stream.h" +#include "filter.h" +#include "str.h" +#include "log.h" +#include "routemap.h" +#include "buffer.h" +#include "sockunion.h" +#include "plist.h" +#include "thread.h" +#include "workqueue.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_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" +#include "bgpd/bgp_fsm.h" +#include "bgpd/bgp_mplsvpn.h" +#include "bgpd/bgp_nexthop.h" +#include "bgpd/bgp_damp.h" +#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) +{ + struct bgp_node *rn; + struct bgp_node *prn = NULL; + + assert (table); + if (!table) + return NULL; + + if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)) + { + prn = bgp_node_get (table, (struct prefix *) prd); + + if (prn->info == NULL) + prn->info = bgp_table_init (afi, safi); + else + bgp_unlock_node (prn); + table = prn->info; + } + + rn = bgp_node_get (table, p); + + 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) +{ + struct bgp_info_extra *new; + new = XCALLOC (MTYPE_BGP_ROUTE_EXTRA, sizeof (struct bgp_info_extra)); + return new; +} + +static void +bgp_info_extra_free (struct bgp_info_extra **extra) +{ + if (extra && *extra) + { + if ((*extra)->damp_info) + bgp_damp_info_free ((*extra)->damp_info, 0); + + (*extra)->damp_info = NULL; + + XFREE (MTYPE_BGP_ROUTE_EXTRA, *extra); + + *extra = NULL; + } +} + +/* Get bgp_info extra information for the given bgp_info, lazy allocated + * if required. + */ +struct bgp_info_extra * +bgp_info_extra_get (struct bgp_info *ri) +{ + if (!ri->extra) + ri->extra = bgp_info_extra_new(); + return ri->extra; +} + +/* 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 */ + + XFREE (MTYPE_BGP_ROUTE, binfo); +} + +struct bgp_info * +bgp_info_lock (struct bgp_info *binfo) +{ + binfo->lock++; + return binfo; +} + +struct bgp_info * +bgp_info_unlock (struct bgp_info *binfo) +{ + assert (binfo && binfo->lock > 0); + binfo->lock--; + + if (binfo->lock == 0) + { +#if 0 + zlog_debug ("%s: unlocked and freeing", __func__); + zlog_backtrace (LOG_DEBUG); +#endif + bgp_info_free (binfo); + return NULL; + } + +#if 0 + if (binfo->lock == 1) + { + zlog_debug ("%s: unlocked to 1", __func__); + zlog_backtrace (LOG_DEBUG); + } +#endif + + return binfo; +} + +void +bgp_info_add (struct bgp_node *rn, struct bgp_info *ri) +{ + struct bgp_info *top; + + top = rn->info; + + ri->next = rn->info; + ri->prev = NULL; + if (top) + top->prev = ri; + rn->info = ri; + + bgp_info_lock (ri); + bgp_lock_node (rn); + peer_lock (ri->peer); /* bgp_info peer reference */ +} + +/* Do the actual removal of info from RIB, for use by bgp_process + completion callback *only* */ +static void +bgp_info_reap (struct bgp_node *rn, struct bgp_info *ri) +{ + if (ri->next) + ri->next->prev = ri->prev; + if (ri->prev) + ri->prev->next = ri->next; + else + rn->info = ri->next; + + bgp_info_mpath_dequeue (ri); + bgp_info_unlock (ri); + bgp_unlock_node (rn); +} + +void +bgp_info_delete (struct bgp_node *rn, struct bgp_info *ri) +{ + bgp_info_set_flag (rn, ri, BGP_INFO_REMOVED); + /* set of previous already took care of pcount */ + UNSET_FLAG (ri->flags, BGP_INFO_VALID); +} + +/* undo the effects of a previous call to bgp_info_delete; typically + called when a route is deleted and then quickly re-added before the + deletion has been processed */ +static void +bgp_info_restore (struct bgp_node *rn, struct bgp_info *ri) +{ + bgp_info_unset_flag (rn, ri, BGP_INFO_REMOVED); + /* unset of previous already took care of pcount */ + SET_FLAG (ri->flags, BGP_INFO_VALID); +} + +/* Adjust pcount as required */ +static void +bgp_pcount_adjust (struct bgp_node *rn, struct bgp_info *ri) +{ + 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 (table->type != BGP_TABLE_MAIN + || ri->peer == ri->peer->bgp->peer_self) + return; + + 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[table->afi][table->safi]) + ri->peer->pcount[table->afi][table->safi]--; + else + { + zlog_warn ("%s: Asked to decrement 0 prefix count for peer %s", + __func__, ri->peer->host); + zlog_backtrace (LOG_WARNING); + zlog_warn ("%s: Please report to Quagga bugzilla", __func__); + } + } + else if (BGP_INFO_COUNTABLE (ri) + && !CHECK_FLAG (ri->flags, BGP_INFO_COUNTED)) + { + SET_FLAG (ri->flags, BGP_INFO_COUNTED); + ri->peer->pcount[table->afi][table->safi]++; + } +} + + +/* Set/unset bgp_info flags, adjusting any other state as needed. + * This is here primarily to keep prefix-count in check. + */ +void +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 countability state */ + if (!CHECK_FLAG (flag, BGP_INFO_VALID|BGP_INFO_HISTORY|BGP_INFO_REMOVED)) + return; + + bgp_pcount_adjust (rn, ri); +} + +void +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 countability state */ + if (!CHECK_FLAG (flag, BGP_INFO_VALID|BGP_INFO_HISTORY|BGP_INFO_REMOVED)) + return; + + bgp_pcount_adjust (rn, ri); +} + +/* Get MED value. If MED value is missing and "bgp bestpath + missing-as-worst" is specified, treat it as the worst value. */ +static u_int32_t +bgp_med_value (struct attr *attr, struct bgp *bgp) +{ + if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC)) + return attr->med; + else + { + if (bgp_flag_check (bgp, BGP_FLAG_MED_MISSING_AS_WORST)) + return BGP_MED_MAX; + else + return 0; + } +} + +/* 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, + 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; + 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; + int confed_as_route; + int ret; + + /* 0. Null check. */ + if (new == NULL) + return 1; + if (exist == NULL) + return -1; + + newattr = new->attr; + existattr = exist->attr; + newattre = newattr->extra; + existattre = existattr->extra; + + /* 1. Weight check. */ + new_weight = exist_weight = 0; + + if (newattre) + new_weight = newattre->weight; + if (existattre) + exist_weight = existattre->weight; + + if (new_weight > exist_weight) + return -1; + if (new_weight < exist_weight) + return 1; + + /* 2. Local preference check. */ + 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 (new_pref > exist_pref) + return -1; + if (new_pref < exist_pref) + return 1; + + /* 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 (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 (newattr->aspath); + aspath_hops += aspath_count_confeds (newattr->aspath); + + if ( aspath_hops < (exist_hops + exist_confeds)) + return -1; + if ( aspath_hops > (exist_hops + exist_confeds)) + return 1; + } + else + { + int newhops = aspath_count_hops (newattr->aspath); + + if (newhops < exist_hops) + return -1; + if (newhops > exist_hops) + return 1; + } + } + + /* 5. Origin check. */ + if (newattr->origin < existattr->origin) + return -1; + if (newattr->origin > existattr->origin) + return 1; + + /* 6. MED check. */ + 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 (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; + if (new_med > exist_med) + return 1; + } + + /* 7. Peer type check. */ + 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; + + /* 8. IGP metric check. */ + 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)) + { + 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; + } + + /* 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) + && new_sort == BGP_PEER_EBGP + && exist_sort == BGP_PEER_EBGP) + { + if (CHECK_FLAG (new->flags, BGP_INFO_SELECTED)) + return -1; + if (CHECK_FLAG (exist->flags, BGP_INFO_SELECTED)) + return 1; + } + + /* 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 (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; + if (ntohl (new_id.s_addr) > ntohl (exist_id.s_addr)) + return 1; + + /* 12. Cluster length comparision. */ + 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; + if (new_cluster > exist_cluster) + 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 1; + if (ret == -1) + return -1; + + return -1; +} + +static enum filter_type +bgp_input_filter (struct peer *peer, struct prefix *p, struct attr *attr, + afi_t afi, safi_t safi) +{ + struct bgp_filter *filter; + + filter = &peer->filter[afi][safi]; + +#define FILTER_EXIST_WARN(F,f,filter) \ + if (BGP_DEBUG (update, UPDATE_IN) \ + && !(F ## _IN (filter))) \ + plog_warn (peer->log, "%s: Could not find configured input %s-list %s!", \ + peer->host, #f, F ## _IN_NAME(filter)); + + if (DISTRIBUTE_IN_NAME (filter)) { + FILTER_EXIST_WARN(DISTRIBUTE, distribute, filter); + + if (access_list_apply (DISTRIBUTE_IN (filter), p) == FILTER_DENY) + return FILTER_DENY; + } + + if (PREFIX_LIST_IN_NAME (filter)) { + FILTER_EXIST_WARN(PREFIX_LIST, prefix, filter); + + if (prefix_list_apply (PREFIX_LIST_IN (filter), p) == PREFIX_DENY) + return FILTER_DENY; + } + + if (FILTER_LIST_IN_NAME (filter)) { + FILTER_EXIST_WARN(FILTER_LIST, as, filter); + + if (as_list_apply (FILTER_LIST_IN (filter), attr->aspath)== AS_FILTER_DENY) + return FILTER_DENY; + } + + return FILTER_PERMIT; +#undef FILTER_EXIST_WARN +} + +static enum filter_type +bgp_output_filter (struct peer *peer, struct prefix *p, struct attr *attr, + afi_t afi, safi_t safi) +{ + struct bgp_filter *filter; + + filter = &peer->filter[afi][safi]; + +#define FILTER_EXIST_WARN(F,f,filter) \ + if (BGP_DEBUG (update, UPDATE_OUT) \ + && !(F ## _OUT (filter))) \ + plog_warn (peer->log, "%s: Could not find configured output %s-list %s!", \ + peer->host, #f, F ## _OUT_NAME(filter)); + + if (DISTRIBUTE_OUT_NAME (filter)) { + FILTER_EXIST_WARN(DISTRIBUTE, distribute, filter); + + if (access_list_apply (DISTRIBUTE_OUT (filter), p) == FILTER_DENY) + return FILTER_DENY; + } + + if (PREFIX_LIST_OUT_NAME (filter)) { + FILTER_EXIST_WARN(PREFIX_LIST, prefix, filter); + + if (prefix_list_apply (PREFIX_LIST_OUT (filter), p) == PREFIX_DENY) + return FILTER_DENY; + } + + if (FILTER_LIST_OUT_NAME (filter)) { + FILTER_EXIST_WARN(FILTER_LIST, as, filter); + + if (as_list_apply (FILTER_LIST_OUT (filter), attr->aspath) == AS_FILTER_DENY) + return FILTER_DENY; + } + + return FILTER_PERMIT; +#undef FILTER_EXIST_WARN +} + +/* If community attribute includes no_export then return 1. */ +static int +bgp_community_filter (struct peer *peer, struct attr *attr) +{ + if (attr->community) + { + /* NO_ADVERTISE check. */ + if (community_include (attr->community, COMMUNITY_NO_ADVERTISE)) + return 1; + + /* NO_EXPORT check. */ + if (peer->sort == BGP_PEER_EBGP && + community_include (attr->community, COMMUNITY_NO_EXPORT)) + return 1; + + /* NO_EXPORT_SUBCONFED check. */ + if (peer->sort == BGP_PEER_EBGP + || peer->sort == BGP_PEER_CONFED) + if (community_include (attr->community, COMMUNITY_NO_EXPORT_SUBCONFED)) + return 1; + } + return 0; +} + +/* Route reflection loop check. */ +static int +bgp_cluster_filter (struct peer *peer, struct attr *attr) +{ + struct in_addr cluster_id; + + if (attr->extra && attr->extra->cluster) + { + if (peer->bgp->config & BGP_CONFIG_CLUSTER_ID) + cluster_id = peer->bgp->cluster_id; + else + cluster_id = peer->bgp->router_id; + + if (cluster_loop_check (attr->extra->cluster, cluster_id)) + return 1; + } + return 0; +} + +static int +bgp_input_modifier (struct peer *peer, struct prefix *p, struct attr *attr, + afi_t afi, safi_t safi) +{ + struct bgp_filter *filter; + struct bgp_info info; + route_map_result_t ret; + + filter = &peer->filter[afi][safi]; + + /* Apply default weight value. */ + if (peer->weight) + (bgp_attr_extra_get (attr))->weight = peer->weight; + + /* Route map apply. */ + if (ROUTE_MAP_IN_NAME (filter)) + { + /* Duplicate current value to new strucutre for modification. */ + info.peer = peer; + info.attr = attr; + + SET_FLAG (peer->rmap_type, PEER_RMAP_TYPE_IN); + + /* Apply BGP route map to the attribute. */ + ret = route_map_apply (ROUTE_MAP_IN (filter), p, RMAP_BGP, &info); + + peer->rmap_type = 0; + + if (ret == RMAP_DENYMATCH) + /* 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) +{ + struct bgp_filter *filter; + struct bgp_info info; + route_map_result_t ret; + + filter = &peer->filter[afi][safi]; + + /* Route map apply. */ + if (ROUTE_MAP_EXPORT_NAME (filter)) + { + /* Duplicate current value to new strucutre for modification. */ + info.peer = rsclient; + info.attr = attr; + + SET_FLAG (rsclient->rmap_type, PEER_RMAP_TYPE_EXPORT); + + /* Apply BGP route map to the attribute. */ + ret = route_map_apply (ROUTE_MAP_EXPORT (filter), p, RMAP_BGP, &info); + + rsclient->rmap_type = 0; + + if (ret == RMAP_DENYMATCH) + { + /* Free newly generated AS path and community by route-map. */ + bgp_attr_flush (attr); + return RMAP_DENY; + } + } + return RMAP_PERMIT; +} + +static int +bgp_import_modifier (struct peer *rsclient, struct peer *peer, + struct prefix *p, struct attr *attr, afi_t afi, safi_t safi) +{ + struct bgp_filter *filter; + struct bgp_info info; + route_map_result_t ret; + + filter = &rsclient->filter[afi][safi]; + + /* Apply default weight value. */ + if (peer->weight) + (bgp_attr_extra_get (attr))->weight = peer->weight; + + /* Route map apply. */ + if (ROUTE_MAP_IMPORT_NAME (filter)) + { + /* Duplicate current value to new strucutre for modification. */ + info.peer = peer; + info.attr = attr; + + SET_FLAG (peer->rmap_type, PEER_RMAP_TYPE_IMPORT); + + /* Apply BGP route map to the attribute. */ + ret = route_map_apply (ROUTE_MAP_IMPORT (filter), p, RMAP_BGP, &info); + + 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; + } + } + 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) +{ + int ret; + char buf[SU_ADDRSTRLEN]; + struct bgp_filter *filter; + struct peer *from; + 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; + + /* Do not send announces to RS-clients from the 'normal' bgp_table. */ + if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)) + return 0; + + /* Do not send back route to sender. */ + if (from == peer) + return 0; + + /* Aggregate-address suppress check. */ + if (ri->extra && ri->extra->suppress) + if (! UNSUPPRESS_MAP_NAME (filter)) + return 0; + + /* Default route check. */ + if (CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_DEFAULT_ORIGINATE)) + { + if (p->family == AF_INET && p->u.prefix4.s_addr == INADDR_ANY) + return 0; + else if (p->family == AF_INET6 && p->prefixlen == 0) + return 0; + } + + /* Transparency check. */ + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT) + && CHECK_FLAG (from->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)) + transparent = 1; + else + transparent = 0; + + /* If community is not disabled check the no-export and local. */ + 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 (riattr->flag & ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID)) + { + if (IPV4_ADDR_SAME (&peer->remote_id, &riattr->extra->originator_id)) + { + if (BGP_DEBUG (filter, FILTER)) + zlog (peer->log, LOG_DEBUG, + "%s [Update:SEND] %s/%d originator-id is same as remote router-id", + peer->host, + inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), + p->prefixlen); + return 0; + } + } + + /* ORF prefix-list filter check */ + if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_ADV) + && (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_RCV) + || CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_OLD_RCV))) + if (peer->orf_plist[afi][safi]) + { + if (prefix_list_apply (peer->orf_plist[afi][safi], p) == PREFIX_DENY) + return 0; + } + + /* Output filter check. */ + if (bgp_output_filter (peer, p, riattr, afi, safi) == FILTER_DENY) + { + if (BGP_DEBUG (filter, FILTER)) + zlog (peer->log, LOG_DEBUG, + "%s [Update:SEND] %s/%d is filtered", + peer->host, + inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), + p->prefixlen); + return 0; + } + +#ifdef BGP_SEND_ASPATH_CHECK + /* AS path loop check. */ + if (aspath_loop_check (riattr->aspath, peer->as)) + { + if (BGP_DEBUG (filter, FILTER)) + zlog (peer->log, LOG_DEBUG, + "%s [Update:SEND] suppress announcement to peer AS %u is AS path.", + peer->host, peer->as); + return 0; + } + + /* 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(riattr->aspath, bgp->confed_id)) + { + if (BGP_DEBUG (filter, FILTER)) + zlog (peer->log, LOG_DEBUG, + "%s [Update:SEND] suppress announcement to peer AS %u is AS path.", + peer->host, + bgp->confed_id); + return 0; + } + } +#endif /* BGP_SEND_ASPATH_CHECK */ + + /* Route-Reflect check. */ + if (from->sort == BGP_PEER_IBGP && peer->sort == BGP_PEER_IBGP) + reflect = 1; + else + reflect = 0; + + /* IBGP reflection check. */ + if (reflect) + { + /* A route from a Client peer. */ + if (CHECK_FLAG (from->af_flags[afi][safi], PEER_FLAG_REFLECTOR_CLIENT)) + { + /* Reflect to all the Non-Client peers and also to the + Client peers other than the originator. Originator check + is already done. So there is noting to do. */ + /* no bgp client-to-client reflection check. */ + if (bgp_flag_check (bgp, BGP_FLAG_NO_CLIENT_TO_CLIENT)) + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_REFLECTOR_CLIENT)) + return 0; + } + else + { + /* A route from a Non-client peer. Reflect to all other + clients. */ + if (! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_REFLECTOR_CLIENT)) + return 0; + } + } + + /* For modify attribute, copy it to temporary structure. */ + bgp_attr_dup (attr, riattr); + + /* If local-preference is not set. */ + 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 == BGP_PEER_EBGP + && attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC)) + { + if (ri->peer != bgp->peer_self && ! transparent + && ! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MED_UNCHANGED)) + 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 && ! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_SELF_ALL)) + || (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_UNCHANGED) + && ((NEXTHOP_IS_V4 && attr->nexthop.s_addr) + || (NEXTHOP_IS_V6 && + ! IN6_IS_ADDR_UNSPECIFIED(&attr->extra->mp_nexthop_global)) + ))) + { + /* NEXT-HOP Unchanged. */ + } + else if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_SELF) + || (NEXTHOP_IS_V4 && attr->nexthop.s_addr == 0) + || (NEXTHOP_IS_V6 && + IN6_IS_ADDR_UNSPECIFIED(&attr->extra->mp_nexthop_global)) + || (peer->sort == BGP_PEER_EBGP + && (bgp_multiaccess_check_v4 (attr->nexthop, peer) == 0))) + { + /* Set IPv4 nexthop. */ + if (NEXTHOP_IS_V4) + { + 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); + } + /* Set IPv6 nexthop. */ + 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; + } + } + + if (p->family == AF_INET6 && safi != SAFI_ENCAP) + { + /* Left nexthop_local unchanged if so configured. */ + if ( CHECK_FLAG (peer->af_flags[afi][safi], + PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED) ) + { + if ( IN6_IS_ADDR_LINKLOCAL (&attr->extra->mp_nexthop_local) ) + attr->extra->mp_nexthop_len=32; + else + attr->extra->mp_nexthop_len=16; + } + + /* Default nexthop_local treatment for non-RS-Clients */ + else + { + /* Link-local address should not be transit to different peer. */ + attr->extra->mp_nexthop_len = 16; + + /* Set link-local address for shared network peer. */ + if (peer->shared_network + && ! IN6_IS_ADDR_UNSPECIFIED (&peer->nexthop.v6_local)) + { + memcpy (&attr->extra->mp_nexthop_local, &peer->nexthop.v6_local, + IPV6_MAX_BYTELEN); + attr->extra->mp_nexthop_len = 32; + } + + /* If bgpd act as BGP-4+ route-reflector, do not send link-local + address.*/ + if (reflect) + attr->extra->mp_nexthop_len = 16; + + /* If BGP-4+ link-local nexthop is not link-local nexthop. */ + if (! IN6_IS_ADDR_LINKLOCAL (&peer->nexthop.v6_local)) + attr->extra->mp_nexthop_len = 16; + } + + } + + /* If this is EBGP peer and remove-private-AS is set. */ + 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 (); + + /* Route map & unsuppress-map apply. */ + if (ROUTE_MAP_OUT_NAME (filter) + || (ri->extra && ri->extra->suppress) ) + { + struct bgp_info info; + 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, 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; + } + + SET_FLAG (peer->rmap_type, PEER_RMAP_TYPE_OUT); + + if (ri->extra && ri->extra->suppress) + ret = route_map_apply (UNSUPPRESS_MAP (filter), p, RMAP_BGP, &info); + else + ret = route_map_apply (ROUTE_MAP_OUT (filter), p, RMAP_BGP, &info); + + peer->rmap_type = 0; + + if (ret == RMAP_DENYMATCH) + { + bgp_attr_flush (attr); + return 0; + } + } + return 1; +} + +static int +bgp_announce_check_rsclient (struct bgp_info *ri, struct peer *rsclient, + struct prefix *p, struct attr *attr, afi_t afi, safi_t safi) +{ + int ret; + char buf[SU_ADDRSTRLEN]; + 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; + + /* Do not send back route to sender. */ + if (from == rsclient) + return 0; + + /* Aggregate-address suppress check. */ + if (ri->extra && ri->extra->suppress) + if (! UNSUPPRESS_MAP_NAME (filter)) + return 0; + + /* Default route check. */ + if (CHECK_FLAG (rsclient->af_sflags[afi][safi], + PEER_STATUS_DEFAULT_ORIGINATE)) + { + if (p->family == AF_INET && p->u.prefix4.s_addr == INADDR_ANY) + return 0; + else if (p->family == AF_INET6 && p->prefixlen == 0) + return 0; + } + + /* If the attribute has originator-id and it is same as remote + peer's id. */ + if (riattr->flag & ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID)) + { + if (IPV4_ADDR_SAME (&rsclient->remote_id, + &riattr->extra->originator_id)) + { + if (BGP_DEBUG (filter, FILTER)) + zlog (rsclient->log, LOG_DEBUG, + "%s [Update:SEND] %s/%d originator-id is same as remote router-id", + rsclient->host, + inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), + p->prefixlen); + return 0; + } + } + + /* ORF prefix-list filter check */ + if (CHECK_FLAG (rsclient->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_ADV) + && (CHECK_FLAG (rsclient->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_RCV) + || CHECK_FLAG (rsclient->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_OLD_RCV))) + if (rsclient->orf_plist[afi][safi]) + { + if (prefix_list_apply (rsclient->orf_plist[afi][safi], p) == PREFIX_DENY) + return 0; + } + + /* Output filter check. */ + if (bgp_output_filter (rsclient, p, riattr, afi, safi) == FILTER_DENY) + { + if (BGP_DEBUG (filter, FILTER)) + zlog (rsclient->log, LOG_DEBUG, + "%s [Update:SEND] %s/%d is filtered", + rsclient->host, + inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), + p->prefixlen); + return 0; + } + +#ifdef BGP_SEND_ASPATH_CHECK + /* AS path loop check. */ + if (aspath_loop_check (riattr->aspath, rsclient->as)) + { + if (BGP_DEBUG (filter, FILTER)) + zlog (rsclient->log, LOG_DEBUG, + "%s [Update:SEND] suppress announcement to peer AS %u is AS path.", + rsclient->host, rsclient->as); + return 0; + } +#endif /* BGP_SEND_ASPATH_CHECK */ + + /* For modify attribute, copy it to temporary structure. */ + bgp_attr_dup (attr, riattr); + + /* next-hop-set */ + if ((p->family == AF_INET && attr->nexthop.s_addr == 0) + || (p->family == AF_INET6 && + IN6_IS_ADDR_UNSPECIFIED(&attr->extra->mp_nexthop_global)) + ) + { + /* Set IPv4 nexthop. */ + if (p->family == AF_INET) + { + 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); + } + /* Set IPv6 nexthop. */ + if (p->family == AF_INET6) + { + /* IPv6 global nexthop must be included. */ + memcpy (&attr->extra->mp_nexthop_global, &rsclient->nexthop.v6_global, + IPV6_MAX_BYTELEN); + attr->extra->mp_nexthop_len = 16; + } + } + + if (p->family == AF_INET6) + { + struct attr_extra *attre = attr->extra; + + /* Left nexthop_local unchanged if so configured. */ + if ( CHECK_FLAG (rsclient->af_flags[afi][safi], + PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED) ) + { + if ( IN6_IS_ADDR_LINKLOCAL (&attre->mp_nexthop_local) ) + attre->mp_nexthop_len=32; + else + attre->mp_nexthop_len=16; + } + + /* Default nexthop_local treatment for RS-Clients */ + else + { + /* Announcer and RS-Client are both in the same network */ + if (rsclient->shared_network && from->shared_network && + (rsclient->ifindex == from->ifindex)) + { + if ( IN6_IS_ADDR_LINKLOCAL (&attre->mp_nexthop_local) ) + attre->mp_nexthop_len=32; + else + attre->mp_nexthop_len=16; + } + + /* Set link-local address for shared network peer. */ + else if (rsclient->shared_network + && IN6_IS_ADDR_LINKLOCAL (&rsclient->nexthop.v6_local)) + { + memcpy (&attre->mp_nexthop_local, &rsclient->nexthop.v6_local, + IPV6_MAX_BYTELEN); + attre->mp_nexthop_len = 32; + } + + else + attre->mp_nexthop_len = 16; + } + + } + + /* If this is EBGP peer and remove-private-AS is set. */ + 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 (); + + /* Route map & unsuppress-map apply. */ + if (ROUTE_MAP_OUT_NAME (filter) || (ri->extra && ri->extra->suppress) ) + { + info.peer = rsclient; + info.attr = attr; + + SET_FLAG (rsclient->rmap_type, PEER_RMAP_TYPE_OUT); + + if (ri->extra && ri->extra->suppress) + ret = route_map_apply (UNSUPPRESS_MAP (filter), p, RMAP_BGP, &info); + else + ret = route_map_apply (ROUTE_MAP_OUT (filter), p, RMAP_BGP, &info); + + rsclient->rmap_type = 0; + + if (ret == RMAP_DENYMATCH) + { + bgp_attr_flush (attr); + return 0; + } + } + + return 1; +} + +struct bgp_info_pair +{ + struct bgp_info *old; + struct bgp_info *new; +}; + +static void +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; + struct bgp_info *ri; + 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)) + for (ri1 = rn->info; ri1; ri1 = ri1->next) + { + if (CHECK_FLAG (ri1->flags, BGP_INFO_DMED_CHECK)) + 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) + { + if (CHECK_FLAG (ri2->flags, BGP_INFO_DMED_CHECK)) + 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 (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. */ + old_select = NULL; + new_select = NULL; + for (ri = rn->info; (ri != NULL) && (nextri = ri->next, 1); ri = nextri) + { + if (CHECK_FLAG (ri->flags, BGP_INFO_SELECTED)) + old_select = ri; + + if (BGP_INFO_HOLDDOWN (ri)) + { + /* reap REMOVED routes, if needs be + * selected route must stay for a while longer though + */ + if (CHECK_FLAG (ri->flags, BGP_INFO_REMOVED) + && (ri != old_select)) + bgp_info_reap (rn, ri); + + 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))) + { + bgp_info_unset_flag (rn, ri, BGP_INFO_DMED_CHECK); + continue; + } + bgp_info_unset_flag (rn, ri, BGP_INFO_DMED_CHECK); + bgp_info_unset_flag (rn, ri, BGP_INFO_DMED_SELECTED); + + 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); + } + } + + 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 +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; + struct attr_extra extra; + + memset (&attr, 0, sizeof(struct attr)); + memset (&extra, 0, sizeof(struct attr_extra)); + + p = &rn->p; + + /* Announce route to Established peer. */ + if (peer->status != Established) + return 0; + + /* Address family configuration check. */ + if (! peer->afc_nego[afi][safi]) + return 0; + + /* First update is deferred until ORF or ROUTE-REFRESH is received */ + if (CHECK_FLAG (peer->af_sflags[afi][safi], + PEER_STATUS_ORF_WAIT_REFRESH)) + return 0; + + /* 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, + withdraw it. */ + if (selected && bgp_announce_check (selected, peer, p, &attr, afi, safi)) + bgp_adj_out_set (rn, peer, p, &attr, afi, safi, selected); + else + bgp_adj_out_unset (rn, peer, p, afi, safi); + break; + case BGP_TABLE_RSCLIENT: + /* Announcement to peer->conf. If the route is filtered, + withdraw it. */ + if (selected && + bgp_announce_check_rsclient (selected, peer, p, &attr, afi, safi)) + bgp_adj_out_set (rn, peer, p, &attr, afi, safi, selected); + else + bgp_adj_out_unset (rn, peer, p, afi, safi); + break; + } + + bgp_attr_flush (&attr); + return 0; +} + +struct bgp_process_queue +{ + struct bgp *bgp; + struct bgp_node *rn; + afi_t afi; + safi_t safi; +}; + +static wq_item_status +bgp_process_rsclient (struct work_queue *wq, void *data) +{ + struct bgp_process_queue *pq = data; + struct bgp *bgp = pq->bgp; + struct bgp_node *rn = pq->rn; + afi_t afi = pq->afi; + safi_t safi = pq->safi; + struct bgp_info *new_select; + struct bgp_info *old_select; + struct bgp_info_pair old_and_new; + struct listnode *node, *nnode; + struct peer *rsclient = bgp_node_table (rn)->owner; + + /* Best path selection. */ + bgp_best_selection (bgp, rn, &old_and_new, afi, safi); + new_select = old_and_new.new; + old_select = old_and_new.old; + + if (CHECK_FLAG (rsclient->sflags, PEER_STATUS_GROUP)) + { + if (rsclient->group) + for (ALL_LIST_ELEMENTS (rsclient->group->peer, node, nnode, rsclient)) + { + /* Nothing to do. */ + if (old_select && old_select == new_select) + if (!CHECK_FLAG (old_select->flags, BGP_INFO_ATTR_CHANGED)) + continue; + + 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); + } + + bgp_process_announce_selected (rsclient, new_select, rn, + afi, safi); + } + } + else + { + 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); + } + bgp_process_announce_selected (rsclient, new_select, rn, afi, safi); + } + + if (old_select && CHECK_FLAG (old_select->flags, BGP_INFO_REMOVED)) + bgp_info_reap (rn, old_select); + + UNSET_FLAG (rn->flags, BGP_NODE_PROCESS_SCHEDULED); + return WQ_SUCCESS; +} + +static wq_item_status +bgp_process_main (struct work_queue *wq, void *data) +{ + struct bgp_process_queue *pq = data; + struct bgp *bgp = pq->bgp; + struct bgp_node *rn = pq->rn; + afi_t afi = pq->afi; + safi_t safi = pq->safi; + struct prefix *p = &rn->p; + struct bgp_info *new_select; + struct bgp_info *old_select; + struct bgp_info_pair old_and_new; + struct listnode *node, *nnode; + struct peer *peer; + + /* Best path selection. */ + 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 + && !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) || + 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); + } + + + /* Check each BGP peer. */ + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + bgp_process_announce_selected (peer, new_select, rn, afi, safi); + } + + /* FIB update. */ + if ((safi == SAFI_UNICAST || safi == SAFI_MULTICAST) && (! bgp->name && + ! bgp_option_check (BGP_OPT_NO_FIB))) + { + if (new_select + && new_select->type == ZEBRA_ROUTE_BGP + && new_select->sub_type == BGP_ROUTE_NORMAL) + bgp_zebra_announce (p, new_select, bgp, safi); + else + { + /* Withdraw the route from the kernel. */ + if (old_select + && old_select->type == ZEBRA_ROUTE_BGP + && old_select->sub_type == BGP_ROUTE_NORMAL) + bgp_zebra_withdraw (p, old_select, safi); + } + } + + /* 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); + + UNSET_FLAG (rn->flags, BGP_NODE_PROCESS_SCHEDULED); + return WQ_SUCCESS; +} + +static void +bgp_processq_del (struct work_queue *wq, void *data) +{ + struct bgp_process_queue *pq = data; + struct bgp_table *table = bgp_node_table (pq->rn); + + bgp_unlock (pq->bgp); + bgp_unlock_node (pq->rn); + bgp_table_unlock (table); + XFREE (MTYPE_BGP_PROCESS_QUEUE, pq); +} + +static void +bgp_process_queue_init (void) +{ + bm->process_main_queue + = work_queue_new (bm->master, "process_main_queue"); + bm->process_rsclient_queue + = work_queue_new (bm->master, "process_rsclient_queue"); + + if ( !(bm->process_main_queue && bm->process_rsclient_queue) ) + { + zlog_err ("%s: Failed to allocate work queue", __func__); + exit (1); + } + + bm->process_main_queue->spec.workfunc = &bgp_process_main; + bm->process_main_queue->spec.del_item_data = &bgp_processq_del; + bm->process_main_queue->spec.max_retries = 0; + bm->process_main_queue->spec.hold = 50; + + 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 +bgp_process (struct bgp *bgp, struct bgp_node *rn, afi_t afi, safi_t safi) +{ + struct bgp_process_queue *pqnode; + + /* already scheduled for processing? */ + 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 (); + + pqnode = XCALLOC (MTYPE_BGP_PROCESS_QUEUE, + sizeof (struct bgp_process_queue)); + if (!pqnode) + return; + + /* all unlocked in bgp_processq_del */ + 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 (bgp_node_table (rn)->type) + { + case BGP_TABLE_MAIN: + work_queue_add (bm->process_main_queue, pqnode); + break; + case BGP_TABLE_RSCLIENT: + work_queue_add (bm->process_rsclient_queue, pqnode); + break; + } + + SET_FLAG (rn->flags, BGP_NODE_PROCESS_SCHEDULED); + return; +} + +static int +bgp_maximum_prefix_restart_timer (struct thread *thread) +{ + struct peer *peer; + + peer = THREAD_ARG (thread); + peer->t_pmax_restart = NULL; + + if (BGP_DEBUG (events, EVENTS)) + zlog_debug ("%s Maximum-prefix restart timer expired, restore peering", + peer->host); + + peer_clear (peer); + + return 0; +} + +int +bgp_maximum_prefix_overflow (struct peer *peer, afi_t afi, + safi_t safi, int always) +{ + if (!CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX)) + return 0; + + if (peer->pcount[afi][safi] > peer->pmax[afi][safi]) + { + if (CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_PREFIX_LIMIT) + && ! always) + return 0; + + zlog (peer->log, LOG_INFO, + "%%MAXPFXEXCEED: No. of %s prefix received from %s %ld exceed, " + "limit %ld", afi_safi_print (afi, safi), peer->host, + peer->pcount[afi][safi], peer->pmax[afi][safi]); + SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_PREFIX_LIMIT); + + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING)) + return 0; + + { + u_int8_t ndata[7]; + + if (safi == SAFI_MPLS_VPN) + safi = SAFI_MPLS_LABELED_VPN; + + ndata[0] = (afi >> 8); + ndata[1] = afi; + ndata[2] = safi; + ndata[3] = (peer->pmax[afi][safi] >> 24); + ndata[4] = (peer->pmax[afi][safi] >> 16); + ndata[5] = (peer->pmax[afi][safi] >> 8); + ndata[6] = (peer->pmax[afi][safi]); + + SET_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW); + bgp_notify_send_with_data (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_MAX_PREFIX, ndata, 7); + } + + /* restart timer start */ + if (peer->pmax_restart[afi][safi]) + { + peer->v_pmax_restart = peer->pmax_restart[afi][safi] * 60; + + if (BGP_DEBUG (events, EVENTS)) + zlog_debug ("%s Maximum-prefix restart timer started for %d secs", + peer->host, peer->v_pmax_restart); + + BGP_TIMER_ON (peer->t_pmax_restart, bgp_maximum_prefix_restart_timer, + peer->v_pmax_restart); + } + + return 1; + } + else + UNSET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_PREFIX_LIMIT); + + if (peer->pcount[afi][safi] > (peer->pmax[afi][safi] * peer->pmax_threshold[afi][safi] / 100)) + { + if (CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_PREFIX_THRESHOLD) + && ! always) + return 0; + + zlog (peer->log, LOG_INFO, + "%%MAXPFX: No. of %s prefix received from %s reaches %ld, max %ld", + afi_safi_print (afi, safi), peer->host, peer->pcount[afi][safi], + peer->pmax[afi][safi]); + SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_PREFIX_THRESHOLD); + } + else + UNSET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_PREFIX_THRESHOLD); + return 0; +} + +/* Unconditionally remove the route from the RIB, without taking + * damping into consideration (eg, because the session went down) + */ +static void +bgp_rib_remove (struct bgp_node *rn, struct bgp_info *ri, struct peer *peer, + afi_t afi, safi_t safi) +{ + bgp_aggregate_decrement (peer->bgp, &rn->p, ri, afi, safi); + + if (!CHECK_FLAG (ri->flags, BGP_INFO_HISTORY)) + bgp_info_delete (rn, ri); /* keep historical info */ + + bgp_process (peer->bgp, rn, afi, safi); +} + +static void +bgp_rib_withdraw (struct bgp_node *rn, struct bgp_info *ri, struct peer *peer, + afi_t afi, safi_t safi, struct prefix_rd *prd) +{ + int status = BGP_DAMP_NONE; + + /* apply dampening, if result is suppressed, we'll be retaining + * the bgp_info in the RIB for historical reference. + */ + if (CHECK_FLAG (peer->bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING) + && peer->sort == BGP_PEER_EBGP) + if ( (status = bgp_damp_withdraw (ri, rn, afi, safi, 0)) + == BGP_DAMP_SUPPRESSED) + { + bgp_aggregate_decrement (peer->bgp, &rn->p, ri, afi, safi); + return; + } + + 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, + int sub_type, struct prefix_rd *prd, u_char *tag) +{ + struct bgp_node *rn; + struct bgp *bgp; + struct attr new_attr; + struct attr_extra new_extra; + struct attr *attr_new; + struct attr *attr_new2; + struct bgp_info *ri; + struct bgp_info *new; + const char *reason; + char buf[SU_ADDRSTRLEN]; + + /* Do not insert announces from a rsclient into its own 'bgp_table'. */ + if (peer == rsclient) + return; + + bgp = peer->bgp; + rn = bgp_afi_node_get (rsclient->rib[afi][safi], afi, safi, p, prd); + + /* Check previously received route. */ + for (ri = rn->info; ri; ri = ri->next) + if (ri->peer == peer && ri->type == type && ri->sub_type == sub_type) + break; + + /* AS path loop check. */ + if (aspath_loop_check (attr->aspath, rsclient->as) > rsclient->allowas_in[afi][safi]) + { + reason = "as-path contains our own AS;"; + goto filtered; + } + + /* Route reflector originator ID check. */ + if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID) + && IPV4_ADDR_SAME (&rsclient->remote_id, &attr->extra->originator_id)) + { + reason = "originator is us;"; + goto filtered; + } + + new_attr.extra = &new_extra; + bgp_attr_dup (&new_attr, attr); + + /* Apply export policy. */ + if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT) && + bgp_export_modifier (rsclient, peer, p, &new_attr, afi, safi) == RMAP_DENY) + { + reason = "export-policy;"; + goto filtered; + } + + attr_new2 = bgp_attr_intern (&new_attr); + + /* Apply import policy. */ + if (bgp_import_modifier (rsclient, peer, p, &new_attr, afi, safi) == RMAP_DENY) + { + bgp_attr_unintern (&attr_new2); + + reason = "import-policy;"; + goto filtered; + } + + attr_new = bgp_attr_intern (&new_attr); + bgp_attr_unintern (&attr_new2); + + /* IPv4 unicast next hop check. */ + if ((afi == AFI_IP) && ((safi == SAFI_UNICAST) || safi == SAFI_MULTICAST)) + { + /* Next hop must not be 0.0.0.0 nor Class D/E address. */ + if (new_attr.nexthop.s_addr == 0 + || IPV4_CLASS_DE (ntohl (new_attr.nexthop.s_addr))) + { + bgp_attr_unintern (&attr_new); + + reason = "martian next-hop;"; + goto filtered; + } + } + + /* If the update is implicit withdraw. */ + if (ri) + { + ri->uptime = bgp_clock (); + + /* Same attribute comes in. */ + if (!CHECK_FLAG(ri->flags, BGP_INFO_REMOVED) + && attrhash_cmp (ri->attr, attr_new)) + { + + + if (BGP_DEBUG (update, UPDATE_IN)) + zlog (peer->log, LOG_DEBUG, + "%s rcvd %s/%d for RS-client %s...duplicate ignored", + peer->host, + inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), + p->prefixlen, rsclient->host); + + bgp_unlock_node (rn); + bgp_attr_unintern (&attr_new); + bgp_attr_flush(&new_attr); + return; + } + + /* Withdraw/Announce before we fully processed the withdraw */ + if (CHECK_FLAG(ri->flags, BGP_INFO_REMOVED)) + bgp_info_restore (rn, ri); + + /* Received Logging. */ + if (BGP_DEBUG (update, UPDATE_IN)) + zlog (peer->log, LOG_DEBUG, "%s rcvd %s/%d for RS-client %s", + peer->host, + inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), + p->prefixlen, rsclient->host); + + /* The attribute is changed. */ + bgp_info_set_flag (rn, ri, BGP_INFO_ATTR_CHANGED); + + /* Update to new attribute. */ + bgp_attr_unintern (&ri->attr); + ri->attr = attr_new; + + /* Update MPLS tag. */ + if (safi == SAFI_MPLS_VPN) + memcpy ((bgp_info_extra_get (ri))->tag, tag, 3); + + bgp_info_set_flag (rn, ri, BGP_INFO_VALID); + + /* Process change. */ + bgp_process (bgp, rn, afi, safi); + bgp_unlock_node (rn); + + return; + } + + /* Received Logging. */ + if (BGP_DEBUG (update, UPDATE_IN)) + { + zlog (peer->log, LOG_DEBUG, "%s rcvd %s/%d for RS-client %s", + peer->host, + inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), + p->prefixlen, rsclient->host); + } + + 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); + + bgp_info_set_flag (rn, new, BGP_INFO_VALID); + + /* 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); + + return; + + filtered: + + /* This BGP update is filtered. Log the reason then update BGP entry. */ + if (BGP_DEBUG (update, UPDATE_IN)) + zlog (peer->log, LOG_DEBUG, + "%s rcvd UPDATE about %s/%d -- DENIED for RS-client %s due to: %s", + peer->host, + inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), + p->prefixlen, rsclient->host, reason); + + if (ri) + bgp_rib_remove (rn, ri, peer, afi, safi); + + bgp_unlock_node (rn); + + return; +} + +static void +bgp_withdraw_rsclient (struct peer *rsclient, afi_t afi, safi_t safi, + struct peer *peer, struct prefix *p, int type, int sub_type, + struct prefix_rd *prd, u_char *tag) +{ + struct bgp_node *rn; + struct bgp_info *ri; + char buf[SU_ADDRSTRLEN]; + + if (rsclient == peer) + return; + + rn = bgp_afi_node_get (rsclient->rib[afi][safi], afi, safi, p, prd); + + /* Lookup withdrawn route. */ + for (ri = rn->info; ri; ri = ri->next) + if (ri->peer == peer && ri->type == type && ri->sub_type == sub_type) + break; + + /* Withdraw specified route from routing table. */ + if (ri && ! CHECK_FLAG (ri->flags, BGP_INFO_HISTORY)) + 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, + inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), + p->prefixlen); + + /* Unlock bgp_node_get() lock. */ + bgp_unlock_node (rn); +} + +static int +bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr, + afi_t afi, safi_t safi, int type, int sub_type, + struct prefix_rd *prd, u_char *tag, int soft_reconfig) +{ + int ret; + int aspath_loop_count = 0; + struct bgp_node *rn; + struct bgp *bgp; + 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 (! 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. */ + for (ri = rn->info; ri; ri = ri->next) + if (ri->peer == peer && ri->type == type && ri->sub_type == sub_type) + break; + + /* AS path local-as loop check. */ + if (peer->change_local_as) + { + if (! CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND)) + aspath_loop_count = 1; + + if (aspath_loop_check (attr->aspath, peer->change_local_as) > aspath_loop_count) + { + reason = "as-path contains our own AS;"; + goto filtered; + } + } + + /* AS path loop check. */ + if (aspath_loop_check (attr->aspath, bgp->as) > peer->allowas_in[afi][safi] + || (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION) + && aspath_loop_check(attr->aspath, bgp->confed_id) + > peer->allowas_in[afi][safi])) + { + reason = "as-path contains our own AS;"; + goto filtered; + } + + /* Route reflector originator ID check. */ + if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID) + && IPV4_ADDR_SAME (&bgp->router_id, &attr->extra->originator_id)) + { + reason = "originator is us;"; + goto filtered; + } + + /* Route reflector cluster ID check. */ + if (bgp_cluster_filter (peer, attr)) + { + reason = "reflected from the same cluster;"; + goto filtered; + } + + /* Apply incoming filter. */ + if (bgp_input_filter (peer, p, attr, afi, safi) == FILTER_DENY) + { + reason = "filter;"; + goto filtered; + } + + 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) + { + /* Next hop must not be 0.0.0.0 nor Class D/E address. Next hop + must not be my own address. */ + 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; + } + } + + attr_new = bgp_attr_intern (&new_attr); + + /* If the update is implicit withdraw. */ + if (ri) + { + ri->uptime = bgp_clock (); + + /* Same attribute comes in. */ + if (!CHECK_FLAG (ri->flags, BGP_INFO_REMOVED) + && attrhash_cmp (ri->attr, attr_new)) + { + if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING) + && peer->sort == BGP_PEER_EBGP + && CHECK_FLAG (ri->flags, BGP_INFO_HISTORY)) + { + if (BGP_DEBUG (update, UPDATE_IN)) + zlog (peer->log, LOG_DEBUG, "%s rcvd %s/%d", + peer->host, + inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), + p->prefixlen); + + if (bgp_damp_update (ri, rn, afi, safi) != BGP_DAMP_SUPPRESSED) + { + bgp_aggregate_increment (bgp, p, ri, afi, safi); + bgp_process (bgp, rn, afi, safi); + } + } + else /* Duplicate - odd */ + { + if (BGP_DEBUG (update, UPDATE_IN)) + zlog (peer->log, LOG_DEBUG, + "%s rcvd %s/%d...duplicate ignored", + peer->host, + inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), + p->prefixlen); + + /* graceful restart STALE flag unset. */ + if (CHECK_FLAG (ri->flags, BGP_INFO_STALE)) + { + bgp_info_unset_flag (rn, ri, BGP_INFO_STALE); + bgp_process (bgp, rn, afi, safi); + } + } + + bgp_unlock_node (rn); + bgp_attr_unintern (&attr_new); + bgp_attr_flush (&new_attr); + + return 0; + } + + /* Withdraw/Announce before we fully processed the withdraw */ + if (CHECK_FLAG(ri->flags, BGP_INFO_REMOVED)) + { + if (BGP_DEBUG (update, UPDATE_IN)) + zlog (peer->log, LOG_DEBUG, "%s rcvd %s/%d, flapped quicker than processing", + peer->host, + inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), + p->prefixlen); + bgp_info_restore (rn, ri); + } + + /* Received Logging. */ + if (BGP_DEBUG (update, UPDATE_IN)) + zlog (peer->log, LOG_DEBUG, "%s rcvd %s/%d", + peer->host, + inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), + p->prefixlen); + + /* graceful restart STALE flag unset. */ + if (CHECK_FLAG (ri->flags, BGP_INFO_STALE)) + bgp_info_unset_flag (rn, ri, BGP_INFO_STALE); + + /* The attribute is changed. */ + bgp_info_set_flag (rn, ri, BGP_INFO_ATTR_CHANGED); + + /* implicit withdraw, decrement aggregate and pcount here. + * only if update is accepted, they'll increment below. + */ + bgp_aggregate_decrement (bgp, p, ri, afi, safi); + + /* Update bgp route dampening information. */ + if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING) + && peer->sort == BGP_PEER_EBGP) + { + /* This is implicit withdraw so we should update dampening + information. */ + if (! CHECK_FLAG (ri->flags, BGP_INFO_HISTORY)) + bgp_damp_withdraw (ri, rn, afi, safi, 1); + } + + /* Update to new attribute. */ + bgp_attr_unintern (&ri->attr); + ri->attr = attr_new; + + /* Update MPLS tag. */ + 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 == 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); + return 0; + } + } + + /* Nexthop reachability check. */ + 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 (ri, NULL, connected)) + 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); + } + } + else + 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); + + return 0; + } + + /* Received Logging. */ + if (BGP_DEBUG (update, UPDATE_IN)) + { + zlog (peer->log, LOG_DEBUG, "%s rcvd %s/%d", + peer->host, + inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), + p->prefixlen); + } + + /* Make new BGP info. */ + 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) + { + 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 + { + 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); + + /* Increment prefix */ + 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); + + 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)) + return -1; + + /* Process change. */ + bgp_process (bgp, rn, afi, safi); + + return 0; + + /* This BGP update is filtered. Log the reason then update BGP + entry. */ + filtered: + if (BGP_DEBUG (update, UPDATE_IN)) + zlog (peer->log, LOG_DEBUG, + "%s rcvd UPDATE about %s/%d -- DENIED due to: %s", + peer->host, + inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), + p->prefixlen, reason); + + if (ri) + bgp_rib_remove (rn, ri, peer, afi, safi); + + bgp_unlock_node (rn); + bgp_attr_flush (&new_attr); + + return 0; +} + +int +bgp_update (struct peer *peer, struct prefix *p, struct attr *attr, + afi_t afi, safi_t safi, int type, int sub_type, + struct prefix_rd *prd, u_char *tag, int soft_reconfig) +{ + struct peer *rsclient; + struct listnode *node, *nnode; + struct bgp *bgp; + int ret; + + ret = bgp_update_main (peer, p, attr, afi, safi, type, sub_type, prd, tag, + soft_reconfig); + + bgp = peer->bgp; + + /* Process the update for each RS-client. */ + for (ALL_LIST_ELEMENTS (bgp->rsclient, node, nnode, rsclient)) + { + if (CHECK_FLAG (rsclient->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)) + bgp_update_rsclient (rsclient, afi, safi, attr, peer, p, type, + sub_type, prd, tag); + } + + return ret; +} + +int +bgp_withdraw (struct peer *peer, struct prefix *p, struct attr *attr, + afi_t afi, safi_t safi, int type, int sub_type, + struct prefix_rd *prd, u_char *tag) +{ + struct bgp *bgp; + char buf[SU_ADDRSTRLEN]; + struct bgp_node *rn; + struct bgp_info *ri; + struct peer *rsclient; + struct listnode *node, *nnode; + + 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)) + { + if (CHECK_FLAG (rsclient->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)) + bgp_withdraw_rsclient (rsclient, afi, safi, peer, p, type, sub_type, prd, tag); + } + + /* Logging. */ + if (BGP_DEBUG (update, UPDATE_IN)) + zlog (peer->log, LOG_DEBUG, "%s rcvd UPDATE about %s/%d -- withdrawn", + peer->host, + inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), + p->prefixlen); + + /* Lookup withdrawn route. */ + for (ri = rn->info; ri; ri = ri->next) + if (ri->peer == peer && ri->type == type && ri->sub_type == sub_type) + break; + + /* Withdraw specified route from routing table. */ + if (ri && ! CHECK_FLAG (ri->flags, BGP_INFO_HISTORY)) + 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, + inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), + p->prefixlen); + + /* Unlock bgp_node_get() lock. */ + bgp_unlock_node (rn); + + return 0; +} + +void +bgp_default_originate (struct peer *peer, afi_t afi, safi_t safi, int withdraw) +{ + struct bgp *bgp; + struct attr attr; + struct aspath *aspath; + struct prefix p; + struct peer *from; + struct bgp_node *rn; + struct bgp_info *ri; + int ret = RMAP_DENYMATCH; + + if (!(afi == AFI_IP || afi == AFI_IP6)) + return; + + bgp = peer->bgp; + from = bgp->peer_self; + + bgp_attr_default_set (&attr, BGP_ORIGIN_IGP); + aspath = attr.aspath; + attr.local_pref = bgp->default_local_pref; + memcpy (&attr.nexthop, &peer->nexthop.v4, IPV4_MAX_BYTELEN); + + if (afi == AFI_IP) + str2prefix ("0.0.0.0/0", &p); + else if (afi == AFI_IP6) + { + struct attr_extra *ae = attr.extra; + + str2prefix ("::/0", &p); + + /* IPv6 global nexthop must be included. */ + memcpy (&ae->mp_nexthop_global, &peer->nexthop.v6_global, + IPV6_MAX_BYTELEN); + ae->mp_nexthop_len = 16; + + /* If the peer is on shared nextwork and we have link-local + nexthop set it. */ + if (peer->shared_network + && !IN6_IS_ADDR_UNSPECIFIED (&peer->nexthop.v6_local)) + { + memcpy (&ae->mp_nexthop_local, &peer->nexthop.v6_local, + IPV6_MAX_BYTELEN); + ae->mp_nexthop_len = 32; + } + } + + if (peer->default_rmap[afi][safi].name) + { + SET_FLAG (bgp->peer_self->rmap_type, PEER_RMAP_TYPE_DEFAULT); + 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) + withdraw = 1; + } + + if (withdraw) + { + if (CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_DEFAULT_ORIGINATE)) + bgp_default_withdraw_send (peer, afi, safi); + UNSET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_DEFAULT_ORIGINATE); + } + else + { + 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; + 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) && (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) + { + if ( (rsclient) ? + (bgp_announce_check_rsclient (ri, peer, &rn->p, &attr, afi, safi)) + : (bgp_announce_check (ri, peer, &rn->p, &attr, afi, 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_flush_encap(&attr); +} + +void +bgp_announce_route (struct peer *peer, afi_t afi, safi_t safi) +{ + struct bgp_node *rn; + struct bgp_table *table; + + if (peer->status != Established) + return; + + if (! peer->afc_nego[afi][safi]) + return; + + /* First update is deferred until ORF or ROUTE-REFRESH is received */ + if (CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_WAIT_REFRESH)) + return; + + 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; + rn = bgp_route_next(rn)) + if ((table = (rn->info)) != NULL) + bgp_announce_table (peer, afi, safi, table, 0); + + if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)) + bgp_announce_table (peer, afi, safi, NULL, 1); +} + +void +bgp_announce_route_all (struct peer *peer) +{ + afi_t afi; + safi_t safi; + + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + bgp_announce_route (peer, afi, safi); +} + +static void +bgp_soft_reconfig_table_rsclient (struct peer *rsclient, afi_t afi, + safi_t safi, struct bgp_table *table, struct prefix_rd *prd) +{ + struct bgp_node *rn; + struct bgp_adj_in *ain; + + if (! table) + table = rsclient->bgp->rib[afi][safi]; + + 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, prd, tag); + } +} + +void +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) && (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) + { + 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 prefix_rd *prd) +{ + int ret; + struct bgp_node *rn; + struct bgp_adj_in *ain; + + if (! table) + table = peer->bgp->rib[afi][safi]; + + for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) + for (ain = rn->adj_in; ain; ain = ain->next) + { + if (ain->peer == peer) + { + struct bgp_info *ri = rn->info; + u_char *tag = (ri && ri->extra) ? ri->extra->tag : NULL; + + ret = bgp_update (peer, &rn->p, ain->attr, afi, safi, + ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, + prd, tag, 1); + + if (ret < 0) + { + bgp_unlock_node (rn); + return; + } + continue; + } + } +} + +void +bgp_soft_reconfig_in (struct peer *peer, afi_t afi, safi_t safi) +{ + struct bgp_node *rn; + struct bgp_table *table; + + if (peer->status != Established) + return; + + 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) + { + 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 +{ + struct bgp_node *rn; + enum bgp_clear_route_type purpose; +}; + +static wq_item_status +bgp_clear_route_node (struct work_queue *wq, void *data) +{ + struct bgp_clear_node_queue *cnq = data; + struct bgp_node *rn = cnq->rn; + struct peer *peer = wq->spec.data; + struct bgp_info *ri; + afi_t afi = bgp_node_table (rn)->afi; + safi_t safi = bgp_node_table (rn)->safi; + + assert (rn && peer); + + for (ri = rn->info; ri; ri = ri->next) + if (ri->peer == peer || cnq->purpose == BGP_CLEAR_ROUTE_MY_RSCLIENT) + { + /* graceful restart STALE flag set. */ + if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT) + && peer->nsf[afi][safi] + && ! CHECK_FLAG (ri->flags, BGP_INFO_STALE) + && ! CHECK_FLAG (ri->flags, BGP_INFO_UNUSEABLE)) + bgp_info_set_flag (rn, ri, BGP_INFO_STALE); + else + bgp_rib_remove (rn, ri, peer, afi, safi); + break; + } + return WQ_SUCCESS; +} + +static void +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 = bgp_node_table (rn); + + bgp_unlock_node (rn); + bgp_table_unlock (table); + XFREE (MTYPE_BGP_CLEAR_NODE_QUEUE, cnq); +} + +static void +bgp_clear_node_complete (struct work_queue *wq) +{ + struct peer *peer = wq->spec.data; + + /* Tickle FSM to start moving again */ + BGP_EVENT_ADD (peer, Clearing_Completed); + + peer_unlock (peer); /* bgp_clear_route */ +} + +static void +bgp_clear_node_queue_init (struct peer *peer) +{ + char wname[sizeof("clear xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx")]; + + snprintf (wname, sizeof(wname), "clear %s", peer->host); +#undef CLEAR_QUEUE_NAME_LEN + + if ( (peer->clear_node_queue = work_queue_new (bm->master, wname)) == NULL) + { + zlog_err ("%s: Failed to allocate work queue", __func__); + exit (1); + } + peer->clear_node_queue->spec.hold = 10; + peer->clear_node_queue->spec.workfunc = &bgp_clear_route_node; + peer->clear_node_queue->spec.del_item_data = &bgp_clear_node_queue_del; + peer->clear_node_queue->spec.completion_func = &bgp_clear_node_complete; + peer->clear_node_queue->spec.max_retries = 0; + + /* we only 'lock' this peer reference when the queue is actually active */ + peer->clear_node_queue->spec.data = peer; +} + +static void +bgp_clear_route_table (struct peer *peer, afi_t afi, safi_t safi, + struct bgp_table *table, struct peer *rsclient, + enum bgp_clear_route_type purpose) +{ + struct bgp_node *rn; + + + if (! table) + table = (rsclient) ? rsclient->rib[afi][safi] : peer->bgp->rib[afi][safi]; + + /* If still no table => afi/safi isn't configured at all or smth. */ + if (! table) + return; + + for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) + { + struct bgp_info *ri; + struct bgp_adj_in *ain; + struct bgp_adj_out *aout; + + /* XXX:TODO: This is suboptimal, every non-empty route_node is + * queued for every clearing peer, regardless of whether it is + * relevant to the peer at hand. + * + * Overview: There are 3 different indices which need to be + * scrubbed, potentially, when a peer is removed: + * + * 1 peer's routes visible via the RIB (ie accepted routes) + * 2 peer's routes visible by the (optional) peer's adj-in index + * 3 other routes visible by the peer's adj-out index + * + * 3 there is no hurry in scrubbing, once the struct peer is + * removed from bgp->peer, we could just GC such deleted peer's + * adj-outs at our leisure. + * + * 1 and 2 must be 'scrubbed' in some way, at least made + * invisible via RIB index before peer session is allowed to be + * brought back up. So one needs to know when such a 'search' is + * complete. + * + * Ideally: + * + * - there'd be a single global queue or a single RIB walker + * - rather than tracking which route_nodes still need to be + * examined on a peer basis, we'd track which peers still + * aren't cleared + * + * Given that our per-peer prefix-counts now should be reliable, + * this may actually be achievable. It doesn't seem to be a huge + * problem at this time, + */ + for (ain = rn->adj_in; ain; ain = ain->next) + if (ain->peer == peer || purpose == BGP_CLEAR_ROUTE_MY_RSCLIENT) + { + bgp_adj_in_remove (rn, ain); + bgp_unlock_node (rn); + break; + } + for (aout = rn->adj_out; aout; aout = aout->next) + if (aout->peer == peer || purpose == BGP_CLEAR_ROUTE_MY_RSCLIENT) + { + bgp_adj_out_remove (rn, aout, peer, afi, safi); + bgp_unlock_node (rn); + break; + } + + for (ri = rn->info; ri; ri = ri->next) + if (ri->peer == peer || purpose == BGP_CLEAR_ROUTE_MY_RSCLIENT) + { + 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; +} + +void +bgp_clear_route (struct peer *peer, afi_t afi, safi_t safi, + enum bgp_clear_route_type purpose) +{ + struct bgp_node *rn; + struct bgp_table *table; + struct peer *rsclient; + struct listnode *node, *nnode; + + if (peer->clear_node_queue == NULL) + bgp_clear_node_queue_init (peer); + + /* bgp_fsm.c keeps sessions in state Clearing, not transitioning to + * Idle until it receives a Clearing_Completed event. This protects + * against peers which flap faster than we can we clear, which could + * lead to: + * + * a) race with routes from the new session being installed before + * clear_route_node visits the node (to delete the route of that + * peer) + * b) resource exhaustion, clear_route_node likely leads to an entry + * 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) && (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; + rn = bgp_route_next (rn)) + if ((table = rn->info) != NULL) + bgp_clear_route_table (peer, afi, safi, table, NULL, purpose); + + for (ALL_LIST_ELEMENTS (peer->bgp->rsclient, node, nnode, rsclient)) + if (CHECK_FLAG(rsclient->af_flags[afi][safi], + PEER_FLAG_RSERVER_CLIENT)) + bgp_clear_route_table (peer, afi, safi, NULL, rsclient, purpose); + 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; + + default: + assert (0); + break; + } + + /* unlock if no nodes got added to the clear-node-queue. */ + if (!peer->clear_node_queue->thread) + peer_unlock (peer); + +} + +void +bgp_clear_route_all (struct peer *peer) +{ + afi_t afi; + safi_t safi; + + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + 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) +{ + struct bgp_table *table; + struct bgp_node *rn; + struct bgp_adj_in *ain; + + table = peer->bgp->rib[afi][safi]; + + for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) + for (ain = rn->adj_in; ain ; ain = ain->next) + if (ain->peer == peer) + { + bgp_adj_in_remove (rn, ain); + bgp_unlock_node (rn); + break; + } +} + +void +bgp_clear_stale_route (struct peer *peer, afi_t afi, safi_t safi) +{ + struct bgp_node *rn; + struct bgp_info *ri; + struct bgp_table *table; + + table = peer->bgp->rib[afi][safi]; + + for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) + { + for (ri = rn->info; ri; ri = ri->next) + if (ri->peer == peer) + { + if (CHECK_FLAG (ri->flags, BGP_INFO_STALE)) + bgp_rib_remove (rn, ri, peer, afi, safi); + break; + } + } +} + +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; + afi_t afi; + + for (ALL_LIST_ELEMENTS (bm->bgp, node, nnode, bgp)) + { + for (afi = AFI_IP; afi < AFI_MAX; ++afi) + { + struct bgp_node *rn; + + bgp_cleanup_table(bgp->rib[afi][SAFI_UNICAST], 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(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); + } + } + } + } +} + +void +bgp_reset (void) +{ + vty_reset (); + bgp_zclient_reset (); + access_list_reset (); + prefix_list_reset (); +} + +/* Parse NLRI stream. Withdraw NLRI is recognized by NULL attr + value. */ +int +bgp_nlri_parse_ip (struct peer *peer, struct attr *attr, + struct bgp_nlri *packet) +{ + u_char *pnt; + u_char *lim; + struct prefix p; + int psize; + int ret; + + /* Check peer status. */ + if (peer->status != Established) + return 0; + + 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. */ + memset (&p, 0, sizeof (struct prefix)); + + /* Fetch prefix length. */ + p.prefixlen = *pnt++; + /* afi/safi validity already verified by caller, bgp_update_receive */ + p.family = afi2family (packet->afi); + + /* 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) + { + 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); + + /* Check address. */ + if (packet->afi == AFI_IP && packet->safi == SAFI_UNICAST) + { + if (IN_CLASSD (ntohl (p.u.prefix4.s_addr))) + { + /* + * 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, + "%s: IPv4 unicast NLRI is multicast address %s, ignoring", + peer->host, inet_ntoa (p.u.prefix4)); + continue; + } + } + + /* Check address. */ + if (packet->afi == AFI_IP6 && packet->safi == SAFI_UNICAST) + { + if (IN6_IS_ADDR_LINKLOCAL (&p.u.prefix6)) + { + char buf[BUFSIZ]; + + 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; + } + } + + /* Normal process. */ + if (attr) + ret = bgp_update (peer, &p, attr, packet->afi, packet->safi, + ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL, NULL, 0); + else + ret = bgp_withdraw (peer, &p, attr, packet->afi, packet->safi, + ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL, NULL); + + /* Address family configuration mismatch or maximum-prefix count + overflow. */ + if (ret < 0) + return -1; + } + + /* Packet length consistency check. */ + if (pnt != lim) + { + plog_err (peer->log, + "%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) +{ + return XCALLOC (MTYPE_BGP_STATIC, sizeof (struct bgp_static)); +} + +static void +bgp_static_free (struct bgp_static *bgp_static) +{ + if (bgp_static->rmap.name) + free (bgp_static->rmap.name); + XFREE (MTYPE_BGP_STATIC, bgp_static); +} + +static void +bgp_static_withdraw_rsclient (struct bgp *bgp, struct peer *rsclient, + struct prefix *p, afi_t afi, safi_t safi) +{ + struct bgp_node *rn; + struct bgp_info *ri; + + rn = bgp_afi_node_get (rsclient->rib[afi][safi], afi, safi, p, NULL); + + /* Check selected route and self inserted route. */ + 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; + + /* Withdraw static BGP route from routing table. */ + if (ri) + { + bgp_info_delete (rn, ri); + bgp_process (bgp, rn, afi, safi); + } + + /* Unlock bgp_node_lookup. */ + bgp_unlock_node (rn); +} + +static void +bgp_static_update_rsclient (struct peer *rsclient, struct prefix *p, + 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_new; + struct attr attr; + struct attr new_attr; + struct attr_extra new_extra; + struct bgp *bgp; + int ret; + char buf[SU_ADDRSTRLEN]; + + bgp = rsclient->bgp; + + assert (bgp_static); + if (!bgp_static) + return; + + rn = bgp_afi_node_get (rsclient->rib[afi][safi], afi, safi, p, NULL); + + 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); + + if (bgp_static->atomic) + attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE); + + /* Apply network route-map for export to this rsclient. */ + if (bgp_static->rmap.name) + { + struct attr attr_tmp = attr; + info.peer = rsclient; + info.attr = &attr_tmp; + + SET_FLAG (rsclient->rmap_type, PEER_RMAP_TYPE_EXPORT); + SET_FLAG (rsclient->rmap_type, PEER_RMAP_TYPE_NETWORK); + + ret = route_map_apply (bgp_static->rmap.map, p, RMAP_BGP, &info); + + rsclient->rmap_type = 0; + + if (ret == RMAP_DENYMATCH) + { + /* Free uninterned attribute. */ + bgp_attr_flush (&attr_tmp); + + /* Unintern original. */ + aspath_unintern (&attr.aspath); + bgp_static_withdraw_rsclient (bgp, rsclient, p, afi, safi); + bgp_attr_extra_free (&attr); + + return; + } + attr_new = bgp_attr_intern (&attr_tmp); + } + 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); + + if (bgp_import_modifier (rsclient, bgp->peer_self, p, &new_attr, afi, safi) + == RMAP_DENY) + { + /* This BGP update is filtered. Log the reason then update BGP entry. */ + if (BGP_DEBUG (update, UPDATE_IN)) + zlog (rsclient->log, LOG_DEBUG, + "Static UPDATE about %s/%d -- DENIED for RS-client %s due to: import-policy", + inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), + p->prefixlen, rsclient->host); + + bgp->peer_self->rmap_type = 0; + + bgp_attr_unintern (&attr_new); + aspath_unintern (&attr.aspath); + bgp_attr_extra_free (&attr); + + bgp_static_withdraw_rsclient (bgp, rsclient, p, afi, safi); + + return; + } + + bgp->peer_self->rmap_type = 0; + + bgp_attr_unintern (&attr_new); + attr_new = bgp_attr_intern (&new_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); + bgp_attr_unintern (&ri->attr); + 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); + 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); + /* 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); + + /* 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); +} + +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_node *rn; + struct bgp_info *ri; + struct bgp_info *new; + struct bgp_info info; + struct attr attr; + struct attr *attr_new; + int ret; + + assert (bgp_static); + if (!bgp_static) + return; + + rn = bgp_afi_node_get (bgp->rib[afi][safi], afi, safi, p, NULL); + + 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); + + if (bgp_static->atomic) + attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE); + + /* Apply route-map. */ + if (bgp_static->rmap.name) + { + struct attr attr_tmp = attr; + 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 (bgp, p, afi, safi); + 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 (); + + /* 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); + 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); + /* 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); + + /* 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); +} + +void +bgp_static_update (struct bgp *bgp, struct prefix *p, + struct bgp_static *bgp_static, afi_t afi, safi_t safi) +{ + struct peer *rsclient; + struct listnode *node, *nnode; + + bgp_static_update_main (bgp, p, bgp_static, afi, safi); + + for (ALL_LIST_ELEMENTS (bgp->rsclient, node, nnode, rsclient)) + { + if (CHECK_FLAG (rsclient->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)) + bgp_static_update_rsclient (rsclient, p, bgp_static, afi, safi); + } +} + +void +bgp_static_withdraw (struct bgp *bgp, struct prefix *p, afi_t afi, + safi_t safi) +{ + struct bgp_node *rn; + struct bgp_info *ri; + + /* 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) + if (ri->peer == bgp->peer_self + && ri->type == ZEBRA_ROUTE_BGP + && ri->sub_type == BGP_ROUTE_STATIC) + break; + + /* Withdraw static BGP route from routing table. */ + 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); + } + + /* Unlock bgp_node_lookup. */ + bgp_unlock_node (rn); +} + +void +bgp_check_local_routes_rsclient (struct peer *rsclient, afi_t afi, safi_t safi) +{ + struct bgp_static *bgp_static; + struct bgp *bgp; + struct bgp_node *rn; + struct prefix *p; + + bgp = rsclient->bgp; + + for (rn = bgp_table_top (bgp->route[afi][safi]); rn; rn = bgp_route_next (rn)) + if ((bgp_static = rn->info) != NULL) + { + p = &rn->p; + + bgp_static_update_rsclient (rsclient, p, bgp_static, + afi, safi); + } +} + +/* + * Used for SAFI_MPLS_VPN and SAFI_ENCAP + */ +static void +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; + + rn = bgp_afi_node_get (bgp->rib[afi][safi], afi, safi, p, prd); + + /* Check selected route and self inserted route. */ + 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; + + /* Withdraw static BGP route from routing table. */ + if (ri) + { + bgp_aggregate_decrement (bgp, p, ri, afi, safi); + bgp_info_delete (rn, ri); + bgp_process (bgp, rn, afi, safi); + } + + /* Unlock bgp_node_lookup. */ + 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 +bgp_static_set (struct vty *vty, struct bgp *bgp, const char *ip_str, + afi_t afi, safi_t safi, const char *rmap, int backdoor) +{ + int ret; + struct prefix p; + struct bgp_static *bgp_static; + struct bgp_node *rn; + u_char need_update = 0; + + /* Convert IP prefix string to struct prefix. */ + ret = str2prefix (ip_str, &p); + if (! ret) + { + vty_out (vty, "%% Malformed prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + 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; + } + + apply_mask (&p); + + /* Set BGP static route configuration. */ + rn = bgp_node_get (bgp->route[afi][safi], &p); + + if (rn->info) + { + /* Configuration change. */ + bgp_static = rn->info; + + /* Check previous routes are installed into BGP. */ + if (bgp_static->valid && bgp_static->backdoor != backdoor) + need_update = 1; + + bgp_static->backdoor = backdoor; + + if (rmap) + { + if (bgp_static->rmap.name) + free (bgp_static->rmap.name); + bgp_static->rmap.name = strdup (rmap); + bgp_static->rmap.map = route_map_lookup_by_name (rmap); + } + else + { + if (bgp_static->rmap.name) + free (bgp_static->rmap.name); + bgp_static->rmap.name = NULL; + bgp_static->rmap.map = NULL; + bgp_static->valid = 0; + } + bgp_unlock_node (rn); + } + else + { + /* New configuration. */ + bgp_static = bgp_static_new (); + bgp_static->backdoor = backdoor; + bgp_static->valid = 0; + bgp_static->igpmetric = 0; + bgp_static->igpnexthop.s_addr = 0; + + if (rmap) + { + if (bgp_static->rmap.name) + free (bgp_static->rmap.name); + bgp_static->rmap.name = strdup (rmap); + bgp_static->rmap.map = route_map_lookup_by_name (rmap); + } + rn->info = bgp_static; + } + + 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); + + return CMD_SUCCESS; +} + +/* Configure static BGP network. */ +static int +bgp_static_unset (struct vty *vty, struct bgp *bgp, const char *ip_str, + afi_t afi, safi_t safi) +{ + int ret; + struct prefix p; + struct bgp_static *bgp_static; + struct bgp_node *rn; + + /* Convert IP prefix string to struct prefix. */ + ret = str2prefix (ip_str, &p); + if (! ret) + { + vty_out (vty, "%% Malformed prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + 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; + } + + apply_mask (&p); + + rn = bgp_node_lookup (bgp->route[afi][safi], &p); + if (! rn) + { + vty_out (vty, "%% Can't find specified static route configuration.%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + bgp_static = rn->info; + + /* Update BGP RIB. */ + if (! bgp_static->backdoor) + bgp_static_withdraw (bgp, &p, afi, safi); + + /* Clear configuration. */ + bgp_static_free (bgp_static); + rn->info = NULL; + bgp_unlock_node (rn); + bgp_unlock_node (rn); + + return CMD_SUCCESS; +} + +/* Called from bgp_delete(). Delete all static routes from the BGP + instance. */ +void +bgp_static_delete (struct bgp *bgp) +{ + afi_t afi; + safi_t safi; + struct bgp_node *rn; + struct bgp_node *rm; + struct bgp_table *table; + struct bgp_static *bgp_static; + + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + for (rn = bgp_table_top (bgp->route[afi][safi]); rn; rn = bgp_route_next (rn)) + if (rn->info != NULL) + { + 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_safi (bgp, &rm->p, + AFI_IP, safi, + (struct prefix_rd *)&rn->p, + bgp_static->tag); + bgp_static_free (bgp_static); + rn->info = NULL; + bgp_unlock_node (rn); + } + } + else + { + bgp_static = rn->info; + bgp_static_withdraw (bgp, &rn->p, afi, safi); + bgp_static_free (bgp_static); + rn->info = NULL; + bgp_unlock_node (rn); + } + } +} + +/* + * 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_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; + struct prefix_rd prd; + struct bgp *bgp; + struct bgp_node *prn; + struct bgp_node *rn; + struct bgp_table *table; + struct bgp_static *bgp_static; + u_char tag[3]; + + bgp = vty->index; + + ret = str2prefix (ip_str, &p); + if (! ret) + { + vty_out (vty, "%% Malformed prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + apply_mask (&p); + + ret = str2prefix_rd (rd_str, &prd); + if (! ret) + { + vty_out (vty, "%% Malformed rd%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ret = str2tag (tag_str, tag); + if (! ret) + { + vty_out (vty, "%% Malformed tag%s", VTY_NEWLINE); + return CMD_WARNING; + } + + prn = bgp_node_get (bgp->route[AFI_IP][safi], + (struct prefix *)&prd); + if (prn->info == NULL) + prn->info = bgp_table_init (AFI_IP, safi); + else + bgp_unlock_node (prn); + table = prn->info; + + rn = bgp_node_get (table, &p); + + if (rn->info) + { + vty_out (vty, "%% Same network configuration exists%s", VTY_NEWLINE); + bgp_unlock_node (rn); + } + else + { + /* New configuration. */ + bgp_static = bgp_static_new (); + 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->valid = 1; + bgp_static_update_safi (bgp, &p, bgp_static, AFI_IP, safi); + } + + return CMD_SUCCESS; +} + +/* Configure static BGP network. */ +int +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; + struct prefix p; + struct prefix_rd prd; + struct bgp_node *prn; + struct bgp_node *rn; + struct bgp_table *table; + struct bgp_static *bgp_static; + u_char tag[3]; + + bgp = vty->index; + + /* Convert IP prefix string to struct prefix. */ + ret = str2prefix (ip_str, &p); + if (! ret) + { + vty_out (vty, "%% Malformed prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + apply_mask (&p); + + ret = str2prefix_rd (rd_str, &prd); + if (! ret) + { + vty_out (vty, "%% Malformed rd%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ret = str2tag (tag_str, tag); + if (! ret) + { + vty_out (vty, "%% Malformed tag%s", VTY_NEWLINE); + return CMD_WARNING; + } + + prn = bgp_node_get (bgp->route[AFI_IP][safi], + (struct prefix *)&prd); + if (prn->info == NULL) + prn->info = bgp_table_init (AFI_IP, safi); + else + bgp_unlock_node (prn); + table = prn->info; + + rn = bgp_node_lookup (table, &p); + + if (rn) + { + bgp_static_withdraw_safi (bgp, &p, AFI_IP, safi, &prd, tag); + + bgp_static = rn->info; + bgp_static_free (bgp_static); + rn->info = NULL; + bgp_unlock_node (rn); + bgp_unlock_node (rn); + } + else + vty_out (vty, "%% Can't find the route%s", VTY_NEWLINE); + + return CMD_SUCCESS; +} + +DEFUN (bgp_network, + bgp_network_cmd, + "network A.B.C.D/M", + "Specify a network to announce via BGP\n" + "IP prefix /, e.g., 35.0.0.0/8\n") +{ + return bgp_static_set (vty, vty->index, argv[0], + AFI_IP, bgp_node_safi (vty), NULL, 0); +} + +DEFUN (bgp_network_route_map, + bgp_network_route_map_cmd, + "network A.B.C.D/M route-map WORD", + "Specify a network to announce via BGP\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Route-map to modify the attributes\n" + "Name of the route map\n") +{ + return bgp_static_set (vty, vty->index, argv[0], + AFI_IP, bgp_node_safi (vty), argv[1], 0); +} + +DEFUN (bgp_network_backdoor, + bgp_network_backdoor_cmd, + "network A.B.C.D/M backdoor", + "Specify a network to announce via BGP\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Specify a BGP backdoor route\n") +{ + return bgp_static_set (vty, vty->index, argv[0], AFI_IP, SAFI_UNICAST, + NULL, 1); +} + +DEFUN (bgp_network_mask, + bgp_network_mask_cmd, + "network A.B.C.D mask A.B.C.D", + "Specify a network to announce via BGP\n" + "Network number\n" + "Network mask\n" + "Network mask\n") +{ + int ret; + char prefix_str[BUFSIZ]; + + ret = netmask_str2prefix_str (argv[0], argv[1], prefix_str); + if (! ret) + { + vty_out (vty, "%% Inconsistent address and mask%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_static_set (vty, vty->index, prefix_str, + AFI_IP, bgp_node_safi (vty), NULL, 0); +} + +DEFUN (bgp_network_mask_route_map, + bgp_network_mask_route_map_cmd, + "network A.B.C.D mask A.B.C.D route-map WORD", + "Specify a network to announce via BGP\n" + "Network number\n" + "Network mask\n" + "Network mask\n" + "Route-map to modify the attributes\n" + "Name of the route map\n") +{ + int ret; + char prefix_str[BUFSIZ]; + + ret = netmask_str2prefix_str (argv[0], argv[1], prefix_str); + if (! ret) + { + vty_out (vty, "%% Inconsistent address and mask%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_static_set (vty, vty->index, prefix_str, + AFI_IP, bgp_node_safi (vty), argv[2], 0); +} + +DEFUN (bgp_network_mask_backdoor, + bgp_network_mask_backdoor_cmd, + "network A.B.C.D mask A.B.C.D backdoor", + "Specify a network to announce via BGP\n" + "Network number\n" + "Network mask\n" + "Network mask\n" + "Specify a BGP backdoor route\n") +{ + int ret; + char prefix_str[BUFSIZ]; + + ret = netmask_str2prefix_str (argv[0], argv[1], prefix_str); + if (! ret) + { + vty_out (vty, "%% Inconsistent address and mask%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_static_set (vty, vty->index, prefix_str, AFI_IP, SAFI_UNICAST, + NULL, 1); +} + +DEFUN (bgp_network_mask_natural, + bgp_network_mask_natural_cmd, + "network A.B.C.D", + "Specify a network to announce via BGP\n" + "Network number\n") +{ + int ret; + char prefix_str[BUFSIZ]; + + ret = netmask_str2prefix_str (argv[0], NULL, prefix_str); + if (! ret) + { + vty_out (vty, "%% Inconsistent address and mask%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_static_set (vty, vty->index, prefix_str, + AFI_IP, bgp_node_safi (vty), NULL, 0); +} + +DEFUN (bgp_network_mask_natural_route_map, + bgp_network_mask_natural_route_map_cmd, + "network A.B.C.D route-map WORD", + "Specify a network to announce via BGP\n" + "Network number\n" + "Route-map to modify the attributes\n" + "Name of the route map\n") +{ + int ret; + char prefix_str[BUFSIZ]; + + ret = netmask_str2prefix_str (argv[0], NULL, prefix_str); + if (! ret) + { + vty_out (vty, "%% Inconsistent address and mask%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_static_set (vty, vty->index, prefix_str, + AFI_IP, bgp_node_safi (vty), argv[1], 0); +} + +DEFUN (bgp_network_mask_natural_backdoor, + bgp_network_mask_natural_backdoor_cmd, + "network A.B.C.D backdoor", + "Specify a network to announce via BGP\n" + "Network number\n" + "Specify a BGP backdoor route\n") +{ + int ret; + char prefix_str[BUFSIZ]; + + ret = netmask_str2prefix_str (argv[0], NULL, prefix_str); + if (! ret) + { + vty_out (vty, "%% Inconsistent address and mask%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_static_set (vty, vty->index, prefix_str, AFI_IP, SAFI_UNICAST, + NULL, 1); +} + +DEFUN (no_bgp_network, + no_bgp_network_cmd, + "no network A.B.C.D/M", + NO_STR + "Specify a network to announce via BGP\n" + "IP prefix /, e.g., 35.0.0.0/8\n") +{ + return bgp_static_unset (vty, vty->index, argv[0], AFI_IP, + bgp_node_safi (vty)); +} + +ALIAS (no_bgp_network, + no_bgp_network_route_map_cmd, + "no network A.B.C.D/M route-map WORD", + NO_STR + "Specify a network to announce via BGP\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Route-map to modify the attributes\n" + "Name of the route map\n") + +ALIAS (no_bgp_network, + no_bgp_network_backdoor_cmd, + "no network A.B.C.D/M backdoor", + NO_STR + "Specify a network to announce via BGP\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Specify a BGP backdoor route\n") + +DEFUN (no_bgp_network_mask, + no_bgp_network_mask_cmd, + "no network A.B.C.D mask A.B.C.D", + NO_STR + "Specify a network to announce via BGP\n" + "Network number\n" + "Network mask\n" + "Network mask\n") +{ + int ret; + char prefix_str[BUFSIZ]; + + ret = netmask_str2prefix_str (argv[0], argv[1], prefix_str); + if (! ret) + { + vty_out (vty, "%% Inconsistent address and mask%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_static_unset (vty, vty->index, prefix_str, AFI_IP, + bgp_node_safi (vty)); +} + +ALIAS (no_bgp_network_mask, + no_bgp_network_mask_route_map_cmd, + "no network A.B.C.D mask A.B.C.D route-map WORD", + NO_STR + "Specify a network to announce via BGP\n" + "Network number\n" + "Network mask\n" + "Network mask\n" + "Route-map to modify the attributes\n" + "Name of the route map\n") + +ALIAS (no_bgp_network_mask, + no_bgp_network_mask_backdoor_cmd, + "no network A.B.C.D mask A.B.C.D backdoor", + NO_STR + "Specify a network to announce via BGP\n" + "Network number\n" + "Network mask\n" + "Network mask\n" + "Specify a BGP backdoor route\n") + +DEFUN (no_bgp_network_mask_natural, + no_bgp_network_mask_natural_cmd, + "no network A.B.C.D", + NO_STR + "Specify a network to announce via BGP\n" + "Network number\n") +{ + int ret; + char prefix_str[BUFSIZ]; + + ret = netmask_str2prefix_str (argv[0], NULL, prefix_str); + if (! ret) + { + vty_out (vty, "%% Inconsistent address and mask%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_static_unset (vty, vty->index, prefix_str, AFI_IP, + bgp_node_safi (vty)); +} + +ALIAS (no_bgp_network_mask_natural, + no_bgp_network_mask_natural_route_map_cmd, + "no network A.B.C.D route-map WORD", + NO_STR + "Specify a network to announce via BGP\n" + "Network number\n" + "Route-map to modify the attributes\n" + "Name of the route map\n") + +ALIAS (no_bgp_network_mask_natural, + no_bgp_network_mask_natural_backdoor_cmd, + "no network A.B.C.D backdoor", + NO_STR + "Specify a network to announce via BGP\n" + "Network number\n" + "Specify a BGP backdoor route\n") + +DEFUN (ipv6_bgp_network, + ipv6_bgp_network_cmd, + "network X:X::X:X/M", + "Specify a network to announce via BGP\n" + "IPv6 prefix /\n") +{ + return bgp_static_set (vty, vty->index, argv[0], AFI_IP6, bgp_node_safi(vty), + NULL, 0); +} + +DEFUN (ipv6_bgp_network_route_map, + ipv6_bgp_network_route_map_cmd, + "network X:X::X:X/M route-map WORD", + "Specify a network to announce via BGP\n" + "IPv6 prefix /\n" + "Route-map to modify the attributes\n" + "Name of the route map\n") +{ + return bgp_static_set (vty, vty->index, argv[0], AFI_IP6, + bgp_node_safi (vty), argv[1], 0); +} + +DEFUN (no_ipv6_bgp_network, + no_ipv6_bgp_network_cmd, + "no network X:X::X:X/M", + NO_STR + "Specify a network to announce via BGP\n" + "IPv6 prefix /\n") +{ + return bgp_static_unset (vty, vty->index, argv[0], AFI_IP6, bgp_node_safi(vty)); +} + +ALIAS (no_ipv6_bgp_network, + no_ipv6_bgp_network_route_map_cmd, + "no network X:X::X:X/M route-map WORD", + NO_STR + "Specify a network to announce via BGP\n" + "IPv6 prefix /\n" + "Route-map to modify the attributes\n" + "Name of the route map\n") + +ALIAS (ipv6_bgp_network, + old_ipv6_bgp_network_cmd, + "ipv6 bgp network X:X::X:X/M", + IPV6_STR + BGP_STR + "Specify a network to announce via BGP\n" + "IPv6 prefix /, e.g., 3ffe::/16\n") + +ALIAS (no_ipv6_bgp_network, + old_no_ipv6_bgp_network_cmd, + "no ipv6 bgp network X:X::X:X/M", + NO_STR + IPV6_STR + BGP_STR + "Specify a network to announce via BGP\n" + "IPv6 prefix /, e.g., 3ffe::/16\n") + +/* stubs for removed AS-Pathlimit commands, kept for config compatibility */ +ALIAS_DEPRECATED (bgp_network, + bgp_network_ttl_cmd, + "network A.B.C.D/M pathlimit <0-255>", + "Specify a network to announce via BGP\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "AS-Path hopcount limit attribute\n" + "AS-Pathlimit TTL, in number of AS-Path hops\n") +ALIAS_DEPRECATED (bgp_network_backdoor, + bgp_network_backdoor_ttl_cmd, + "network A.B.C.D/M backdoor pathlimit <0-255>", + "Specify a network to announce via BGP\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Specify a BGP backdoor route\n" + "AS-Path hopcount limit attribute\n" + "AS-Pathlimit TTL, in number of AS-Path hops\n") +ALIAS_DEPRECATED (bgp_network_mask, + bgp_network_mask_ttl_cmd, + "network A.B.C.D mask A.B.C.D pathlimit <0-255>", + "Specify a network to announce via BGP\n" + "Network number\n" + "Network mask\n" + "Network mask\n" + "AS-Path hopcount limit attribute\n" + "AS-Pathlimit TTL, in number of AS-Path hops\n") +ALIAS_DEPRECATED (bgp_network_mask_backdoor, + bgp_network_mask_backdoor_ttl_cmd, + "network A.B.C.D mask A.B.C.D backdoor pathlimit <0-255>", + "Specify a network to announce via BGP\n" + "Network number\n" + "Network mask\n" + "Network mask\n" + "Specify a BGP backdoor route\n" + "AS-Path hopcount limit attribute\n" + "AS-Pathlimit TTL, in number of AS-Path hops\n") +ALIAS_DEPRECATED (bgp_network_mask_natural, + bgp_network_mask_natural_ttl_cmd, + "network A.B.C.D pathlimit <0-255>", + "Specify a network to announce via BGP\n" + "Network number\n" + "AS-Path hopcount limit attribute\n" + "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>", + "Specify a network to announce via BGP\n" + "Network number\n" + "Specify a BGP backdoor route\n" + "AS-Path hopcount limit attribute\n" + "AS-Pathlimit TTL, in number of AS-Path hops\n") +ALIAS_DEPRECATED (no_bgp_network, + no_bgp_network_ttl_cmd, + "no network A.B.C.D/M pathlimit <0-255>", + NO_STR + "Specify a network to announce via BGP\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "AS-Path hopcount limit attribute\n" + "AS-Pathlimit TTL, in number of AS-Path hops\n") +ALIAS_DEPRECATED (no_bgp_network, + no_bgp_network_backdoor_ttl_cmd, + "no network A.B.C.D/M backdoor pathlimit <0-255>", + NO_STR + "Specify a network to announce via BGP\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Specify a BGP backdoor route\n" + "AS-Path hopcount limit attribute\n" + "AS-Pathlimit TTL, in number of AS-Path hops\n") +ALIAS_DEPRECATED (no_bgp_network, + no_bgp_network_mask_ttl_cmd, + "no network A.B.C.D mask A.B.C.D pathlimit <0-255>", + NO_STR + "Specify a network to announce via BGP\n" + "Network number\n" + "Network mask\n" + "Network mask\n" + "AS-Path hopcount limit attribute\n" + "AS-Pathlimit TTL, in number of AS-Path hops\n") +ALIAS_DEPRECATED (no_bgp_network_mask, + no_bgp_network_mask_backdoor_ttl_cmd, + "no network A.B.C.D mask A.B.C.D backdoor pathlimit <0-255>", + NO_STR + "Specify a network to announce via BGP\n" + "Network number\n" + "Network mask\n" + "Network mask\n" + "Specify a BGP backdoor route\n" + "AS-Path hopcount limit attribute\n" + "AS-Pathlimit TTL, in number of AS-Path hops\n") +ALIAS_DEPRECATED (no_bgp_network_mask_natural, + no_bgp_network_mask_natural_ttl_cmd, + "no network A.B.C.D pathlimit <0-255>", + NO_STR + "Specify a network to announce via BGP\n" + "Network number\n" + "AS-Path hopcount limit attribute\n" + "AS-Pathlimit TTL, in number of AS-Path hops\n") +ALIAS_DEPRECATED (no_bgp_network_mask_natural, + no_bgp_network_mask_natural_backdoor_ttl_cmd, + "no network A.B.C.D backdoor pathlimit <0-255>", + NO_STR + "Specify a network to announce via BGP\n" + "Network number\n" + "Specify a BGP backdoor route\n" + "AS-Path hopcount limit attribute\n" + "AS-Pathlimit TTL, in number of AS-Path hops\n") +ALIAS_DEPRECATED (ipv6_bgp_network, + ipv6_bgp_network_ttl_cmd, + "network X:X::X:X/M pathlimit <0-255>", + "Specify a network to announce via BGP\n" + "IPv6 prefix /\n" + "AS-Path hopcount limit attribute\n" + "AS-Pathlimit TTL, in number of AS-Path hops\n") +ALIAS_DEPRECATED (no_ipv6_bgp_network, + no_ipv6_bgp_network_ttl_cmd, + "no network X:X::X:X/M pathlimit <0-255>", + NO_STR + "Specify a network to announce via BGP\n" + "IPv6 prefix /\n" + "AS-Path hopcount limit attribute\n" + "AS-Pathlimit TTL, in number of AS-Path hops\n") + +/* Aggreagete address: + + advertise-map Set condition to advertise attribute + as-set Generate AS set path information + attribute-map Set attributes of aggregate + route-map Set parameters of aggregate + summary-only Filter more specific routes from updates + suppress-map Conditionally filter more specific routes from updates + + */ +struct bgp_aggregate +{ + /* Summary-only flag. */ + u_char summary_only; + + /* AS set generation. */ + u_char as_set; + + /* Route-map for aggregated route. */ + struct route_map *map; + + /* Suppress-count. */ + unsigned long count; + + /* SAFI configuration. */ + safi_t safi; +}; + +static struct bgp_aggregate * +bgp_aggregate_new (void) +{ + return XCALLOC (MTYPE_BGP_AGGREGATE, sizeof (struct bgp_aggregate)); +} + +static void +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, + struct bgp_aggregate *aggregate) +{ + struct bgp_table *table; + struct bgp_node *top; + struct bgp_node *rn; + u_char origin; + struct aspath *aspath = NULL; + struct aspath *asmerge = NULL; + struct community *community = NULL; + struct community *commerge = NULL; + struct bgp_info *ri; + struct bgp_info *new; + int first = 1; + unsigned long match = 0; + 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 + 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. In all + other case the value of the ORIGIN attribute of the aggregated + route is INTERNAL. */ + origin = BGP_ORIGIN_IGP; + + table = bgp->rib[afi][safi]; + + top = bgp_node_get (table, p); + for (rn = bgp_node_get (table, p); rn; rn = bgp_route_next_until (rn, top)) + if (rn->p.prefixlen > p->prefixlen) + { + match = 0; + + for (ri = rn->info; ri; ri = ri->next) + { + if (BGP_INFO_HOLDDOWN (ri)) + continue; + + if (del && ri == del) + continue; + + if (! rinew && first) + first = 0; + +#ifdef AGGREGATE_NEXTHOP_CHECK + if (! IPV4_ADDR_SAME (&ri->attr->nexthop, &nexthop) + || ri->attr->med != med) + { + if (aspath) + aspath_free (aspath); + if (community) + community_free (community); + bgp_unlock_node (rn); + bgp_unlock_node (top); + return; + } +#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) + { + (bgp_info_extra_get (ri))->suppress++; + bgp_info_set_flag (rn, ri, BGP_INFO_ATTR_CHANGED); + match++; + } + + aggregate->count++; + + if (origin < ri->attr->origin) + origin = ri->attr->origin; + + if (aggregate->as_set) + { + if (aspath) + { + asmerge = aspath_aggregate (aspath, ri->attr->aspath); + aspath_free (aspath); + aspath = asmerge; + } + else + aspath = aspath_dup (ri->attr->aspath); + + if (ri->attr->community) + { + if (community) + { + commerge = community_merge (community, + ri->attr->community); + community = community_uniq_sort (commerge); + community_free (commerge); + } + else + community = community_dup (ri->attr->community); + } + } + } + } + if (match) + bgp_process (bgp, rn, afi, safi); + } + bgp_unlock_node (top); + + if (rinew) + { + aggregate->count++; + + if (aggregate->summary_only) + (bgp_info_extra_get (rinew))->suppress++; + + if (origin < rinew->attr->origin) + origin = rinew->attr->origin; + + if (aggregate->as_set) + { + if (aspath) + { + asmerge = aspath_aggregate (aspath, rinew->attr->aspath); + aspath_free (aspath); + aspath = asmerge; + } + else + aspath = aspath_dup (rinew->attr->aspath); + + if (rinew->attr->community) + { + if (community) + { + commerge = community_merge (community, + rinew->attr->community); + community = community_uniq_sort (commerge); + community_free (commerge); + } + else + community = community_dup (rinew->attr->community); + } + } + } + + if (aggregate->count > 0) + { + rn = bgp_node_get (table, p); + 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); + + bgp_info_add (rn, new); + bgp_unlock_node (rn); + bgp_process (bgp, rn, afi, safi); + } + else + { + if (aspath) + aspath_free (aspath); + if (community) + community_free (community); + } +} + +void bgp_aggregate_delete (struct bgp *, struct prefix *, afi_t, safi_t, + struct bgp_aggregate *); + +void +bgp_aggregate_increment (struct bgp *bgp, struct prefix *p, + struct bgp_info *ri, afi_t afi, safi_t safi) +{ + 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) || (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; + + if (BGP_INFO_HOLDDOWN (ri)) + return; + + child = bgp_node_get (table, p); + + /* Aggregate address configuration check. */ + 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); + bgp_aggregate_route (bgp, &rn->p, ri, afi, safi, NULL, aggregate); + } + bgp_unlock_node (child); +} + +void +bgp_aggregate_decrement (struct bgp *bgp, struct prefix *p, + struct bgp_info *del, afi_t afi, safi_t safi) +{ + 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) || (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 (table, p); + + /* Aggregate address configuration check. */ + 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); + bgp_aggregate_route (bgp, &rn->p, NULL, afi, safi, del, aggregate); + } + 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) +{ + struct bgp_table *table; + struct bgp_node *top; + struct bgp_node *rn; + struct bgp_info *new; + struct bgp_info *ri; + unsigned long match; + u_char origin = BGP_ORIGIN_IGP; + struct aspath *aspath = NULL; + struct aspath *asmerge = NULL; + struct community *community = NULL; + struct community *commerge = NULL; + u_char atomic_aggregate = 0; + + table = bgp->rib[afi][safi]; + + /* Sanity check. */ + if (afi == AFI_IP && p->prefixlen == IPV4_MAX_BITLEN) + return; + if (afi == AFI_IP6 && p->prefixlen == IPV6_MAX_BITLEN) + return; + + /* If routes exists below this node, generate aggregate routes. */ + top = bgp_node_get (table, p); + for (rn = bgp_node_get (table, p); rn; rn = bgp_route_next_until (rn, top)) + if (rn->p.prefixlen > p->prefixlen) + { + match = 0; + + for (ri = rn->info; ri; ri = ri->next) + { + 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 + route announcement. */ + if (aggregate->summary_only) + { + (bgp_info_extra_get (ri))->suppress++; + 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 (aspath) + { + asmerge = aspath_aggregate (aspath, ri->attr->aspath); + aspath_free (aspath); + aspath = asmerge; + } + else + aspath = aspath_dup (ri->attr->aspath); + + if (ri->attr->community) + { + if (community) + { + commerge = community_merge (community, + ri->attr->community); + community = community_uniq_sort (commerge); + community_free (commerge); + } + else + community = community_dup (ri->attr->community); + } + } + aggregate->count++; + } + } + + /* If this node is suppressed, process the change. */ + if (match) + bgp_process (bgp, rn, afi, safi); + } + bgp_unlock_node (top); + + /* Add aggregate route to BGP table. */ + if (aggregate->count) + { + rn = bgp_node_get (table, p); + 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); + + bgp_info_add (rn, new); + bgp_unlock_node (rn); + + /* Process change. */ + bgp_process (bgp, rn, afi, safi); + } + else + { + if (aspath) + aspath_free (aspath); + if (community) + community_free (community); + } +} + +void +bgp_aggregate_delete (struct bgp *bgp, struct prefix *p, afi_t afi, + safi_t safi, struct bgp_aggregate *aggregate) +{ + struct bgp_table *table; + struct bgp_node *top; + struct bgp_node *rn; + struct bgp_info *ri; + unsigned long match; + + table = bgp->rib[afi][safi]; + + if (afi == AFI_IP && p->prefixlen == IPV4_MAX_BITLEN) + return; + if (afi == AFI_IP6 && p->prefixlen == IPV6_MAX_BITLEN) + return; + + /* If routes exists below this node, generate aggregate routes. */ + top = bgp_node_get (table, p); + for (rn = bgp_node_get (table, p); rn; rn = bgp_route_next_until (rn, top)) + if (rn->p.prefixlen > p->prefixlen) + { + match = 0; + + for (ri = rn->info; ri; ri = ri->next) + { + if (BGP_INFO_HOLDDOWN (ri)) + continue; + + if (ri->sub_type != BGP_ROUTE_AGGREGATE) + { + if (aggregate->summary_only && ri->extra) + { + ri->extra->suppress--; + + if (ri->extra->suppress == 0) + { + bgp_info_set_flag (rn, ri, BGP_INFO_ATTR_CHANGED); + match++; + } + } + aggregate->count--; + } + } + + /* If this node was suppressed, process the change. */ + if (match) + bgp_process (bgp, rn, afi, safi); + } + bgp_unlock_node (top); + + /* Delete aggregate route from BGP table. */ + rn = bgp_node_get (table, p); + + for (ri = rn->info; ri; ri = ri->next) + if (ri->peer == bgp->peer_self + && ri->type == ZEBRA_ROUTE_BGP + && ri->sub_type == BGP_ROUTE_AGGREGATE) + break; + + /* Withdraw static BGP route from routing table. */ + if (ri) + { + bgp_info_delete (rn, ri); + bgp_process (bgp, rn, afi, safi); + } + + /* Unlock bgp_node_lookup. */ + bgp_unlock_node (rn); +} + +/* Aggregate route attribute. */ +#define AGGREGATE_SUMMARY_ONLY 1 +#define AGGREGATE_AS_SET 1 + +static int +bgp_aggregate_unset (struct vty *vty, const char *prefix_str, + afi_t afi, safi_t safi) +{ + int ret; + struct prefix p; + struct bgp_node *rn; + struct bgp *bgp; + struct bgp_aggregate *aggregate; + + /* Convert string to prefix structure. */ + ret = str2prefix (prefix_str, &p); + if (!ret) + { + vty_out (vty, "Malformed prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + apply_mask (&p); + + /* Get BGP structure. */ + bgp = vty->index; + + /* Old configuration check. */ + rn = bgp_node_lookup (bgp->aggregate[afi][safi], &p); + if (! rn) + { + vty_out (vty, "%% There is no aggregate-address configuration.%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + aggregate = rn->info; + if (aggregate->safi & SAFI_UNICAST) + bgp_aggregate_delete (bgp, &p, afi, SAFI_UNICAST, aggregate); + if (aggregate->safi & SAFI_MULTICAST) + bgp_aggregate_delete (bgp, &p, afi, SAFI_MULTICAST, aggregate); + + /* Unlock aggregate address configuration. */ + rn->info = NULL; + bgp_aggregate_free (aggregate); + bgp_unlock_node (rn); + bgp_unlock_node (rn); + + return CMD_SUCCESS; +} + +static int +bgp_aggregate_set (struct vty *vty, const char *prefix_str, + afi_t afi, safi_t safi, + u_char summary_only, u_char as_set) +{ + int ret; + struct prefix p; + struct bgp_node *rn; + struct bgp *bgp; + struct bgp_aggregate *aggregate; + + /* Convert string to prefix structure. */ + ret = str2prefix (prefix_str, &p); + if (!ret) + { + vty_out (vty, "Malformed prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + apply_mask (&p); + + /* Get BGP structure. */ + bgp = vty->index; + + /* Old configuration check. */ + rn = bgp_node_get (bgp->aggregate[afi][safi], &p); + + if (rn->info) + { + vty_out (vty, "There is already same aggregate network.%s", VTY_NEWLINE); + /* try to remove the old entry */ + ret = bgp_aggregate_unset (vty, prefix_str, afi, safi); + if (ret) + { + vty_out (vty, "Error deleting aggregate.%s", VTY_NEWLINE); + bgp_unlock_node (rn); + return CMD_WARNING; + } + } + + /* Make aggregate address structure. */ + aggregate = bgp_aggregate_new (); + aggregate->summary_only = summary_only; + aggregate->as_set = as_set; + aggregate->safi = safi; + rn->info = aggregate; + + /* Aggregate address insert into BGP routing table. */ + if (safi & SAFI_UNICAST) + bgp_aggregate_add (bgp, &p, afi, SAFI_UNICAST, aggregate); + if (safi & SAFI_MULTICAST) + bgp_aggregate_add (bgp, &p, afi, SAFI_MULTICAST, aggregate); + + return CMD_SUCCESS; +} + +DEFUN (aggregate_address, + aggregate_address_cmd, + "aggregate-address A.B.C.D/M", + "Configure BGP aggregate entries\n" + "Aggregate prefix\n") +{ + return bgp_aggregate_set (vty, argv[0], AFI_IP, bgp_node_safi (vty), 0, 0); +} + +DEFUN (aggregate_address_mask, + aggregate_address_mask_cmd, + "aggregate-address A.B.C.D A.B.C.D", + "Configure BGP aggregate entries\n" + "Aggregate address\n" + "Aggregate mask\n") +{ + int ret; + char prefix_str[BUFSIZ]; + + ret = netmask_str2prefix_str (argv[0], argv[1], prefix_str); + + if (! ret) + { + vty_out (vty, "%% Inconsistent address and mask%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_aggregate_set (vty, prefix_str, AFI_IP, bgp_node_safi (vty), + 0, 0); +} + +DEFUN (aggregate_address_summary_only, + aggregate_address_summary_only_cmd, + "aggregate-address A.B.C.D/M summary-only", + "Configure BGP aggregate entries\n" + "Aggregate prefix\n" + "Filter more specific routes from updates\n") +{ + return bgp_aggregate_set (vty, argv[0], AFI_IP, bgp_node_safi (vty), + AGGREGATE_SUMMARY_ONLY, 0); +} + +DEFUN (aggregate_address_mask_summary_only, + aggregate_address_mask_summary_only_cmd, + "aggregate-address A.B.C.D A.B.C.D summary-only", + "Configure BGP aggregate entries\n" + "Aggregate address\n" + "Aggregate mask\n" + "Filter more specific routes from updates\n") +{ + int ret; + char prefix_str[BUFSIZ]; + + ret = netmask_str2prefix_str (argv[0], argv[1], prefix_str); + + if (! ret) + { + vty_out (vty, "%% Inconsistent address and mask%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_aggregate_set (vty, prefix_str, AFI_IP, bgp_node_safi (vty), + AGGREGATE_SUMMARY_ONLY, 0); +} + +DEFUN (aggregate_address_as_set, + aggregate_address_as_set_cmd, + "aggregate-address A.B.C.D/M as-set", + "Configure BGP aggregate entries\n" + "Aggregate prefix\n" + "Generate AS set path information\n") +{ + return bgp_aggregate_set (vty, argv[0], AFI_IP, bgp_node_safi (vty), + 0, AGGREGATE_AS_SET); +} + +DEFUN (aggregate_address_mask_as_set, + aggregate_address_mask_as_set_cmd, + "aggregate-address A.B.C.D A.B.C.D as-set", + "Configure BGP aggregate entries\n" + "Aggregate address\n" + "Aggregate mask\n" + "Generate AS set path information\n") +{ + int ret; + char prefix_str[BUFSIZ]; + + ret = netmask_str2prefix_str (argv[0], argv[1], prefix_str); + + if (! ret) + { + vty_out (vty, "%% Inconsistent address and mask%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_aggregate_set (vty, prefix_str, AFI_IP, bgp_node_safi (vty), + 0, AGGREGATE_AS_SET); +} + + +DEFUN (aggregate_address_as_set_summary, + aggregate_address_as_set_summary_cmd, + "aggregate-address A.B.C.D/M as-set summary-only", + "Configure BGP aggregate entries\n" + "Aggregate prefix\n" + "Generate AS set path information\n" + "Filter more specific routes from updates\n") +{ + return bgp_aggregate_set (vty, argv[0], AFI_IP, bgp_node_safi (vty), + AGGREGATE_SUMMARY_ONLY, AGGREGATE_AS_SET); +} + +ALIAS (aggregate_address_as_set_summary, + aggregate_address_summary_as_set_cmd, + "aggregate-address A.B.C.D/M summary-only as-set", + "Configure BGP aggregate entries\n" + "Aggregate prefix\n" + "Filter more specific routes from updates\n" + "Generate AS set path information\n") + +DEFUN (aggregate_address_mask_as_set_summary, + aggregate_address_mask_as_set_summary_cmd, + "aggregate-address A.B.C.D A.B.C.D as-set summary-only", + "Configure BGP aggregate entries\n" + "Aggregate address\n" + "Aggregate mask\n" + "Generate AS set path information\n" + "Filter more specific routes from updates\n") +{ + int ret; + char prefix_str[BUFSIZ]; + + ret = netmask_str2prefix_str (argv[0], argv[1], prefix_str); + + if (! ret) + { + vty_out (vty, "%% Inconsistent address and mask%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_aggregate_set (vty, prefix_str, AFI_IP, bgp_node_safi (vty), + AGGREGATE_SUMMARY_ONLY, AGGREGATE_AS_SET); +} + +ALIAS (aggregate_address_mask_as_set_summary, + aggregate_address_mask_summary_as_set_cmd, + "aggregate-address A.B.C.D A.B.C.D summary-only as-set", + "Configure BGP aggregate entries\n" + "Aggregate address\n" + "Aggregate mask\n" + "Filter more specific routes from updates\n" + "Generate AS set path information\n") + +DEFUN (no_aggregate_address, + no_aggregate_address_cmd, + "no aggregate-address A.B.C.D/M", + NO_STR + "Configure BGP aggregate entries\n" + "Aggregate prefix\n") +{ + return bgp_aggregate_unset (vty, argv[0], AFI_IP, bgp_node_safi (vty)); +} + +ALIAS (no_aggregate_address, + no_aggregate_address_summary_only_cmd, + "no aggregate-address A.B.C.D/M summary-only", + NO_STR + "Configure BGP aggregate entries\n" + "Aggregate prefix\n" + "Filter more specific routes from updates\n") + +ALIAS (no_aggregate_address, + no_aggregate_address_as_set_cmd, + "no aggregate-address A.B.C.D/M as-set", + NO_STR + "Configure BGP aggregate entries\n" + "Aggregate prefix\n" + "Generate AS set path information\n") + +ALIAS (no_aggregate_address, + no_aggregate_address_as_set_summary_cmd, + "no aggregate-address A.B.C.D/M as-set summary-only", + NO_STR + "Configure BGP aggregate entries\n" + "Aggregate prefix\n" + "Generate AS set path information\n" + "Filter more specific routes from updates\n") + +ALIAS (no_aggregate_address, + no_aggregate_address_summary_as_set_cmd, + "no aggregate-address A.B.C.D/M summary-only as-set", + NO_STR + "Configure BGP aggregate entries\n" + "Aggregate prefix\n" + "Filter more specific routes from updates\n" + "Generate AS set path information\n") + +DEFUN (no_aggregate_address_mask, + no_aggregate_address_mask_cmd, + "no aggregate-address A.B.C.D A.B.C.D", + NO_STR + "Configure BGP aggregate entries\n" + "Aggregate address\n" + "Aggregate mask\n") +{ + int ret; + char prefix_str[BUFSIZ]; + + ret = netmask_str2prefix_str (argv[0], argv[1], prefix_str); + + if (! ret) + { + vty_out (vty, "%% Inconsistent address and mask%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_aggregate_unset (vty, prefix_str, AFI_IP, bgp_node_safi (vty)); +} + +ALIAS (no_aggregate_address_mask, + no_aggregate_address_mask_summary_only_cmd, + "no aggregate-address A.B.C.D A.B.C.D summary-only", + NO_STR + "Configure BGP aggregate entries\n" + "Aggregate address\n" + "Aggregate mask\n" + "Filter more specific routes from updates\n") + +ALIAS (no_aggregate_address_mask, + no_aggregate_address_mask_as_set_cmd, + "no aggregate-address A.B.C.D A.B.C.D as-set", + NO_STR + "Configure BGP aggregate entries\n" + "Aggregate address\n" + "Aggregate mask\n" + "Generate AS set path information\n") + +ALIAS (no_aggregate_address_mask, + no_aggregate_address_mask_as_set_summary_cmd, + "no aggregate-address A.B.C.D A.B.C.D as-set summary-only", + NO_STR + "Configure BGP aggregate entries\n" + "Aggregate address\n" + "Aggregate mask\n" + "Generate AS set path information\n" + "Filter more specific routes from updates\n") + +ALIAS (no_aggregate_address_mask, + no_aggregate_address_mask_summary_as_set_cmd, + "no aggregate-address A.B.C.D A.B.C.D summary-only as-set", + NO_STR + "Configure BGP aggregate entries\n" + "Aggregate address\n" + "Aggregate mask\n" + "Filter more specific routes from updates\n" + "Generate AS set path information\n") + +DEFUN (ipv6_aggregate_address, + ipv6_aggregate_address_cmd, + "aggregate-address X:X::X:X/M", + "Configure BGP aggregate entries\n" + "Aggregate prefix\n") +{ + return bgp_aggregate_set (vty, argv[0], AFI_IP6, SAFI_UNICAST, 0, 0); +} + +DEFUN (ipv6_aggregate_address_summary_only, + ipv6_aggregate_address_summary_only_cmd, + "aggregate-address X:X::X:X/M summary-only", + "Configure BGP aggregate entries\n" + "Aggregate prefix\n" + "Filter more specific routes from updates\n") +{ + return bgp_aggregate_set (vty, argv[0], AFI_IP6, SAFI_UNICAST, + AGGREGATE_SUMMARY_ONLY, 0); +} + +DEFUN (no_ipv6_aggregate_address, + no_ipv6_aggregate_address_cmd, + "no aggregate-address X:X::X:X/M", + NO_STR + "Configure BGP aggregate entries\n" + "Aggregate prefix\n") +{ + return bgp_aggregate_unset (vty, argv[0], AFI_IP6, SAFI_UNICAST); +} + +DEFUN (no_ipv6_aggregate_address_summary_only, + no_ipv6_aggregate_address_summary_only_cmd, + "no aggregate-address X:X::X:X/M summary-only", + NO_STR + "Configure BGP aggregate entries\n" + "Aggregate prefix\n" + "Filter more specific routes from updates\n") +{ + return bgp_aggregate_unset (vty, argv[0], AFI_IP6, SAFI_UNICAST); +} + +ALIAS (ipv6_aggregate_address, + old_ipv6_aggregate_address_cmd, + "ipv6 bgp aggregate-address X:X::X:X/M", + IPV6_STR + BGP_STR + "Configure BGP aggregate entries\n" + "Aggregate prefix\n") + +ALIAS (ipv6_aggregate_address_summary_only, + old_ipv6_aggregate_address_summary_only_cmd, + "ipv6 bgp aggregate-address X:X::X:X/M summary-only", + IPV6_STR + BGP_STR + "Configure BGP aggregate entries\n" + "Aggregate prefix\n" + "Filter more specific routes from updates\n") + +ALIAS (no_ipv6_aggregate_address, + old_no_ipv6_aggregate_address_cmd, + "no ipv6 bgp aggregate-address X:X::X:X/M", + NO_STR + IPV6_STR + BGP_STR + "Configure BGP aggregate entries\n" + "Aggregate prefix\n") + +ALIAS (no_ipv6_aggregate_address_summary_only, + old_no_ipv6_aggregate_address_summary_only_cmd, + "no ipv6 bgp aggregate-address X:X::X:X/M summary-only", + NO_STR + IPV6_STR + BGP_STR + "Configure BGP aggregate entries\n" + "Aggregate prefix\n" + "Filter more specific routes from updates\n") + +/* 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, route_tag_t tag) +{ + struct bgp *bgp; + struct listnode *node, *nnode; + struct bgp_info *new; + struct bgp_info *bi; + struct bgp_info info; + struct bgp_node *bn; + struct attr attr; + struct attr *new_attr; + afi_t afi; + int ret; + + /* Make default attribute. */ + bgp_attr_default_set (&attr, BGP_ORIGIN_INCOMPLETE); + if (nexthop) + attr.nexthop = *nexthop; + + if (nexthop6) + { + struct attr_extra *extra = bgp_attr_extra_get(&attr); + extra->mp_nexthop_global = *nexthop6; + extra->mp_nexthop_len = 16; + } + + 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)) + { + afi = family2afi (p->family); + + 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].name) + { + info.peer = bgp->peer_self; + info.attr = &attr_new; + + SET_FLAG (bgp->peer_self->rmap_type, PEER_RMAP_TYPE_REDISTRIBUTE); + + ret = route_map_apply (bgp->rmap[afi][type].map, p, RMAP_BGP, + &info); + + bgp->peer_self->rmap_type = 0; + + if (ret == RMAP_DENYMATCH) + { + /* Free uninterned attribute. */ + bgp_attr_flush (&attr_new); + + /* Unintern original. */ + aspath_unintern (&attr.aspath); + bgp_attr_extra_free (&attr); + bgp_redistribute_delete (p, type); + return; + } + } + + bn = bgp_afi_node_get (bgp->rib[afi][SAFI_UNICAST], + afi, SAFI_UNICAST, p, NULL); + + new_attr = bgp_attr_intern (&attr_new); + + for (bi = bn->info; bi; bi = bi->next) + if (bi->peer == bgp->peer_self + && bi->sub_type == BGP_ROUTE_REDISTRIBUTE) + break; + + if (bi) + { + if (attrhash_cmp (bi->attr, new_attr) && + !CHECK_FLAG(bi->flags, BGP_INFO_REMOVED)) + { + bgp_attr_unintern (&new_attr); + aspath_unintern (&attr.aspath); + bgp_attr_extra_free (&attr); + bgp_unlock_node (bn); + return; + } + else + { + /* The attribute is changed. */ + bgp_info_set_flag (bn, bi, BGP_INFO_ATTR_CHANGED); + + /* Rewrite BGP route information. */ + if (CHECK_FLAG(bi->flags, BGP_INFO_REMOVED)) + bgp_info_restore(bn, bi); + else + bgp_aggregate_decrement (bgp, p, bi, afi, SAFI_UNICAST); + bgp_attr_unintern (&bi->attr); + bi->attr = new_attr; + bi->uptime = bgp_clock (); + + /* Process change. */ + bgp_aggregate_increment (bgp, p, bi, afi, SAFI_UNICAST); + bgp_process (bgp, bn, afi, SAFI_UNICAST); + bgp_unlock_node (bn); + aspath_unintern (&attr.aspath); + bgp_attr_extra_free (&attr); + return; + } + } + + new = info_make(type, BGP_ROUTE_REDISTRIBUTE, bgp->peer_self, + new_attr, bn); + SET_FLAG (new->flags, BGP_INFO_VALID); + + bgp_aggregate_increment (bgp, p, new, afi, SAFI_UNICAST); + bgp_info_add (bn, new); + bgp_unlock_node (bn); + bgp_process (bgp, bn, afi, SAFI_UNICAST); + } + } + + /* Unintern original. */ + aspath_unintern (&attr.aspath); + bgp_attr_extra_free (&attr); +} + +void +bgp_redistribute_delete (struct prefix *p, u_char type) +{ + struct bgp *bgp; + struct listnode *node, *nnode; + afi_t afi; + struct bgp_node *rn; + struct bgp_info *ri; + + for (ALL_LIST_ELEMENTS (bm->bgp, node, nnode, bgp)) + { + afi = family2afi (p->family); + + if (bgp->redist[afi][type]) + { + rn = bgp_afi_node_get (bgp->rib[afi][SAFI_UNICAST], afi, SAFI_UNICAST, p, NULL); + + for (ri = rn->info; ri; ri = ri->next) + if (ri->peer == bgp->peer_self + && ri->type == type) + break; + + if (ri) + { + bgp_aggregate_decrement (bgp, p, ri, afi, SAFI_UNICAST); + bgp_info_delete (rn, ri); + bgp_process (bgp, rn, afi, SAFI_UNICAST); + } + bgp_unlock_node (rn); + } + } +} + +/* Withdraw specified route type's route. */ +void +bgp_redistribute_withdraw (struct bgp *bgp, afi_t afi, int type) +{ + struct bgp_node *rn; + struct bgp_info *ri; + struct bgp_table *table; + + table = bgp->rib[afi][SAFI_UNICAST]; + + for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) + { + for (ri = rn->info; ri; ri = ri->next) + if (ri->peer == bgp->peer_self + && ri->type == type) + break; + + if (ri) + { + bgp_aggregate_decrement (bgp, &rn->p, ri, afi, SAFI_UNICAST); + bgp_info_delete (rn, ri); + bgp_process (bgp, rn, afi, SAFI_UNICAST); + } + } +} + +/* Static function to display route. */ +static void +route_vty_out_route (struct prefix *p, struct vty *vty) +{ + int len; + u_int32_t destination; + char buf[BUFSIZ]; + + if (p->family == AF_INET) + { + len = vty_out (vty, "%s", inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ)); + destination = ntohl (p->u.prefix4.s_addr); + + if ((IN_CLASSC (destination) && p->prefixlen == 24) + || (IN_CLASSB (destination) && p->prefixlen == 16) + || (IN_CLASSA (destination) && p->prefixlen == 8) + || p->u.prefix4.s_addr == 0) + { + /* When mask is natural, mask is not displayed. */ + } + else + len += vty_out (vty, "/%d", p->prefixlen); + } + else + len = vty_out (vty, "%s/%d", inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ), + p->prefixlen); + + len = 17 - len; + if (len < 1) + vty_out (vty, "%s%*s", VTY_NEWLINE, 20, " "); + else + vty_out (vty, "%*s", len, " "); +} + +enum bgp_display_type +{ + normal_list, +}; + +/* Print the short form route status for a bgp_info */ +static void +route_vty_short_status_out (struct vty *vty, struct bgp_info *binfo) +{ + /* Route status display. */ + if (CHECK_FLAG (binfo->flags, BGP_INFO_REMOVED)) + vty_out (vty, "R"); + else if (CHECK_FLAG (binfo->flags, BGP_INFO_STALE)) + vty_out (vty, "S"); + else if (binfo->extra && binfo->extra->suppress) + vty_out (vty, "s"); + else if (CHECK_FLAG (binfo->flags, BGP_INFO_VALID) && + ! CHECK_FLAG (binfo->flags, BGP_INFO_HISTORY)) + vty_out (vty, "*"); + else + vty_out (vty, " "); + + /* Selected */ + if (CHECK_FLAG (binfo->flags, BGP_INFO_HISTORY)) + vty_out (vty, "h"); + else if (CHECK_FLAG (binfo->flags, BGP_INFO_DAMPED)) + 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, " "); + + /* Internal route. */ + if ((binfo->peer->as) && (binfo->peer->as == binfo->peer->local_as)) + vty_out (vty, "i"); + else + vty_out (vty, " "); +} + +/* called from terminal list command */ +void +route_vty_out( + struct vty *vty, + struct prefix *p, + struct bgp_info *binfo, + int display, + safi_t safi) +{ + struct attr *attr; + + /* short status lead text */ + route_vty_short_status_out (vty, binfo); + + /* print prefix and mask */ + if (!display) + route_vty_out_route (p, vty); + else + vty_out (vty, "%*s", 17, " "); + + /* Print attribute */ + attr = binfo->attr; + if (attr) + { + + /* + * 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, "?"); + } + } 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, " "); + + if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF)) + vty_out (vty, "%7u", attr->local_pref); + else + vty_out (vty, " "); + + vty_out (vty, "%7u ", (attr->extra ? attr->extra->weight : 0)); + + /* Print aspath */ + if (attr->aspath) + aspath_print_vty (vty, "%s", attr->aspath, " "); + + /* Print origin */ + vty_out (vty, "%s", bgp_origin_str[attr->origin]); + } + vty_out (vty, "%s", VTY_NEWLINE); +} + +/* called from terminal list command */ +void +route_vty_out_tmp (struct vty *vty, struct prefix *p, + struct attr *attr, safi_t safi) +{ + /* Route status display. */ + vty_out (vty, "*"); + vty_out (vty, ">"); + vty_out (vty, " "); + + /* print prefix and mask */ + route_vty_out_route (p, vty); + + /* Print attribute */ + if (attr) + { + if (p->family == AF_INET) + { + 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)); + } + else if (p->family == AF_INET6) + { + int len; + char buf[BUFSIZ]; + + assert (attr->extra); + + 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, " "); + } + + if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC)) + 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); + else + vty_out (vty, " "); + + vty_out (vty, "%7u ", (attr->extra ? attr->extra->weight : 0)); + + /* Print aspath */ + if (attr->aspath) + aspath_print_vty (vty, "%s", attr->aspath, " "); + + /* Print origin */ + vty_out (vty, "%s", bgp_origin_str[attr->origin]); + } + + vty_out (vty, "%s", VTY_NEWLINE); +} + +void +route_vty_out_tag (struct vty *vty, struct prefix *p, + struct bgp_info *binfo, int display, safi_t safi) +{ + struct attr *attr; + u_int32_t label = 0; + + if (!binfo->extra) + return; + + /* short status lead text */ + route_vty_short_status_out (vty, binfo); + + /* print prefix and mask */ + if (! display) + route_vty_out_route (p, vty); + else + vty_out (vty, "%*s", 17, " "); + + /* Print attribute */ + attr = binfo->attr; + if (attr) + { + if (p->family == AF_INET) + { + 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)); + } + else if (p->family == AF_INET6) + { + assert (attr->extra); + char buf[BUFSIZ]; + char buf1[BUFSIZ]; + if (attr->extra->mp_nexthop_len == 16) + vty_out (vty, "%s", + inet_ntop (AF_INET6, &attr->extra->mp_nexthop_global, + buf, BUFSIZ)); + else if (attr->extra->mp_nexthop_len == 32) + vty_out (vty, "%s(%s)", + inet_ntop (AF_INET6, &attr->extra->mp_nexthop_global, + buf, BUFSIZ), + inet_ntop (AF_INET6, &attr->extra->mp_nexthop_local, + buf1, BUFSIZ)); + + } + } + + label = decode_label (binfo->extra->tag); + + vty_out (vty, "notag/%d", label); + + vty_out (vty, "%s", VTY_NEWLINE); +} + +/* dampening route */ +static void +damp_route_vty_out (struct vty *vty, struct prefix *p, + struct bgp_info *binfo, int display, safi_t safi) +{ + struct attr *attr; + int len; + char timebuf[BGP_UPTIME_LEN]; + + /* short status lead text */ + route_vty_short_status_out (vty, binfo); + + /* print prefix and mask */ + if (! display) + route_vty_out_route (p, vty); + else + vty_out (vty, "%*s", 17, " "); + + len = vty_out (vty, "%s", binfo->peer->host); + len = 17 - len; + if (len < 1) + vty_out (vty, "%s%*s", VTY_NEWLINE, 34, " "); + else + vty_out (vty, "%*s", len, " "); + + vty_out (vty, "%s ", bgp_damp_reuse_time_vty (vty, binfo, timebuf, BGP_UPTIME_LEN)); + + /* Print attribute */ + attr = binfo->attr; + if (attr) + { + /* Print aspath */ + if (attr->aspath) + aspath_print_vty (vty, "%s", attr->aspath, " "); + + /* Print origin */ + vty_out (vty, "%s", bgp_origin_str[attr->origin]); + } + vty_out (vty, "%s", VTY_NEWLINE); +} + +/* flap route */ +static void +flap_route_vty_out (struct vty *vty, struct prefix *p, + struct bgp_info *binfo, int display, safi_t safi) +{ + struct attr *attr; + struct bgp_damp_info *bdi; + char timebuf[BGP_UPTIME_LEN]; + int len; + + if (!binfo->extra) + return; + + bdi = binfo->extra->damp_info; + + /* short status lead text */ + route_vty_short_status_out (vty, binfo); + + /* print prefix and mask */ + if (! display) + route_vty_out_route (p, vty); + else + vty_out (vty, "%*s", 17, " "); + + len = vty_out (vty, "%s", binfo->peer->host); + len = 16 - len; + if (len < 1) + vty_out (vty, "%s%*s", VTY_NEWLINE, 33, " "); + else + vty_out (vty, "%*s", len, " "); + + len = vty_out (vty, "%d", bdi->flap); + len = 5 - len; + if (len < 1) + vty_out (vty, " "); + else + vty_out (vty, "%*s ", len, " "); + + vty_out (vty, "%s ", peer_uptime (bdi->start_time, + timebuf, BGP_UPTIME_LEN)); + + if (CHECK_FLAG (binfo->flags, BGP_INFO_DAMPED) + && ! CHECK_FLAG (binfo->flags, BGP_INFO_HISTORY)) + vty_out (vty, "%s ", bgp_damp_reuse_time_vty (vty, binfo, timebuf, BGP_UPTIME_LEN)); + else + vty_out (vty, "%*s ", 8, " "); + + /* Print attribute */ + attr = binfo->attr; + if (attr) + { + /* Print aspath */ + if (attr->aspath) + aspath_print_vty (vty, "%s", attr->aspath, " "); + + /* Print origin */ + vty_out (vty, "%s", bgp_origin_str[attr->origin]); + } + vty_out (vty, "%s", VTY_NEWLINE); +} + +static void +route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p, + struct bgp_info *binfo, afi_t afi, safi_t safi) +{ + char buf[INET6_ADDRSTRLEN]; + char buf1[BUFSIZ]; + struct attr *attr; + int sockunion_vty_out (struct vty *, union sockunion *); +#ifdef HAVE_CLOCK_MONOTONIC + time_t tbuf; +#endif + + attr = binfo->attr; + + if (attr) + { + /* Line1 display AS-path, Aggregator */ + if (attr->aspath) + { + vty_out (vty, " "); + if (aspath_count_hops (attr->aspath) == 0) + vty_out (vty, "Local"); + else + aspath_print_vty (vty, "%s", attr->aspath, ""); + } + + if (CHECK_FLAG (binfo->flags, BGP_INFO_REMOVED)) + vty_out (vty, ", (removed)"); + if (CHECK_FLAG (binfo->flags, BGP_INFO_STALE)) + vty_out (vty, ", (stale)"); + if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR))) + vty_out (vty, ", (aggregated by %u %s)", + attr->extra->aggregator_as, + inet_ntoa (attr->extra->aggregator_addr)); + if (CHECK_FLAG (binfo->peer->af_flags[afi][safi], PEER_FLAG_REFLECTOR_CLIENT)) + vty_out (vty, ", (Received from a RR-client)"); + if (CHECK_FLAG (binfo->peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)) + vty_out (vty, ", (Received from a RS-client)"); + if (CHECK_FLAG (binfo->flags, BGP_INFO_HISTORY)) + vty_out (vty, ", (history entry)"); + else if (CHECK_FLAG (binfo->flags, BGP_INFO_DAMPED)) + vty_out (vty, ", (suppressed due to dampening)"); + vty_out (vty, "%s", VTY_NEWLINE); + + /* Line2 display Next-hop, Neighbor, Router-id */ + if (p->family == AF_INET) + { + vty_out (vty, " %s", ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)) ? + inet_ntoa (attr->extra->mp_nexthop_global_in) : + inet_ntoa (attr->nexthop)); + } + else + { + assert (attr->extra); + vty_out (vty, " %s", + inet_ntop (AF_INET6, &attr->extra->mp_nexthop_global, + buf, INET6_ADDRSTRLEN)); + } + + if (binfo->peer == bgp->peer_self) + { + vty_out (vty, " from %s ", + p->family == AF_INET ? "0.0.0.0" : "::"); + vty_out (vty, "(%s)", inet_ntoa(bgp->router_id)); + } + else + { + if (! CHECK_FLAG (binfo->flags, BGP_INFO_VALID)) + vty_out (vty, " (inaccessible)"); + else if (binfo->extra && binfo->extra->igpmetric) + 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 + vty_out (vty, " (%s)", inet_ntop (AF_INET, &binfo->peer->remote_id, buf1, BUFSIZ)); + } + vty_out (vty, "%s", VTY_NEWLINE); + + /* display nexthop local */ + if (attr->extra && attr->extra->mp_nexthop_len == 32) + { + vty_out (vty, " (%s)%s", + inet_ntop (AF_INET6, &attr->extra->mp_nexthop_local, + buf, INET6_ADDRSTRLEN), + VTY_NEWLINE); + } + + /* 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)) + vty_out (vty, ", metric %u", attr->med); + + if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) + vty_out (vty, ", localpref %u", attr->local_pref); + else + vty_out (vty, ", localpref %u", bgp->default_local_pref); + + 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_VALID)) + vty_out (vty, ", invalid"); + else if (! CHECK_FLAG (binfo->flags, BGP_INFO_HISTORY)) + vty_out (vty, ", valid"); + + if (binfo->peer != bgp->peer_self) + { + if (binfo->peer->as == binfo->peer->local_as) + vty_out (vty, ", internal"); + else + vty_out (vty, ", %s", + (bgp_confederation_peers_check(bgp, binfo->peer->as) ? "confed-external" : "external")); + } + else if (binfo->sub_type == BGP_ROUTE_AGGREGATE) + vty_out (vty, ", aggregated, local"); + else if (binfo->type != ZEBRA_ROUTE_BGP) + vty_out (vty, ", sourced"); + else + vty_out (vty, ", sourced, local"); + + 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"); + + vty_out (vty, "%s", VTY_NEWLINE); + + /* Line 4 display Community */ + if (attr->community) + vty_out (vty, " Community: %s%s", attr->community->str, + VTY_NEWLINE); + + /* Line 5 display Extended-community */ + 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 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))) + { + assert (attr->extra); + if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)) + vty_out (vty, " Originator: %s", + inet_ntoa (attr->extra->originator_id)); + + if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST)) + { + int i; + vty_out (vty, ", Cluster list: "); + for (i = 0; i < attr->extra->cluster->length / 4; i++) + vty_out (vty, "%s ", + inet_ntoa (attr->extra->cluster->list[i])); + } + vty_out (vty, "%s", VTY_NEWLINE); + } + + if (binfo->extra && binfo->extra->damp_info) + bgp_damp_info_vty (vty, binfo); + + /* Line 8 display Uptime */ +#ifdef HAVE_CLOCK_MONOTONIC + tbuf = time(NULL) - (bgp_clock() - binfo->uptime); + vty_out (vty, " Last update: %s", ctime(&tbuf)); +#else + vty_out (vty, " Last update: %s", ctime(&binfo->uptime)); +#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, = 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" +#define BGP_SHOW_FLAP_HEADER " Network From Flaps Duration Reuse Path%s" + +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_route_map, + 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, + 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, + bgp_show_type_flap_cidr_only, + bgp_show_type_flap_regexp, + bgp_show_type_flap_filter_list, + bgp_show_type_flap_prefix_list, + bgp_show_type_flap_prefix_longer, + bgp_show_type_flap_route_map, + bgp_show_type_flap_neighbor, + bgp_show_type_dampend_paths, + bgp_show_type_damp_neighbor +}; + +static int +bgp_show_table (struct vty *vty, struct bgp_table *table, struct in_addr *router_id, + enum bgp_show_type type, void *output_arg) +{ + struct bgp_info *ri; + struct bgp_node *rn; + 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)) + if (rn->info != NULL) + { + display = 0; + + 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 + || type == bgp_show_type_flap_cidr_only + || type == bgp_show_type_flap_regexp + || type == bgp_show_type_flap_filter_list + || type == bgp_show_type_flap_prefix_list + || type == bgp_show_type_flap_prefix_longer + || type == bgp_show_type_flap_route_map + || type == bgp_show_type_flap_neighbor + || type == bgp_show_type_dampend_paths + || type == bgp_show_type_damp_neighbor) + { + if (!(ri->extra && ri->extra->damp_info)) + continue; + } + if (type == bgp_show_type_regexp + || type == bgp_show_type_flap_regexp) + { + regex_t *regex = output_arg; + + if (bgp_regexec (regex, ri->attr->aspath) == REG_NOMATCH) + continue; + } + if (type == bgp_show_type_prefix_list + || type == bgp_show_type_flap_prefix_list) + { + struct prefix_list *plist = output_arg; + + if (prefix_list_apply (plist, &rn->p) != PREFIX_PERMIT) + continue; + } + if (type == bgp_show_type_filter_list + || type == bgp_show_type_flap_filter_list) + { + struct as_list *as_list = output_arg; + + if (as_list_apply (as_list, ri->attr->aspath) != AS_FILTER_PERMIT) + continue; + } + if (type == bgp_show_type_route_map + || type == bgp_show_type_flap_route_map) + { + struct route_map *rmap = output_arg; + struct bgp_info binfo; + 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); + if (ret == RMAP_DENYMATCH) + continue; + } + if (type == bgp_show_type_neighbor + || type == bgp_show_type_flap_neighbor + || type == bgp_show_type_damp_neighbor) + { + union sockunion *su = output_arg; + + if (ri->peer->su_remote == NULL || ! sockunion_same(ri->peer->su_remote, su)) + continue; + } + if (type == bgp_show_type_cidr_only + || type == bgp_show_type_flap_cidr_only) + { + u_int32_t destination; + + destination = ntohl (rn->p.u.prefix4.s_addr); + if (IN_CLASSC (destination) && rn->p.prefixlen == 24) + continue; + if (IN_CLASSB (destination) && rn->p.prefixlen == 16) + continue; + if (IN_CLASSA (destination) && rn->p.prefixlen == 8) + continue; + } + if (type == bgp_show_type_prefix_longer + || type == bgp_show_type_flap_prefix_longer) + { + struct prefix *p = output_arg; + + if (! prefix_match (p, &rn->p)) + continue; + } + if (type == bgp_show_type_community_all) + { + if (! ri->attr->community) + continue; + } + if (type == bgp_show_type_community) + { + struct community *com = output_arg; + + if (! ri->attr->community || + ! community_match (ri->attr->community, com)) + continue; + } + if (type == bgp_show_type_community_exact) + { + struct community *com = output_arg; + + if (! ri->attr->community || + ! community_cmp (ri->attr->community, com)) + continue; + } + if (type == bgp_show_type_community_list) + { + struct community_list *list = output_arg; + + if (! community_list_match (ri->attr->community, list)) + continue; + } + if (type == bgp_show_type_community_list_exact) + { + struct community_list *list = output_arg; + + if (! community_list_exact_match (ri->attr->community, list)) + continue; + } + 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; + + if (! prefix_match (&rn->p, p)) + continue; + + if (type == bgp_show_type_flap_prefix) + if (p->prefixlen != rn->p.prefixlen) + continue; + } + if (type == bgp_show_type_dampend_paths + || type == bgp_show_type_damp_neighbor) + { + if (! CHECK_FLAG (ri->flags, BGP_INFO_DAMPED) + || CHECK_FLAG (ri->flags, BGP_INFO_HISTORY)) + continue; + } + + if (header) + { + vty_out (vty, "BGP table version is 0, local router ID is %s%s", inet_ntoa (*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); + if (type == bgp_show_type_dampend_paths + || type == bgp_show_type_damp_neighbor) + vty_out (vty, BGP_SHOW_DAMP_HEADER, VTY_NEWLINE); + else if (type == bgp_show_type_flap_statistics + || type == bgp_show_type_flap_address + || type == bgp_show_type_flap_prefix + || type == bgp_show_type_flap_cidr_only + || type == bgp_show_type_flap_regexp + || type == bgp_show_type_flap_filter_list + || type == bgp_show_type_flap_prefix_list + || type == bgp_show_type_flap_prefix_longer + || type == bgp_show_type_flap_route_map + || type == bgp_show_type_flap_neighbor) + vty_out (vty, BGP_SHOW_FLAP_HEADER, VTY_NEWLINE); + else + vty_out (vty, BGP_SHOW_HEADER, VTY_NEWLINE); + header = 0; + } + + if (type == bgp_show_type_dampend_paths + || type == bgp_show_type_damp_neighbor) + damp_route_vty_out (vty, &rn->p, ri, display, SAFI_UNICAST); + else if (type == bgp_show_type_flap_statistics + || type == bgp_show_type_flap_address + || type == bgp_show_type_flap_prefix + || type == bgp_show_type_flap_cidr_only + || type == bgp_show_type_flap_regexp + || type == bgp_show_type_flap_filter_list + || type == bgp_show_type_flap_prefix_list + || type == bgp_show_type_flap_prefix_longer + || type == bgp_show_type_flap_route_map + || type == bgp_show_type_flap_neighbor) + flap_route_vty_out (vty, &rn->p, ri, display, SAFI_UNICAST); + else + route_vty_out (vty, &rn->p, ri, display, SAFI_UNICAST); + display++; + } + if (display) + output_count++; + } + + /* No route is displayed */ + if (output_count == 0) + { + if (type == bgp_show_type_normal) + vty_out (vty, "No BGP 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; +} + +static int +bgp_show (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, + enum bgp_show_type type, void *output_arg) +{ + struct bgp_table *table; + + if (bgp == NULL) { + bgp = bgp_get_default (); + } + + if (bgp == NULL) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + + table = bgp->rib[afi][safi]; + + return bgp_show_table (vty, table, &bgp->router_id, type, output_arg); +} + +/* Header of detailed BGP route information */ +static void +route_vty_out_detail_header (struct vty *vty, struct bgp *bgp, + struct bgp_node *rn, + struct prefix_rd *prd, afi_t afi, safi_t safi) +{ + struct bgp_info *ri; + struct prefix *p; + struct peer *peer; + struct listnode *node, *nnode; + char buf1[INET6_ADDRSTRLEN]; + char buf2[INET6_ADDRSTRLEN]; + int count = 0; + int best = 0; + int suppress = 0; + int no_export = 0; + 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", + (printrd ? prefix_rd2str (prd, buf1, RD_ADDRSTRLEN) : ""), + printrd ? ":" : "", + inet_ntop (p->family, &p->u.prefix, buf2, INET6_ADDRSTRLEN), + p->prefixlen, VTY_NEWLINE); + + for (ri = rn->info; ri; ri = ri->next) + { + count++; + if (CHECK_FLAG (ri->flags, BGP_INFO_SELECTED)) + { + best = count; + if (ri->extra && ri->extra->suppress) + suppress = 1; + if (ri->attr->community != NULL) + { + if (community_include (ri->attr->community, COMMUNITY_NO_ADVERTISE)) + no_advertise = 1; + if (community_include (ri->attr->community, COMMUNITY_NO_EXPORT)) + no_export = 1; + if (community_include (ri->attr->community, COMMUNITY_LOCAL_AS)) + local_as = 1; + } + } + } + + vty_out (vty, "Paths: (%d available", count); + if (best) + { + vty_out (vty, ", best #%d", best); + if (safi == SAFI_UNICAST) + vty_out (vty, ", table Default-IP-Routing-Table"); + } + else + vty_out (vty, ", no best path"); + if (no_advertise) + vty_out (vty, ", not advertised to any peer"); + else if (no_export) + vty_out (vty, ", not advertised to EBGP peer"); + else if (local_as) + vty_out (vty, ", not advertised outside local AS"); + if (suppress) + vty_out (vty, ", Advertisements suppressed by an aggregate."); + vty_out (vty, ")%s", VTY_NEWLINE); + + /* advertised peer */ + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + if (bgp_adj_out_lookup (peer, p, afi, safi, rn)) + { + if (! first) + vty_out (vty, " Advertised to non peer-group peers:%s ", VTY_NEWLINE); + vty_out (vty, " %s", sockunion2str (&peer->su, buf1, SU_ADDRSTRLEN)); + first = 1; + } + } + if (! first) + vty_out (vty, " Not advertised to any peer"); + vty_out (vty, "%s", VTY_NEWLINE); +} + +/* Display specified route of BGP table. */ +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, enum bgp_path_type pathtype) +{ + int ret; + int header; + int display = 0; + struct prefix match; + struct bgp_node *rn; + struct bgp_node *rm; + 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) + { + vty_out (vty, "address is malformed%s", VTY_NEWLINE); + return CMD_WARNING; + } + + match.family = afi2family (afi); + + if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)) + { + 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) + { + header = 1; + + if ((rm = bgp_node_match (table, &match)) != NULL) + { + if (prefix_check && rm->p.prefixlen != match.prefixlen) + { + bgp_unlock_node (rm); + continue; + } + + for (ri = rm->info; ri; ri = ri->next) + { + if (header) + { + route_vty_out_detail_header (vty, bgp, rm, (struct prefix_rd *)&rn->p, + AFI_IP, safi); + + header = 0; + } + display++; + + 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); + } + } + } + } + else + { + header = 1; + + if ((rn = bgp_node_match (rib, &match)) != NULL) + { + if (! prefix_check || rn->p.prefixlen == match.prefixlen) + { + for (ri = rn->info; ri; ri = ri->next) + { + if (header) + { + route_vty_out_detail_header (vty, bgp, rn, NULL, afi, safi); + header = 0; + } + display++; + + 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); + } + } + + bgp_unlock_node (rn); + } + } + + if (! display) + { + vty_out (vty, "%% Network not in table%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +/* Display specified route of Main RIB */ +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, enum bgp_path_type pathtype) +{ + struct bgp *bgp; + + /* 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; + } + } + + return bgp_show_route_in_table (vty, bgp, bgp->rib[afi][safi], ip_str, + afi, safi, prd, prefix_check, pathtype); +} + +/* BGP route print out function. */ +DEFUN (show_ip_bgp, + show_ip_bgp_cmd, + "show ip bgp", + SHOW_STR + IP_STR + BGP_STR) +{ + return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, bgp_show_type_normal, NULL); +} + +DEFUN (show_ip_bgp_ipv4, + show_ip_bgp_ipv4_cmd, + "show ip bgp ipv4 (unicast|multicast)", + SHOW_STR + IP_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_IP, SAFI_MULTICAST, bgp_show_type_normal, + NULL); + + return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, bgp_show_type_normal, NULL); +} + +DEFUN (show_ip_bgp_route, + show_ip_bgp_route_cmd, + "show ip bgp A.B.C.D", + SHOW_STR + IP_STR + 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, BGP_PATH_ALL); +} + +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" + "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, 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); +} + +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, + "show ip bgp vpnv4 all A.B.C.D", + SHOW_STR + IP_STR + BGP_STR + "Display VPNv4 NLRI specific information\n" + "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, 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", + SHOW_STR + IP_STR + BGP_STR + "Display VPNv4 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; + + 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); +} + +DEFUN (show_ip_bgp_prefix, + show_ip_bgp_prefix_cmd, + "show ip bgp A.B.C.D/M", + SHOW_STR + IP_STR + 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, 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, + show_ip_bgp_ipv4_prefix_cmd, + "show ip 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" + "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, BGP_PATH_ALL); + + return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL); +} + +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" + "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, + "show ip bgp vpnv4 all A.B.C.D/M", + SHOW_STR + IP_STR + BGP_STR + "Display VPNv4 NLRI specific information\n" + "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, BGP_PATH_ALL); +} + +DEFUN (show_ip_bgp_vpnv4_rd_prefix, + show_ip_bgp_vpnv4_rd_prefix_cmd, + "show ip bgp vpnv4 rd ASN:nn_or_IP-address:nn A.B.C.D/M", + SHOW_STR + IP_STR + BGP_STR + "Display VPNv4 NLRI specific information\n" + "Display information for a route distinguisher\n" + "VPN Route Distinguisher\n" + "IP prefix /, e.g., 35.0.0.0/8\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_route (vty, NULL, argv[1], AFI_IP, SAFI_MPLS_VPN, &prd, 1, BGP_PATH_ALL); +} + +DEFUN (show_ip_bgp_view, + show_ip_bgp_view_cmd, + "show ip bgp view WORD", + SHOW_STR + IP_STR + BGP_STR + "BGP view\n" + "View name\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_IP, SAFI_UNICAST, bgp_show_type_normal, NULL); +} + +DEFUN (show_ip_bgp_view_route, + show_ip_bgp_view_route_cmd, + "show ip bgp view WORD A.B.C.D", + SHOW_STR + IP_STR + BGP_STR + "BGP view\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, BGP_PATH_ALL); +} + +DEFUN (show_ip_bgp_view_prefix, + show_ip_bgp_view_prefix_cmd, + "show ip bgp view WORD A.B.C.D/M", + SHOW_STR + IP_STR + BGP_STR + "BGP view\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, BGP_PATH_ALL); +} + +DEFUN (show_bgp, + show_bgp_cmd, + "show bgp", + SHOW_STR + BGP_STR) +{ + return bgp_show (vty, NULL, AFI_IP6, SAFI_UNICAST, bgp_show_type_normal, + NULL); +} + +ALIAS (show_bgp, + show_bgp_ipv6_cmd, + "show bgp ipv6", + SHOW_STR + BGP_STR + "Address family\n") + +/* old command */ +DEFUN (show_ipv6_bgp, + show_ipv6_bgp_cmd, + "show ipv6 bgp", + SHOW_STR + IP_STR + BGP_STR) +{ + return bgp_show (vty, NULL, AFI_IP6, SAFI_UNICAST, bgp_show_type_normal, + NULL); +} + +DEFUN (show_bgp_route, + show_bgp_route_cmd, + "show bgp X:X::X:X", + SHOW_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, BGP_PATH_ALL); +} + +DEFUN (show_bgp_ipv4_safi, + 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") +{ + 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_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" + "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_bgp_route_pathtype, + show_bgp_route_pathtype_cmd, + "show bgp X:X::X:X (bestpath|multipath)", + SHOW_STR + BGP_STR + "Network in the BGP routing table to display\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_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_route_pathtype, + show_bgp_ipv6_route_pathtype_cmd, + "show bgp ipv6 X:X::X:X (bestpath|multipath)", + SHOW_STR + BGP_STR + "Address family\n" + "Network in the BGP routing table to display\n" + "Display only the bestpath\n" + "Display only multipaths\n") + +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" + "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) + 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); +} + +DEFUN (show_bgp_ipv4_vpn_route, + show_bgp_ipv4_vpn_route_cmd, + "show bgp ipv4 vpn A.B.C.D", + SHOW_STR + BGP_STR + "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_IP, SAFI_MPLS_VPN, NULL, 0, BGP_PATH_ALL); +} + +DEFUN (show_bgp_ipv6_vpn_route, + show_bgp_ipv6_vpn_route_cmd, + "show bgp ipv6 vpn X:X::X:X", + SHOW_STR + BGP_STR + "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_MPLS_VPN, NULL, 0, BGP_PATH_ALL); +} + +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 + 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") +{ + 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); +} + +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 + "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; + + 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 + "IPv6 prefix /\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_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_prefix_pathtype, + show_bgp_ipv6_prefix_pathtype_cmd, + "show bgp ipv6 X:X::X:X/M (bestpath|multipath)", + SHOW_STR + BGP_STR + "Address family\n" + "IPv6 prefix /\n" + "Display only the bestpath\n" + "Display only multipaths\n") + +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 + 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") +{ + 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); +} + +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 + "Display ENCAP NLRI specific information\n" + "Network in the BGP routing table to display\n") +{ + return bgp_show_route (vty, NULL, argv[0], AFI_IP, SAFI_ENCAP, NULL, 0, BGP_PATH_ALL); +} + +DEFUN (show_bgp_ipv6_encap_route, + show_bgp_ipv6_encap_route_cmd, + "show bgp ipv6 encap X:X::X:X", + SHOW_STR + 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_ENCAP, NULL, 0, BGP_PATH_ALL); +} + +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 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_IP, safi, &prd, 0, BGP_PATH_ALL); +} + +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 + 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 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_bgp_ipv4_prefix, + show_bgp_ipv4_prefix_cmd, + "show bgp ipv4 A.B.C.D/M", + SHOW_STR + BGP_STR + IP_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, BGP_PATH_ALL); +} + +DEFUN (show_bgp_ipv4_safi_prefix, + show_bgp_ipv4_safi_prefix_cmd, + "show bgp ipv4 (unicast|multicast) A.B.C.D/M", + 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") +{ + if (strncmp (argv[0], "m", 1) == 0) + 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, BGP_PATH_ALL); +} + +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 + IP_STR + "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_IP, SAFI_MPLS_VPN, NULL, 1, BGP_PATH_ALL); +} + +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 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); +} + +DEFUN (show_bgp_ipv4_encap_prefix, + show_bgp_ipv4_encap_prefix_cmd, + "show bgp ipv4 encap A.B.C.D/M", + SHOW_STR + BGP_STR + 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_route (vty, NULL, argv[0], AFI_IP, SAFI_ENCAP, NULL, 1, BGP_PATH_ALL); +} + +DEFUN (show_bgp_ipv6_encap_prefix, + show_bgp_ipv6_encap_prefix_cmd, + "show bgp ipv6 encap X:X::X:X/M", + SHOW_STR + BGP_STR + 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_route (vty, NULL, argv[0], AFI_IP6, SAFI_ENCAP, NULL, 1, BGP_PATH_ALL); +} + +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") +{ + 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_IP, safi, &prd, 1, BGP_PATH_ALL); +} + +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 + 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") +{ + 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_bgp_afi_safi_view, + show_bgp_afi_safi_view_cmd, + "show bgp view WORD (ipv4|ipv6) (encap|mulicast|unicast|vpn)", + SHOW_STR + BGP_STR + "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" + ) +{ + 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_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 + BGP_STR + "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") +{ + 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, 0, BGP_PATH_ALL); +} + +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 + "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") +{ + 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); +} + +/* new001 */ +DEFUN (show_bgp_afi, + show_bgp_afi_cmd, + "show bgp (ipv4|ipv6)", + SHOW_STR + BGP_STR + "Address family\n" + "Address family\n") +{ + afi_t afi; + + 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_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); +} + +DEFUN (show_bgp_ipv6_route, + show_bgp_ipv6_route_cmd, + "show bgp ipv6 X:X::X:X", + SHOW_STR + BGP_STR + "Address family\n" + "Network in the BGP routing table to display\n") +{ + return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_UNICAST, NULL, 0, BGP_PATH_ALL); +} + +DEFUN (show_bgp_ipv6_safi_route, + show_bgp_ipv6_safi_route_cmd, + "show bgp ipv6 (unicast|multicast) X:X::X:X", + SHOW_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_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); +} + +/* 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, BGP_PATH_ALL); +} + +DEFUN (show_bgp_prefix, + show_bgp_prefix_cmd, + "show bgp X:X::X:X/M", + SHOW_STR + BGP_STR + "IPv6 prefix /\n") +{ + return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL); +} + + +/* new002 */ +DEFUN (show_bgp_ipv6_prefix, + show_bgp_ipv6_prefix_cmd, + "show bgp ipv6 X:X::X:X/M", + SHOW_STR + BGP_STR + "Address family\n" + "IPv6 prefix /, e.g., 3ffe::/16\n") +{ + return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL); +} +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") +{ + 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_route (vty, NULL, argv[1], AFI_IP6, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL); +} + +/* 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 + "IPv6 prefix /, e.g., 3ffe::/16\n") +{ + return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL); +} + +DEFUN (show_bgp_view, + show_bgp_view_cmd, + "show bgp view WORD", + SHOW_STR + BGP_STR + "BGP view\n" + "View name\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); +} + +DEFUN (show_bgp_view_ipv6, + show_bgp_view_ipv6_cmd, + "show bgp view WORD ipv6", + SHOW_STR + BGP_STR + "BGP view\n" + "View name\n" + "Address family\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); +} + +DEFUN (show_bgp_view_route, + show_bgp_view_route_cmd, + "show bgp view WORD X:X::X:X", + SHOW_STR + BGP_STR + "BGP view\n" + "View name\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, BGP_PATH_ALL); +} + +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" + "Network in the BGP routing table to display\n") +{ + return bgp_show_route (vty, argv[0], argv[1], AFI_IP6, SAFI_UNICAST, NULL, 0, BGP_PATH_ALL); +} + +/* old command */ +DEFUN (show_ipv6_mbgp, + show_ipv6_mbgp_cmd, + "show ipv6 mbgp", + SHOW_STR + IP_STR + MBGP_STR) +{ + return bgp_show (vty, NULL, AFI_IP6, SAFI_MULTICAST, bgp_show_type_normal, + NULL); +} + +/* old command */ +DEFUN (show_ipv6_mbgp_route, + show_ipv6_mbgp_route_cmd, + "show ipv6 mbgp X:X::X:X", + SHOW_STR + IP_STR + MBGP_STR + "Network in the MBGP routing table to display\n") +{ + return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_MULTICAST, 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", + SHOW_STR + IP_STR + MBGP_STR + "IPv6 prefix /, e.g., 3ffe::/16\n") +{ + return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_MULTICAST, NULL, 1, BGP_PATH_ALL); +} + +DEFUN (show_bgp_view_prefix, + show_bgp_view_prefix_cmd, + "show bgp view WORD X:X::X:X/M", + SHOW_STR + BGP_STR + "BGP view\n" + "View name\n" + "IPv6 prefix /\n") +{ + return bgp_show_route (vty, argv[0], argv[1], AFI_IP6, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL); +} + +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" + "IPv6 prefix /\n") +{ + return bgp_show_route (vty, argv[0], argv[1], AFI_IP6, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL); +} + +static int +bgp_show_regexp (struct vty *vty, int argc, const char **argv, afi_t afi, + safi_t safi, enum bgp_show_type type) +{ + 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); + + regex = bgp_regcomp (regstr); + XFREE(MTYPE_TMP, regstr); + if (! regex) + { + vty_out (vty, "Can't compile regexp %s%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + + rc = bgp_show (vty, NULL, afi, safi, type, regex); + bgp_regex_free (regex); + return rc; +} + + +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 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_regexp); +} + +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 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 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 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_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); +} + +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 + "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 + "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 + 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_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 + "Address family\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") +{ + 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_regexp (vty, argc-1, argv+1, AFI_IP, safi, + bgp_show_type_regexp); +} + +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 + "Address family\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") +{ + 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_regexp (vty, argc-1, argv+1, AFI_IP6, safi, + bgp_show_type_regexp); +} + +DEFUN (show_bgp_ipv6_regexp, + show_bgp_ipv6_regexp_cmd, + "show bgp ipv6 regexp .LINE", + 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") +{ + return bgp_show_regexp (vty, argc, argv, AFI_IP6, SAFI_UNICAST, + bgp_show_type_regexp); +} + +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; + + 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 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); +} + +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 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_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 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_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 conforming to the prefix-list\n" + "IP prefix-list name\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); + + return bgp_show_prefix_list (vty, argv[1], AFI_IP, SAFI_UNICAST, + bgp_show_type_prefix_list); +} + +DEFUN (show_bgp_prefix_list, + show_bgp_prefix_list_cmd, + "show bgp prefix-list WORD", + SHOW_STR + BGP_STR + "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_bgp_prefix_list, + show_bgp_ipv6_prefix_list_cmd, + "show bgp ipv6 prefix-list WORD", + 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); +} + +DEFUN (show_bgp_ipv4_prefix_list, + show_bgp_ipv4_prefix_list_cmd, + "show bgp ipv4 prefix-list WORD", + SHOW_STR + BGP_STR + 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); +} + +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 + 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_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 + 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 + 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 + 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") + +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 + "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); +} + +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" + "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); +} + +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 + IP_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_IP, SAFI_UNICAST, + bgp_show_type_filter_list); +} + +DEFUN (show_ip_bgp_flap_filter_list, + show_ip_bgp_flap_filter_list_cmd, + "show ip bgp flap-statistics filter-list WORD", + 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") +{ + 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 */ +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 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_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 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_ipv4_filter_list, + show_bgp_ipv4_filter_list_cmd, + "show bgp ipv4 filter-list WORD", + SHOW_STR + BGP_STR + IP_STR + "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_filter_list); +} + +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 + 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; + + 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 + 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") + +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 + 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; + + 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 + 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") + +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 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_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 + "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 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); +} + + +DEFUN (show_ip_bgp_ipv4_dampening_parameters, + show_ip_bgp_ipv4_dampening_parameters_cmd, + "show ip bgp ipv4 (unicast|multicast) dampening parameters", + SHOW_STR + IP_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display detailed information about dampening\n" + "Display detail of configured dampening parameters\n") +{ + 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); +} + + +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 + IP_STR + BGP_STR + "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); + + 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 + IP_STR + BGP_STR + "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); + + 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 + IP_STR + 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_IP, SAFI_UNICAST, + bgp_show_type_route_map); +} + +DEFUN (show_ip_bgp_flap_route_map, + show_ip_bgp_flap_route_map_cmd, + "show ip bgp flap-statistics route-map 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") +{ + return bgp_show_route_map (vty, argv[0], AFI_IP, SAFI_UNICAST, + bgp_show_type_flap_route_map); +} + +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 + 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") + +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 + 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") +{ + if (strncmp (argv[0], "m", 1) == 0) + return bgp_show_route_map (vty, argv[1], AFI_IP, SAFI_MULTICAST, + bgp_show_type_route_map); + + return bgp_show_route_map (vty, argv[1], AFI_IP, SAFI_UNICAST, + bgp_show_type_route_map); +} + +DEFUN (show_bgp_route_map, + show_bgp_route_map_cmd, + "show bgp route-map WORD", + SHOW_STR + 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); +} + +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") +{ + return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, + bgp_show_type_cidr_only, NULL); +} + +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 flap statistics of routes\n" + "Display only routes with non-natural netmasks\n") +{ + return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, + bgp_show_type_flap_cidr_only, NULL); +} + +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 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", + SHOW_STR + IP_STR + BGP_STR + "Display routes matching the communities\n") +{ + return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, + bgp_show_type_community_all, NULL); +} + +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 communities\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); +} + +DEFUN (show_bgp_community_all, + show_bgp_community_all_cmd, + "show bgp community", + SHOW_STR + BGP_STR + "Display routes matching the communities\n") +{ + return bgp_show (vty, NULL, AFI_IP6, SAFI_UNICAST, + bgp_show_type_community_all, NULL); +} + +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 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") +{ + return bgp_show (vty, NULL, AFI_IP6, SAFI_UNICAST, + bgp_show_type_community_all, NULL); +} + +/* 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); +} + +/* 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 large-communities\n") +{ + return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, + bgp_show_type_lcommunity_all, NULL); +} + +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" + "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_lcommunity_all, + show_ipv6_bgp_lcommunity_all_cmd, + "show ipv6 bgp large-community", + SHOW_STR + IPV6_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); +} + +/* old command */ +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 large-communities\n") +{ + return bgp_show (vty, NULL, AFI_IP6, SAFI_MULTICAST, + bgp_show_type_lcommunity_all, NULL); +} + + +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_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 + 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 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_IP, safi, + bgp_show_type_flap_route_map); +} + +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" + "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_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_bgp_ipv4_safi_route_map, + show_bgp_ipv4_safi_route_map_cmd, + "show bgp ipv4 (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_IP, safi, + bgp_show_type_route_map); +} + +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_bgp_ipv6_route_map, + show_bgp_ipv6_route_map_cmd, + "show bgp ipv6 route-map WORD", + SHOW_STR + BGP_STR + "Address family\n" + "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); +} + +DEFUN (show_bgp_ipv4_cidr_only, + show_bgp_ipv4_cidr_only_cmd, + "show bgp ipv4 cidr-only", + SHOW_STR + BGP_STR + IP_STR + "Display only routes with non-natural netmasks\n") +{ + return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, + bgp_show_type_cidr_only, NULL); +} + +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 + "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") +{ + 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_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" + "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") + +DEFUN (show_bgp_ipv4_safi_cidr_only, + show_bgp_ipv4_safi_cidr_only_cmd, + "show bgp ipv4 (unicast|multicast) cidr-only", + SHOW_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); +} + +/* 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 + 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") +{ + safi_t safi; + afi_t afi; + + 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; + } + + 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 == 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; + } + + 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); + + 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) + return CMD_WARNING; + + return bgp_peer_counts (vty, peer, AFI_IP, SAFI_MPLS_VPN); +} + +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") +{ + 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_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 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_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") +{ + 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_ENCAP); +} + + +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; + int header1 = 1; + 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; + } + + 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++; + } + } + } + + 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]) + { + vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (in && ! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG)) + { + vty_out (vty, "%% Inbound soft reconfiguration not enabled%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + 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", + 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 routes advertised to a BGP 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) + return CMD_WARNING; + + return peer_adj_routes (vty, peer, AFI_IP, SAFI_UNICAST, 0); +} + +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" + "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; + + 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" + "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") +{ + 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); +} + +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; + + 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, 1); +} + +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); +} + +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; + + 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, 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", + 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 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", + 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 information received from a BGP neighbor\n" + "Display the prefixlist filter\n") +{ + char name[BUFSIZ]; + union sockunion su; + struct peer *peer; + int count, ret; + + 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; + + 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; +} + +/* 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") + +/* 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 peer_adj_routes (vty, peer, AFI_IP6, SAFI_MULTICAST, 1); +} + +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") +{ + char name[BUFSIZ]; + union sockunion su; + struct peer *peer; + struct bgp *bgp; + int count, ret; + + /* 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; + } + + 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); + 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); + } + + return CMD_SUCCESS; +} + + +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 + "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 received routes from neighbor\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 peer_adj_routes (vty, peer, AFI_IP, SAFI_MULTICAST, 1); + + return peer_adj_routes (vty, peer, AFI_IP, SAFI_UNICAST, 1); +} + +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 + BGP_STR + "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 (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, 0); +} + +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 + "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 (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_IP6, safi, 0); +} + +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 + 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 the routes advertised to a BGP 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) + return CMD_WARNING; + + return peer_adj_routes (vty, peer, AFI_IP6, SAFI_UNICAST, 0); +} + +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 + 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 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) + return CMD_WARNING; + + return peer_adj_routes (vty, peer, AFI_IP6, SAFI_UNICAST, 1); +} + +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 + 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; + + 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 + 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; + + 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, + "show bgp view WORD (ipv4|ipv6) (unicast|multicast) neighbors (A.B.C.D|X:X::X:X) (advertised-routes|received-routes)", + SHOW_STR + BGP_STR + "BGP view\n" + "View name\n" + "Address family\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 advertised routes to neighbor\n" + "Display the received routes from neighbor\n") +{ + int afi; + int safi; + int in; + struct peer *peer; + + peer = peer_lookup_in_view (vty, argv[0], argv[3]); + + if (! peer) + 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; + in = (strncmp (argv[4], "r", 1) == 0) ? 1 : 0; + + return peer_adj_routes (vty, peer, afi, safi, in); +} + +DEFUN (show_ip_bgp_neighbor_received_prefix_filter, + show_ip_bgp_neighbor_received_prefix_filter_cmd, + "show ip bgp neighbors (A.B.C.D|X:X::X:X) received prefix-filter", + 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 information received from a BGP neighbor\n" + "Display the prefixlist filter\n") +{ + char name[BUFSIZ]; + union sockunion su; + struct peer *peer; + int count, ret; + + 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; + + sprintf (name, "%s.%d.%d", peer->host, AFI_IP, SAFI_UNICAST); + count = prefix_bgp_show_prefix_list (NULL, AFI_IP, name); + if (count) + { + vty_out (vty, "Address family: IPv4 Unicast%s", VTY_NEWLINE); + prefix_bgp_show_prefix_list (vty, AFI_IP, name); + } + + return CMD_SUCCESS; +} + +DEFUN (show_ip_bgp_ipv4_neighbor_received_prefix_filter, + show_ip_bgp_ipv4_neighbor_received_prefix_filter_cmd, + "show ip bgp ipv4 (unicast|multicast) 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 information received from a BGP neighbor\n" + "Display the prefixlist filter\n") +{ + char name[BUFSIZ]; + union sockunion su; + struct peer *peer; + int count, ret; + + 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; + + if (strncmp (argv[0], "m", 1) == 0) + { + sprintf (name, "%s.%d.%d", peer->host, AFI_IP, SAFI_MULTICAST); + count = prefix_bgp_show_prefix_list (NULL, AFI_IP, name); + if (count) + { + vty_out (vty, "Address family: IPv4 Multicast%s", VTY_NEWLINE); + prefix_bgp_show_prefix_list (vty, AFI_IP, name); + } + } + else + { + sprintf (name, "%s.%d.%d", peer->host, AFI_IP, SAFI_UNICAST); + count = prefix_bgp_show_prefix_list (NULL, AFI_IP, name); + if (count) + { + vty_out (vty, "Address family: IPv4 Unicast%s", VTY_NEWLINE); + prefix_bgp_show_prefix_list (vty, AFI_IP, name); + } + } + + return CMD_SUCCESS; +} + +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", + 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 received routes from neighbor\n") + +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" + "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; + + 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); + if (! peer) + return CMD_WARNING; + + 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; +} + +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 + 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; + + 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); + 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 + 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 information received from a BGP neighbor\n" + "Display the prefixlist filter\n") +{ + char name[BUFSIZ]; + union sockunion su; + struct peer *peer; + int count, ret; + + 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; + + 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_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" + "Display information received from a BGP neighbor\n" + "Display the prefixlist filter\n") +{ + char name[BUFSIZ]; + union sockunion su; + struct peer *peer; + struct bgp *bgp; + int count, ret; + + /* 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; + } + + 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); + 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); + } + + return CMD_SUCCESS; +} + +static int +bgp_show_neighbor_route (struct vty *vty, struct peer *peer, afi_t afi, + safi_t safi, enum bgp_show_type type) +{ + if (! peer || ! peer->afc[afi][safi]) + { + vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE); + return CMD_WARNING; + } + + 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", + 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 routes learned from neighbor\n") +{ + struct peer *peer; + + peer = peer_lookup_in_view (vty, NULL, argv[0]); + if (! peer) + return CMD_WARNING; + + return bgp_show_neighbor_route (vty, peer, AFI_IP, SAFI_UNICAST, + bgp_show_type_neighbor); +} + +DEFUN (show_ip_bgp_neighbor_flap, + show_ip_bgp_neighbor_flap_cmd, + "show ip bgp neighbors (A.B.C.D|X:X::X:X) flap-statistics", + 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 flap statistics of the routes learned from neighbor\n") +{ + struct peer *peer; + + peer = peer_lookup_in_view (vty, NULL, argv[0]); + if (! peer) + return CMD_WARNING; + + return bgp_show_neighbor_route (vty, peer, AFI_IP, SAFI_UNICAST, + bgp_show_type_flap_neighbor); +} + +DEFUN (show_ip_bgp_neighbor_damp, + show_ip_bgp_neighbor_damp_cmd, + "show ip bgp neighbors (A.B.C.D|X:X::X:X) dampened-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 dampened routes received from neighbor\n") +{ + struct peer *peer; + + peer = peer_lookup_in_view (vty, NULL, argv[0]); + if (! peer) + return CMD_WARNING; + + return bgp_show_neighbor_route (vty, peer, AFI_IP, SAFI_UNICAST, + bgp_show_type_damp_neighbor); +} + +DEFUN (show_ip_bgp_ipv4_neighbor_routes, + show_ip_bgp_ipv4_neighbor_routes_cmd, + "show ip bgp ipv4 (unicast|multicast) neighbors (A.B.C.D|X:X::X:X) 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" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Display routes learned from neighbor\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_show_neighbor_route (vty, peer, AFI_IP, SAFI_MULTICAST, + bgp_show_type_neighbor); + + return bgp_show_neighbor_route (vty, peer, AFI_IP, SAFI_UNICAST, + bgp_show_type_neighbor); +} + +DEFUN (show_ip_bgp_view_rsclient, + show_ip_bgp_view_rsclient_cmd, + "show ip bgp view WORD rsclient (A.B.C.D|X:X::X:X)", + SHOW_STR + IP_STR + BGP_STR + "BGP view\n" + "View name\n" + "Information about Route Server Client\n" + NEIGHBOR_ADDR_STR) +{ + 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); +} + +ALIAS (show_ip_bgp_view_rsclient, + show_ip_bgp_rsclient_cmd, + "show ip bgp rsclient (A.B.C.D|X:X::X:X)", + SHOW_STR + IP_STR + BGP_STR + "Information about Route Server Client\n" + NEIGHBOR_ADDR_STR) + +DEFUN (show_bgp_view_ipv4_safi_rsclient, + show_bgp_view_ipv4_safi_rsclient_cmd, + "show bgp view WORD ipv4 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X)", + 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) +{ + struct bgp_table *table; + struct peer *peer; + safi_t safi; + + if (argc == 3) { + 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]) + { + 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], + 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]; + + 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)", + 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 + "Network in the BGP routing table to display\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; + } + } + 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; + } + + 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; +} + + 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], + (argc == 4) ? argv[3] : argv[2], + AFI_IP, safi, NULL, 0, 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_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" + "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") +{ + 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; + } + } + 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; + } + + 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; +} + + 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], + (argc == 4) ? argv[3] : argv[2], + AFI_IP, safi, NULL, 1, BGP_PATH_ALL); +} + +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", + SHOW_STR + IP_STR + BGP_STR + "BGP view\n" + "View name\n" + "Information about Route Server Client\n" + NEIGHBOR_ADDR_STR + "IP prefix /, e.g., 35.0.0.0/8\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, 1, BGP_PATH_ALL); +} + +ALIAS (show_ip_bgp_view_rsclient_prefix, + show_ip_bgp_rsclient_prefix_cmd, + "show ip bgp rsclient (A.B.C.D|X:X::X:X) A.B.C.D/M", + SHOW_STR + IP_STR + BGP_STR + "Information about Route Server Client\n" + NEIGHBOR_ADDR_STR + "IP prefix /, e.g., 35.0.0.0/8\n") + +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") + +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 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_neighbor_route (vty, peer, AFI_IP6, SAFI_UNICAST, + bgp_show_type_neighbor); +} + +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") +{ + 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_neighbor_route (vty, peer, AFI_IP6, SAFI_UNICAST, + bgp_show_type_damp_neighbor); +} + +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" + "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") +{ + 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_neighbor_route (vty, peer, AFI_IP6, SAFI_UNICAST, + bgp_show_type_damp_neighbor); +} + +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" + "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") +{ + 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_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" + "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") +{ + 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_neighbor_route (vty, peer, AFI_IP6, SAFI_UNICAST, + 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_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") + +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" + "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 routes learned 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) + return CMD_WARNING; + + return bgp_show_neighbor_route (vty, peer, AFI_IP6, SAFI_UNICAST, + bgp_show_type_neighbor); +} + +ALIAS (show_bgp_view_neighbor_routes, + show_bgp_neighbor_routes_cmd, + "show bgp neighbors (A.B.C.D|X:X::X:X) 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 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", + 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 routes learned from neighbor\n") + +/* old command */ +ALIAS (show_bgp_view_neighbor_routes, + ipv6_bgp_neighbor_routes_cmd, + "show ipv6 bgp neighbors (A.B.C.D|X:X::X:X) 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 routes learned from neighbor\n") + +/* old command */ +DEFUN (ipv6_mbgp_neighbor_routes, + ipv6_mbgp_neighbor_routes_cmd, + "show ipv6 mbgp neighbors (A.B.C.D|X:X::X:X) 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 routes learned from neighbor\n") +{ + struct peer *peer; + + peer = peer_lookup_in_view (vty, NULL, argv[0]); + if (! peer) + return CMD_WARNING; + + return bgp_show_neighbor_route (vty, peer, AFI_IP6, SAFI_MULTICAST, + bgp_show_type_neighbor); +} + +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", + 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 flap statistics of the routes learned 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", + 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 dampened routes received from neighbor\n") + +DEFUN (show_bgp_view_rsclient, + show_bgp_view_rsclient_cmd, + "show bgp view WORD rsclient (A.B.C.D|X:X::X:X)", + SHOW_STR + BGP_STR + "BGP view\n" + "View name\n" + "Information about Route Server Client\n" + NEIGHBOR_ADDR_STR) +{ + 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_rsclient, + show_bgp_rsclient_cmd, + "show bgp rsclient (A.B.C.D|X:X::X:X)", + SHOW_STR + BGP_STR + "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" + "View name\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Information about Route Server Client\n" + NEIGHBOR_ADDR_STR) +{ + struct bgp_table *table; + struct peer *peer; + safi_t safi; + + if (argc == 3) { + 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_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; + } + + table = peer->rib[AFI_IP6][safi]; + + return bgp_show_table (vty, table, &peer->remote_id, bgp_show_type_normal, NULL); +} + +ALIAS (show_bgp_view_ipv6_safi_rsclient, + show_bgp_ipv6_safi_rsclient_cmd, + "show bgp ipv6 (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_bgp_view_rsclient_route, + show_bgp_view_rsclient_route_cmd, + "show bgp view WORD rsclient (A.B.C.D|X:X::X:X) X:X::X:X", + 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") +{ + 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); +} + +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 + BGP_STR + "Information about Route Server Client\n" + 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" + "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") +{ + 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; + } + } + 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; + } + + if (! peer) + return CMD_WARNING; + + 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_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, 1, BGP_PATH_ALL); +} + +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" + "View name\n" + IP6_STR + "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_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, 1, BGP_PATH_ALL); +} + +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 + BGP_STR + "Information about Route Server Client\n" + 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" + "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., 3ffe::/16\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; + } + } + 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; + } + + if (! peer) + return CMD_WARNING; + + 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, 1, BGP_PATH_ALL); +} + +ALIAS (show_bgp_view_ipv6_safi_rsclient_prefix, + show_bgp_ipv6_safi_rsclient_prefix_cmd, + "show bgp ipv6 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X) X:X::X:X/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., 3ffe::/16\n") + +struct bgp_table *bgp_distance_table; + +struct bgp_distance +{ + /* Distance value for the IP source prefix. */ + u_char distance; + + /* Name of the access-list to be matched. */ + char *access_list; +}; + +static struct bgp_distance * +bgp_distance_new (void) +{ + return XCALLOC (MTYPE_BGP_DISTANCE, sizeof (struct bgp_distance)); +} + +static void +bgp_distance_free (struct bgp_distance *bdistance) +{ + XFREE (MTYPE_BGP_DISTANCE, bdistance); +} + +static int +bgp_distance_set (struct vty *vty, const char *distance_str, + const char *ip_str, const char *access_list_str) +{ + int ret; + struct prefix p; + u_char distance; + struct bgp_node *rn; + struct bgp_distance *bdistance; + + ret = str2prefix (ip_str, &p); + if (ret == 0) + { + vty_out (vty, "Malformed prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + + distance = atoi (distance_str); + + /* Get BGP distance node. */ + rn = bgp_node_get (bgp_distance_table, (struct prefix *) &p); + if (rn->info) + { + bdistance = rn->info; + bgp_unlock_node (rn); + } + else + { + bdistance = bgp_distance_new (); + rn->info = bdistance; + } + + /* Set distance value. */ + bdistance->distance = distance; + + /* Reset access-list configuration. */ + if (bdistance->access_list) + { + free (bdistance->access_list); + bdistance->access_list = NULL; + } + if (access_list_str) + bdistance->access_list = strdup (access_list_str); + + return CMD_SUCCESS; +} + +static int +bgp_distance_unset (struct vty *vty, const char *distance_str, + const char *ip_str, const char *access_list_str) +{ + int ret; + struct prefix p; + u_char distance; + struct bgp_node *rn; + struct bgp_distance *bdistance; + + ret = str2prefix (ip_str, &p); + if (ret == 0) + { + vty_out (vty, "Malformed prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + + distance = atoi (distance_str); + + rn = bgp_node_lookup (bgp_distance_table, (struct prefix *)&p); + if (! rn) + { + vty_out (vty, "Can't find specified prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + + 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); + + rn->info = NULL; + bgp_unlock_node (rn); + bgp_unlock_node (rn); + + return CMD_SUCCESS; +} + +/* Apply BGP information to distance method. */ +u_char +bgp_distance_apply (struct prefix *p, struct bgp_info *rinfo, struct bgp *bgp) +{ + struct bgp_node *rn; + struct prefix_ipv4 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_INET) + return 0; + + peer = rinfo->peer; + + if (peer->su.sa.sa_family != AF_INET) + return 0; + + memset (&q, 0, sizeof (struct prefix_ipv4)); + q.family = AF_INET; + q.prefix = peer->su.sin.sin_addr; + q.prefixlen = IPV4_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_IP, 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_IP][SAFI_UNICAST], p); + if (rn) + { + bgp_static = rn->info; + bgp_unlock_node (rn); + + if (bgp_static->backdoor) + { + if (bgp->distance_local) + return bgp->distance_local; + else + return ZEBRA_IBGP_DISTANCE_DEFAULT; + } + } + + if (peer->sort == BGP_PEER_EBGP) + { + if (bgp->distance_ebgp) + return bgp->distance_ebgp; + return ZEBRA_EBGP_DISTANCE_DEFAULT; + } + else + { + if (bgp->distance_ibgp) + return bgp->distance_ibgp; + return ZEBRA_IBGP_DISTANCE_DEFAULT; + } +} + +#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>", + "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->distance_ebgp = atoi (argv[0]); + bgp->distance_ibgp = atoi (argv[1]); + bgp->distance_local = atoi (argv[2]); + return CMD_SUCCESS; +} + +DEFUN (no_bgp_distance, + no_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->distance_ebgp= 0; + bgp->distance_ibgp = 0; + bgp->distance_local = 0; + return CMD_SUCCESS; +} + +ALIAS (no_bgp_distance, + no_bgp_distance2_cmd, + "no distance bgp", + NO_STR + "Define an administrative distance\n" + "BGP distance\n") + +DEFUN (bgp_distance_source, + bgp_distance_source_cmd, + "distance <1-255> A.B.C.D/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_bgp_distance_source, + no_bgp_distance_source_cmd, + "no distance <1-255> A.B.C.D/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 (bgp_distance_source_access_list, + bgp_distance_source_access_list_cmd, + "distance <1-255> A.B.C.D/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_bgp_distance_source_access_list, + no_bgp_distance_source_access_list_cmd, + "no distance <1-255> A.B.C.D/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; +} + +#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>", + "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") +{ + struct bgp *bgp; + int half = DEFAULT_HALF_LIFE * 60; + int reuse = DEFAULT_REUSE; + int suppress = DEFAULT_SUPPRESS; + int max = 4 * half; + + if (argc == 4) + { + half = atoi (argv[0]) * 60; + reuse = atoi (argv[1]); + suppress = atoi (argv[2]); + max = atoi (argv[3]) * 60; + } + else if (argc == 1) + { + half = atoi (argv[0]) * 60; + max = 4 * half; + } + + 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); +} + +ALIAS (bgp_damp_set, + bgp_damp_set2_cmd, + "bgp dampening <1-45>", + "BGP Specific commands\n" + "Enable route-flap dampening\n" + "Half-life time for the penalty\n") + +ALIAS (bgp_damp_set, + bgp_damp_set3_cmd, + "bgp dampening", + "BGP Specific commands\n" + "Enable route-flap dampening\n") + +DEFUN (bgp_damp_unset, + bgp_damp_unset_cmd, + "no bgp dampening", + NO_STR + "BGP Specific commands\n" + "Enable route-flap dampening\n") +{ + struct bgp *bgp; + + 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; + + 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_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 + 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") +{ + 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_bgp_ipv6_safi_flap_statistics, + show_bgp_ipv6_safi_flap_statistics_cmd, + "show bgp ipv6 (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") +{ + 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, + const char *ip_str, afi_t afi, safi_t safi, + struct prefix_rd *prd, int prefix_check) +{ + int ret; + struct prefix match; + struct bgp_node *rn; + struct bgp_node *rm; + struct bgp_info *ri; + struct bgp_info *ri_temp; + struct bgp *bgp; + struct bgp_table *table; + + /* 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); + + if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)) + { + 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; + + if ((table = rn->info) != NULL) + if ((rm = bgp_node_match (table, &match)) != NULL) + { + if (! prefix_check || rm->p.prefixlen == match.prefixlen) + { + ri = rm->info; + while (ri) + { + if (ri->extra && ri->extra->damp_info) + { + ri_temp = ri->next; + bgp_damp_info_free (ri->extra->damp_info, 1); + ri = ri_temp; + } + else + ri = ri->next; + } + } + + bgp_unlock_node (rm); + } + } + } + else + { + if ((rn = bgp_node_match (bgp->rib[afi][safi], &match)) != NULL) + { + if (! prefix_check || rn->p.prefixlen == match.prefixlen) + { + ri = rn->info; + while (ri) + { + if (ri->extra && ri->extra->damp_info) + { + ri_temp = ri->next; + bgp_damp_info_free (ri->extra->damp_info, 1); + ri = ri_temp; + } + else + ri = ri->next; + } + } + + bgp_unlock_node (rn); + } + } + + return CMD_SUCCESS; +} + +DEFUN (clear_ip_bgp_dampening, + clear_ip_bgp_dampening_cmd, + "clear ip bgp dampening", + CLEAR_STR + IP_STR + BGP_STR + "Clear route flap dampening information\n") +{ + bgp_damp_info_clean (); + return CMD_SUCCESS; +} + +DEFUN (clear_ip_bgp_dampening_prefix, + clear_ip_bgp_dampening_prefix_cmd, + "clear ip bgp dampening A.B.C.D/M", + CLEAR_STR + IP_STR + BGP_STR + "Clear route flap dampening information\n" + "IP prefix /, e.g., 35.0.0.0/8\n") +{ + return bgp_clear_damp_route (vty, NULL, argv[0], AFI_IP, + SAFI_UNICAST, NULL, 1); +} + +DEFUN (clear_ip_bgp_dampening_address, + clear_ip_bgp_dampening_address_cmd, + "clear ip bgp dampening A.B.C.D", + CLEAR_STR + IP_STR + BGP_STR + "Clear route flap dampening information\n" + "Network to clear damping information\n") +{ + return bgp_clear_damp_route (vty, NULL, argv[0], AFI_IP, + SAFI_UNICAST, NULL, 0); +} + +DEFUN (clear_ip_bgp_dampening_address_mask, + clear_ip_bgp_dampening_address_mask_cmd, + "clear ip bgp dampening A.B.C.D A.B.C.D", + CLEAR_STR + IP_STR + BGP_STR + "Clear route flap dampening information\n" + "Network to clear damping information\n" + "Network mask\n") +{ + int ret; + char prefix_str[BUFSIZ]; + + ret = netmask_str2prefix_str (argv[0], argv[1], prefix_str); + if (! ret) + { + vty_out (vty, "%% Inconsistent address and mask%s", VTY_NEWLINE); + return CMD_WARNING; + } + + 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) +{ + struct bgp_node *prn; + struct bgp_node *rn; + struct bgp_table *table; + struct prefix *p; + struct prefix_rd *prd; + struct bgp_static *bgp_static; + u_int32_t label; + char buf[SU_ADDRSTRLEN]; + char rdbuf[RD_ADDRSTRLEN]; + + /* Network configuration. */ + for (prn = bgp_table_top (bgp->route[afi][safi]); prn; prn = bgp_route_next (prn)) + if ((table = prn->info) != NULL) + for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) + if ((bgp_static = rn->info) != NULL) + { + p = &rn->p; + prd = (struct prefix_rd *) &prn->p; + + /* "address-family" display. */ + bgp_config_write_family_header (vty, afi, safi, write); + + /* "network" configuration display. */ + prefix_rd2str (prd, rdbuf, RD_ADDRSTRLEN); + label = decode_label (bgp_static->tag); + + vty_out (vty, " network %s/%d rd %s tag %d", + inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), + p->prefixlen, + rdbuf, label); + vty_out (vty, "%s", VTY_NEWLINE); + } + return 0; +} + +/* Configuration of static route announcement and aggregate + information. */ +int +bgp_config_write_network (struct vty *vty, struct bgp *bgp, + afi_t afi, safi_t safi, int *write) +{ + struct bgp_node *rn; + struct prefix *p; + struct bgp_static *bgp_static; + struct bgp_aggregate *bgp_aggregate; + char buf[SU_ADDRSTRLEN]; + + if (afi == AFI_IP && ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP))) + return bgp_config_write_network_vpnv4 (vty, bgp, afi, safi, write); + + /* Network configuration. */ + for (rn = bgp_table_top (bgp->route[afi][safi]); rn; rn = bgp_route_next (rn)) + if ((bgp_static = rn->info) != NULL) + { + p = &rn->p; + + /* "address-family" display. */ + bgp_config_write_family_header (vty, afi, safi, write); + + /* "network" configuration display. */ + if (bgp_option_check (BGP_OPT_CONFIG_CISCO) && afi == AFI_IP) + { + u_int32_t destination; + struct in_addr netmask; + + destination = ntohl (p->u.prefix4.s_addr); + masklen2ip (p->prefixlen, &netmask); + vty_out (vty, " network %s", + inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN)); + + if ((IN_CLASSC (destination) && p->prefixlen == 24) + || (IN_CLASSB (destination) && p->prefixlen == 16) + || (IN_CLASSA (destination) && p->prefixlen == 8) + || p->u.prefix4.s_addr == 0) + { + /* Natural mask is not display. */ + } + else + vty_out (vty, " mask %s", inet_ntoa (netmask)); + } + else + { + vty_out (vty, " network %s/%d", + inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), + p->prefixlen); + } + + if (bgp_static->rmap.name) + vty_out (vty, " route-map %s", bgp_static->rmap.name); + else + { + if (bgp_static->backdoor) + vty_out (vty, " backdoor"); + } + + vty_out (vty, "%s", VTY_NEWLINE); + } + + /* Aggregate-address configuration. */ + for (rn = bgp_table_top (bgp->aggregate[afi][safi]); rn; rn = bgp_route_next (rn)) + if ((bgp_aggregate = rn->info) != NULL) + { + p = &rn->p; + + /* "address-family" display. */ + bgp_config_write_family_header (vty, afi, safi, write); + + if (bgp_option_check (BGP_OPT_CONFIG_CISCO) && afi == AFI_IP) + { + struct in_addr netmask; + + masklen2ip (p->prefixlen, &netmask); + vty_out (vty, " aggregate-address %s %s", + inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), + inet_ntoa (netmask)); + } + else + { + vty_out (vty, " aggregate-address %s/%d", + inet_ntop (p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), + p->prefixlen); + } + + if (bgp_aggregate->as_set) + vty_out (vty, " as-set"); + + if (bgp_aggregate->summary_only) + vty_out (vty, " summary-only"); + + vty_out (vty, "%s", VTY_NEWLINE); + } + + return 0; +} + +int +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; + + 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; +} + +/* Allocate routing table structure and install commands. */ +void +bgp_route_init (void) +{ + /* Init BGP distance table. */ + bgp_distance_table = bgp_table_init (AFI_IP, SAFI_UNICAST); + + /* IPv4 BGP commands. */ + install_element (BGP_NODE, &bgp_network_cmd); + install_element (BGP_NODE, &bgp_network_mask_cmd); + install_element (BGP_NODE, &bgp_network_mask_natural_cmd); + install_element (BGP_NODE, &bgp_network_route_map_cmd); + install_element (BGP_NODE, &bgp_network_mask_route_map_cmd); + install_element (BGP_NODE, &bgp_network_mask_natural_route_map_cmd); + install_element (BGP_NODE, &bgp_network_backdoor_cmd); + install_element (BGP_NODE, &bgp_network_mask_backdoor_cmd); + install_element (BGP_NODE, &bgp_network_mask_natural_backdoor_cmd); + install_element (BGP_NODE, &no_bgp_network_cmd); + install_element (BGP_NODE, &no_bgp_network_mask_cmd); + install_element (BGP_NODE, &no_bgp_network_mask_natural_cmd); + install_element (BGP_NODE, &no_bgp_network_route_map_cmd); + install_element (BGP_NODE, &no_bgp_network_mask_route_map_cmd); + install_element (BGP_NODE, &no_bgp_network_mask_natural_route_map_cmd); + install_element (BGP_NODE, &no_bgp_network_backdoor_cmd); + install_element (BGP_NODE, &no_bgp_network_mask_backdoor_cmd); + install_element (BGP_NODE, &no_bgp_network_mask_natural_backdoor_cmd); + + install_element (BGP_NODE, &aggregate_address_cmd); + install_element (BGP_NODE, &aggregate_address_mask_cmd); + install_element (BGP_NODE, &aggregate_address_summary_only_cmd); + install_element (BGP_NODE, &aggregate_address_mask_summary_only_cmd); + install_element (BGP_NODE, &aggregate_address_as_set_cmd); + install_element (BGP_NODE, &aggregate_address_mask_as_set_cmd); + install_element (BGP_NODE, &aggregate_address_as_set_summary_cmd); + install_element (BGP_NODE, &aggregate_address_mask_as_set_summary_cmd); + install_element (BGP_NODE, &aggregate_address_summary_as_set_cmd); + install_element (BGP_NODE, &aggregate_address_mask_summary_as_set_cmd); + install_element (BGP_NODE, &no_aggregate_address_cmd); + install_element (BGP_NODE, &no_aggregate_address_summary_only_cmd); + install_element (BGP_NODE, &no_aggregate_address_as_set_cmd); + install_element (BGP_NODE, &no_aggregate_address_as_set_summary_cmd); + install_element (BGP_NODE, &no_aggregate_address_summary_as_set_cmd); + install_element (BGP_NODE, &no_aggregate_address_mask_cmd); + install_element (BGP_NODE, &no_aggregate_address_mask_summary_only_cmd); + install_element (BGP_NODE, &no_aggregate_address_mask_as_set_cmd); + install_element (BGP_NODE, &no_aggregate_address_mask_as_set_summary_cmd); + install_element (BGP_NODE, &no_aggregate_address_mask_summary_as_set_cmd); + + /* IPv4 unicast configuration. */ + install_element (BGP_IPV4_NODE, &bgp_network_cmd); + install_element (BGP_IPV4_NODE, &bgp_network_mask_cmd); + install_element (BGP_IPV4_NODE, &bgp_network_mask_natural_cmd); + install_element (BGP_IPV4_NODE, &bgp_network_route_map_cmd); + install_element (BGP_IPV4_NODE, &bgp_network_mask_route_map_cmd); + install_element (BGP_IPV4_NODE, &bgp_network_mask_natural_route_map_cmd); + install_element (BGP_IPV4_NODE, &no_bgp_network_cmd); + install_element (BGP_IPV4_NODE, &no_bgp_network_mask_cmd); + install_element (BGP_IPV4_NODE, &no_bgp_network_mask_natural_cmd); + install_element (BGP_IPV4_NODE, &no_bgp_network_route_map_cmd); + install_element (BGP_IPV4_NODE, &no_bgp_network_mask_route_map_cmd); + install_element (BGP_IPV4_NODE, &no_bgp_network_mask_natural_route_map_cmd); + + install_element (BGP_IPV4_NODE, &aggregate_address_cmd); + install_element (BGP_IPV4_NODE, &aggregate_address_mask_cmd); + install_element (BGP_IPV4_NODE, &aggregate_address_summary_only_cmd); + install_element (BGP_IPV4_NODE, &aggregate_address_mask_summary_only_cmd); + install_element (BGP_IPV4_NODE, &aggregate_address_as_set_cmd); + install_element (BGP_IPV4_NODE, &aggregate_address_mask_as_set_cmd); + install_element (BGP_IPV4_NODE, &aggregate_address_as_set_summary_cmd); + install_element (BGP_IPV4_NODE, &aggregate_address_mask_as_set_summary_cmd); + install_element (BGP_IPV4_NODE, &aggregate_address_summary_as_set_cmd); + install_element (BGP_IPV4_NODE, &aggregate_address_mask_summary_as_set_cmd); + install_element (BGP_IPV4_NODE, &no_aggregate_address_cmd); + install_element (BGP_IPV4_NODE, &no_aggregate_address_summary_only_cmd); + install_element (BGP_IPV4_NODE, &no_aggregate_address_as_set_cmd); + install_element (BGP_IPV4_NODE, &no_aggregate_address_as_set_summary_cmd); + install_element (BGP_IPV4_NODE, &no_aggregate_address_summary_as_set_cmd); + install_element (BGP_IPV4_NODE, &no_aggregate_address_mask_cmd); + install_element (BGP_IPV4_NODE, &no_aggregate_address_mask_summary_only_cmd); + install_element (BGP_IPV4_NODE, &no_aggregate_address_mask_as_set_cmd); + install_element (BGP_IPV4_NODE, &no_aggregate_address_mask_as_set_summary_cmd); + install_element (BGP_IPV4_NODE, &no_aggregate_address_mask_summary_as_set_cmd); + + /* IPv4 multicast configuration. */ + install_element (BGP_IPV4M_NODE, &bgp_network_cmd); + install_element (BGP_IPV4M_NODE, &bgp_network_mask_cmd); + install_element (BGP_IPV4M_NODE, &bgp_network_mask_natural_cmd); + install_element (BGP_IPV4M_NODE, &bgp_network_route_map_cmd); + install_element (BGP_IPV4M_NODE, &bgp_network_mask_route_map_cmd); + install_element (BGP_IPV4M_NODE, &bgp_network_mask_natural_route_map_cmd); + install_element (BGP_IPV4M_NODE, &no_bgp_network_cmd); + install_element (BGP_IPV4M_NODE, &no_bgp_network_mask_cmd); + install_element (BGP_IPV4M_NODE, &no_bgp_network_mask_natural_cmd); + install_element (BGP_IPV4M_NODE, &no_bgp_network_route_map_cmd); + install_element (BGP_IPV4M_NODE, &no_bgp_network_mask_route_map_cmd); + install_element (BGP_IPV4M_NODE, &no_bgp_network_mask_natural_route_map_cmd); + install_element (BGP_IPV4M_NODE, &aggregate_address_cmd); + install_element (BGP_IPV4M_NODE, &aggregate_address_mask_cmd); + install_element (BGP_IPV4M_NODE, &aggregate_address_summary_only_cmd); + install_element (BGP_IPV4M_NODE, &aggregate_address_mask_summary_only_cmd); + install_element (BGP_IPV4M_NODE, &aggregate_address_as_set_cmd); + install_element (BGP_IPV4M_NODE, &aggregate_address_mask_as_set_cmd); + install_element (BGP_IPV4M_NODE, &aggregate_address_as_set_summary_cmd); + install_element (BGP_IPV4M_NODE, &aggregate_address_mask_as_set_summary_cmd); + install_element (BGP_IPV4M_NODE, &aggregate_address_summary_as_set_cmd); + install_element (BGP_IPV4M_NODE, &aggregate_address_mask_summary_as_set_cmd); + install_element (BGP_IPV4M_NODE, &no_aggregate_address_cmd); + install_element (BGP_IPV4M_NODE, &no_aggregate_address_summary_only_cmd); + install_element (BGP_IPV4M_NODE, &no_aggregate_address_as_set_cmd); + install_element (BGP_IPV4M_NODE, &no_aggregate_address_as_set_summary_cmd); + install_element (BGP_IPV4M_NODE, &no_aggregate_address_summary_as_set_cmd); + install_element (BGP_IPV4M_NODE, &no_aggregate_address_mask_cmd); + install_element (BGP_IPV4M_NODE, &no_aggregate_address_mask_summary_only_cmd); + install_element (BGP_IPV4M_NODE, &no_aggregate_address_mask_as_set_cmd); + 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_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_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_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); + install_element (VIEW_NODE, &show_ip_bgp_view_route_cmd); + install_element (VIEW_NODE, &show_ip_bgp_view_prefix_cmd); + install_element (VIEW_NODE, &show_ip_bgp_regexp_cmd); + install_element (VIEW_NODE, &show_ip_bgp_ipv4_regexp_cmd); + install_element (VIEW_NODE, &show_ip_bgp_prefix_list_cmd); + install_element (VIEW_NODE, &show_ip_bgp_ipv4_prefix_list_cmd); + install_element (VIEW_NODE, &show_ip_bgp_filter_list_cmd); + install_element (VIEW_NODE, &show_ip_bgp_ipv4_filter_list_cmd); + install_element (VIEW_NODE, &show_ip_bgp_route_map_cmd); + install_element (VIEW_NODE, &show_ip_bgp_ipv4_route_map_cmd); + install_element (VIEW_NODE, &show_ip_bgp_cidr_only_cmd); + install_element (VIEW_NODE, &show_ip_bgp_ipv4_cidr_only_cmd); + install_element (VIEW_NODE, &show_ip_bgp_community_all_cmd); + install_element (VIEW_NODE, &show_ip_bgp_ipv4_community_all_cmd); + install_element (VIEW_NODE, &show_ip_bgp_community_cmd); + install_element (VIEW_NODE, &show_ip_bgp_community2_cmd); + install_element (VIEW_NODE, &show_ip_bgp_community3_cmd); + install_element (VIEW_NODE, &show_ip_bgp_community4_cmd); + install_element (VIEW_NODE, &show_ip_bgp_ipv4_community_cmd); + 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_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); + install_element (VIEW_NODE, &show_ip_bgp_community4_exact_cmd); + install_element (VIEW_NODE, &show_ip_bgp_ipv4_community_exact_cmd); + install_element (VIEW_NODE, &show_ip_bgp_ipv4_community2_exact_cmd); + install_element (VIEW_NODE, &show_ip_bgp_ipv4_community3_exact_cmd); + install_element (VIEW_NODE, &show_ip_bgp_ipv4_community4_exact_cmd); + install_element (VIEW_NODE, &show_ip_bgp_community_list_cmd); + 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_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_ip_bgp_rsclient_route_cmd); + install_element (VIEW_NODE, &show_ip_bgp_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_ip_bgp_view_rsclient_route_cmd); + install_element (VIEW_NODE, &show_ip_bgp_view_rsclient_prefix_cmd); + + 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_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_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); + install_element (RESTRICTED_NODE, &show_ip_bgp_view_prefix_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_community_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_community2_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_community3_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_community4_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_community_cmd); + 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_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); + install_element (RESTRICTED_NODE, &show_ip_bgp_community4_exact_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_community_exact_cmd); + 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_ip_bgp_rsclient_prefix_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_view_rsclient_route_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_view_rsclient_prefix_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_route_cmd); + install_element (VIEW_NODE, &show_bgp_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_prefix_list_cmd); + install_element (VIEW_NODE, &show_bgp_filter_list_cmd); + install_element (VIEW_NODE, &show_bgp_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_community2_cmd); + install_element (VIEW_NODE, &show_bgp_community3_cmd); + install_element (VIEW_NODE, &show_bgp_community4_cmd); + install_element (VIEW_NODE, &show_bgp_community_exact_cmd); + install_element (VIEW_NODE, &show_bgp_community2_exact_cmd); + install_element (VIEW_NODE, &show_bgp_community3_exact_cmd); + install_element (VIEW_NODE, &show_bgp_community4_exact_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_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_neighbor_advertised_route_cmd); + install_element (VIEW_NODE, &show_bgp_neighbor_received_routes_cmd); + install_element (VIEW_NODE, &show_bgp_neighbor_routes_cmd); + install_element (VIEW_NODE, &show_bgp_neighbor_received_prefix_filter_cmd); + install_element (VIEW_NODE, &show_bgp_neighbor_flap_cmd); + install_element (VIEW_NODE, &show_bgp_neighbor_damp_cmd); + install_element (VIEW_NODE, &show_bgp_rsclient_cmd); + install_element (VIEW_NODE, &show_bgp_view_cmd); + install_element (VIEW_NODE, &show_bgp_view_route_cmd); + install_element (VIEW_NODE, &show_bgp_view_prefix_cmd); + install_element (VIEW_NODE, &show_bgp_view_neighbor_advertised_route_cmd); + install_element (VIEW_NODE, &show_bgp_view_neighbor_received_routes_cmd); + install_element (VIEW_NODE, &show_bgp_view_neighbor_routes_cmd); + install_element (VIEW_NODE, &show_bgp_view_neighbor_received_prefix_filter_cmd); + install_element (VIEW_NODE, &show_bgp_view_neighbor_flap_cmd); + install_element (VIEW_NODE, &show_bgp_view_neighbor_damp_cmd); + install_element (VIEW_NODE, &show_bgp_view_rsclient_cmd); + + install_element (RESTRICTED_NODE, &show_bgp_route_cmd); + install_element (RESTRICTED_NODE, &show_bgp_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_community2_cmd); + install_element (RESTRICTED_NODE, &show_bgp_community3_cmd); + install_element (RESTRICTED_NODE, &show_bgp_community4_cmd); + install_element (RESTRICTED_NODE, &show_bgp_community_exact_cmd); + install_element (RESTRICTED_NODE, &show_bgp_community2_exact_cmd); + install_element (RESTRICTED_NODE, &show_bgp_community3_exact_cmd); + install_element (RESTRICTED_NODE, &show_bgp_community4_exact_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_prefix_cmd); + install_element (RESTRICTED_NODE, &show_bgp_view_neighbor_received_prefix_filter_cmd); + + install_element (ENABLE_NODE, &show_bgp_statistics_vpnv4_cmd); + install_element (ENABLE_NODE, &show_bgp_statistics_view_vpnv4_cmd); + + 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); + install_element (VIEW_NODE, &show_ipv6_bgp_regexp_cmd); + install_element (VIEW_NODE, &show_ipv6_bgp_prefix_list_cmd); + install_element (VIEW_NODE, &show_ipv6_bgp_filter_list_cmd); + install_element (VIEW_NODE, &show_ipv6_bgp_community_all_cmd); + install_element (VIEW_NODE, &show_ipv6_bgp_community_cmd); + install_element (VIEW_NODE, &show_ipv6_bgp_community2_cmd); + install_element (VIEW_NODE, &show_ipv6_bgp_community3_cmd); + install_element (VIEW_NODE, &show_ipv6_bgp_community4_cmd); + install_element (VIEW_NODE, &show_ipv6_bgp_community_exact_cmd); + install_element (VIEW_NODE, &show_ipv6_bgp_community2_exact_cmd); + install_element (VIEW_NODE, &show_ipv6_bgp_community3_exact_cmd); + 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); + install_element (VIEW_NODE, &show_ipv6_mbgp_prefix_cmd); + install_element (VIEW_NODE, &show_ipv6_mbgp_regexp_cmd); + install_element (VIEW_NODE, &show_ipv6_mbgp_prefix_list_cmd); + install_element (VIEW_NODE, &show_ipv6_mbgp_filter_list_cmd); + install_element (VIEW_NODE, &show_ipv6_mbgp_community_all_cmd); + install_element (VIEW_NODE, &show_ipv6_mbgp_community_cmd); + install_element (VIEW_NODE, &show_ipv6_mbgp_community2_cmd); + install_element (VIEW_NODE, &show_ipv6_mbgp_community3_cmd); + install_element (VIEW_NODE, &show_ipv6_mbgp_community4_cmd); + install_element (VIEW_NODE, &show_ipv6_mbgp_community_exact_cmd); + install_element (VIEW_NODE, &show_ipv6_mbgp_community2_exact_cmd); + install_element (VIEW_NODE, &show_ipv6_mbgp_community3_exact_cmd); + 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); + install_element (VIEW_NODE, &ipv6_bgp_neighbor_advertised_route_cmd); + install_element (VIEW_NODE, &ipv6_mbgp_neighbor_advertised_route_cmd); + install_element (VIEW_NODE, &ipv6_bgp_neighbor_received_routes_cmd); + install_element (VIEW_NODE, &ipv6_mbgp_neighbor_received_routes_cmd); + install_element (VIEW_NODE, &ipv6_bgp_neighbor_routes_cmd); + install_element (VIEW_NODE, &ipv6_mbgp_neighbor_routes_cmd); + /* 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 (VIEW_NODE, &show_bgp_ipv6_community_list_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_community_list_exact_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); + + 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 +bgp_route_finish (void) +{ + bgp_table_unlock (bgp_distance_table); + bgp_distance_table = NULL; +} diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h new file mode 100644 index 0000000..332714c --- /dev/null +++ b/bgpd/bgp_route.h @@ -0,0 +1,273 @@ +/* BGP routing information base + Copyright (C) 1996, 97, 98, 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. */ + +#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. + */ +struct bgp_info_extra +{ + /* Pointer to dampening structure. */ + struct bgp_damp_info *damp_info; + + /* This route is suppressed with aggregation. */ + int suppress; + + /* Nexthop reachability check. */ + u_int32_t igpmetric; + + /* MPLS label. */ + u_char tag[3]; +}; + +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; + + /* Attribute structure. */ + struct attr *attr; + + /* Extra information */ + struct bgp_info_extra *extra; + + + /* Multipath information */ + struct bgp_info_mpath *mpath; + + /* Uptime. */ + time_t uptime; + + /* reference count */ + int lock; + + /* BGP information status. */ + u_int16_t flags; +#define BGP_INFO_IGP_CHANGED (1 << 0) +#define BGP_INFO_DAMPED (1 << 1) +#define BGP_INFO_HISTORY (1 << 2) +#define BGP_INFO_SELECTED (1 << 3) +#define BGP_INFO_VALID (1 << 4) +#define BGP_INFO_ATTR_CHANGED (1 << 5) +#define BGP_INFO_DMED_CHECK (1 << 6) +#define BGP_INFO_DMED_SELECTED (1 << 7) +#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; + + /* When above type is BGP. This sub type specify BGP sub type + information. */ + u_char sub_type; +#define BGP_ROUTE_NORMAL 0 +#define BGP_ROUTE_STATIC 1 +#define BGP_ROUTE_AGGREGATE 2 +#define BGP_ROUTE_REDISTRIBUTE 3 +}; + +/* BGP static route configuration. */ +struct bgp_static +{ + /* Backdoor configuration. */ + int backdoor; + + /* Import check status. */ + u_char valid; + + /* IGP metric. */ + u_int32_t igpmetric; + + /* IGP nexthop. */ + struct in_addr igpnexthop; + + /* Atomic set reference count (ie cause of pathlimit) */ + u_int32_t atomic; + + /* BGP redistribute route-map. */ + struct + { + char *name; + 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) +/* Macro to check BGP information is alive or not. Sadly, + * not equivalent to just checking previous, because of the + * sense of the additional VALID flag. + */ +#define BGP_INFO_HOLDDOWN(BI) \ + (! CHECK_FLAG ((BI)->flags, BGP_INFO_VALID) \ + || CHECK_FLAG ((BI)->flags, BGP_INFO_UNUSEABLE)) + +#define DISTRIBUTE_IN_NAME(F) ((F)->dlist[FILTER_IN].name) +#define DISTRIBUTE_IN(F) ((F)->dlist[FILTER_IN].alist) +#define DISTRIBUTE_OUT_NAME(F) ((F)->dlist[FILTER_OUT].name) +#define DISTRIBUTE_OUT(F) ((F)->dlist[FILTER_OUT].alist) + +#define PREFIX_LIST_IN_NAME(F) ((F)->plist[FILTER_IN].name) +#define PREFIX_LIST_IN(F) ((F)->plist[FILTER_IN].plist) +#define PREFIX_LIST_OUT_NAME(F) ((F)->plist[FILTER_OUT].name) +#define PREFIX_LIST_OUT(F) ((F)->plist[FILTER_OUT].plist) + +#define FILTER_LIST_IN_NAME(F) ((F)->aslist[FILTER_IN].name) +#define FILTER_LIST_IN(F) ((F)->aslist[FILTER_IN].aslist) +#define FILTER_LIST_OUT_NAME(F) ((F)->aslist[FILTER_OUT].name) +#define FILTER_LIST_OUT(F) ((F)->aslist[FILTER_OUT].aslist) + +#define ROUTE_MAP_IN_NAME(F) ((F)->map[RMAP_IN].name) +#define ROUTE_MAP_IN(F) ((F)->map[RMAP_IN].map) +#define ROUTE_MAP_OUT_NAME(F) ((F)->map[RMAP_OUT].name) +#define ROUTE_MAP_OUT(F) ((F)->map[RMAP_OUT].map) + +#define ROUTE_MAP_IMPORT_NAME(F) ((F)->map[RMAP_IMPORT].name) +#define ROUTE_MAP_IMPORT(F) ((F)->map[RMAP_IMPORT].map) +#define ROUTE_MAP_EXPORT_NAME(F) ((F)->map[RMAP_EXPORT].name) +#define ROUTE_MAP_EXPORT(F) ((F)->map[RMAP_EXPORT].map) + +#define UNSUPPRESS_MAP_NAME(F) ((F)->usmap.name) +#define UNSUPPRESS_MAP(F) ((F)->usmap.map) + +enum bgp_clear_route_type +{ + BGP_CLEAR_ROUTE_NORMAL, + 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); +extern void bgp_cleanup_routes (void); +extern void bgp_announce_route (struct peer *, afi_t, safi_t); +extern void bgp_announce_route_all (struct peer *); +extern void bgp_default_originate (struct peer *, afi_t, safi_t, int); +extern void bgp_soft_reconfig_in (struct peer *, afi_t, safi_t); +extern void bgp_soft_reconfig_rsclient (struct peer *, afi_t, safi_t); +extern void bgp_check_local_routes_rsclient (struct peer *rsclient, afi_t afi, safi_t safi); +extern void bgp_clear_route (struct peer *, afi_t, safi_t, + enum bgp_clear_route_type); +extern void bgp_clear_route_all (struct peer *); +extern void bgp_clear_adj_in (struct peer *, afi_t, safi_t); +extern void bgp_clear_stale_route (struct peer *, afi_t, safi_t); + +extern struct bgp_info *bgp_info_lock (struct bgp_info *); +extern struct bgp_info *bgp_info_unlock (struct bgp_info *); +extern void bgp_info_add (struct bgp_node *rn, struct bgp_info *ri); +extern void bgp_info_delete (struct bgp_node *rn, struct bgp_info *ri); +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_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, route_tag_t); +extern void bgp_redistribute_delete (struct prefix *, u_char); +extern void bgp_redistribute_withdraw (struct bgp *, afi_t, int); + +extern void bgp_static_delete (struct bgp *); +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_safi (safi_t safi, struct vty *vty, const char *, + const char *, const char *, 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 */ +extern int bgp_update (struct peer *, struct prefix *, struct attr *, + afi_t, safi_t, int, int, struct prefix_rd *, + u_char *, int); +extern int bgp_withdraw (struct peer *, struct prefix *, struct attr *, + afi_t, safi_t, int, int, struct prefix_rd *, u_char *); + +/* 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 *, afi_t, safi_t, int *); + +extern void bgp_aggregate_increment (struct bgp *, struct prefix *, struct bgp_info *, + afi_t, safi_t); +extern void bgp_aggregate_decrement (struct bgp *, struct prefix *, struct bgp_info *, + 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 *); + +extern void route_vty_out (struct vty *, struct prefix *, struct bgp_info *, int, safi_t); +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 new file mode 100644 index 0000000..ccd73b6 --- /dev/null +++ b/bgpd/bgp_routemap.c @@ -0,0 +1,4690 @@ +/* Route map function of bgpd. + Copyright (C) 1998, 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 "prefix.h" +#include "filter.h" +#include "routemap.h" +#include "command.h" +#include "linklist.h" +#include "plist.h" +#include "memory.h" +#include "log.h" +#ifdef HAVE_LIBPCREPOSIX +# include +#else +# ifdef HAVE_GNU_REGEX +# include +# else +# include "regex-gnu.h" +# endif /* HAVE_GNU_REGEX */ +#endif /* HAVE_LIBPCREPOSIX */ +#include "buffer.h" +#include "sockunion.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_table.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_regex.h" +#include "bgpd/bgp_community.h" +#include "bgpd/bgp_clist.h" +#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. + +o Cisco route-map + + match as-path : Done + community : Done + lcommunity : Done + interface : Not yet + ip address : Done + ip next-hop : Done + ip route-source : Done + ip prefix-list : Done + ipv6 address : Done + ipv6 next-hop : Done + ipv6 route-source: (This will not be implemented by bgpd) + ipv6 prefix-list : Done + length : (This will not be implemented by bgpd) + metric : Done + route-type : (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) + interface : (This will not be implemented by bgpd) + ip default : (This will not be implemented by bgpd) + ip next-hop : Done + ip precedence : (This will not be implemented by bgpd) + ip tos : (This will not be implemented by bgpd) + level : (This will not be implemented by bgpd) + local-preference : Done + metric : Done + metric-type : Not yet + origin : Done + tag : Done + weight : Done + +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 + received in bgp_info->peer. If it is the same, or if the peer structure + received is a peer_group containing it, returns RMAP_MATCH. */ +static route_map_result_t +route_match_peer (void *rule, struct prefix *prefix, route_map_object_t type, + void *object) +{ + union sockunion *su; + 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; + + if (type == RMAP_BGP) + { + su = rule; + peer = ((struct bgp_info *) object)->peer; + + if ( ! CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_IMPORT) && + ! CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_EXPORT) ) + return RMAP_NOMATCH; + + /* If su='0.0.0.0' (command 'match peer local'), and it's a NETWORK, + REDISTRIBUTE or DEFAULT_GENERATED route => return RMAP_MATCH */ + if (sockunion_same (su, &su_def)) + { + int ret; + if ( CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_NETWORK) || + CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_REDISTRIBUTE) || + CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_DEFAULT)) + ret = RMAP_MATCH; + else + ret = RMAP_NOMATCH; + return ret; + } + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + if (sockunion_same (su, &peer->su)) + return RMAP_MATCH; + + return RMAP_NOMATCH; + } + else + { + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + if (sockunion_same (su, &peer->su)) + return RMAP_MATCH; + } + return RMAP_NOMATCH; + } + } + return RMAP_NOMATCH; +} + +static void * +route_match_peer_compile (const char *arg) +{ + union sockunion *su; + int ret; + + su = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (union sockunion)); + + ret = str2sockunion (strcmp(arg, "local") ? arg : "0.0.0.0", su); + if (ret < 0) { + XFREE (MTYPE_ROUTE_MAP_COMPILED, su); + return NULL; + } + + return su; +} + +/* Free route map's compiled `ip address' value. */ +static void +route_match_peer_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for ip address matching. */ +struct route_map_rule_cmd route_match_peer_cmd = +{ + "peer", + route_match_peer, + route_match_peer_compile, + route_match_peer_free +}; + +/* `match ip address IP_ACCESS_LIST' */ + +/* Match function should return 1 if match is success else return + zero. */ +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; + /* struct prefix_ipv4 match; */ + + if (type == RMAP_BGP) + { + alist = access_list_lookup (AFI_IP, (char *) rule); + if (alist == NULL) + return RMAP_NOMATCH; + + return (access_list_apply (alist, prefix) == FILTER_DENY ? + RMAP_NOMATCH : RMAP_MATCH); + } + return RMAP_NOMATCH; +} + +/* Route map `ip address' match statement. `arg' should be + access-list name. */ +static void * +route_match_ip_address_compile (const char *arg) +{ + return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); +} + +/* Free route map's compiled `ip address' value. */ +static void +route_match_ip_address_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for ip address matching. */ +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 +}; + +/* `match ip next-hop IP_ADDRESS' */ + +/* Match function return 1 if match is success else return zero. */ +static route_map_result_t +route_match_ip_next_hop (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct access_list *alist; + struct bgp_info *bgp_info; + struct prefix_ipv4 p; + + if (type == RMAP_BGP) + { + bgp_info = object; + p.family = AF_INET; + p.prefix = bgp_info->attr->nexthop; + p.prefixlen = IPV4_MAX_BITLEN; + + alist = access_list_lookup (AFI_IP, (char *) rule); + if (alist == NULL) + return RMAP_NOMATCH; + + return (access_list_apply (alist, &p) == FILTER_DENY ? + RMAP_NOMATCH : RMAP_MATCH); + } + return RMAP_NOMATCH; +} + +/* Route map `ip next-hop' match statement. `arg' is + access-list name. */ +static void * +route_match_ip_next_hop_compile (const char *arg) +{ + return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); +} + +/* Free route map's compiled `ip address' value. */ +static void +route_match_ip_next_hop_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for ip next-hop matching. */ +struct route_map_rule_cmd route_match_ip_next_hop_cmd = +{ + "ip next-hop", + route_match_ip_next_hop, + 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. */ +static route_map_result_t +route_match_ip_route_source (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct access_list *alist; + struct bgp_info *bgp_info; + struct peer *peer; + struct prefix_ipv4 p; + + if (type == RMAP_BGP) + { + bgp_info = object; + peer = bgp_info->peer; + + if (! peer || sockunion_family (&peer->su) != AF_INET) + return RMAP_NOMATCH; + + p.family = AF_INET; + p.prefix = peer->su.sin.sin_addr; + p.prefixlen = IPV4_MAX_BITLEN; + + alist = access_list_lookup (AFI_IP, (char *) rule); + if (alist == NULL) + return RMAP_NOMATCH; + + return (access_list_apply (alist, &p) == FILTER_DENY ? + RMAP_NOMATCH : RMAP_MATCH); + } + return RMAP_NOMATCH; +} + +/* Route map `ip route-source' match statement. `arg' is + access-list name. */ +static void * +route_match_ip_route_source_compile (const char *arg) +{ + return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); +} + +/* Free route map's compiled `ip address' value. */ +static void +route_match_ip_route_source_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for ip route-source matching. */ +struct route_map_rule_cmd route_match_ip_route_source_cmd = +{ + "ip route-source", + route_match_ip_route_source, + route_match_ip_route_source_compile, + route_match_ip_route_source_free +}; + +/* `match ip address prefix-list PREFIX_LIST' */ + +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_BGP) + { + plist = prefix_list_lookup (AFI_IP, (char *) rule); + if (plist == NULL) + return RMAP_NOMATCH; + + return (prefix_list_apply (plist, prefix) == PREFIX_DENY ? + RMAP_NOMATCH : 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 +}; + +/* `match ip next-hop prefix-list PREFIX_LIST' */ + +static route_map_result_t +route_match_ip_next_hop_prefix_list (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct prefix_list *plist; + struct bgp_info *bgp_info; + struct prefix_ipv4 p; + + if (type == RMAP_BGP) + { + bgp_info = object; + p.family = AF_INET; + p.prefix = bgp_info->attr->nexthop; + p.prefixlen = IPV4_MAX_BITLEN; + + plist = prefix_list_lookup (AFI_IP, (char *) rule); + if (plist == NULL) + return RMAP_NOMATCH; + + return (prefix_list_apply (plist, &p) == PREFIX_DENY ? + RMAP_NOMATCH : RMAP_MATCH); + } + return RMAP_NOMATCH; +} + +static void * +route_match_ip_next_hop_prefix_list_compile (const char *arg) +{ + return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); +} + +static void +route_match_ip_next_hop_prefix_list_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +struct route_map_rule_cmd route_match_ip_next_hop_prefix_list_cmd = +{ + "ip next-hop prefix-list", + route_match_ip_next_hop_prefix_list, + 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 +route_match_ip_route_source_prefix_list (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct prefix_list *plist; + struct bgp_info *bgp_info; + struct peer *peer; + struct prefix_ipv4 p; + + if (type == RMAP_BGP) + { + bgp_info = object; + peer = bgp_info->peer; + + if (! peer || sockunion_family (&peer->su) != AF_INET) + return RMAP_NOMATCH; + + p.family = AF_INET; + p.prefix = peer->su.sin.sin_addr; + p.prefixlen = IPV4_MAX_BITLEN; + + plist = prefix_list_lookup (AFI_IP, (char *) rule); + if (plist == NULL) + return RMAP_NOMATCH; + + return (prefix_list_apply (plist, &p) == PREFIX_DENY ? + RMAP_NOMATCH : RMAP_MATCH); + } + return RMAP_NOMATCH; +} + +static void * +route_match_ip_route_source_prefix_list_compile (const char *arg) +{ + return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); +} + +static void +route_match_ip_route_source_prefix_list_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +struct route_map_rule_cmd route_match_ip_route_source_prefix_list_cmd = +{ + "ip route-source prefix-list", + route_match_ip_route_source_prefix_list, + route_match_ip_route_source_prefix_list_compile, + route_match_ip_route_source_prefix_list_free +}; + +/* `match local-preference LOCAL-PREF' */ + +/* Match function return 1 if match is success else return zero. */ +static route_map_result_t +route_match_local_pref (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + u_int32_t *local_pref; + struct bgp_info *bgp_info; + + if (type == RMAP_BGP) + { + local_pref = rule; + bgp_info = object; + + if (bgp_info->attr->local_pref == *local_pref) + return RMAP_MATCH; + else + return RMAP_NOMATCH; + } + return RMAP_NOMATCH; +} + +/* Route map `match local-preference' match statement. + `arg' is local-pref value */ +static void * +route_match_local_pref_compile (const char *arg) +{ + u_int32_t *local_pref; + char *endptr = NULL; + unsigned long tmpval; + + /* Locpref value shoud be integer. */ + if (! all_digit (arg)) + return NULL; + + errno = 0; + tmpval = strtoul (arg, &endptr, 10); + if (*endptr != '\0' || errno || tmpval > UINT32_MAX) + return NULL; + + 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 local-preference' value. */ +static void +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_value_compile, + route_value_free, +}; + +/* `match as-path ASPATH' */ + +/* Match function for as-path match. I assume given object is */ +static route_map_result_t +route_match_aspath (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + + struct as_list *as_list; + struct bgp_info *bgp_info; + + if (type == RMAP_BGP) + { + as_list = as_list_lookup ((char *) rule); + if (as_list == NULL) + return RMAP_NOMATCH; + + bgp_info = object; + + /* Perform match. */ + return ((as_list_apply (as_list, bgp_info->attr->aspath) == AS_FILTER_DENY) ? RMAP_NOMATCH : RMAP_MATCH); + } + return RMAP_NOMATCH; +} + +/* Compile function for as-path match. */ +static void * +route_match_aspath_compile (const char *arg) +{ + return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); +} + +/* Compile function for as-path match. */ +static void +route_match_aspath_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for aspath matching. */ +struct route_map_rule_cmd route_match_aspath_cmd = +{ + "as-path", + route_match_aspath, + route_match_aspath_compile, + route_match_aspath_free +}; + +/* `match community COMMUNIY' */ +struct rmap_community +{ + char *name; + int exact; +}; + +/* Match function for community match. */ +static route_map_result_t +route_match_community (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, COMMUNITY_LIST_MASTER); + if (! list) + return RMAP_NOMATCH; + + if (rcom->exact) + { + if (community_list_exact_match (bgp_info->attr->community, list)) + return RMAP_MATCH; + } + else + { + if (community_list_match (bgp_info->attr->community, list)) + return RMAP_MATCH; + } + } + return RMAP_NOMATCH; +} + +/* Compile function for community match. */ +static void * +route_match_community_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); + rcom->exact = 1; + } + else + { + rcom->name = XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); + rcom->exact = 0; + } + return rcom; +} + +/* Compile function for community match. */ +static void +route_match_community_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_community_cmd = +{ + "community", + route_match_community, + 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_map_object_t type, void *object) +{ + struct community_list *list; + struct bgp_info *bgp_info; + + 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) + return RMAP_NOMATCH; + + if (ecommunity_list_match (bgp_info->attr->extra->ecommunity, list)) + return RMAP_MATCH; + } + return RMAP_NOMATCH; +} + +/* Compile function for extcommunity match. */ +static void * +route_match_ecommunity_compile (const char *arg) +{ + return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); +} + +/* Compile function for extcommunity match. */ +static void +route_match_ecommunity_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for community matching. */ +struct route_map_rule_cmd route_match_ecommunity_cmd = +{ + "extcommunity", + route_match_ecommunity, + 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, + route_map_object_t type, void *object) +{ + u_char *origin; + struct bgp_info *bgp_info; + + if (type == RMAP_BGP) + { + origin = rule; + bgp_info = object; + + if (bgp_info->attr->origin == *origin) + return RMAP_MATCH; + } + + return RMAP_NOMATCH; +} + +static void * +route_match_origin_compile (const char *arg) +{ + u_char *origin; + + origin = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_char)); + + if (strcmp (arg, "igp") == 0) + *origin = 0; + else if (strcmp (arg, "egp") == 0) + *origin = 1; + else + *origin = 2; + + return origin; +} + +/* Free route map's compiled `ip address' value. */ +static void +route_match_origin_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for origin matching. */ +struct route_map_rule_cmd route_match_origin_cmd = +{ + "origin", + route_match_origin, + route_match_origin_compile, + route_match_origin_free +}; + +/* match probability { */ + +static route_map_result_t +route_match_probability (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + long r = random(); + + switch (*(long *) rule) + { + case 0: break; + case RAND_MAX: return RMAP_MATCH; + default: + if (r < *(long *) rule) + { + return RMAP_MATCH; + } + } + + return RMAP_NOMATCH; +} + +static void * +route_match_probability_compile (const char *arg) +{ + long *lobule; + unsigned perc; + + perc = atoi (arg); + lobule = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (long)); + + switch (perc) + { + case 0: *lobule = 0; break; + case 100: *lobule = RAND_MAX; break; + default: *lobule = RAND_MAX / 100 * perc; + } + + return lobule; +} + +static void +route_match_probability_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +struct route_map_rule_cmd route_match_probability_cmd = +{ + "probability", + route_match_probability, + route_match_probability_compile, + route_match_probability_free +}; + +/* } */ + +/* `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 +{ + struct in_addr *address; + int peer_address; +}; + +static route_map_result_t +route_set_ip_nexthop (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct rmap_ip_nexthop_set *rins = rule; + struct bgp_info *bgp_info; + struct peer *peer; + + if (type == RMAP_BGP) + { + bgp_info = object; + peer = bgp_info->peer; + + if (rins->peer_address) + { + 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_INET) + { + 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) + { + bgp_info->attr->nexthop.s_addr = sockunion2ip (peer->su_local); + bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP); + } + } + else + { + /* Set next hop value. */ + bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP); + bgp_info->attr->nexthop = *rins->address; + } + } + + return RMAP_OKAY; +} + +/* Route map `ip nexthop' compile function. Given string is converted + to struct in_addr structure. */ +static void * +route_set_ip_nexthop_compile (const char *arg) +{ + struct rmap_ip_nexthop_set *rins; + struct in_addr *address = NULL; + int peer_address = 0; + int ret; + + if (strcmp (arg, "peer-address") == 0) + peer_address = 1; + else + { + address = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct in_addr)); + ret = inet_aton (arg, address); + + if (ret == 0) + { + XFREE (MTYPE_ROUTE_MAP_COMPILED, address); + return NULL; + } + } + + rins = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct rmap_ip_nexthop_set)); + + rins->address = address; + rins->peer_address = peer_address; + + return rins; +} + +/* Free route map's compiled `ip nexthop' value. */ +static void +route_set_ip_nexthop_free (void *rule) +{ + struct rmap_ip_nexthop_set *rins = rule; + + if (rins->address) + XFREE (MTYPE_ROUTE_MAP_COMPILED, rins->address); + + XFREE (MTYPE_ROUTE_MAP_COMPILED, rins); +} + +/* Route map commands for ip nexthop set. */ +struct route_map_rule_cmd route_set_ip_nexthop_cmd = +{ + "ip next-hop", + route_set_ip_nexthop, + route_set_ip_nexthop_compile, + route_set_ip_nexthop_free +}; + +/* `set local-preference LOCAL_PREF' */ + +/* Set local preference. */ +static route_map_result_t +route_set_local_pref (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct rmap_value *rv; + struct bgp_info *bgp_info; + u_int32_t locpref = 0; + + if (type == RMAP_BGP) + { + /* Fetch routemap's rule information. */ + 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 = route_value_adjust(rv, locpref, bgp_info->peer); + } + + return RMAP_OKAY; +} + +/* Set local preference rule structure. */ +struct route_map_rule_cmd route_set_local_pref_cmd = +{ + "local-preference", + route_set_local_pref, + route_value_compile, + route_value_free, +}; + +/* `set weight WEIGHT' */ + +/* Set weight. */ +static route_map_result_t +route_set_weight (void *rule, struct prefix *prefix, route_map_object_t type, + void *object) +{ + struct rmap_value *rv; + struct bgp_info *bgp_info; + u_int32_t weight; + + if (type == RMAP_BGP) + { + /* Fetch routemap's rule information. */ + rv = rule; + bgp_info = object; + + /* Set weight value. */ + 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; + } + + return RMAP_OKAY; +} + +/* Set local preference rule structure. */ +struct route_map_rule_cmd route_set_weight_cmd = +{ + "weight", + route_set_weight, + route_value_compile, + route_value_free, +}; + +/* `set metric METRIC' */ + +/* Set metric to attribute. */ +static route_map_result_t +route_set_metric (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct rmap_value *rv; + struct bgp_info *bgp_info; + u_int32_t med = 0; + + if (type == RMAP_BGP) + { + /* Fetch routemap's rule information. */ + rv = rule; + bgp_info = object; + + if (bgp_info->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC)) + med = bgp_info->attr->med; + + 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 rule structure. */ +struct route_map_rule_cmd route_set_metric_cmd = +{ + "metric", + route_set_metric, + route_value_compile, + route_value_free, +}; + +/* `set as-path prepend ASPATH' */ + +/* For AS path prepend mechanism. */ +static route_map_result_t +route_set_aspath_prepend (void *rule, struct prefix *prefix, route_map_object_t type, void *object) +{ + struct aspath *aspath; + struct aspath *new; + struct bgp_info *binfo; + + if (type == RMAP_BGP) + { + binfo = object; + + if (binfo->attr->aspath->refcnt) + new = aspath_dup (binfo->attr->aspath); + else + new = binfo->attr->aspath; + + 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; +} + +static void * +route_set_aspath_prepend_compile (const char *arg) +{ + unsigned int num; + + if (sscanf(arg, "last-as %u", &num) == 1 && num > 0 && num < 10) + return (void*)(uintptr_t)num; + + return route_aspath_compile(arg); +} + +static void +route_set_aspath_prepend_free (void *rule) +{ + if ((uintptr_t)rule > 10) + route_aspath_free(rule); +} + + +/* Set as-path prepend rule structure. */ +struct route_map_rule_cmd route_set_aspath_prepend_cmd = +{ + "as-path prepend", + route_set_aspath_prepend, + route_set_aspath_prepend_compile, + route_set_aspath_prepend_free, +}; + +/* `set as-path exclude ASn' */ + +/* For ASN exclude mechanism. + * Iterate over ASns requested and filter them from the given AS_PATH one by one. + * Make a deep copy of existing AS_PATH, but for the first ASn only. + */ +static route_map_result_t +route_set_aspath_exclude (void *rule, struct prefix *dummy, route_map_object_t type, void *object) +{ + struct aspath * new_path, * exclude_path; + struct bgp_info *binfo; + + if (type == RMAP_BGP) + { + exclude_path = rule; + binfo = object; + if (binfo->attr->aspath->refcnt) + new_path = aspath_dup (binfo->attr->aspath); + else + new_path = binfo->attr->aspath; + binfo->attr->aspath = aspath_filter_exclude (new_path, exclude_path); + } + return RMAP_OKAY; +} + +/* Set ASn exlude rule structure. */ +struct route_map_rule_cmd route_set_aspath_exclude_cmd = +{ + "as-path exclude", + route_set_aspath_exclude, + route_aspath_compile, + route_aspath_free, +}; + +/* `set community COMMUNITY' */ +struct rmap_com_set +{ + struct community *com; + int additive; + int none; +}; + +/* For community set mechanism. */ +static route_map_result_t +route_set_community (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct rmap_com_set *rcs; + struct bgp_info *binfo; + struct attr *attr; + struct community *new = NULL; + struct community *old; + struct community *merge; + + if (type == RMAP_BGP) + { + rcs = rule; + binfo = object; + attr = binfo->attr; + old = attr->community; + + /* "none" case. */ + if (rcs->none) + { + 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; + } + + /* "additive" case. */ + if (rcs->additive && old) + { + merge = community_merge (community_dup (old), rcs->com); + + /* 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) + community_free (old); + new = community_uniq_sort (merge); + community_free (merge); + } + else + new = community_dup (rcs->com); + + /* will be interned by caller if required */ + attr->community = new; + + attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES); + } + + return RMAP_OKAY; +} + +/* Compile function for set community. */ +static void * +route_set_community_compile (const char *arg) +{ + struct rmap_com_set *rcs; + struct community *com = 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'; + } + + com = community_str2com (arg); + + if (additive) + *(sp - 1) = ' '; + + if (! com) + return NULL; + } + + rcs = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct rmap_com_set)); + rcs->com = com; + rcs->additive = additive; + rcs->none = none; + + return rcs; +} + +/* Free function for set community. */ +static void +route_set_community_free (void *rule) +{ + struct rmap_com_set *rcs = rule; + + if (rcs->com) + community_free (rcs->com); + XFREE (MTYPE_ROUTE_MAP_COMPILED, rcs); +} + +/* Set community rule structure. */ +struct route_map_rule_cmd route_set_community_cmd = +{ + "community", + route_set_community, + 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. */ +static route_map_result_t +route_set_community_delete (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct community_list *list; + struct community *merge; + struct community *new; + struct community *old; + struct bgp_info *binfo; + + if (type == RMAP_BGP) + { + if (! rule) + return RMAP_OKAY; + + binfo = object; + list = community_list_lookup (bgp_clist, rule, COMMUNITY_LIST_MASTER); + old = binfo->attr->community; + + if (list && old) + { + merge = community_list_match_delete (community_dup (old), list); + new = community_uniq_sort (merge); + community_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) + community_free (old); + + if (new->size == 0) + { + binfo->attr->community = NULL; + binfo->attr->flag &= ~ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES); + community_free (new); + } + else + { + binfo->attr->community = new; + binfo->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES); + } + } + } + + return RMAP_OKAY; +} + +/* Compile function for set community. */ +static void * +route_set_community_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 community. */ +static void +route_set_community_delete_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Set community rule structure. */ +struct route_map_rule_cmd route_set_community_delete_cmd = +{ + "comm-list", + route_set_community_delete, + route_set_community_delete_compile, + route_set_community_delete_free, +}; + +/* `set extcommunity rt COMMUNITY' */ + +/* For community set mechanism. Used by _rt and _soo. */ +static route_map_result_t +route_set_ecommunity (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct ecommunity *ecom; + struct ecommunity *new_ecom; + struct ecommunity *old_ecom; + struct bgp_info *bgp_info; + + if (type == RMAP_BGP) + { + ecom = rule; + bgp_info = object; + + if (! ecom) + return RMAP_OKAY; + + /* We assume additive for Extended Community. */ + old_ecom = (bgp_attr_extra_get (bgp_info->attr))->ecommunity; + + if (old_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); + + /* 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); + } + return RMAP_OKAY; +} + +/* Compile function for set community. */ +static void * +route_set_ecommunity_rt_compile (const char *arg) +{ + struct ecommunity *ecom; + + ecom = ecommunity_str2com (arg, ECOMMUNITY_ROUTE_TARGET, 0); + if (! ecom) + return NULL; + return ecommunity_intern (ecom); +} + +/* Free function for set community. Used by _rt and _soo */ +static void +route_set_ecommunity_free (void *rule) +{ + struct ecommunity *ecom = rule; + ecommunity_unintern (&ecom); +} + +/* Set community rule structure. */ +struct route_map_rule_cmd route_set_ecommunity_rt_cmd = +{ + "extcommunity rt", + route_set_ecommunity, + route_set_ecommunity_rt_compile, + route_set_ecommunity_free, +}; + +/* `set extcommunity soo COMMUNITY' */ + +/* Compile function for set community. */ +static void * +route_set_ecommunity_soo_compile (const char *arg) +{ + struct ecommunity *ecom; + + ecom = ecommunity_str2com (arg, ECOMMUNITY_SITE_ORIGIN, 0); + if (! ecom) + return NULL; + + return ecommunity_intern (ecom); +} + +/* Set community rule structure. */ +struct route_map_rule_cmd route_set_ecommunity_soo_cmd = +{ + "extcommunity soo", + route_set_ecommunity, + route_set_ecommunity_soo_compile, + route_set_ecommunity_free, +}; + +/* `set origin ORIGIN' */ + +/* For origin set. */ +static route_map_result_t +route_set_origin (void *rule, struct prefix *prefix, route_map_object_t type, void *object) +{ + u_char *origin; + struct bgp_info *bgp_info; + + if (type == RMAP_BGP) + { + origin = rule; + bgp_info = object; + + bgp_info->attr->origin = *origin; + } + + return RMAP_OKAY; +} + +/* Compile function for origin set. */ +static void * +route_set_origin_compile (const char *arg) +{ + u_char *origin; + + origin = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_char)); + + if (strcmp (arg, "igp") == 0) + *origin = 0; + else if (strcmp (arg, "egp") == 0) + *origin = 1; + else + *origin = 2; + + return origin; +} + +/* Compile function for origin set. */ +static void +route_set_origin_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Set origin rule structure. */ +struct route_map_rule_cmd route_set_origin_cmd = +{ + "origin", + route_set_origin, + route_set_origin_compile, + route_set_origin_free, +}; + +/* `set atomic-aggregate' */ + +/* For atomic aggregate set. */ +static route_map_result_t +route_set_atomic_aggregate (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct bgp_info *bgp_info; + + if (type == RMAP_BGP) + { + bgp_info = object; + bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE); + } + + return RMAP_OKAY; +} + +/* Compile function for atomic aggregate. */ +static void * +route_set_atomic_aggregate_compile (const char *arg) +{ + return (void *)1; +} + +/* Compile function for atomic aggregate. */ +static void +route_set_atomic_aggregate_free (void *rule) +{ + return; +} + +/* Set atomic aggregate rule structure. */ +struct route_map_rule_cmd route_set_atomic_aggregate_cmd = +{ + "atomic-aggregate", + route_set_atomic_aggregate, + route_set_atomic_aggregate_compile, + route_set_atomic_aggregate_free, +}; + +/* `set aggregator as AS A.B.C.D' */ +struct aggregator +{ + as_t as; + struct in_addr address; +}; + +static route_map_result_t +route_set_aggregator_as (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct bgp_info *bgp_info; + struct aggregator *aggregator; + struct attr_extra *ae; + + if (type == RMAP_BGP) + { + bgp_info = object; + aggregator = rule; + ae = bgp_attr_extra_get (bgp_info->attr); + + ae->aggregator_as = aggregator->as; + ae->aggregator_addr = aggregator->address; + bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR); + } + + return RMAP_OKAY; +} + +static void * +route_set_aggregator_as_compile (const char *arg) +{ + struct aggregator *aggregator; + char as[10]; + char address[20]; + + aggregator = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct aggregator)); + sscanf (arg, "%s %s", as, address); + + aggregator->as = strtoul (as, NULL, 10); + inet_aton (address, &aggregator->address); + + return aggregator; +} + +static void +route_set_aggregator_as_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +struct route_map_rule_cmd route_set_aggregator_as_cmd = +{ + "aggregator as", + route_set_aggregator_as, + route_set_aggregator_as_compile, + route_set_aggregator_as_free, +}; + +/* 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 +route_match_ipv6_address (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct access_list *alist; + + if (type == RMAP_BGP) + { + alist = access_list_lookup (AFI_IP6, (char *) rule); + if (alist == NULL) + return RMAP_NOMATCH; + + return (access_list_apply (alist, prefix) == FILTER_DENY ? + RMAP_NOMATCH : 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); +} + +/* Route map commands for ip address matching. */ +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 +}; + +/* `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 = rule; + struct bgp_info *bgp_info; + + if (type == RMAP_BGP) + { + bgp_info = object; + + if (!bgp_info->attr->extra) + return RMAP_NOMATCH; + + 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, addr)) + return RMAP_MATCH; + + return RMAP_NOMATCH; + } + + return RMAP_NOMATCH; +} + +static void * +route_match_ipv6_next_hop_compile (const char *arg) +{ + struct in6_addr *address; + int ret; + + address = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct in6_addr)); + + ret = inet_pton (AF_INET6, arg, address); + if (!ret) + { + XFREE (MTYPE_ROUTE_MAP_COMPILED, address); + return NULL; + } + + return address; +} + +static void +route_match_ipv6_next_hop_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +struct route_map_rule_cmd route_match_ipv6_next_hop_cmd = +{ + "ipv6 next-hop", + route_match_ipv6_next_hop, + route_match_ipv6_next_hop_compile, + route_match_ipv6_next_hop_free +}; + +/* `match ipv6 address prefix-list PREFIX_LIST' */ + +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; + + if (type == RMAP_BGP) + { + plist = prefix_list_lookup (AFI_IP6, (char *) rule); + if (plist == NULL) + return RMAP_NOMATCH; + + return (prefix_list_apply (plist, prefix) == PREFIX_DENY ? + RMAP_NOMATCH : 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 +}; + +/* `set ipv6 nexthop global IP_ADDRESS' */ + +/* Set nexthop to object. ojbect must be pointer to struct attr. */ +static route_map_result_t +route_set_ipv6_nexthop_global (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct in6_addr *address; + struct bgp_info *bgp_info; + + if (type == RMAP_BGP) + { + /* Fetch routemap's rule information. */ + address = rule; + bgp_info = object; + + /* Set next hop value. */ + (bgp_attr_extra_get (bgp_info->attr))->mp_nexthop_global = *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_global_compile (const char *arg) +{ + int ret; + struct in6_addr *address; + + address = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct in6_addr)); + + ret = inet_pton (AF_INET6, arg, address); + + if (ret == 0) + { + XFREE (MTYPE_ROUTE_MAP_COMPILED, address); + return NULL; + } + + return address; +} + +/* Free route map's compiled `ip next-hop' value. */ +static void +route_set_ipv6_nexthop_global_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_global_cmd = +{ + "ipv6 next-hop global", + route_set_ipv6_nexthop_global, + 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. */ +static route_map_result_t +route_set_ipv6_nexthop_local (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct in6_addr *address; + struct bgp_info *bgp_info; + + if (type == RMAP_BGP) + { + /* Fetch routemap's rule information. */ + address = rule; + bgp_info = object; + + /* Set next hop value. */ + (bgp_attr_extra_get (bgp_info->attr))->mp_nexthop_local = *address; + + /* Set nexthop length. */ + if (bgp_info->attr->extra->mp_nexthop_len != 32) + bgp_info->attr->extra->mp_nexthop_len = 32; + } + + return RMAP_OKAY; +} + +/* Route map `ip nexthop' compile function. Given string is converted + to struct in_addr structure. */ +static void * +route_set_ipv6_nexthop_local_compile (const char *arg) +{ + int ret; + struct in6_addr *address; + + address = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct in6_addr)); + + ret = inet_pton (AF_INET6, arg, address); + + if (ret == 0) + { + XFREE (MTYPE_ROUTE_MAP_COMPILED, address); + return NULL; + } + + return address; +} + +/* Free route map's compiled `ip nexthop' value. */ +static void +route_set_ipv6_nexthop_local_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_local_cmd = +{ + "ipv6 next-hop local", + route_set_ipv6_nexthop_local, + route_set_ipv6_nexthop_local_compile, + route_set_ipv6_nexthop_local_free +}; + +/* `set ipv6 nexthop peer-address' */ + +/* Set nexthop to object. ojbect must be pointer to struct attr. */ +static route_map_result_t +route_set_ipv6_nexthop_peer (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct in6_addr peer_address; + struct bgp_info *bgp_info; + struct peer *peer; + + 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 +route_set_vpnv4_nexthop (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct in_addr *address; + struct bgp_info *bgp_info; + + if (type == RMAP_BGP) + { + /* Fetch routemap's rule information. */ + address = rule; + bgp_info = object; + + /* 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; +} + +static void * +route_set_vpnv4_nexthop_compile (const char *arg) +{ + int ret; + struct in_addr *address; + + address = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct in_addr)); + + ret = inet_aton (arg, address); + + if (ret == 0) + { + XFREE (MTYPE_ROUTE_MAP_COMPILED, address); + return NULL; + } + + return address; +} + +static void +route_set_vpnv4_nexthop_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for ip nexthop set. */ +struct route_map_rule_cmd route_set_vpnv4_nexthop_cmd = +{ + "vpnv4 next-hop", + route_set_vpnv4_nexthop, + route_set_vpnv4_nexthop_compile, + route_set_vpnv4_nexthop_free +}; + +/* `set originator-id' */ + +/* For origin set. */ +static route_map_result_t +route_set_originator_id (void *rule, struct prefix *prefix, route_map_object_t type, void *object) +{ + struct in_addr *address; + struct bgp_info *bgp_info; + + if (type == RMAP_BGP) + { + address = rule; + bgp_info = object; + + bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID); + (bgp_attr_extra_get (bgp_info->attr))->originator_id = *address; + } + + return RMAP_OKAY; +} + +/* Compile function for originator-id set. */ +static void * +route_set_originator_id_compile (const char *arg) +{ + int ret; + struct in_addr *address; + + address = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct in_addr)); + + ret = inet_aton (arg, address); + + if (ret == 0) + { + XFREE (MTYPE_ROUTE_MAP_COMPILED, address); + return NULL; + } + + return address; +} + +/* Compile function for originator_id set. */ +static void +route_set_originator_id_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Set originator-id rule structure. */ +struct route_map_rule_cmd route_set_originator_id_cmd = +{ + "originator-id", + route_set_originator_id, + 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, + const char *command, const char *arg) +{ + int ret; + + ret = route_map_add_match (index, command, arg); + if (ret) + { + switch (ret) + { + case RMAP_RULE_MISSING: + vty_out (vty, "%% BGP Can't find rule.%s", VTY_NEWLINE); + return CMD_WARNING; + case RMAP_COMPILE_ERROR: + vty_out (vty, "%% BGP Argument is malformed.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + return CMD_SUCCESS; +} + +/* Delete bgp route map rule. */ +static int +bgp_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, "%% BGP Can't find rule.%s", VTY_NEWLINE); + return CMD_WARNING; + case RMAP_COMPILE_ERROR: + vty_out (vty, "%% BGP Argument is malformed.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + return CMD_SUCCESS; +} + +/* Add bgp route map rule. */ +static int +bgp_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, "%% BGP Can't find rule.%s", VTY_NEWLINE); + return CMD_WARNING; + case RMAP_COMPILE_ERROR: + vty_out (vty, "%% BGP Argument is malformed.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + return CMD_SUCCESS; +} + +/* Delete bgp route map rule. */ +static int +bgp_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, "%% BGP Can't find rule.%s", VTY_NEWLINE); + return CMD_WARNING; + case RMAP_COMPILE_ERROR: + vty_out (vty, "%% BGP Argument is malformed.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + return CMD_SUCCESS; +} + +/* Hook function for updating route_map assignment. */ +static void +bgp_route_map_update (const char *unused) +{ + int i; + afi_t afi; + safi_t safi; + int direct; + struct listnode *node, *nnode; + struct listnode *mnode, *mnnode; + struct bgp *bgp; + struct peer *peer; + struct peer_group *group; + struct bgp_filter *filter; + 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)) + { + 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++) + { + filter = &peer->filter[afi][safi]; + + for (direct = RMAP_IN; direct < RMAP_MAX; direct++) + { + if (filter->map[direct].name) + filter->map[direct].map = + route_map_lookup_by_name (filter->map[direct].name); + else + filter->map[direct].map = NULL; + } + + if (filter->usmap.name) + filter->usmap.map = route_map_lookup_by_name (filter->usmap.name); + else + filter->usmap.map = NULL; + } + } + for (ALL_LIST_ELEMENTS (bgp->group, node, nnode, group)) + { + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + { + filter = &group->conf->filter[afi][safi]; + + for (direct = RMAP_IN; direct < RMAP_MAX; direct++) + { + if (filter->map[direct].name) + filter->map[direct].map = + route_map_lookup_by_name (filter->map[direct].name); + else + filter->map[direct].map = NULL; + } + + if (filter->usmap.name) + filter->usmap.map = route_map_lookup_by_name (filter->usmap.name); + else + filter->usmap.map = NULL; + } + } + } + + /* For default-originate route-map updates. */ + for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp)) + { + 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 (peer->default_rmap[afi][safi].name) + peer->default_rmap[afi][safi].map = + route_map_lookup_by_name (peer->default_rmap[afi][safi].name); + else + peer->default_rmap[afi][safi].map = NULL; + } + } + } + + /* For network route-map updates. */ + for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp)) + { + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + for (bn = bgp_table_top (bgp->route[afi][safi]); bn; + bn = bgp_route_next (bn)) + if ((bgp_static = bn->info) != NULL) + { + if (bgp_static->rmap.name) + bgp_static->rmap.map = + route_map_lookup_by_name (bgp_static->rmap.name); + else + bgp_static->rmap.map = NULL; + } + } + + /* For redistribute route-map updates. */ + for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp)) + { + for (i = 0; i < ZEBRA_ROUTE_MAX; i++) + { + 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" + "IP address of peer\n" + "IPv6 address of peer\n") +{ + return bgp_route_match_add (vty, vty->index, "peer", argv[0]); +} + +DEFUN (match_peer_local, + match_peer_local_cmd, + "match peer local", + MATCH_STR + "Match peer address\n" + "Static or Redistributed routes\n") +{ + return bgp_route_match_add (vty, vty->index, "peer", "local"); +} + +DEFUN (no_match_peer, + no_match_peer_cmd, + "no match peer", + NO_STR + MATCH_STR + "Match peer address\n") +{ + if (argc == 0) + return bgp_route_match_delete (vty, vty->index, "peer", NULL); + + return bgp_route_match_delete (vty, vty->index, "peer", argv[0]); +} + +ALIAS (no_match_peer, + no_match_peer_val_cmd, + "no match peer (A.B.C.D|X:X::X:X)", + NO_STR + MATCH_STR + "Match peer address\n" + "IP address of peer\n" + "IPv6 address of peer\n") + +ALIAS (no_match_peer, + no_match_peer_local_cmd, + "no match peer local", + NO_STR + MATCH_STR + "Match peer address\n" + "Static or Redistributed routes\n") + +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 bgp_route_match_add (vty, vty->index, "ip address", argv[0]); +} + +DEFUN (no_match_ip_address, + no_match_ip_address_cmd, + "no match ip address", + NO_STR + MATCH_STR + IP_STR + "Match address of route\n") +{ + if (argc == 0) + return bgp_route_match_delete (vty, vty->index, "ip address", NULL); + + return bgp_route_match_delete (vty, vty->index, "ip address", argv[0]); +} + +ALIAS (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") + +DEFUN (match_ip_next_hop, + match_ip_next_hop_cmd, + "match ip next-hop (<1-199>|<1300-2699>|WORD)", + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "IP access-list number\n" + "IP access-list number (expanded range)\n" + "IP Access-list name\n") +{ + return bgp_route_match_add (vty, vty->index, "ip next-hop", argv[0]); +} + +DEFUN (no_match_ip_next_hop, + no_match_ip_next_hop_cmd, + "no match ip next-hop", + NO_STR + MATCH_STR + IP_STR + "Match next-hop address of route\n") +{ + if (argc == 0) + return bgp_route_match_delete (vty, vty->index, "ip next-hop", NULL); + + return bgp_route_match_delete (vty, vty->index, "ip next-hop", argv[0]); +} + +ALIAS (no_match_ip_next_hop, + no_match_ip_next_hop_val_cmd, + "no match ip next-hop (<1-199>|<1300-2699>|WORD)", + NO_STR + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "IP access-list number\n" + "IP access-list number (expanded range)\n" + "IP Access-list name\n") + +/* match probability { */ + +DEFUN (match_probability, + match_probability_cmd, + "match probability <0-100>", + MATCH_STR + "Match portion of routes defined by percentage value\n" + "Percentage of routes\n") +{ + return bgp_route_match_add (vty, vty->index, "probability", argv[0]); +} + +DEFUN (no_match_probability, + no_match_probability_cmd, + "no match probability", + NO_STR + MATCH_STR + "Match portion of routes defined by percentage value\n") +{ + return bgp_route_match_delete (vty, vty->index, "probability", argc ? argv[0] : NULL); +} + +ALIAS (no_match_probability, + no_match_probability_val_cmd, + "no match probability <1-99>", + NO_STR + MATCH_STR + "Match portion of routes defined by percentage value\n" + "Percentage of routes\n") + +/* } */ + +DEFUN (match_ip_route_source, + match_ip_route_source_cmd, + "match ip route-source (<1-199>|<1300-2699>|WORD)", + MATCH_STR + IP_STR + "Match advertising source address of route\n" + "IP access-list number\n" + "IP access-list number (expanded range)\n" + "IP standard access-list name\n") +{ + return bgp_route_match_add (vty, vty->index, "ip route-source", argv[0]); +} + +DEFUN (no_match_ip_route_source, + no_match_ip_route_source_cmd, + "no match ip route-source", + NO_STR + MATCH_STR + IP_STR + "Match advertising source address of route\n") +{ + if (argc == 0) + return bgp_route_match_delete (vty, vty->index, "ip route-source", NULL); + + return bgp_route_match_delete (vty, vty->index, "ip route-source", argv[0]); +} + +ALIAS (no_match_ip_route_source, + no_match_ip_route_source_val_cmd, + "no match ip route-source (<1-199>|<1300-2699>|WORD)", + NO_STR + MATCH_STR + IP_STR + "Match advertising source address of route\n" + "IP access-list number\n" + "IP access-list number (expanded range)\n" + "IP standard access-list name\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 bgp_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 bgp_route_match_delete (vty, vty->index, "ip address prefix-list", NULL); + + return bgp_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_ip_next_hop_prefix_list, + match_ip_next_hop_prefix_list_cmd, + "match ip next-hop prefix-list WORD", + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") +{ + return bgp_route_match_add (vty, vty->index, "ip next-hop prefix-list", argv[0]); +} + +DEFUN (no_match_ip_next_hop_prefix_list, + no_match_ip_next_hop_prefix_list_cmd, + "no match ip next-hop prefix-list", + NO_STR + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "Match entries of prefix-lists\n") +{ + if (argc == 0) + return bgp_route_match_delete (vty, vty->index, "ip next-hop prefix-list", NULL); + + return bgp_route_match_delete (vty, vty->index, "ip next-hop prefix-list", argv[0]); +} + +ALIAS (no_match_ip_next_hop_prefix_list, + no_match_ip_next_hop_prefix_list_val_cmd, + "no match ip next-hop prefix-list WORD", + NO_STR + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") + +DEFUN (match_ip_route_source_prefix_list, + match_ip_route_source_prefix_list_cmd, + "match ip route-source prefix-list WORD", + MATCH_STR + IP_STR + "Match advertising source address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") +{ + return bgp_route_match_add (vty, vty->index, "ip route-source prefix-list", argv[0]); +} + +DEFUN (no_match_ip_route_source_prefix_list, + no_match_ip_route_source_prefix_list_cmd, + "no match ip route-source prefix-list", + NO_STR + MATCH_STR + IP_STR + "Match advertising source address of route\n" + "Match entries of prefix-lists\n") +{ + if (argc == 0) + return bgp_route_match_delete (vty, vty->index, "ip route-source prefix-list", NULL); + + return bgp_route_match_delete (vty, vty->index, "ip route-source prefix-list", argv[0]); +} + +ALIAS (no_match_ip_route_source_prefix_list, + no_match_ip_route_source_prefix_list_val_cmd, + "no match ip route-source prefix-list WORD", + NO_STR + MATCH_STR + IP_STR + "Match advertising source address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") + +DEFUN (match_metric, + match_metric_cmd, + "match metric <0-4294967295>", + MATCH_STR + "Match metric of route\n" + "Metric value\n") +{ + return bgp_route_match_add (vty, vty->index, "metric", argv[0]); +} + +DEFUN (no_match_metric, + no_match_metric_cmd, + "no match metric", + NO_STR + MATCH_STR + "Match metric of route\n") +{ + if (argc == 0) + return bgp_route_match_delete (vty, vty->index, "metric", NULL); + + return bgp_route_match_delete (vty, vty->index, "metric", argv[0]); +} + +ALIAS (no_match_metric, + no_match_metric_val_cmd, + "no match metric <0-4294967295>", + NO_STR + MATCH_STR + "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)", + MATCH_STR + "Match BGP community list\n" + "Community-list number (standard)\n" + "Community-list number (expanded)\n" + "Community-list name\n") +{ + return bgp_route_match_add (vty, vty->index, "community", argv[0]); +} + +DEFUN (match_community_exact, + match_community_exact_cmd, + "match community (<1-99>|<100-500>|WORD) exact-match", + MATCH_STR + "Match BGP community list\n" + "Community-list number (standard)\n" + "Community-list number (expanded)\n" + "Community-list name\n" + "Do exact matching of communities\n") +{ + int ret; + char *argstr; + + argstr = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, + strlen (argv[0]) + strlen ("exact-match") + 2); + + sprintf (argstr, "%s exact-match", argv[0]); + + ret = bgp_route_match_add (vty, vty->index, "community", argstr); + + XFREE (MTYPE_ROUTE_MAP_COMPILED, argstr); + + return ret; +} + +DEFUN (no_match_community, + no_match_community_cmd, + "no match community", + NO_STR + MATCH_STR + "Match BGP community list\n") +{ + return bgp_route_match_delete (vty, vty->index, "community", NULL); +} + +ALIAS (no_match_community, + no_match_community_val_cmd, + "no match community (<1-99>|<100-500>|WORD)", + NO_STR + MATCH_STR + "Match BGP community list\n" + "Community-list number (standard)\n" + "Community-list number (expanded)\n" + "Community-list name\n") + +ALIAS (no_match_community, + no_match_community_exact_cmd, + "no match community (<1-99>|<100-500>|WORD) exact-match", + NO_STR + MATCH_STR + "Match BGP community list\n" + "Community-list number (standard)\n" + "Community-list number (expanded)\n" + "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)", + MATCH_STR + "Match BGP/VPN extended community list\n" + "Extended community-list number (standard)\n" + "Extended community-list number (expanded)\n" + "Extended community-list name\n") +{ + return bgp_route_match_add (vty, vty->index, "extcommunity", argv[0]); +} + +DEFUN (no_match_ecommunity, + no_match_ecommunity_cmd, + "no match extcommunity", + NO_STR + MATCH_STR + "Match BGP/VPN extended community list\n") +{ + return bgp_route_match_delete (vty, vty->index, "extcommunity", NULL); +} + +ALIAS (no_match_ecommunity, + no_match_ecommunity_val_cmd, + "no match extcommunity (<1-99>|<100-500>|WORD)", + NO_STR + MATCH_STR + "Match BGP/VPN extended community list\n" + "Extended community-list number (standard)\n" + "Extended community-list number (expanded)\n" + "Extended community-list name\n") + +DEFUN (match_aspath, + match_aspath_cmd, + "match as-path WORD", + MATCH_STR + "Match BGP AS path list\n" + "AS path access-list name\n") +{ + return bgp_route_match_add (vty, vty->index, "as-path", argv[0]); +} + +DEFUN (no_match_aspath, + no_match_aspath_cmd, + "no match as-path", + NO_STR + MATCH_STR + "Match BGP AS path list\n") +{ + return bgp_route_match_delete (vty, vty->index, "as-path", NULL); +} + +ALIAS (no_match_aspath, + no_match_aspath_val_cmd, + "no match as-path WORD", + NO_STR + MATCH_STR + "Match BGP AS path list\n" + "AS path access-list name\n") + +DEFUN (match_origin, + match_origin_cmd, + "match origin (egp|igp|incomplete)", + MATCH_STR + "BGP origin code\n" + "remote EGP\n" + "local IGP\n" + "unknown heritage\n") +{ + if (strncmp (argv[0], "igp", 2) == 0) + return bgp_route_match_add (vty, vty->index, "origin", "igp"); + if (strncmp (argv[0], "egp", 1) == 0) + return bgp_route_match_add (vty, vty->index, "origin", "egp"); + if (strncmp (argv[0], "incomplete", 2) == 0) + return bgp_route_match_add (vty, vty->index, "origin", "incomplete"); + + return CMD_WARNING; +} + +DEFUN (no_match_origin, + no_match_origin_cmd, + "no match origin", + NO_STR + MATCH_STR + "BGP origin code\n") +{ + return bgp_route_match_delete (vty, vty->index, "origin", NULL); +} + +ALIAS (no_match_origin, + no_match_origin_val_cmd, + "no match origin (egp|igp|incomplete)", + NO_STR + MATCH_STR + "BGP origin code\n" + "remote EGP\n" + "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", + SET_STR + IP_STR + "Next hop address\n" + "IP address of next hop\n") +{ + union sockunion su; + int ret; + + ret = str2sockunion (argv[0], &su); + if (ret < 0) + { + vty_out (vty, "%% Malformed Next-hop address%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_route_set_add (vty, vty->index, "ip next-hop", argv[0]); +} + +DEFUN (set_ip_nexthop_peer, + set_ip_nexthop_peer_cmd, + "set ip next-hop peer-address", + SET_STR + IP_STR + "Next hop address\n" + "Use peer address (for BGP only)\n") +{ + return bgp_route_set_add (vty, vty->index, "ip next-hop", "peer-address"); +} + +DEFUN_DEPRECATED (no_set_ip_nexthop_peer, + no_set_ip_nexthop_peer_cmd, + "no set ip next-hop peer-address", + NO_STR + SET_STR + IP_STR + "Next hop address\n" + "Use peer address (for BGP only)\n") +{ + return bgp_route_set_delete (vty, vty->index, "ip next-hop", NULL); +} + + +DEFUN (no_set_ip_nexthop, + no_set_ip_nexthop_cmd, + "no set ip next-hop", + NO_STR + SET_STR + "Next hop address\n") +{ + if (argc == 0) + return bgp_route_set_delete (vty, vty->index, "ip next-hop", NULL); + + return bgp_route_set_delete (vty, vty->index, "ip next-hop", argv[0]); +} + +ALIAS (no_set_ip_nexthop, + no_set_ip_nexthop_val_cmd, + "no set ip next-hop A.B.C.D", + NO_STR + SET_STR + IP_STR + "Next hop address\n" + "IP address of next hop\n") + +DEFUN (set_metric, + set_metric_cmd, + "set metric <0-4294967295>", + SET_STR + "Metric value for destination routing protocol\n" + "Metric value\n") +{ + return bgp_route_set_add (vty, vty->index, "metric", argv[0]); +} + +ALIAS (set_metric, + set_metric_addsub_cmd, + "set metric <+/-metric>", + SET_STR + "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", + NO_STR + SET_STR + "Metric value for destination routing protocol\n") +{ + if (argc == 0) + return bgp_route_set_delete (vty, vty->index, "metric", NULL); + + return bgp_route_set_delete (vty, vty->index, "metric", argv[0]); +} + +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") + +DEFUN (set_local_pref, + set_local_pref_cmd, + "set local-preference <0-4294967295>", + SET_STR + "BGP local preference path attribute\n" + "Preference value\n") +{ + return bgp_route_set_add (vty, vty->index, "local-preference", argv[0]); +} + +DEFUN (no_set_local_pref, + no_set_local_pref_cmd, + "no set local-preference", + NO_STR + SET_STR + "BGP local preference path attribute\n") +{ + if (argc == 0) + return bgp_route_set_delete (vty, vty->index, "local-preference", NULL); + + return bgp_route_set_delete (vty, vty->index, "local-preference", argv[0]); +} + +ALIAS (no_set_local_pref, + no_set_local_pref_val_cmd, + "no set local-preference <0-4294967295>", + NO_STR + SET_STR + "BGP local preference path attribute\n" + "Preference value\n") + +DEFUN (set_weight, + set_weight_cmd, + "set weight <0-4294967295>", + SET_STR + "BGP weight for routing table\n" + "Weight value\n") +{ + return bgp_route_set_add (vty, vty->index, "weight", argv[0]); +} + +DEFUN (no_set_weight, + no_set_weight_cmd, + "no set weight", + NO_STR + SET_STR + "BGP weight for routing table\n") +{ + if (argc == 0) + return bgp_route_set_delete (vty, vty->index, "weight", NULL); + + return bgp_route_set_delete (vty, vty->index, "weight", argv[0]); +} + +ALIAS (no_set_weight, + no_set_weight_val_cmd, + "no set weight <0-4294967295>", + NO_STR + SET_STR + "BGP weight for routing table\n" + "Weight value\n") + +DEFUN (set_aspath_prepend, + set_aspath_prepend_cmd, + "set as-path prepend ." CMD_AS_RANGE, + SET_STR + "Transform BGP AS_PATH attribute\n" + "Prepend to the as-path\n" + "AS number\n") +{ + int ret; + char *str; + + str = argv_concat (argv, argc, 0); + ret = bgp_route_set_add (vty, vty->index, "as-path prepend", str); + XFREE (MTYPE_TMP, str); + + 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", + NO_STR + SET_STR + "Transform BGP AS_PATH attribute\n" + "Prepend to the as-path\n") +{ + int ret; + char *str; + + if (argc == 0) + return bgp_route_set_delete (vty, vty->index, "as-path prepend", NULL); + + str = argv_concat (argv, argc, 0); + ret = bgp_route_set_delete (vty, vty->index, "as-path prepend", str); + XFREE (MTYPE_TMP, str); + return ret; +} + +ALIAS (no_set_aspath_prepend, + no_set_aspath_prepend_val_cmd, + "no set as-path prepend ." CMD_AS_RANGE, + NO_STR + SET_STR + "Transform BGP AS_PATH attribute\n" + "Prepend to the as-path\n" + "AS number\n") + +DEFUN (set_aspath_exclude, + set_aspath_exclude_cmd, + "set as-path exclude ." CMD_AS_RANGE, + SET_STR + "Transform BGP AS-path attribute\n" + "Exclude from the as-path\n" + "AS number\n") +{ + int ret; + char *str; + + str = argv_concat (argv, argc, 0); + ret = bgp_route_set_add (vty, vty->index, "as-path exclude", str); + XFREE (MTYPE_TMP, str); + return ret; +} + +DEFUN (no_set_aspath_exclude, + no_set_aspath_exclude_cmd, + "no set as-path exclude", + NO_STR + SET_STR + "Transform BGP AS_PATH attribute\n" + "Exclude from the as-path\n") +{ + int ret; + char *str; + + if (argc == 0) + return bgp_route_set_delete (vty, vty->index, "as-path exclude", NULL); + + str = argv_concat (argv, argc, 0); + ret = bgp_route_set_delete (vty, vty->index, "as-path exclude", str); + XFREE (MTYPE_TMP, str); + return ret; +} + +ALIAS (no_set_aspath_exclude, + no_set_aspath_exclude_val_cmd, + "no set as-path exclude ." CMD_AS_RANGE, + NO_STR + SET_STR + "Transform BGP AS_PATH attribute\n" + "Exclude from the as-path\n" + "AS number\n") + +DEFUN (set_community, + set_community_cmd, + "set community .AA:NN", + SET_STR + "BGP community attribute\n" + "Community number in aa:nn format or local-AS|no-advertise|no-export|internet or additive\n") +{ + int i; + int first = 0; + int additive = 0; + struct buffer *b; + struct community *com = NULL; + char *str; + char *argstr; + int ret; + + b = buffer_new (1024); + + for (i = 0; i < argc; i++) + { + if (strncmp (argv[i], "additive", strlen (argv[i])) == 0) + { + additive = 1; + continue; + } + + if (first) + buffer_putc (b, ' '); + else + first = 1; + + if (strncmp (argv[i], "internet", strlen (argv[i])) == 0) + { + buffer_putstr (b, "internet"); + continue; + } + if (strncmp (argv[i], "local-AS", strlen (argv[i])) == 0) + { + buffer_putstr (b, "local-AS"); + continue; + } + if (strncmp (argv[i], "no-a", strlen ("no-a")) == 0 + && strncmp (argv[i], "no-advertise", strlen (argv[i])) == 0) + { + buffer_putstr (b, "no-advertise"); + continue; + } + if (strncmp (argv[i], "no-e", strlen ("no-e"))== 0 + && strncmp (argv[i], "no-export", strlen (argv[i])) == 0) + { + buffer_putstr (b, "no-export"); + continue; + } + buffer_putstr (b, argv[i]); + } + buffer_putc (b, '\0'); + + /* Fetch result string then compile it to communities attribute. */ + str = buffer_getstr (b); + buffer_free (b); + + if (str) + { + com = community_str2com (str); + XFREE (MTYPE_TMP, str); + } + + /* Can't compile user input into communities attribute. */ + if (! com) + { + vty_out (vty, "%% Malformed communities attribute%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Set communites attribute string. */ + str = community_str (com); + + if (additive) + { + argstr = XCALLOC (MTYPE_TMP, strlen (str) + strlen (" additive") + 1); + strcpy (argstr, str); + strcpy (argstr + strlen (str), " additive"); + ret = bgp_route_set_add (vty, vty->index, "community", argstr); + XFREE (MTYPE_TMP, argstr); + } + else + ret = bgp_route_set_add (vty, vty->index, "community", str); + + community_free (com); + + return ret; +} + +DEFUN (set_community_none, + set_community_none_cmd, + "set community none", + SET_STR + "BGP community attribute\n" + "No community attribute\n") +{ + return bgp_route_set_add (vty, vty->index, "community", "none"); +} + +DEFUN (no_set_community, + no_set_community_cmd, + "no set community", + NO_STR + SET_STR + "BGP community attribute\n") +{ + return bgp_route_set_delete (vty, vty->index, "community", NULL); +} + +ALIAS (no_set_community, + no_set_community_val_cmd, + "no set community .AA:NN", + NO_STR + SET_STR + "BGP community attribute\n" + "Community number in aa:nn format or local-AS|no-advertise|no-export|internet or additive\n") + +ALIAS (no_set_community, + no_set_community_none_cmd, + "no set community none", + NO_STR + SET_STR + "BGP community attribute\n" + "No community attribute\n") + +DEFUN (set_community_delete, + set_community_delete_cmd, + "set comm-list (<1-99>|<100-500>|WORD) delete", + SET_STR + "set BGP community list (for deletion)\n" + "Community-list number (standard)\n" + "Community-list number (expanded)\n" + "Community-list name\n" + "Delete matching 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, "comm-list", str); + + XFREE (MTYPE_TMP, str); + return CMD_SUCCESS; +} + +DEFUN (no_set_community_delete, + no_set_community_delete_cmd, + "no set comm-list", + NO_STR + SET_STR + "set BGP community list (for deletion)\n") +{ + return bgp_route_set_delete (vty, vty->index, "comm-list", NULL); +} + +ALIAS (no_set_community_delete, + no_set_community_delete_val_cmd, + "no set comm-list (<1-99>|<100-500>|WORD) delete", + NO_STR + SET_STR + "set BGP community list (for deletion)\n" + "Community-list number (standard)\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", + SET_STR + "BGP extended community attribute\n" + "Route Target extended community\n" + "VPN extended community\n") +{ + int ret; + char *str; + + str = argv_concat (argv, argc, 0); + ret = bgp_route_set_add (vty, vty->index, "extcommunity rt", str); + XFREE (MTYPE_TMP, str); + + return ret; +} + +DEFUN (no_set_ecommunity_rt, + no_set_ecommunity_rt_cmd, + "no set extcommunity rt", + NO_STR + SET_STR + "BGP extended community attribute\n" + "Route Target extended community\n") +{ + return bgp_route_set_delete (vty, vty->index, "extcommunity rt", NULL); +} + +ALIAS (no_set_ecommunity_rt, + no_set_ecommunity_rt_val_cmd, + "no set extcommunity rt .ASN:nn_or_IP-address:nn", + NO_STR + SET_STR + "BGP extended community attribute\n" + "Route Target extended community\n" + "VPN extended community\n") + +DEFUN (set_ecommunity_soo, + set_ecommunity_soo_cmd, + "set extcommunity soo .ASN:nn_or_IP-address:nn", + SET_STR + "BGP extended community attribute\n" + "Site-of-Origin extended community\n" + "VPN extended community\n") +{ + int ret; + char *str; + + str = argv_concat (argv, argc, 0); + ret = bgp_route_set_add (vty, vty->index, "extcommunity soo", str); + XFREE (MTYPE_TMP, str); + return ret; +} + +DEFUN (no_set_ecommunity_soo, + no_set_ecommunity_soo_cmd, + "no set extcommunity soo", + NO_STR + SET_STR + "BGP extended community attribute\n" + "Site-of-Origin extended community\n") +{ + return bgp_route_set_delete (vty, vty->index, "extcommunity soo", NULL); +} + +ALIAS (no_set_ecommunity_soo, + no_set_ecommunity_soo_val_cmd, + "no set extcommunity soo .ASN:nn_or_IP-address:nn", + NO_STR + SET_STR + "BGP extended community attribute\n" + "Site-of-Origin extended community\n" + "VPN extended community\n") + +DEFUN (set_origin, + set_origin_cmd, + "set origin (egp|igp|incomplete)", + SET_STR + "BGP origin code\n" + "remote EGP\n" + "local IGP\n" + "unknown heritage\n") +{ + if (strncmp (argv[0], "igp", 2) == 0) + return bgp_route_set_add (vty, vty->index, "origin", "igp"); + if (strncmp (argv[0], "egp", 1) == 0) + return bgp_route_set_add (vty, vty->index, "origin", "egp"); + if (strncmp (argv[0], "incomplete", 2) == 0) + return bgp_route_set_add (vty, vty->index, "origin", "incomplete"); + + return CMD_WARNING; +} + +DEFUN (no_set_origin, + no_set_origin_cmd, + "no set origin", + NO_STR + SET_STR + "BGP origin code\n") +{ + return bgp_route_set_delete (vty, vty->index, "origin", NULL); +} + +ALIAS (no_set_origin, + no_set_origin_val_cmd, + "no set origin (egp|igp|incomplete)", + NO_STR + SET_STR + "BGP origin code\n" + "remote EGP\n" + "local IGP\n" + "unknown heritage\n") + +DEFUN (set_atomic_aggregate, + set_atomic_aggregate_cmd, + "set atomic-aggregate", + SET_STR + "BGP atomic aggregate attribute\n" ) +{ + return bgp_route_set_add (vty, vty->index, "atomic-aggregate", NULL); +} + +DEFUN (no_set_atomic_aggregate, + no_set_atomic_aggregate_cmd, + "no set atomic-aggregate", + NO_STR + SET_STR + "BGP atomic aggregate attribute\n" ) +{ + return bgp_route_set_delete (vty, vty->index, "atomic-aggregate", NULL); +} + +DEFUN (set_aggregator_as, + set_aggregator_as_cmd, + "set aggregator as " CMD_AS_RANGE " A.B.C.D", + SET_STR + "BGP aggregator attribute\n" + "AS number of aggregator\n" + "AS number\n" + "IP address of aggregator\n") +{ + int ret; + as_t as __attribute__((unused)); /* dummy for VTY_GET_INTEGER_RANGE */ + struct in_addr address; + char *argstr; + + VTY_GET_INTEGER_RANGE ("AS", as, argv[0], 1, BGP_AS4_MAX); + + ret = inet_aton (argv[1], &address); + if (ret == 0) + { + vty_out (vty, "Aggregator IP address is invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + + argstr = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, + strlen (argv[0]) + strlen (argv[1]) + 2); + + sprintf (argstr, "%s %s", argv[0], argv[1]); + + ret = bgp_route_set_add (vty, vty->index, "aggregator as", argstr); + + XFREE (MTYPE_ROUTE_MAP_COMPILED, argstr); + + return ret; +} + +DEFUN (no_set_aggregator_as, + no_set_aggregator_as_cmd, + "no set aggregator as", + NO_STR + SET_STR + "BGP aggregator attribute\n" + "AS number of aggregator\n") +{ + int ret; + as_t as __attribute__((unused)); /* dummy for VTY_GET_INTEGER_RANGE */ + struct in_addr address; + char *argstr; + + if (argv == 0) + return bgp_route_set_delete (vty, vty->index, "aggregator as", NULL); + + VTY_GET_INTEGER_RANGE ("AS", as, argv[0], 1, BGP_AS4_MAX); + + ret = inet_aton (argv[1], &address); + if (ret == 0) + { + vty_out (vty, "Aggregator IP address is invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + + argstr = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, + strlen (argv[0]) + strlen (argv[1]) + 2); + + sprintf (argstr, "%s %s", argv[0], argv[1]); + + ret = bgp_route_set_delete (vty, vty->index, "aggregator as", argstr); + + XFREE (MTYPE_ROUTE_MAP_COMPILED, argstr); + + return ret; +} + +ALIAS (no_set_aggregator_as, + no_set_aggregator_as_val_cmd, + "no set aggregator as " CMD_AS_RANGE " A.B.C.D", + NO_STR + SET_STR + "BGP aggregator attribute\n" + "AS number of aggregator\n" + "AS number\n" + "IP address of aggregator\n") + +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", + MATCH_STR + IPV6_STR + "Match IPv6 address of route\n" + "IPv6 access-list name\n") +{ + return bgp_route_match_add (vty, vty->index, "ipv6 address", argv[0]); +} + +DEFUN (no_match_ipv6_address, + no_match_ipv6_address_cmd, + "no match ipv6 address WORD", + NO_STR + MATCH_STR + IPV6_STR + "Match IPv6 address of route\n" + "IPv6 access-list name\n") +{ + return bgp_route_match_delete (vty, vty->index, "ipv6 address", argv[0]); +} + +DEFUN (match_ipv6_next_hop, + match_ipv6_next_hop_cmd, + "match ipv6 next-hop X:X::X:X", + MATCH_STR + IPV6_STR + "Match IPv6 next-hop address of route\n" + "IPv6 address of next hop\n") +{ + return bgp_route_match_add (vty, vty->index, "ipv6 next-hop", argv[0]); +} + +DEFUN (no_match_ipv6_next_hop, + no_match_ipv6_next_hop_cmd, + "no match ipv6 next-hop X:X::X:X", + NO_STR + MATCH_STR + IPV6_STR + "Match IPv6 next-hop address of route\n" + "IPv6 address of next hop\n") +{ + return bgp_route_match_delete (vty, vty->index, "ipv6 next-hop", argv[0]); +} + +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 bgp_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 WORD", + NO_STR + MATCH_STR + IPV6_STR + "Match address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") +{ + 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", + SET_STR + IPV6_STR + "IPv6 next-hop address\n" + "IPv6 global address\n" + "IPv6 address of next hop\n") +{ + return bgp_route_set_add (vty, vty->index, "ipv6 next-hop global", argv[0]); +} + +DEFUN (no_set_ipv6_nexthop_global, + no_set_ipv6_nexthop_global_cmd, + "no set ipv6 next-hop global", + NO_STR + SET_STR + IPV6_STR + "IPv6 next-hop address\n" + "IPv6 global address\n") +{ + if (argc == 0) + return bgp_route_set_delete (vty, vty->index, "ipv6 next-hop global", NULL); + + return bgp_route_set_delete (vty, vty->index, "ipv6 next-hop global", argv[0]); +} + +ALIAS (no_set_ipv6_nexthop_global, + no_set_ipv6_nexthop_global_val_cmd, + "no set ipv6 next-hop global X:X::X:X", + NO_STR + SET_STR + IPV6_STR + "IPv6 next-hop address\n" + "IPv6 global address\n" + "IPv6 address of next hop\n") + +DEFUN (set_ipv6_nexthop_local, + set_ipv6_nexthop_local_cmd, + "set ipv6 next-hop local X:X::X:X", + SET_STR + IPV6_STR + "IPv6 next-hop address\n" + "IPv6 local address\n" + "IPv6 address of next hop\n") +{ + return bgp_route_set_add (vty, vty->index, "ipv6 next-hop local", argv[0]); +} + +DEFUN (no_set_ipv6_nexthop_local, + no_set_ipv6_nexthop_local_cmd, + "no set ipv6 next-hop local", + NO_STR + SET_STR + IPV6_STR + "IPv6 next-hop address\n" + "IPv6 local address\n") +{ + if (argc == 0) + return bgp_route_set_delete (vty, vty->index, "ipv6 next-hop local", NULL); + + return bgp_route_set_delete (vty, vty->index, "ipv6 next-hop local", argv[0]); +} + +ALIAS (no_set_ipv6_nexthop_local, + no_set_ipv6_nexthop_local_val_cmd, + "no set ipv6 next-hop local X:X::X:X", + NO_STR + SET_STR + IPV6_STR + "IPv6 next-hop address\n" + "IPv6 local address\n" + "IPv6 address of next hop\n") + +DEFUN (set_vpnv4_nexthop, + set_vpnv4_nexthop_cmd, + "set vpnv4 next-hop A.B.C.D", + SET_STR + "VPNv4 information\n" + "VPNv4 next-hop address\n" + "IP address of next hop\n") +{ + return bgp_route_set_add (vty, vty->index, "vpnv4 next-hop", argv[0]); +} + +DEFUN (no_set_vpnv4_nexthop, + no_set_vpnv4_nexthop_cmd, + "no set vpnv4 next-hop", + NO_STR + SET_STR + "VPNv4 information\n" + "VPNv4 next-hop address\n") +{ + if (argc == 0) + return bgp_route_set_delete (vty, vty->index, "vpnv4 next-hop", NULL); + + return bgp_route_set_delete (vty, vty->index, "vpnv4 next-hop", argv[0]); +} + +ALIAS (no_set_vpnv4_nexthop, + no_set_vpnv4_nexthop_val_cmd, + "no set vpnv4 next-hop A.B.C.D", + NO_STR + SET_STR + "VPNv4 information\n" + "VPNv4 next-hop address\n" + "IP address of next hop\n") + +DEFUN (set_originator_id, + set_originator_id_cmd, + "set originator-id A.B.C.D", + SET_STR + "BGP originator ID attribute\n" + "IP address of originator\n") +{ + return bgp_route_set_add (vty, vty->index, "originator-id", argv[0]); +} + +DEFUN (no_set_originator_id, + no_set_originator_id_cmd, + "no set originator-id", + NO_STR + SET_STR + "BGP originator ID attribute\n") +{ + if (argc == 0) + return bgp_route_set_delete (vty, vty->index, "originator-id", NULL); + + return bgp_route_set_delete (vty, vty->index, "originator-id", argv[0]); +} + +ALIAS (no_set_originator_id, + no_set_originator_id_val_cmd, + "no set originator-id A.B.C.D", + NO_STR + SET_STR + "BGP originator ID attribute\n" + "IP address of originator\n") + +DEFUN_DEPRECATED (set_pathlimit_ttl, + set_pathlimit_ttl_cmd, + "set pathlimit ttl <1-255>", + SET_STR + "BGP AS-Pathlimit attribute\n" + "Set AS-Path Hop-count TTL\n") +{ + return CMD_SUCCESS; +} + +DEFUN_DEPRECATED (no_set_pathlimit_ttl, + no_set_pathlimit_ttl_cmd, + "no set pathlimit ttl", + NO_STR + SET_STR + "BGP AS-Pathlimit attribute\n" + "Set AS-Path Hop-count TTL\n") +{ + return CMD_SUCCESS; +} + +ALIAS (no_set_pathlimit_ttl, + no_set_pathlimit_ttl_val_cmd, + "no set pathlimit ttl <1-255>", + NO_STR + MATCH_STR + "BGP AS-Pathlimit attribute\n" + "Set AS-Path Hop-count TTL\n") + +DEFUN_DEPRECATED (match_pathlimit_as, + match_pathlimit_as_cmd, + "match pathlimit as <1-65535>", + MATCH_STR + "BGP AS-Pathlimit attribute\n" + "Match Pathlimit AS number\n") +{ + return CMD_SUCCESS; +} + +DEFUN_DEPRECATED (no_match_pathlimit_as, + no_match_pathlimit_as_cmd, + "no match pathlimit as", + NO_STR + MATCH_STR + "BGP AS-Pathlimit attribute\n" + "Match Pathlimit AS number\n") +{ + return CMD_SUCCESS; +} + +ALIAS (no_match_pathlimit_as, + no_match_pathlimit_as_val_cmd, + "no match pathlimit as <1-65535>", + NO_STR + MATCH_STR + "BGP AS-Pathlimit attribute\n" + "Match Pathlimit ASN\n") + + +/* Initialization of route map. */ +void +bgp_route_map_init (void) +{ + route_map_init (); + route_map_init_vty (); + route_map_add_hook (bgp_route_map_update); + 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); + route_map_install_match (&route_match_ip_address_prefix_list_cmd); + route_map_install_match (&route_match_ip_next_hop_prefix_list_cmd); + 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); + route_map_install_set (&route_set_weight_cmd); + route_map_install_set (&route_set_metric_cmd); + route_map_install_set (&route_set_aspath_prepend_cmd); + route_map_install_set (&route_set_aspath_exclude_cmd); + route_map_install_set (&route_set_origin_cmd); + route_map_install_set (&route_set_atomic_aggregate_cmd); + 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); + install_element (RMAP_NODE, &no_match_peer_cmd); + install_element (RMAP_NODE, &no_match_peer_val_cmd); + install_element (RMAP_NODE, &no_match_peer_local_cmd); + install_element (RMAP_NODE, &match_ip_address_cmd); + install_element (RMAP_NODE, &no_match_ip_address_cmd); + install_element (RMAP_NODE, &no_match_ip_address_val_cmd); + install_element (RMAP_NODE, &match_ip_next_hop_cmd); + install_element (RMAP_NODE, &no_match_ip_next_hop_cmd); + install_element (RMAP_NODE, &no_match_ip_next_hop_val_cmd); + install_element (RMAP_NODE, &match_ip_route_source_cmd); + install_element (RMAP_NODE, &no_match_ip_route_source_cmd); + install_element (RMAP_NODE, &no_match_ip_route_source_val_cmd); + install_element (RMAP_NODE, &match_ip_address_prefix_list_cmd); + install_element (RMAP_NODE, &no_match_ip_address_prefix_list_cmd); + install_element (RMAP_NODE, &no_match_ip_address_prefix_list_val_cmd); + install_element (RMAP_NODE, &match_ip_next_hop_prefix_list_cmd); + install_element (RMAP_NODE, &no_match_ip_next_hop_prefix_list_cmd); + install_element (RMAP_NODE, &no_match_ip_next_hop_prefix_list_val_cmd); + install_element (RMAP_NODE, &match_ip_route_source_prefix_list_cmd); + install_element (RMAP_NODE, &no_match_ip_route_source_prefix_list_cmd); + install_element (RMAP_NODE, &no_match_ip_route_source_prefix_list_val_cmd); + + install_element (RMAP_NODE, &match_aspath_cmd); + install_element (RMAP_NODE, &no_match_aspath_cmd); + install_element (RMAP_NODE, &no_match_aspath_val_cmd); + 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); + install_element (RMAP_NODE, &match_origin_cmd); + install_element (RMAP_NODE, &no_match_origin_cmd); + install_element (RMAP_NODE, &no_match_origin_val_cmd); + 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); + install_element (RMAP_NODE, &no_set_ip_nexthop_cmd); + install_element (RMAP_NODE, &no_set_ip_nexthop_val_cmd); + install_element (RMAP_NODE, &set_local_pref_cmd); + install_element (RMAP_NODE, &no_set_local_pref_cmd); + install_element (RMAP_NODE, &no_set_local_pref_val_cmd); + install_element (RMAP_NODE, &set_weight_cmd); + install_element (RMAP_NODE, &no_set_weight_cmd); + 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); + install_element (RMAP_NODE, &no_set_aspath_exclude_cmd); + install_element (RMAP_NODE, &no_set_aspath_exclude_val_cmd); + install_element (RMAP_NODE, &set_origin_cmd); + install_element (RMAP_NODE, &no_set_origin_cmd); + install_element (RMAP_NODE, &no_set_origin_val_cmd); + install_element (RMAP_NODE, &set_atomic_aggregate_cmd); + install_element (RMAP_NODE, &no_set_atomic_aggregate_cmd); + install_element (RMAP_NODE, &set_aggregator_as_cmd); + install_element (RMAP_NODE, &no_set_aggregator_as_cmd); + install_element (RMAP_NODE, &no_set_aggregator_as_val_cmd); + install_element (RMAP_NODE, &set_community_cmd); + install_element (RMAP_NODE, &set_community_none_cmd); + install_element (RMAP_NODE, &no_set_community_cmd); + install_element (RMAP_NODE, &no_set_community_val_cmd); + install_element (RMAP_NODE, &no_set_community_none_cmd); + 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); + install_element (RMAP_NODE, &set_ecommunity_soo_cmd); + install_element (RMAP_NODE, &no_set_ecommunity_soo_cmd); + install_element (RMAP_NODE, &no_set_ecommunity_soo_val_cmd); + install_element (RMAP_NODE, &set_vpnv4_nexthop_cmd); + install_element (RMAP_NODE, &no_set_vpnv4_nexthop_cmd); + install_element (RMAP_NODE, &no_set_vpnv4_nexthop_val_cmd); + 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); + + 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); + install_element (RMAP_NODE, &no_match_ipv6_next_hop_cmd); + install_element (RMAP_NODE, &match_ipv6_address_prefix_list_cmd); + install_element (RMAP_NODE, &no_match_ipv6_address_prefix_list_cmd); + install_element (RMAP_NODE, &set_ipv6_nexthop_global_cmd); + install_element (RMAP_NODE, &no_set_ipv6_nexthop_global_cmd); + install_element (RMAP_NODE, &no_set_ipv6_nexthop_global_val_cmd); + install_element (RMAP_NODE, &set_ipv6_nexthop_local_cmd); + install_element (RMAP_NODE, &no_set_ipv6_nexthop_local_cmd); + install_element (RMAP_NODE, &no_set_ipv6_nexthop_local_val_cmd); + install_element (RMAP_NODE, &set_ipv6_nexthop_peer_cmd); + install_element (RMAP_NODE, &no_set_ipv6_nexthop_peer_cmd); + + /* AS-Pathlimit: functionality removed, commands kept for + * compatibility. + */ + install_element (RMAP_NODE, &set_pathlimit_ttl_cmd); + install_element (RMAP_NODE, &no_set_pathlimit_ttl_cmd); + install_element (RMAP_NODE, &no_set_pathlimit_ttl_val_cmd); + install_element (RMAP_NODE, &match_pathlimit_as_cmd); + install_element (RMAP_NODE, &no_match_pathlimit_as_cmd); + install_element (RMAP_NODE, &no_match_pathlimit_as_val_cmd); +} diff --git a/bgpd/bgp_snmp.c b/bgpd/bgp_snmp.c new file mode 100644 index 0000000..c4490bf --- /dev/null +++ b/bgpd/bgp_snmp.c @@ -0,0 +1,887 @@ +/* BGP4 SNMP support + Copyright (C) 1999, 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 + +#ifdef HAVE_SNMP +#include +#include + +#include "if.h" +#include "log.h" +#include "prefix.h" +#include "command.h" +#include "thread.h" +#include "smux.h" +#include "filter.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_table.h" +#include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_attr.h" +#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 + +/* BGP TRAP. */ +#define BGPESTABLISHED 1 +#define BGPBACKWARDTRANSITION 2 + +/* BGP MIB bgpVersion. */ +#define BGPVERSION 0 + +/* BGP MIB bgpLocalAs. */ +#define BGPLOCALAS 0 + +/* BGP MIB bgpPeerTable. */ +#define BGPPEERIDENTIFIER 1 +#define BGPPEERSTATE 2 +#define BGPPEERADMINSTATUS 3 +#define BGPPEERNEGOTIATEDVERSION 4 +#define BGPPEERLOCALADDR 5 +#define BGPPEERLOCALPORT 6 +#define BGPPEERREMOTEADDR 7 +#define BGPPEERREMOTEPORT 8 +#define BGPPEERREMOTEAS 9 +#define BGPPEERINUPDATES 10 +#define BGPPEEROUTUPDATES 11 +#define BGPPEERINTOTALMESSAGES 12 +#define BGPPEEROUTTOTALMESSAGES 13 +#define BGPPEERLASTERROR 14 +#define BGPPEERFSMESTABLISHEDTRANSITIONS 15 +#define BGPPEERFSMESTABLISHEDTIME 16 +#define BGPPEERCONNECTRETRYINTERVAL 17 +#define BGPPEERHOLDTIME 18 +#define BGPPEERKEEPALIVE 19 +#define BGPPEERHOLDTIMECONFIGURED 20 +#define BGPPEERKEEPALIVECONFIGURED 21 +#define BGPPEERMINROUTEADVERTISEMENTINTERVAL 22 +#define BGPPEERINUPDATEELAPSEDTIME 23 + +/* BGP MIB bgpIdentifier. */ +#define BGPIDENTIFIER 0 + +/* BGP MIB bgpRcvdPathAttrTable */ +#define BGPPATHATTRPEER 1 +#define BGPPATHATTRDESTNETWORK 2 +#define BGPPATHATTRORIGIN 3 +#define BGPPATHATTRASPATH 4 +#define BGPPATHATTRNEXTHOP 5 +#define BGPPATHATTRINTERASMETRIC 6 + +/* BGP MIB bgp4PathAttrTable. */ +#define BGP4PATHATTRPEER 1 +#define BGP4PATHATTRIPADDRPREFIXLEN 2 +#define BGP4PATHATTRIPADDRPREFIX 3 +#define BGP4PATHATTRORIGIN 4 +#define BGP4PATHATTRASPATHSEGMENT 5 +#define BGP4PATHATTRNEXTHOP 6 +#define BGP4PATHATTRMULTIEXITDISC 7 +#define BGP4PATHATTRLOCALPREF 8 +#define BGP4PATHATTRATOMICAGGREGATE 9 +#define BGP4PATHATTRAGGREGATORAS 10 +#define BGP4PATHATTRAGGREGATORADDR 11 +#define BGP4PATHATTRCALCLOCALPREF 12 +#define BGP4PATHATTRBEST 13 +#define BGP4PATHATTRUNKNOWN 14 + +/* SNMP value hack. */ +#define INTEGER ASN_INTEGER +#define INTEGER32 ASN_INTEGER +#define COUNTER32 ASN_COUNTER +#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 = { .s_addr = 0 }; + +/* Hook functions. */ +static u_char *bgpVersion (struct variable *, oid [], size_t *, int, + size_t *, WriteMethod **); +static u_char *bgpLocalAs (struct variable *, oid [], size_t *, + int, size_t *, WriteMethod **); +static u_char *bgpPeerTable (struct variable *, oid [], size_t *, + int, size_t *, WriteMethod **); +static u_char *bgpRcvdPathAttrTable (struct variable *, oid [], size_t *, + int, size_t *, WriteMethod **); +static u_char *bgpIdentifier (struct variable *, oid [], size_t *, + int, size_t *, WriteMethod **); +static u_char *bgp4PathAttrTable (struct variable *, oid [], size_t *, + int, size_t *, WriteMethod **); +/* static u_char *bgpTraps (); */ + +struct variable bgp_variables[] = +{ + /* BGP version. */ + {BGPVERSION, OCTET_STRING, RONLY, bgpVersion, + 1, {1}}, + /* BGP local AS. */ + {BGPLOCALAS, INTEGER, RONLY, bgpLocalAs, + 1, {2}}, + /* BGP peer table. */ + {BGPPEERIDENTIFIER, IPADDRESS, RONLY, bgpPeerTable, + 3, {3, 1, 1}}, + {BGPPEERSTATE, INTEGER, RONLY, bgpPeerTable, + 3, {3, 1, 2}}, + {BGPPEERADMINSTATUS, INTEGER, RWRITE, bgpPeerTable, + 3, {3, 1, 3}}, + {BGPPEERNEGOTIATEDVERSION, INTEGER32, RONLY, bgpPeerTable, + 3, {3, 1, 4}}, + {BGPPEERLOCALADDR, IPADDRESS, RONLY, bgpPeerTable, + 3, {3, 1, 5}}, + {BGPPEERLOCALPORT, INTEGER, RONLY, bgpPeerTable, + 3, {3, 1, 6}}, + {BGPPEERREMOTEADDR, IPADDRESS, RONLY, bgpPeerTable, + 3, {3, 1, 7}}, + {BGPPEERREMOTEPORT, INTEGER, RONLY, bgpPeerTable, + 3, {3, 1, 8}}, + {BGPPEERREMOTEAS, INTEGER, RONLY, bgpPeerTable, + 3, {3, 1, 9}}, + {BGPPEERINUPDATES, COUNTER32, RONLY, bgpPeerTable, + 3, {3, 1, 10}}, + {BGPPEEROUTUPDATES, COUNTER32, RONLY, bgpPeerTable, + 3, {3, 1, 11}}, + {BGPPEERINTOTALMESSAGES, COUNTER32, RONLY, bgpPeerTable, + 3, {3, 1, 12}}, + {BGPPEEROUTTOTALMESSAGES, COUNTER32, RONLY, bgpPeerTable, + 3, {3, 1, 13}}, + {BGPPEERLASTERROR, OCTET_STRING, RONLY, bgpPeerTable, + 3, {3, 1, 14}}, + {BGPPEERFSMESTABLISHEDTRANSITIONS, COUNTER32, RONLY, bgpPeerTable, + 3, {3, 1, 15}}, + {BGPPEERFSMESTABLISHEDTIME, GAUGE32, RONLY, bgpPeerTable, + 3, {3, 1, 16}}, + {BGPPEERCONNECTRETRYINTERVAL, INTEGER, RWRITE, bgpPeerTable, + 3, {3, 1, 17}}, + {BGPPEERHOLDTIME, INTEGER, RONLY, bgpPeerTable, + 3, {3, 1, 18}}, + {BGPPEERKEEPALIVE, INTEGER, RONLY, bgpPeerTable, + 3, {3, 1, 19}}, + {BGPPEERHOLDTIMECONFIGURED, INTEGER, RWRITE, bgpPeerTable, + 3, {3, 1, 20}}, + {BGPPEERKEEPALIVECONFIGURED, INTEGER, RWRITE, bgpPeerTable, + 3, {3, 1, 21}}, + {BGPPEERMINROUTEADVERTISEMENTINTERVAL, INTEGER, RWRITE, bgpPeerTable, + 3, {3, 1, 23}}, + {BGPPEERINUPDATEELAPSEDTIME, GAUGE32, RONLY, bgpPeerTable, + 3, {3, 1, 24}}, + /* BGP identifier. */ + {BGPIDENTIFIER, IPADDRESS, RONLY, bgpIdentifier, + 1, {4}}, + /* BGP received path attribute table. */ + {BGPPATHATTRPEER, IPADDRESS, RONLY, bgpRcvdPathAttrTable, + 3, {5, 1, 1}}, + {BGPPATHATTRDESTNETWORK, IPADDRESS, RONLY, bgpRcvdPathAttrTable, + 3, {5, 1, 2}}, + {BGPPATHATTRORIGIN, INTEGER, RONLY, bgpRcvdPathAttrTable, + 3, {5, 1, 3}}, + {BGPPATHATTRASPATH, OCTET_STRING, RONLY, bgpRcvdPathAttrTable, + 3, {5, 1, 4}}, + {BGPPATHATTRNEXTHOP, IPADDRESS, RONLY, bgpRcvdPathAttrTable, + 3, {5, 1, 5}}, + {BGPPATHATTRINTERASMETRIC, INTEGER32, RONLY, bgpRcvdPathAttrTable, + 3, {5, 1, 6}}, + /* BGP-4 received path attribute table. */ + {BGP4PATHATTRPEER, IPADDRESS, RONLY, bgp4PathAttrTable, + 3, {6, 1, 1}}, + {BGP4PATHATTRIPADDRPREFIXLEN, INTEGER, RONLY, bgp4PathAttrTable, + 3, {6, 1, 2}}, + {BGP4PATHATTRIPADDRPREFIX, IPADDRESS, RONLY, bgp4PathAttrTable, + 3, {6, 1, 3}}, + {BGP4PATHATTRORIGIN, INTEGER, RONLY, bgp4PathAttrTable, + 3, {6, 1, 4}}, + {BGP4PATHATTRASPATHSEGMENT, OCTET_STRING, RONLY, bgp4PathAttrTable, + 3, {6, 1, 5}}, + {BGP4PATHATTRNEXTHOP, IPADDRESS, RONLY, bgp4PathAttrTable, + 3, {6, 1, 6}}, + {BGP4PATHATTRMULTIEXITDISC, INTEGER, RONLY, bgp4PathAttrTable, + 3, {6, 1, 7}}, + {BGP4PATHATTRLOCALPREF, INTEGER, RONLY, bgp4PathAttrTable, + 3, {6, 1, 8}}, + {BGP4PATHATTRATOMICAGGREGATE, INTEGER, RONLY, bgp4PathAttrTable, + 3, {6, 1, 9}}, + {BGP4PATHATTRAGGREGATORAS, INTEGER, RONLY, bgp4PathAttrTable, + 3, {6, 1, 10}}, + {BGP4PATHATTRAGGREGATORADDR, IPADDRESS, RONLY, bgp4PathAttrTable, + 3, {6, 1, 11}}, + {BGP4PATHATTRCALCLOCALPREF, INTEGER, RONLY, bgp4PathAttrTable, + 3, {6, 1, 12}}, + {BGP4PATHATTRBEST, INTEGER, RONLY, bgp4PathAttrTable, + 3, {6, 1, 13}}, + {BGP4PATHATTRUNKNOWN, OCTET_STRING, RONLY, bgp4PathAttrTable, + 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) +{ + static u_char version; + + if (smux_header_generic(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + /* Retrun BGP version. Zebra bgpd only support version 4. */ + version = (0x80 >> (BGP_VERSION_4 - 1)); + + /* Return octet string length 1. */ + *var_len = 1; + return (u_char *)&version; +} + +static u_char * +bgpLocalAs (struct variable *v, oid name[], size_t *length, + int exact, size_t *var_len, WriteMethod **write_method) +{ + struct bgp *bgp; + + if (smux_header_generic(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + /* Get BGP structure. */ + bgp = bgp_get_default (); + if (! bgp) + return NULL; + + return SNMP_INTEGER (bgp->as); +} + +static struct peer * +peer_lookup_addr_ipv4 (struct in_addr *src) +{ + struct bgp *bgp; + struct peer *peer; + struct listnode *node; + struct in_addr addr; + int ret; + + bgp = bgp_get_default (); + if (! bgp) + return NULL; + + for (ALL_LIST_ELEMENTS_RO (bgp->peer, node, peer)) + { + ret = inet_pton (AF_INET, peer->host, &addr); + if (ret > 0) + { + if (IPV4_ADDR_SAME (&addr, src)) + return peer; + } + } + return NULL; +} + +static struct peer * +bgp_peer_lookup_next (struct in_addr *src) +{ + struct bgp *bgp; + struct peer *peer; + struct listnode *node; + struct in_addr *p; + union sockunion su; + int ret; + + memset (&su, 0, sizeof (union sockunion)); + + bgp = bgp_get_default (); + if (! bgp) + return NULL; + + for (ALL_LIST_ELEMENTS_RO (bgp->peer, node, peer)) + { + ret = inet_pton (AF_INET, peer->host, &su.sin.sin_addr); + if (ret > 0) + { + p = &su.sin.sin_addr; + + if (ntohl (p->s_addr) > ntohl (src->s_addr)) + { + src->s_addr = p->s_addr; + return peer; + } + } + } + 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 - namelen != sizeof (struct in_addr)) + return NULL; + + oid2in_addr (name + namelen, IN_ADDR_SIZE, addr); + + peer = peer_lookup_addr_ipv4 (addr); + return peer; + } + else + { + len = *length - namelen; + if (len > 4) len = 4; + + oid2in_addr (name + namelen, len, addr); + + peer = bgp_peer_lookup_next (addr); + + if (peer == NULL) + return NULL; + + oid_copy_addr (name + namelen, addr, sizeof (struct in_addr)); + *length = sizeof (struct in_addr) + namelen; + + return peer; + } + return NULL; +} + +/* BGP write methods. */ +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 in_addr addr; + struct peer *peer; + long intval; + + if (var_val_type != ASN_INTEGER) + { + return SNMP_ERR_WRONGTYPE; + } + if (var_val_len != sizeof (long)) + { + return SNMP_ERR_WRONGLENGTH; + } + + intval = *(long *)var_val; + + memset (&addr, 0, sizeof (struct in_addr)); + + peer = bgpPeerTable_lookup (NULL, name, &length, &addr, 1); + if (! peer) + return SNMP_ERR_NOSUCHNAME; + + if (action != SNMP_MSG_INTERNAL_SET_COMMIT) + return SNMP_ERR_NOERROR; + + 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 +#define BGP_PeerAdmin_start 2 + /* When the peer is established, */ + if (intval == BGP_PeerAdmin_stop) + BGP_EVENT_ADD (peer, BGP_Stop); + else if (intval == BGP_PeerAdmin_start) + ; /* Do nothing. */ + else + return SNMP_ERR_NOSUCHNAME; + break; + case BGPPEERCONNECTRETRYINTERVAL: + SET_FLAG (peer->config, PEER_CONFIG_CONNECT); + peer->connect = intval; + peer->v_connect = intval; + break; + case BGPPEERHOLDTIMECONFIGURED: + SET_FLAG (peer->config, PEER_CONFIG_TIMER); + peer->holdtime = intval; + peer->v_holdtime = intval; + break; + case BGPPEERKEEPALIVECONFIGURED: + SET_FLAG (peer->config, PEER_CONFIG_TIMER); + peer->keepalive = intval; + peer->v_keepalive = intval; + break; + case BGPPEERMINROUTEADVERTISEMENTINTERVAL: + peer->v_routeadv = intval; + break; + } + return SNMP_ERR_NOERROR; +} + +static u_char * +bgpPeerTable (struct variable *v, oid name[], size_t *length, + int exact, size_t *var_len, WriteMethod **write_method) +{ + static struct in_addr addr; + struct peer *peer; + + 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); + if (! peer) + return NULL; + + switch (v->magic) + { + case BGPPEERIDENTIFIER: + return SNMP_IPADDRESS (peer->remote_id); + break; + case BGPPEERSTATE: + return SNMP_INTEGER (peer->status); + break; + case BGPPEERADMINSTATUS: + *write_method = write_bgpPeerTable; +#define BGP_PeerAdmin_stop 1 +#define BGP_PeerAdmin_start 2 + if (CHECK_FLAG (peer->flags, PEER_FLAG_SHUTDOWN)) + return SNMP_INTEGER (BGP_PeerAdmin_stop); + else + return SNMP_INTEGER (BGP_PeerAdmin_start); + break; + case BGPPEERNEGOTIATEDVERSION: + return SNMP_INTEGER (BGP_VERSION_4); + break; + case BGPPEERLOCALADDR: + if (peer->su_local) + return SNMP_IPADDRESS (peer->su_local->sin.sin_addr); + else + return SNMP_IPADDRESS (bgp_empty_addr); + break; + case BGPPEERLOCALPORT: + if (peer->su_local) + return SNMP_INTEGER (ntohs (peer->su_local->sin.sin_port)); + else + return SNMP_INTEGER (0); + break; + case BGPPEERREMOTEADDR: + if (peer->su_remote) + return SNMP_IPADDRESS (peer->su_remote->sin.sin_addr); + else + return SNMP_IPADDRESS (bgp_empty_addr); + break; + case BGPPEERREMOTEPORT: + if (peer->su_remote) + return SNMP_INTEGER (ntohs (peer->su_remote->sin.sin_port)); + else + return SNMP_INTEGER (0); + break; + case BGPPEERREMOTEAS: + return SNMP_INTEGER (peer->as); + break; + case BGPPEERINUPDATES: + return SNMP_INTEGER (peer->update_in); + break; + case BGPPEEROUTUPDATES: + return SNMP_INTEGER (peer->update_out); + break; + case BGPPEERINTOTALMESSAGES: + return SNMP_INTEGER (peer->open_in + peer->update_in + + peer->keepalive_in + peer->notify_in + + peer->refresh_in + peer->dynamic_cap_in); + break; + case BGPPEEROUTTOTALMESSAGES: + return SNMP_INTEGER (peer->open_out + peer->update_out + + peer->keepalive_out + peer->notify_out + + peer->refresh_out + peer->dynamic_cap_out); + break; + case BGPPEERLASTERROR: + { + static u_char lasterror[2]; + lasterror[0] = peer->notify.code; + lasterror[1] = peer->notify.subcode; + *var_len = 2; + return (u_char *)&lasterror; + } + break; + case BGPPEERFSMESTABLISHEDTRANSITIONS: + return SNMP_INTEGER (peer->established); + break; + case BGPPEERFSMESTABLISHEDTIME: + if (peer->uptime == 0) + return SNMP_INTEGER (0); + else + return SNMP_INTEGER (bgp_clock () - peer->uptime); + break; + case BGPPEERCONNECTRETRYINTERVAL: + *write_method = write_bgpPeerTable; + return SNMP_INTEGER (peer->v_connect); + break; + case BGPPEERHOLDTIME: + return SNMP_INTEGER (peer->v_holdtime); + break; + case BGPPEERKEEPALIVE: + return SNMP_INTEGER (peer->v_keepalive); + break; + case BGPPEERHOLDTIMECONFIGURED: + *write_method = write_bgpPeerTable; + if (CHECK_FLAG (peer->config, PEER_CONFIG_TIMER)) + return SNMP_INTEGER (peer->holdtime); + else + return SNMP_INTEGER (peer->v_holdtime); + break; + case BGPPEERKEEPALIVECONFIGURED: + *write_method = write_bgpPeerTable; + if (CHECK_FLAG (peer->config, PEER_CONFIG_TIMER)) + return SNMP_INTEGER (peer->keepalive); + else + return SNMP_INTEGER (peer->v_keepalive); + break; + case BGPPEERMINROUTEADVERTISEMENTINTERVAL: + *write_method = write_bgpPeerTable; + return SNMP_INTEGER (peer->v_routeadv); + break; + case BGPPEERINUPDATEELAPSEDTIME: + if (peer->update_time == 0) + return SNMP_INTEGER (0); + else + return SNMP_INTEGER (bgp_clock () - peer->update_time); + break; + default: + return NULL; + break; + } + return NULL; +} + +static u_char * +bgpIdentifier (struct variable *v, oid name[], size_t *length, + int exact, size_t *var_len, WriteMethod **write_method) +{ + struct bgp *bgp; + + if (smux_header_generic(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + bgp = bgp_get_default (); + if (!bgp) + return NULL; + + return SNMP_IPADDRESS (bgp->router_id); +} + +static u_char * +bgpRcvdPathAttrTable (struct variable *v, oid name[], size_t *length, + int exact, size_t *var_len, WriteMethod **write_method) +{ + /* Received Path Attribute Table. This table contains, one entry + per path to a network, path attributes received from all peers + running BGP version 3 or less. This table is obsolete, having + been replaced in functionality with the bgp4PathAttrTable. */ + return NULL; +} + +static struct bgp_info * +bgp4PathAttrLookup (struct variable *v, oid name[], size_t *length, + struct bgp *bgp, struct prefix_ipv4 *addr, int exact) +{ + oid *offset; + int offsetlen; + struct bgp_info *binfo; + struct bgp_info *min; + struct bgp_node *rn; + union sockunion su; + unsigned int len; + struct in_addr paddr; + +#define BGP_PATHATTR_ENTRY_OFFSET \ + (IN_ADDR_SIZE + 1 + IN_ADDR_SIZE) + + if (exact) + { + if (*length - v->namelen != BGP_PATHATTR_ENTRY_OFFSET) + return NULL; + + /* Set OID offset for prefix. */ + offset = name + v->namelen; + oid2in_addr (offset, IN_ADDR_SIZE, &addr->prefix); + offset += IN_ADDR_SIZE; + + /* Prefix length. */ + addr->prefixlen = *offset; + offset++; + + /* Peer address. */ + su.sin.sin_family = AF_INET; + oid2in_addr (offset, IN_ADDR_SIZE, &su.sin.sin_addr); + + /* Lookup node. */ + rn = bgp_node_lookup (bgp->rib[AFI_IP][SAFI_UNICAST], + (struct prefix *) addr); + if (rn) + { + bgp_unlock_node (rn); + + for (binfo = rn->info; binfo; binfo = binfo->next) + if (sockunion_same (&binfo->peer->su, &su)) + return binfo; + } + } + else + { + offset = name + v->namelen; + offsetlen = *length - v->namelen; + len = offsetlen; + + if (offsetlen == 0) + rn = bgp_table_top (bgp->rib[AFI_IP][SAFI_UNICAST]); + else + { + if (len > IN_ADDR_SIZE) + len = IN_ADDR_SIZE; + + oid2in_addr (offset, len, &addr->prefix); + + offset += IN_ADDR_SIZE; + offsetlen -= IN_ADDR_SIZE; + + if (offsetlen > 0) + addr->prefixlen = *offset; + else + addr->prefixlen = len * 8; + + rn = bgp_node_get (bgp->rib[AFI_IP][SAFI_UNICAST], + (struct prefix *) addr); + + offset++; + offsetlen--; + } + + if (offsetlen > 0) + { + len = offsetlen; + if (len > IN_ADDR_SIZE) + len = IN_ADDR_SIZE; + + oid2in_addr (offset, len, &paddr); + } + else + paddr.s_addr = 0; + + if (! rn) + return NULL; + + do + { + min = NULL; + + for (binfo = rn->info; binfo; binfo = binfo->next) + { + if (binfo->peer->su.sin.sin_family == AF_INET + && ntohl (paddr.s_addr) + < ntohl (binfo->peer->su.sin.sin_addr.s_addr)) + { + if (min) + { + if (ntohl (binfo->peer->su.sin.sin_addr.s_addr) + < ntohl (min->peer->su.sin.sin_addr.s_addr)) + min = binfo; + } + else + min = binfo; + } + } + + if (min) + { + *length = v->namelen + BGP_PATHATTR_ENTRY_OFFSET; + + offset = name + v->namelen; + oid_copy_addr (offset, &rn->p.u.prefix4, IN_ADDR_SIZE); + offset += IN_ADDR_SIZE; + *offset = rn->p.prefixlen; + offset++; + oid_copy_addr (offset, &min->peer->su.sin.sin_addr, + IN_ADDR_SIZE); + addr->prefix = rn->p.u.prefix4; + addr->prefixlen = rn->p.prefixlen; + + bgp_unlock_node (rn); + + return min; + } + + paddr.s_addr = 0; + } + while ((rn = bgp_route_next (rn)) != NULL); + } + return NULL; +} + +static u_char * +bgp4PathAttrTable (struct variable *v, oid name[], size_t *length, + int exact, size_t *var_len, WriteMethod **write_method) +{ + struct bgp *bgp; + struct bgp_info *binfo; + struct prefix_ipv4 addr; + + bgp = bgp_get_default (); + 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); + if (! binfo) + return NULL; + + switch (v->magic) + { + case BGP4PATHATTRPEER: /* 1 */ + return SNMP_IPADDRESS (binfo->peer->su.sin.sin_addr); + break; + case BGP4PATHATTRIPADDRPREFIXLEN: /* 2 */ + return SNMP_INTEGER (addr.prefixlen); + break; + case BGP4PATHATTRIPADDRPREFIX: /* 3 */ + return SNMP_IPADDRESS (addr.prefix); + break; + case BGP4PATHATTRORIGIN: /* 4 */ + return SNMP_INTEGER (binfo->attr->origin); + break; + case BGP4PATHATTRASPATHSEGMENT: /* 5 */ + return aspath_snmp_pathseg (binfo->attr->aspath, var_len); + break; + case BGP4PATHATTRNEXTHOP: /* 6 */ + return SNMP_IPADDRESS (binfo->attr->nexthop); + break; + case BGP4PATHATTRMULTIEXITDISC: /* 7 */ + return SNMP_INTEGER (binfo->attr->med); + break; + case BGP4PATHATTRLOCALPREF: /* 8 */ + return SNMP_INTEGER (binfo->attr->local_pref); + break; + case BGP4PATHATTRATOMICAGGREGATE: /* 9 */ + return SNMP_INTEGER (1); + break; + case BGP4PATHATTRAGGREGATORAS: /* 10 */ + if (binfo->attr->extra) + return SNMP_INTEGER (binfo->attr->extra->aggregator_as); + else + return SNMP_INTEGER (0); + break; + case BGP4PATHATTRAGGREGATORADDR: /* 11 */ + if (binfo->attr->extra) + return SNMP_IPADDRESS (binfo->attr->extra->aggregator_addr); + else + return SNMP_INTEGER (0); + break; + case BGP4PATHATTRCALCLOCALPREF: /* 12 */ + return SNMP_INTEGER (-1); + break; + case BGP4PATHATTRBEST: /* 13 */ +#define BGP4_PathAttrBest_false 1 +#define BGP4_PathAttrBest_true 2 + if (CHECK_FLAG (binfo->flags, BGP_INFO_SELECTED)) + return SNMP_INTEGER (BGP4_PathAttrBest_true); + else + return SNMP_INTEGER (BGP4_PathAttrBest_false); + break; + case BGP4PATHATTRUNKNOWN: /* 14 */ + *var_len = 0; + return NULL; + break; + } + return NULL; +} + +/* BGP Traps. */ +struct trap_object bgpTrapList[] = +{ + {3, {3, 1, BGPPEERLASTERROR}}, + {3, {3, 1, BGPPEERSTATE}} +}; + +void +bgpTrapEstablished (struct peer *peer) +{ + int ret; + struct in_addr addr; + oid index[sizeof (oid) * IN_ADDR_SIZE]; + + ret = inet_aton (peer->host, &addr); + if (ret == 0) + return; + + oid_copy_addr (index, &addr, IN_ADDR_SIZE); + + 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), + BGPESTABLISHED); +} + +void +bgpTrapBackwardTransition (struct peer *peer) +{ + int ret; + struct in_addr addr; + oid index[sizeof (oid) * IN_ADDR_SIZE]; + + ret = inet_aton (peer->host, &addr); + if (ret == 0) + return; + + oid_copy_addr (index, &addr, IN_ADDR_SIZE); + + 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), + BGPBACKWARDTRANSITION); +} + +void +bgp_snmp_init (void) +{ + smux_init (bm->master); + REGISTER_MIB("mibII/bgp", bgp_variables, variable, bgp_oid); +} +#endif /* HAVE_SNMP */ diff --git a/bgpd/bgp_snmp.h b/bgpd/bgp_snmp.h new file mode 100644 index 0000000..7a0d9dd --- /dev/null +++ b/bgpd/bgp_snmp.h @@ -0,0 +1,28 @@ +/* BGP4 SNMP support + Copyright (C) 1999, 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. */ + +#ifndef _QUAGGA_BGP_SNMP_H +#define _QUAGGA_BGP_SNMP_H + +extern void bgp_snmp_init (void); +extern void bgpTrapEstablished (struct peer *); +extern void bgpTrapBackwardTransition (struct peer *); + +#endif /* _QUAGGA_BGP_SNMP_H */ diff --git a/bgpd/bgp_table.c b/bgpd/bgp_table.c new file mode 100644 index 0000000..92bb957 --- /dev/null +++ b/bgpd/bgp_table.c @@ -0,0 +1,126 @@ +/* BGP routing table + Copyright (C) 1998, 2001 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 "memory.h" +#include "sockunion.h" +#include "vty.h" +#include "filter.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_table.h" + +void +bgp_table_lock (struct bgp_table *rt) +{ + rt->lock++; +} + +void +bgp_table_unlock (struct bgp_table *rt) +{ + assert (rt->lock > 0); + rt->lock--; + + if (rt->lock != 0) + { + return; + } + + route_table_finish (rt->route_table); + rt->route_table = NULL; + + if (rt->owner) + { + peer_unlock (rt->owner); + rt->owner = NULL; + } + + XFREE (MTYPE_BGP_TABLE, rt); +} + +void +bgp_table_finish (struct bgp_table **rt) +{ + if (*rt != NULL) + { + bgp_table_unlock(*rt); + *rt = NULL; + } +} + +/* + * bgp_node_create + */ +static struct route_node * +bgp_node_create (route_table_delegate_t *delegate, struct route_table *table) +{ + struct bgp_node *node; + node = XCALLOC (MTYPE_BGP_NODE, sizeof (struct bgp_node)); + return bgp_node_to_rnode (node); +} + +/* + * bgp_node_destroy + */ +static void +bgp_node_destroy (route_table_delegate_t *delegate, + struct route_table *table, struct route_node *node) +{ + struct bgp_node *bgp_node; + bgp_node = bgp_node_from_rnode (node); + XFREE (MTYPE_BGP_NODE, bgp_node); +} + +/* + * 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 +}; + +/* + * bgp_table_init + */ +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)); + + rt->route_table = route_table_init_with_delegate (&bgp_table_delegate); + + /* + * Set up back pointer to bgp_table. + */ + rt->route_table->info = rt; + + bgp_table_lock (rt); + rt->type = BGP_TABLE_MAIN; + rt->afi = afi; + rt->safi = safi; + + return rt; +} diff --git a/bgpd/bgp_table.h b/bgpd/bgp_table.h new file mode 100644 index 0000000..8e963ae --- /dev/null +++ b/bgpd/bgp_table.h @@ -0,0 +1,314 @@ +/* BGP routing table + Copyright (C) 1998, 2001 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_BGP_TABLE_H +#define _QUAGGA_BGP_TABLE_H + +#include "table.h" + +typedef enum +{ + BGP_TABLE_MAIN, + BGP_TABLE_RSCLIENT, +} bgp_table_t; + +struct bgp_table +{ + bgp_table_t type; + + /* afi/safi of this table */ + afi_t afi; + safi_t safi; + + int lock; + + /* The owner of this 'bgp_table' structure. */ + struct peer *owner; + + struct route_table *route_table; +}; + +struct bgp_node +{ + /* + * 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; + + struct bgp_adj_in *adj_in; + + struct bgp_node *prn; + + 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 **); + + +/* + * 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 new file mode 100644 index 0000000..0040d62 --- /dev/null +++ b/bgpd/bgp_vty.c @@ -0,0 +1,12619 @@ +/* BGP VTY interface. + Copyright (C) 1996, 97, 98, 99, 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 "plist.h" +#include "buffer.h" +#include "linklist.h" +#include "stream.h" +#include "thread.h" +#include "log.h" +#include "memory.h" +#include "hash.h" +#include "filter.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_advertise.h" +#include "bgpd/bgp_attr.h" +#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" +#include "bgpd/bgp_mplsvpn.h" +#include "bgpd/bgp_nexthop.h" +#include "bgpd/bgp_open.h" +#include "bgpd/bgp_regex.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_zebra.h" +#include "bgpd/bgp_table.h" +#include "bgpd/bgp_vty.h" +#include "bgpd/bgp_mpath.h" + +/* Utility function to get address family from current node. */ +afi_t +bgp_node_afi (struct vty *vty) +{ + 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 + node. */ +safi_t +bgp_node_safi (struct vty *vty) +{ + 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 +peer_address_self_check (union sockunion *su) +{ + struct interface *ifp = NULL; + + if (su->sa.sa_family == AF_INET) + ifp = if_lookup_by_ipv4_exact (&su->sin.sin_addr); + else if (su->sa.sa_family == AF_INET6) + ifp = if_lookup_by_ipv6_exact (&su->sin6.sin6_addr); + + if (ifp) + return 1; + + return 0; +} + +/* Utility function for looking up peer from VTY. */ +static struct peer * +peer_lookup_vty (struct vty *vty, const char *ip_str) +{ + int ret; + struct bgp *bgp; + union sockunion su; + struct peer *peer; + + bgp = vty->index; + + ret = str2sockunion (ip_str, &su); + if (ret < 0) + { + vty_out (vty, "%% Malformed address: %s%s", ip_str, VTY_NEWLINE); + return NULL; + } + + peer = peer_lookup (bgp, &su); + if (! peer) + { + vty_out (vty, "%% Specify remote-as or peer-group commands first%s", VTY_NEWLINE); + return NULL; + } + return peer; +} + +/* Utility function for looking up peer or peer group. */ +static struct peer * +peer_and_group_lookup_vty (struct vty *vty, const char *peer_str) +{ + int ret; + struct bgp *bgp; + union sockunion su; + struct peer *peer; + struct peer_group *group; + + bgp = vty->index; + + ret = str2sockunion (peer_str, &su); + if (ret == 0) + { + peer = peer_lookup (bgp, &su); + if (peer) + return peer; + } + else + { + group = peer_group_lookup (bgp, peer_str); + if (group) + return group->conf; + } + + vty_out (vty, "%% Specify remote-as or peer-group commands first%s", + VTY_NEWLINE); + + return NULL; +} + +static int +bgp_vty_return (struct vty *vty, int ret) +{ + const char *str = NULL; + + switch (ret) + { + case BGP_ERR_INVALID_VALUE: + str = "Invalid value"; + break; + case BGP_ERR_INVALID_FLAG: + str = "Invalid flag"; + break; + case BGP_ERR_PEER_INACTIVE: + str = "Activate the neighbor for the address family first"; + break; + case BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER: + str = "Invalid command for a peer-group member"; + break; + case BGP_ERR_PEER_GROUP_SHUTDOWN: + str = "Peer-group has been shutdown. Activate the peer-group first"; + break; + case BGP_ERR_PEER_GROUP_HAS_THE_FLAG: + str = "This peer is a peer-group member. Please change peer-group configuration"; + break; + case BGP_ERR_PEER_FLAG_CONFLICT: + str = "Can't set override-capability and strict-capability-match at the same time"; + break; + case BGP_ERR_PEER_GROUP_MEMBER_EXISTS: + str = "No activate for peergroup can be given only if peer-group has no members"; + break; + case BGP_ERR_PEER_BELONGS_TO_GROUP: + str = "No activate for an individual peer-group member is invalid"; + break; + case BGP_ERR_PEER_GROUP_AF_UNCONFIGURED: + str = "Activate the peer-group for the address family first"; + break; + case BGP_ERR_PEER_GROUP_NO_REMOTE_AS: + str = "Specify remote-as or peer-group remote AS first"; + break; + case BGP_ERR_PEER_GROUP_CANT_CHANGE: + str = "Cannot change the peer-group. Deconfigure first"; + break; + case BGP_ERR_PEER_GROUP_MISMATCH: + str = "Cannot have different peer-group for the neighbor"; + break; + case BGP_ERR_PEER_FILTER_CONFLICT: + str = "Prefix/distribute list can not co-exist"; + break; + case BGP_ERR_NOT_INTERNAL_PEER: + str = "Invalid command. Not an internal neighbor"; + break; + case BGP_ERR_REMOVE_PRIVATE_AS: + str = "Private AS cannot be removed for IBGP peers"; + break; + case BGP_ERR_LOCAL_AS_ALLOWED_ONLY_FOR_EBGP: + str = "Local-AS allowed only for EBGP peers"; + break; + case BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS: + str = "Cannot have local-as same as BGP AS number"; + break; + case BGP_ERR_TCPSIG_FAILED: + str = "Error while applying TCP-Sig to session(s)"; + break; + case BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK: + str = "ebgp-multihop and ttl-security cannot be configured together"; + break; + case BGP_ERR_NO_IBGP_WITH_TTLHACK: + str = "ttl-security only allowed for EBGP peers"; + break; + } + if (str) + { + vty_out (vty, "%% %s%s", str, VTY_NEWLINE); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +/* BGP global configuration. */ + +DEFUN (bgp_multiple_instance_func, + bgp_multiple_instance_cmd, + "bgp multiple-instance", + BGP_STR + "Enable bgp multiple instance\n") +{ + bgp_option_set (BGP_OPT_MULTIPLE_INSTANCE); + return CMD_SUCCESS; +} + +DEFUN (no_bgp_multiple_instance, + no_bgp_multiple_instance_cmd, + "no bgp multiple-instance", + NO_STR + BGP_STR + "BGP multiple instance\n") +{ + int ret; + + ret = bgp_option_unset (BGP_OPT_MULTIPLE_INSTANCE); + if (ret < 0) + { + vty_out (vty, "%% There are more than two BGP instances%s", VTY_NEWLINE); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +DEFUN (bgp_config_type, + bgp_config_type_cmd, + "bgp config-type (cisco|zebra)", + BGP_STR + "Configuration type\n" + "cisco\n" + "zebra\n") +{ + if (strncmp (argv[0], "c", 1) == 0) + bgp_option_set (BGP_OPT_CONFIG_CISCO); + else + bgp_option_unset (BGP_OPT_CONFIG_CISCO); + + return CMD_SUCCESS; +} + +DEFUN (no_bgp_config_type, + no_bgp_config_type_cmd, + "no bgp config-type", + NO_STR + BGP_STR + "Display configuration type\n") +{ + bgp_option_unset (BGP_OPT_CONFIG_CISCO); + return CMD_SUCCESS; +} + +DEFUN (no_synchronization, + no_synchronization_cmd, + "no synchronization", + NO_STR + "Perform IGP synchronization\n") +{ + return CMD_SUCCESS; +} + +DEFUN (no_auto_summary, + no_auto_summary_cmd, + "no auto-summary", + NO_STR + "Enable automatic network number summarization\n") +{ + return CMD_SUCCESS; +} + +DEFUN_DEPRECATED (neighbor_version, + neighbor_version_cmd, + NEIGHBOR_CMD "version (4|4-)", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR + "Set the BGP version to match a neighbor\n" + "Neighbor's BGP version\n") +{ + return CMD_SUCCESS; +} + +/* "router bgp" commands. */ +DEFUN (router_bgp, + router_bgp_cmd, + "router bgp " CMD_AS_RANGE, + ROUTER_STR + BGP_STR + AS_STR) +{ + int ret; + as_t as; + struct bgp *bgp; + const char *name = NULL; + + VTY_GET_INTEGER_RANGE ("AS", as, argv[0], 1, BGP_AS4_MAX); + + if (argc == 2) + name = argv[1]; + + ret = bgp_get (&bgp, &as, name); + switch (ret) + { + case BGP_ERR_MULTIPLE_INSTANCE_NOT_SET: + vty_out (vty, "Please specify 'bgp multiple-instance' first%s", + VTY_NEWLINE); + return CMD_WARNING; + case BGP_ERR_AS_MISMATCH: + vty_out (vty, "BGP is already running; AS is %u%s", as, VTY_NEWLINE); + return CMD_WARNING; + case BGP_ERR_INSTANCE_MISMATCH: + vty_out (vty, "BGP view name and AS number mismatch%s", VTY_NEWLINE); + vty_out (vty, "BGP instance is already running; AS is %u%s", + as, VTY_NEWLINE); + return CMD_WARNING; + } + + vty->node = BGP_NODE; + vty->index = bgp; + + return CMD_SUCCESS; +} + +ALIAS (router_bgp, + router_bgp_view_cmd, + "router bgp " CMD_AS_RANGE " view WORD", + ROUTER_STR + BGP_STR + AS_STR + "BGP view\n" + "view name\n") + +/* "no router bgp" commands. */ +DEFUN (no_router_bgp, + no_router_bgp_cmd, + "no router bgp " CMD_AS_RANGE, + NO_STR + ROUTER_STR + BGP_STR + AS_STR) +{ + as_t as; + struct bgp *bgp; + const char *name = NULL; + + VTY_GET_INTEGER_RANGE ("AS", as, argv[0], 1, BGP_AS4_MAX); + + if (argc == 2) + name = argv[1]; + + /* Lookup bgp structure. */ + bgp = bgp_lookup (as, name); + if (! bgp) + { + vty_out (vty, "%% Can't find BGP instance%s", VTY_NEWLINE); + return CMD_WARNING; + } + + bgp_delete (bgp); + + return CMD_SUCCESS; +} + +ALIAS (no_router_bgp, + no_router_bgp_view_cmd, + "no router bgp " CMD_AS_RANGE " view WORD", + NO_STR + ROUTER_STR + BGP_STR + AS_STR + "BGP view\n" + "view name\n") + +/* BGP router-id. */ + +DEFUN (bgp_router_id, + bgp_router_id_cmd, + "bgp router-id A.B.C.D", + BGP_STR + "Override configured router identifier\n" + "Manually configured router identifier\n") +{ + int ret; + struct in_addr id; + struct bgp *bgp; + + bgp = vty->index; + + ret = inet_aton (argv[0], &id); + if (! ret) + { + vty_out (vty, "%% Malformed bgp router identifier%s", VTY_NEWLINE); + return CMD_WARNING; + } + + bgp_router_id_static_set (bgp, id); + + return CMD_SUCCESS; +} + +DEFUN (no_bgp_router_id, + no_bgp_router_id_cmd, + "no bgp router-id", + NO_STR + BGP_STR + "Override configured router identifier\n") +{ + int ret; + struct in_addr id; + struct bgp *bgp; + + bgp = vty->index; + + if (argc == 1) + { + ret = inet_aton (argv[0], &id); + if (! ret) + { + vty_out (vty, "%% Malformed BGP router identifier%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (! IPV4_ADDR_SAME (&bgp->router_id_static, &id)) + { + vty_out (vty, "%% BGP router-id doesn't match%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + id.s_addr = 0; + bgp_router_id_static_set (bgp, id); + + return CMD_SUCCESS; +} + +ALIAS (no_bgp_router_id, + no_bgp_router_id_val_cmd, + "no bgp router-id A.B.C.D", + NO_STR + BGP_STR + "Override configured router identifier\n" + "Manually configured router identifier\n") + +/* BGP Cluster ID. */ + +DEFUN (bgp_cluster_id, + bgp_cluster_id_cmd, + "bgp cluster-id A.B.C.D", + BGP_STR + "Configure Route-Reflector Cluster-id\n" + "Route-Reflector Cluster-id in IP address format\n") +{ + int ret; + struct bgp *bgp; + struct in_addr cluster; + + bgp = vty->index; + + ret = inet_aton (argv[0], &cluster); + if (! ret) + { + vty_out (vty, "%% Malformed bgp cluster identifier%s", VTY_NEWLINE); + return CMD_WARNING; + } + + bgp_cluster_id_set (bgp, &cluster); + + return CMD_SUCCESS; +} + +ALIAS (bgp_cluster_id, + bgp_cluster_id32_cmd, + "bgp cluster-id <1-4294967295>", + BGP_STR + "Configure Route-Reflector Cluster-id\n" + "Route-Reflector Cluster-id as 32 bit quantity\n") + +DEFUN (no_bgp_cluster_id, + no_bgp_cluster_id_cmd, + "no bgp cluster-id", + NO_STR + BGP_STR + "Configure Route-Reflector Cluster-id\n") +{ + int ret; + struct bgp *bgp; + struct in_addr cluster; + + bgp = vty->index; + + if (argc == 1) + { + ret = inet_aton (argv[0], &cluster); + if (! ret) + { + vty_out (vty, "%% Malformed bgp cluster identifier%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + bgp_cluster_id_unset (bgp); + + return CMD_SUCCESS; +} + +ALIAS (no_bgp_cluster_id, + no_bgp_cluster_id_arg_cmd, + "no bgp cluster-id A.B.C.D", + NO_STR + 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, + "BGP specific commands\n" + "AS confederation parameters\n" + "AS number\n" + "Set routing domain confederation AS\n") +{ + struct bgp *bgp; + as_t as; + + bgp = vty->index; + + VTY_GET_INTEGER_RANGE ("AS", as, argv[0], 1, BGP_AS4_MAX); + + bgp_confederation_id_set (bgp, as); + + return CMD_SUCCESS; +} + +DEFUN (no_bgp_confederation_identifier, + no_bgp_confederation_identifier_cmd, + "no bgp confederation identifier", + NO_STR + "BGP specific commands\n" + "AS confederation parameters\n" + "AS number\n") +{ + struct bgp *bgp; + as_t as __attribute__((unused)); /* Dummy for VTY_GET_INTEGER_RANGE */ + + bgp = vty->index; + + if (argc == 1) + VTY_GET_INTEGER_RANGE ("AS", as, argv[0], 1, BGP_AS4_MAX); + + bgp_confederation_id_unset (bgp); + + return CMD_SUCCESS; +} + +ALIAS (no_bgp_confederation_identifier, + no_bgp_confederation_identifier_arg_cmd, + "no bgp confederation identifier " CMD_AS_RANGE, + NO_STR + "BGP specific commands\n" + "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, + "BGP specific commands\n" + "AS confederation parameters\n" + "Peer ASs in BGP confederation\n" + AS_STR) +{ + struct bgp *bgp; + as_t as; + int i; + + bgp = vty->index; + + for (i = 0; i < argc; i++) + { + VTY_GET_INTEGER_RANGE ("AS", as, argv[i], 1, BGP_AS4_MAX); + + if (bgp->as == as) + { + vty_out (vty, "%% Local member-AS not allowed in confed peer list%s", + VTY_NEWLINE); + continue; + } + + bgp_confederation_peers_add (bgp, as); + } + return CMD_SUCCESS; +} + +DEFUN (no_bgp_confederation_peers, + no_bgp_confederation_peers_cmd, + "no bgp confederation peers ." CMD_AS_RANGE, + NO_STR + "BGP specific commands\n" + "AS confederation parameters\n" + "Peer ASs in BGP confederation\n" + AS_STR) +{ + struct bgp *bgp; + as_t as; + int i; + + bgp = vty->index; + + for (i = 0; i < argc; i++) + { + VTY_GET_INTEGER_RANGE ("AS", as, argv[i], 1, BGP_AS4_MAX); + + bgp_confederation_peers_remove (bgp, as); + } + 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, + bgp_timers_cmd, + "timers bgp <0-65535> <0-65535>", + "Adjust routing timers\n" + "BGP timers\n" + "Keepalive interval\n" + "Holdtime\n") +{ + struct bgp *bgp; + unsigned long keepalive = 0; + unsigned long holdtime = 0; + + bgp = vty->index; + + VTY_GET_INTEGER ("keepalive", keepalive, argv[0]); + VTY_GET_INTEGER ("holdtime", holdtime, argv[1]); + + /* Holdtime value check. */ + if (holdtime < 3 && holdtime != 0) + { + vty_out (vty, "%% hold time value must be either 0 or greater than 3%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + bgp_timers_set (bgp, keepalive, holdtime); + + return CMD_SUCCESS; +} + +DEFUN (no_bgp_timers, + no_bgp_timers_cmd, + "no timers bgp", + NO_STR + "Adjust routing timers\n" + "BGP timers\n") +{ + struct bgp *bgp; + + bgp = vty->index; + bgp_timers_unset (bgp); + + return CMD_SUCCESS; +} + +ALIAS (no_bgp_timers, + no_bgp_timers_arg_cmd, + "no timers bgp <0-65535> <0-65535>", + NO_STR + "Adjust routing timers\n" + "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", + "BGP specific commands\n" + "Configure client to client route reflection\n" + "reflection of routes allowed\n") +{ + struct bgp *bgp; + + bgp = vty->index; + bgp_flag_unset (bgp, BGP_FLAG_NO_CLIENT_TO_CLIENT); + return CMD_SUCCESS; +} + +DEFUN (no_bgp_client_to_client_reflection, + no_bgp_client_to_client_reflection_cmd, + "no bgp client-to-client reflection", + NO_STR + "BGP specific commands\n" + "Configure client to client route reflection\n" + "reflection of routes allowed\n") +{ + struct bgp *bgp; + + bgp = vty->index; + bgp_flag_set (bgp, BGP_FLAG_NO_CLIENT_TO_CLIENT); + return CMD_SUCCESS; +} + +/* "bgp always-compare-med" configuration. */ +DEFUN (bgp_always_compare_med, + bgp_always_compare_med_cmd, + "bgp always-compare-med", + "BGP specific commands\n" + "Allow comparing MED from different neighbors\n") +{ + struct bgp *bgp; + + bgp = vty->index; + bgp_flag_set (bgp, BGP_FLAG_ALWAYS_COMPARE_MED); + return CMD_SUCCESS; +} + +DEFUN (no_bgp_always_compare_med, + no_bgp_always_compare_med_cmd, + "no bgp always-compare-med", + NO_STR + "BGP specific commands\n" + "Allow comparing MED from different neighbors\n") +{ + struct bgp *bgp; + + bgp = vty->index; + bgp_flag_unset (bgp, BGP_FLAG_ALWAYS_COMPARE_MED); + return CMD_SUCCESS; +} + +/* "bgp deterministic-med" configuration. */ +DEFUN (bgp_deterministic_med, + bgp_deterministic_med_cmd, + "bgp deterministic-med", + "BGP specific commands\n" + "Pick the best-MED path among paths advertised from the neighboring AS\n") +{ + struct bgp *bgp; + + bgp = vty->index; + bgp_flag_set (bgp, BGP_FLAG_DETERMINISTIC_MED); + return CMD_SUCCESS; +} + +DEFUN (no_bgp_deterministic_med, + no_bgp_deterministic_med_cmd, + "no bgp deterministic-med", + NO_STR + "BGP specific commands\n" + "Pick the best-MED path among paths advertised from the neighboring AS\n") +{ + struct bgp *bgp; + + bgp = vty->index; + bgp_flag_unset (bgp, BGP_FLAG_DETERMINISTIC_MED); + return CMD_SUCCESS; +} + +/* "bgp graceful-restart" configuration. */ +DEFUN (bgp_graceful_restart, + bgp_graceful_restart_cmd, + "bgp graceful-restart", + "BGP specific commands\n" + "Graceful restart capability parameters\n") +{ + struct bgp *bgp; + + bgp = vty->index; + bgp_flag_set (bgp, BGP_FLAG_GRACEFUL_RESTART); + return CMD_SUCCESS; +} + +DEFUN (no_bgp_graceful_restart, + no_bgp_graceful_restart_cmd, + "no bgp graceful-restart", + NO_STR + "BGP specific commands\n" + "Graceful restart capability parameters\n") +{ + struct bgp *bgp; + + bgp = vty->index; + bgp_flag_unset (bgp, BGP_FLAG_GRACEFUL_RESTART); + return CMD_SUCCESS; +} + +DEFUN (bgp_graceful_restart_stalepath_time, + bgp_graceful_restart_stalepath_time_cmd, + "bgp graceful-restart stalepath-time <1-3600>", + "BGP specific commands\n" + "Graceful restart capability parameters\n" + "Set the max time to hold onto restarting peer's stale paths\n" + "Delay value (seconds)\n") +{ + struct bgp *bgp; + u_int32_t stalepath; + + bgp = vty->index; + if (! bgp) + return CMD_WARNING; + + VTY_GET_INTEGER_RANGE ("stalepath-time", stalepath, argv[0], 1, 3600); + bgp->stalepath_time = stalepath; + 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", + NO_STR + "BGP specific commands\n" + "Graceful restart capability parameters\n" + "Set the max time to hold onto restarting peer's stale paths\n") +{ + struct bgp *bgp; + + bgp = vty->index; + if (! bgp) + return CMD_WARNING; + + bgp->stalepath_time = BGP_DEFAULT_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>", + NO_STR + "BGP specific commands\n" + "Graceful restart capability parameters\n" + "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, + "bgp fast-external-failover", + BGP_STR + "Immediately reset session if a link to a directly connected external peer goes down\n") +{ + struct bgp *bgp; + + bgp = vty->index; + bgp_flag_unset (bgp, BGP_FLAG_NO_FAST_EXT_FAILOVER); + return CMD_SUCCESS; +} + +DEFUN (no_bgp_fast_external_failover, + no_bgp_fast_external_failover_cmd, + "no bgp fast-external-failover", + NO_STR + BGP_STR + "Immediately reset session if a link to a directly connected external peer goes down\n") +{ + struct bgp *bgp; + + bgp = vty->index; + 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, + "bgp enforce-first-as", + BGP_STR + "Enforce the first AS for EBGP routes\n") +{ + struct bgp *bgp; + + bgp = vty->index; + bgp_flag_set (bgp, BGP_FLAG_ENFORCE_FIRST_AS); + return CMD_SUCCESS; +} + +DEFUN (no_bgp_enforce_first_as, + no_bgp_enforce_first_as_cmd, + "no bgp enforce-first-as", + NO_STR + BGP_STR + "Enforce the first AS for EBGP routes\n") +{ + struct bgp *bgp; + + bgp = vty->index; + 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, + "bgp bestpath compare-routerid", + "BGP specific commands\n" + "Change the default bestpath selection\n" + "Compare router-id for identical EBGP paths\n") +{ + struct bgp *bgp; + + bgp = vty->index; + bgp_flag_set (bgp, BGP_FLAG_COMPARE_ROUTER_ID); + return CMD_SUCCESS; +} + +DEFUN (no_bgp_bestpath_compare_router_id, + no_bgp_bestpath_compare_router_id_cmd, + "no bgp bestpath compare-routerid", + NO_STR + "BGP specific commands\n" + "Change the default bestpath selection\n" + "Compare router-id for identical EBGP paths\n") +{ + struct bgp *bgp; + + bgp = vty->index; + 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, + "bgp bestpath as-path ignore", + "BGP specific commands\n" + "Change the default bestpath selection\n" + "AS-path attribute\n" + "Ignore as-path length in selecting a route\n") +{ + struct bgp *bgp; + + bgp = vty->index; + bgp_flag_set (bgp, BGP_FLAG_ASPATH_IGNORE); + return CMD_SUCCESS; +} + +DEFUN (no_bgp_bestpath_aspath_ignore, + no_bgp_bestpath_aspath_ignore_cmd, + "no bgp bestpath as-path ignore", + NO_STR + "BGP specific commands\n" + "Change the default bestpath selection\n" + "AS-path attribute\n" + "Ignore as-path length in selecting a route\n") +{ + struct bgp *bgp; + + bgp = vty->index; + 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, + "bgp bestpath as-path confed", + "BGP specific commands\n" + "Change the default bestpath selection\n" + "AS-path attribute\n" + "Compare path lengths including confederation sets & sequences in selecting a route\n") +{ + struct bgp *bgp; + + bgp = vty->index; + bgp_flag_set (bgp, BGP_FLAG_ASPATH_CONFED); + return CMD_SUCCESS; +} + +DEFUN (no_bgp_bestpath_aspath_confed, + no_bgp_bestpath_aspath_confed_cmd, + "no bgp bestpath as-path confed", + NO_STR + "BGP specific commands\n" + "Change the default bestpath selection\n" + "AS-path attribute\n" + "Compare path lengths including confederation sets & sequences in selecting a route\n") +{ + struct bgp *bgp; + + bgp = vty->index; + 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, + "bgp log-neighbor-changes", + "BGP specific commands\n" + "Log neighbor up/down and reset reason\n") +{ + struct bgp *bgp; + + bgp = vty->index; + bgp_flag_set (bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES); + return CMD_SUCCESS; +} + +DEFUN (no_bgp_log_neighbor_changes, + no_bgp_log_neighbor_changes_cmd, + "no bgp log-neighbor-changes", + NO_STR + "BGP specific commands\n" + "Log neighbor up/down and reset reason\n") +{ + struct bgp *bgp; + + bgp = vty->index; + bgp_flag_unset (bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES); + return CMD_SUCCESS; +} + +/* "bgp bestpath med" configuration. */ +DEFUN (bgp_bestpath_med, + bgp_bestpath_med_cmd, + "bgp bestpath med (confed|missing-as-worst)", + "BGP specific commands\n" + "Change the default bestpath selection\n" + "MED attribute\n" + "Compare MED among confederation paths\n" + "Treat missing MED as the least preferred one\n") +{ + struct bgp *bgp; + + bgp = vty->index; + + if (strncmp (argv[0], "confed", 1) == 0) + bgp_flag_set (bgp, BGP_FLAG_MED_CONFED); + else + bgp_flag_set (bgp, BGP_FLAG_MED_MISSING_AS_WORST); + + return CMD_SUCCESS; +} + +DEFUN (bgp_bestpath_med2, + bgp_bestpath_med2_cmd, + "bgp bestpath med confed missing-as-worst", + "BGP specific commands\n" + "Change the default bestpath selection\n" + "MED attribute\n" + "Compare MED among confederation paths\n" + "Treat missing MED as the least preferred one\n") +{ + struct bgp *bgp; + + bgp = vty->index; + bgp_flag_set (bgp, BGP_FLAG_MED_CONFED); + bgp_flag_set (bgp, BGP_FLAG_MED_MISSING_AS_WORST); + return CMD_SUCCESS; +} + +ALIAS (bgp_bestpath_med2, + bgp_bestpath_med3_cmd, + "bgp bestpath med missing-as-worst confed", + "BGP specific commands\n" + "Change the default bestpath selection\n" + "MED attribute\n" + "Treat missing MED as the least preferred one\n" + "Compare MED among confederation paths\n") + +DEFUN (no_bgp_bestpath_med, + no_bgp_bestpath_med_cmd, + "no bgp bestpath med (confed|missing-as-worst)", + NO_STR + "BGP specific commands\n" + "Change the default bestpath selection\n" + "MED attribute\n" + "Compare MED among confederation paths\n" + "Treat missing MED as the least preferred one\n") +{ + struct bgp *bgp; + + bgp = vty->index; + + if (strncmp (argv[0], "confed", 1) == 0) + bgp_flag_unset (bgp, BGP_FLAG_MED_CONFED); + else + bgp_flag_unset (bgp, BGP_FLAG_MED_MISSING_AS_WORST); + + return CMD_SUCCESS; +} + +DEFUN (no_bgp_bestpath_med2, + no_bgp_bestpath_med2_cmd, + "no bgp bestpath med confed missing-as-worst", + NO_STR + "BGP specific commands\n" + "Change the default bestpath selection\n" + "MED attribute\n" + "Compare MED among confederation paths\n" + "Treat missing MED as the least preferred one\n") +{ + struct bgp *bgp; + + bgp = vty->index; + bgp_flag_unset (bgp, BGP_FLAG_MED_CONFED); + bgp_flag_unset (bgp, BGP_FLAG_MED_MISSING_AS_WORST); + return CMD_SUCCESS; +} + +ALIAS (no_bgp_bestpath_med2, + no_bgp_bestpath_med3_cmd, + "no bgp bestpath med missing-as-worst confed", + NO_STR + "BGP specific commands\n" + "Change the default bestpath selection\n" + "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, + "no bgp default ipv4-unicast", + NO_STR + "BGP specific commands\n" + "Configure BGP defaults\n" + "Activate ipv4-unicast for a peer by default\n") +{ + struct bgp *bgp; + + bgp = vty->index; + bgp_flag_set (bgp, BGP_FLAG_NO_DEFAULT_IPV4); + return CMD_SUCCESS; +} + +DEFUN (bgp_default_ipv4_unicast, + bgp_default_ipv4_unicast_cmd, + "bgp default ipv4-unicast", + "BGP specific commands\n" + "Configure BGP defaults\n" + "Activate ipv4-unicast for a peer by default\n") +{ + struct bgp *bgp; + + bgp = vty->index; + 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, + "bgp network import-check", + "BGP specific commands\n" + "BGP network command\n" + "Check BGP network route exists in IGP\n") +{ + struct bgp *bgp; + + bgp = vty->index; + bgp_flag_set (bgp, BGP_FLAG_IMPORT_CHECK); + return CMD_SUCCESS; +} + +DEFUN (no_bgp_network_import_check, + no_bgp_network_import_check_cmd, + "no bgp network import-check", + NO_STR + "BGP specific commands\n" + "BGP network command\n" + "Check BGP network route exists in IGP\n") +{ + struct bgp *bgp; + + bgp = vty->index; + 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>", + "BGP specific commands\n" + "Configure BGP defaults\n" + "local preference (higher=more preferred)\n" + "Configure default local preference value\n") +{ + struct bgp *bgp; + u_int32_t local_pref; + + bgp = vty->index; + + VTY_GET_INTEGER ("local preference", local_pref, argv[0]); + + bgp_default_local_preference_set (bgp, local_pref); + + return CMD_SUCCESS; +} + +DEFUN (no_bgp_default_local_preference, + no_bgp_default_local_preference_cmd, + "no bgp default local-preference", + NO_STR + "BGP specific commands\n" + "Configure BGP defaults\n" + "local preference (higher=more preferred)\n") +{ + struct bgp *bgp; + + bgp = vty->index; + bgp_default_local_preference_unset (bgp); + return CMD_SUCCESS; +} + +ALIAS (no_bgp_default_local_preference, + no_bgp_default_local_preference_val_cmd, + "no bgp default local-preference <0-4294967295>", + NO_STR + "BGP specific commands\n" + "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) +{ + int ret; + struct bgp *bgp; + as_t as; + union sockunion su; + + bgp = vty->index; + + /* Get AS number. */ + VTY_GET_INTEGER_RANGE ("AS", as, as_str, 1, BGP_AS4_MAX); + + /* If peer is peer group, call proper function. */ + ret = str2sockunion (peer_str, &su); + if (ret < 0) + { + ret = peer_group_remote_as (bgp, peer_str, &as); + if (ret < 0) + { + vty_out (vty, "%% Create the peer-group first%s", VTY_NEWLINE); + return CMD_WARNING; + } + return CMD_SUCCESS; + } + + if (peer_address_self_check (&su)) + { + vty_out (vty, "%% Can not configure the local system as neighbor%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + ret = peer_remote_as (bgp, &su, &as, afi, safi); + + /* This peer belongs to peer group. */ + switch (ret) + { + case BGP_ERR_PEER_GROUP_MEMBER: + vty_out (vty, "%% Peer-group AS %u. Cannot configure remote-as for member%s", as, VTY_NEWLINE); + return CMD_WARNING; + case BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT: + vty_out (vty, "%% The AS# can not be changed from %u to %s, peer-group members must be all internal or all external%s", as, as_str, VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_vty_return (vty, ret); +} + +DEFUN (neighbor_remote_as, + neighbor_remote_as_cmd, + NEIGHBOR_CMD2 "remote-as " CMD_AS_RANGE, + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Specify a BGP neighbor\n" + AS_STR) +{ + 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", + NEIGHBOR_STR + "Neighbor tag\n" + "Configure peer-group\n") +{ + struct bgp *bgp; + struct peer_group *group; + + bgp = vty->index; + + group = peer_group_get (bgp, argv[0]); + if (! group) + return CMD_WARNING; + + return CMD_SUCCESS; +} + +DEFUN (no_neighbor, + no_neighbor_cmd, + NO_NEIGHBOR_CMD2, + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2) +{ + int ret; + union sockunion su; + struct peer_group *group; + struct peer *peer; + + ret = str2sockunion (argv[0], &su); + if (ret < 0) + { + group = peer_group_lookup (vty->index, argv[0]); + if (group) + peer_group_delete (group); + else + { + vty_out (vty, "%% Create the peer-group first%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + else + { + peer = peer_lookup (vty->index, &su); + if (peer) + peer_delete (peer); + } + + return CMD_SUCCESS; +} + +ALIAS (no_neighbor, + no_neighbor_remote_as_cmd, + NO_NEIGHBOR_CMD "remote-as " CMD_AS_RANGE, + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR + "Specify a BGP neighbor\n" + AS_STR) + +DEFUN (no_neighbor_peer_group, + no_neighbor_peer_group_cmd, + "no neighbor WORD peer-group", + NO_STR + NEIGHBOR_STR + "Neighbor tag\n" + "Configure peer-group\n") +{ + struct peer_group *group; + + group = peer_group_lookup (vty->index, argv[0]); + if (group) + peer_group_delete (group); + else + { + vty_out (vty, "%% Create the peer-group first%s", VTY_NEWLINE); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +DEFUN (no_neighbor_peer_group_remote_as, + no_neighbor_peer_group_remote_as_cmd, + "no neighbor WORD remote-as " CMD_AS_RANGE, + NO_STR + NEIGHBOR_STR + "Neighbor tag\n" + "Specify a BGP neighbor\n" + AS_STR) +{ + struct peer_group *group; + + group = peer_group_lookup (vty->index, argv[0]); + if (group) + peer_group_remote_as_delete (group); + else + { + vty_out (vty, "%% Create the peer-group first%s", VTY_NEWLINE); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +DEFUN (neighbor_local_as, + neighbor_local_as_cmd, + NEIGHBOR_CMD2 "local-as " CMD_AS_RANGE, + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Specify a local-as number\n" + "AS number used as local AS\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]), 0, 0); + return bgp_vty_return (vty, ret); +} + +DEFUN (neighbor_local_as_no_prepend, + neighbor_local_as_no_prepend_cmd, + NEIGHBOR_CMD2 "local-as " CMD_AS_RANGE " no-prepend", + 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") +{ + 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, 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", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Specify a local-as number\n") +{ + struct peer *peer; + int ret; + + peer = peer_and_group_lookup_vty (vty, argv[0]); + if (! peer) + return CMD_WARNING; + + ret = peer_local_as_unset (peer); + return bgp_vty_return (vty, ret); +} + +ALIAS (no_neighbor_local_as, + no_neighbor_local_as_val_cmd, + NO_NEIGHBOR_CMD2 "local-as " CMD_AS_RANGE, + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Specify a local-as number\n" + "AS number used as local AS\n") + +ALIAS (no_neighbor_local_as, + no_neighbor_local_as_val2_cmd, + NO_NEIGHBOR_CMD2 "local-as " CMD_AS_RANGE " no-prepend", + 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") + +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", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Set a password\n" + "The password\n") +{ + struct peer *peer; + int ret; + + peer = peer_and_group_lookup_vty (vty, argv[0]); + if (! peer) + return CMD_WARNING; + + ret = peer_password_set (peer, argv[1]); + return bgp_vty_return (vty, ret); +} + +DEFUN (no_neighbor_password, + no_neighbor_password_cmd, + NO_NEIGHBOR_CMD2 "password", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Set a password\n") +{ + struct peer *peer; + int ret; + + peer = peer_and_group_lookup_vty (vty, argv[0]); + if (! peer) + return CMD_WARNING; + + ret = peer_password_unset (peer); + return bgp_vty_return (vty, ret); +} + +DEFUN (neighbor_activate, + neighbor_activate_cmd, + NEIGHBOR_CMD2 "activate", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Enable the Address Family for this Neighbor\n") +{ + struct peer *peer; + + peer = peer_and_group_lookup_vty (vty, argv[0]); + if (! peer) + return CMD_WARNING; + + peer_activate (peer, bgp_node_afi (vty), bgp_node_safi (vty)); + + return CMD_SUCCESS; +} + +DEFUN (no_neighbor_activate, + no_neighbor_activate_cmd, + NO_NEIGHBOR_CMD2 "activate", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Enable the Address Family for this Neighbor\n") +{ + int ret; + struct peer *peer; + + /* Lookup peer. */ + peer = peer_and_group_lookup_vty (vty, argv[0]); + if (! peer) + return CMD_WARNING; + + ret = peer_deactivate (peer, bgp_node_afi (vty), bgp_node_safi (vty)); + + return bgp_vty_return (vty, ret); +} + +DEFUN (neighbor_set_peer_group, + neighbor_set_peer_group_cmd, + NEIGHBOR_CMD "peer-group WORD", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR + "Member of the peer-group\n" + "peer-group name\n") +{ + int ret; + as_t as; + union sockunion su; + struct bgp *bgp; + struct peer_group *group; + + bgp = vty->index; + + ret = str2sockunion (argv[0], &su); + if (ret < 0) + { + vty_out (vty, "%% Malformed address: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + group = peer_group_lookup (bgp, argv[1]); + if (! group) + { + vty_out (vty, "%% Configure the peer-group first%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (peer_address_self_check (&su)) + { + vty_out (vty, "%% Can not configure the local system as neighbor%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + ret = peer_group_bind (bgp, &su, group, bgp_node_afi (vty), + bgp_node_safi (vty), &as); + + if (ret == BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT) + { + vty_out (vty, "%% Peer with AS %u cannot be in this peer-group, members must be all internal or all external%s", as, VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_vty_return (vty, ret); +} + +DEFUN (no_neighbor_set_peer_group, + no_neighbor_set_peer_group_cmd, + NO_NEIGHBOR_CMD "peer-group WORD", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR + "Member of the peer-group\n" + "peer-group name\n") +{ + int ret; + struct bgp *bgp; + struct peer *peer; + struct peer_group *group; + + bgp = vty->index; + + peer = peer_lookup_vty (vty, argv[0]); + if (! peer) + return CMD_WARNING; + + group = peer_group_lookup (bgp, argv[1]); + if (! group) + { + vty_out (vty, "%% Configure the peer-group first%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ret = peer_group_unbind (bgp, peer, group, bgp_node_afi (vty), + bgp_node_safi (vty)); + + 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) +{ + int ret; + struct peer *peer; + + peer = peer_and_group_lookup_vty (vty, ip_str); + if (! peer) + return CMD_WARNING; + + if (set) + ret = peer_flag_set (peer, flag); + else + ret = peer_flag_unset (peer, flag); + + return bgp_vty_return (vty, ret); +} + +static int +peer_flag_set_vty (struct vty *vty, const char *ip_str, u_int16_t flag) +{ + return peer_flag_modify_vty (vty, ip_str, flag, 1); +} + +static int +peer_flag_unset_vty (struct vty *vty, const char *ip_str, u_int16_t flag) +{ + return peer_flag_modify_vty (vty, ip_str, flag, 0); +} + +/* neighbor passive. */ +DEFUN (neighbor_passive, + neighbor_passive_cmd, + NEIGHBOR_CMD2 "passive", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Don't send open messages to this neighbor\n") +{ + return peer_flag_set_vty (vty, argv[0], PEER_FLAG_PASSIVE); +} + +DEFUN (no_neighbor_passive, + no_neighbor_passive_cmd, + NO_NEIGHBOR_CMD2 "passive", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Don't send open messages to this neighbor\n") +{ + return peer_flag_unset_vty (vty, argv[0], PEER_FLAG_PASSIVE); +} + +/* neighbor shutdown. */ +DEFUN (neighbor_shutdown, + neighbor_shutdown_cmd, + NEIGHBOR_CMD2 "shutdown", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Administratively shut down this neighbor\n") +{ + return peer_flag_set_vty (vty, argv[0], PEER_FLAG_SHUTDOWN); +} + +DEFUN (no_neighbor_shutdown, + no_neighbor_shutdown_cmd, + NO_NEIGHBOR_CMD2 "shutdown", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Administratively shut down this neighbor\n") +{ + 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, + NEIGHBOR_CMD2 "capability route-refresh", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Advertise capability to the peer\n" + "Advertise route-refresh capability to this neighbor\n") +{ + return CMD_SUCCESS; +} + +DEFUN_DEPRECATED (no_neighbor_capability_route_refresh, + no_neighbor_capability_route_refresh_cmd, + NO_NEIGHBOR_CMD2 "capability route-refresh", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Advertise capability to the peer\n" + "Advertise route-refresh capability to this neighbor\n") +{ + return CMD_SUCCESS; +} + +/* neighbor capability dynamic. */ +DEFUN (neighbor_capability_dynamic, + neighbor_capability_dynamic_cmd, + NEIGHBOR_CMD2 "capability dynamic", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Advertise capability to the peer\n" + "Advertise dynamic capability to this neighbor\n") +{ + return peer_flag_set_vty (vty, argv[0], PEER_FLAG_DYNAMIC_CAPABILITY); +} + +DEFUN (no_neighbor_capability_dynamic, + no_neighbor_capability_dynamic_cmd, + NO_NEIGHBOR_CMD2 "capability dynamic", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Advertise capability to the peer\n" + "Advertise dynamic capability to this neighbor\n") +{ + 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, + NEIGHBOR_CMD2 "dont-capability-negotiate", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Do not perform capability negotiation\n") +{ + return peer_flag_set_vty (vty, argv[0], PEER_FLAG_DONT_CAPABILITY); +} + +DEFUN (no_neighbor_dont_capability_negotiate, + no_neighbor_dont_capability_negotiate_cmd, + NO_NEIGHBOR_CMD2 "dont-capability-negotiate", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Do not perform capability negotiation\n") +{ + 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) +{ + int ret; + struct peer *peer; + + peer = peer_and_group_lookup_vty (vty, peer_str); + if (! peer) + return CMD_WARNING; + + if (set) + ret = peer_af_flag_set (peer, afi, safi, flag); + else + ret = peer_af_flag_unset (peer, afi, safi, flag); + + return bgp_vty_return (vty, ret); +} + +static int +peer_af_flag_set_vty (struct vty *vty, const char *peer_str, afi_t afi, + safi_t safi, u_int32_t flag) +{ + return peer_af_flag_modify_vty (vty, peer_str, afi, safi, flag, 1); +} + +static int +peer_af_flag_unset_vty (struct vty *vty, const char *peer_str, afi_t afi, + safi_t safi, u_int32_t flag) +{ + 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, + NEIGHBOR_CMD2 "capability orf prefix-list (both|send|receive)", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Advertise capability to the peer\n" + "Advertise ORF capability to the peer\n" + "Advertise prefixlist ORF capability to this neighbor\n" + "Capability to SEND and RECEIVE the ORF to/from this neighbor\n" + "Capability to RECEIVE the ORF from this neighbor\n" + "Capability to SEND the ORF to this neighbor\n") +{ + u_int16_t flag = 0; + + if (strncmp (argv[1], "s", 1) == 0) + flag = PEER_FLAG_ORF_PREFIX_SM; + else if (strncmp (argv[1], "r", 1) == 0) + flag = PEER_FLAG_ORF_PREFIX_RM; + else if (strncmp (argv[1], "b", 1) == 0) + flag = PEER_FLAG_ORF_PREFIX_SM|PEER_FLAG_ORF_PREFIX_RM; + else + return CMD_WARNING; + + return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), flag); +} + +DEFUN (no_neighbor_capability_orf_prefix, + no_neighbor_capability_orf_prefix_cmd, + NO_NEIGHBOR_CMD2 "capability orf prefix-list (both|send|receive)", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Advertise capability to the peer\n" + "Advertise ORF capability to the peer\n" + "Advertise prefixlist ORF capability to this neighbor\n" + "Capability to SEND and RECEIVE the ORF to/from this neighbor\n" + "Capability to RECEIVE the ORF from this neighbor\n" + "Capability to SEND the ORF to this neighbor\n") +{ + u_int16_t flag = 0; + + if (strncmp (argv[1], "s", 1) == 0) + flag = PEER_FLAG_ORF_PREFIX_SM; + else if (strncmp (argv[1], "r", 1) == 0) + flag = PEER_FLAG_ORF_PREFIX_RM; + else if (strncmp (argv[1], "b", 1) == 0) + flag = PEER_FLAG_ORF_PREFIX_SM|PEER_FLAG_ORF_PREFIX_RM; + else + return CMD_WARNING; + + 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 {all}", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Disable the next hop calculation for this neighbor\n" + "Apply also to ibgp-learned routes when acting as a route reflector\n") +{ + 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 {all}", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "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|PEER_FLAG_NEXTHOP_SELF_ALL); +} + +/* neighbor remove-private-AS. */ +DEFUN (neighbor_remove_private_as, + neighbor_remove_private_as_cmd, + NEIGHBOR_CMD2 "remove-private-AS", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Remove private AS number from outbound updates\n") +{ + return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), + PEER_FLAG_REMOVE_PRIVATE_AS); +} + +DEFUN (no_neighbor_remove_private_as, + no_neighbor_remove_private_as_cmd, + NO_NEIGHBOR_CMD2 "remove-private-AS", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Remove private AS number from outbound updates\n") +{ + return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), + PEER_FLAG_REMOVE_PRIVATE_AS); +} + +/* neighbor send-community. */ +DEFUN (neighbor_send_community, + neighbor_send_community_cmd, + NEIGHBOR_CMD2 "send-community", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Send Community attribute to this neighbor\n") +{ + return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), + PEER_FLAG_SEND_COMMUNITY); +} + +DEFUN (no_neighbor_send_community, + no_neighbor_send_community_cmd, + NO_NEIGHBOR_CMD2 "send-community", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Send Community attribute to this neighbor\n") +{ + return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty), + 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|all|extended|standard|large)", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Send Community attribute to this neighbor\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 Large Community attributes\n") +{ + if (strncmp (argv[1], "s", 1) == 0) + return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), + PEER_FLAG_SEND_COMMUNITY); + if (strncmp (argv[1], "e", 1) == 0) + 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_LARGE_COMMUNITY)); +} + +DEFUN (no_neighbor_send_community_type, + no_neighbor_send_community_type_cmd, + 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, 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 Large Community attributes\n") +{ + if (strncmp (argv[1], "s", 1) == 0) + return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), + PEER_FLAG_SEND_COMMUNITY); + if (strncmp (argv[1], "e", 1) == 0) + 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_LARGE_COMMUNITY)); +} + +/* neighbor soft-reconfig. */ +DEFUN (neighbor_soft_reconfiguration, + neighbor_soft_reconfiguration_cmd, + NEIGHBOR_CMD2 "soft-reconfiguration inbound", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Per neighbor soft reconfiguration\n" + "Allow inbound soft reconfiguration for this neighbor\n") +{ + return peer_af_flag_set_vty (vty, argv[0], + bgp_node_afi (vty), bgp_node_safi (vty), + PEER_FLAG_SOFT_RECONFIG); +} + +DEFUN (no_neighbor_soft_reconfiguration, + no_neighbor_soft_reconfiguration_cmd, + NO_NEIGHBOR_CMD2 "soft-reconfiguration inbound", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Per neighbor soft reconfiguration\n" + "Allow inbound soft reconfiguration for this neighbor\n") +{ + return peer_af_flag_unset_vty (vty, argv[0], + 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", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Configure a neighbor as Route Reflector client\n") +{ + struct peer *peer; + + + peer = peer_and_group_lookup_vty (vty, argv[0]); + if (! peer) + return CMD_WARNING; + + return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), + PEER_FLAG_REFLECTOR_CLIENT); +} + +DEFUN (no_neighbor_route_reflector_client, + no_neighbor_route_reflector_client_cmd, + NO_NEIGHBOR_CMD2 "route-reflector-client", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Configure a neighbor as Route Reflector client\n") +{ + return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty), + 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) +{ + int ret; + struct bgp *bgp; + struct peer *peer; + struct peer_group *group; + struct listnode *node, *nnode; + struct bgp_filter *pfilter; + struct bgp_filter *gfilter; + int locked_and_added = 0; + + bgp = vty->index; + + peer = peer_and_group_lookup_vty (vty, peer_str); + if ( ! peer ) + return CMD_WARNING; + + /* If it is already a RS-Client, don't do anything. */ + if ( CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT) ) + return CMD_SUCCESS; + + if ( ! peer_rsclient_active (peer) ) + { + peer = peer_lock (peer); /* rsclient peer list reference */ + listnode_add_sort (bgp->rsclient, peer); + locked_and_added = 1; + } + + ret = peer_af_flag_set (peer, afi, safi, PEER_FLAG_RSERVER_CLIENT); + if (ret < 0) + { + if (locked_and_added) + { + listnode_delete (bgp->rsclient, peer); + peer_unlock (peer); /* rsclient peer list reference */ + } + + return bgp_vty_return (vty, ret); + } + + peer->rib[afi][safi] = bgp_table_init (afi, safi); + peer->rib[afi][safi]->type = BGP_TABLE_RSCLIENT; + /* RIB peer reference. Released when table is free'd in bgp_table_free. */ + peer->rib[afi][safi]->owner = peer_lock (peer); + + /* Check for existing 'network' and 'redistribute' routes. */ + bgp_check_local_routes_rsclient (peer, afi, safi); + + /* Check for routes for peers configured with 'soft-reconfiguration'. */ + bgp_soft_reconfig_rsclient (peer, afi, safi); + + if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) + { + group = peer->group; + gfilter = &peer->filter[afi][safi]; + + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + pfilter = &peer->filter[afi][safi]; + + /* Members of a non-RS-Client group should not be RS-Clients, as that + is checked when the become part of the peer-group */ + ret = peer_af_flag_set (peer, afi, safi, PEER_FLAG_RSERVER_CLIENT); + if (ret < 0) + return bgp_vty_return (vty, ret); + + /* Make peer's RIB point to group's RIB. */ + peer->rib[afi][safi] = group->conf->rib[afi][safi]; + + /* Import policy. */ + if (pfilter->map[RMAP_IMPORT].name) + free (pfilter->map[RMAP_IMPORT].name); + if (gfilter->map[RMAP_IMPORT].name) + { + pfilter->map[RMAP_IMPORT].name = strdup (gfilter->map[RMAP_IMPORT].name); + pfilter->map[RMAP_IMPORT].map = gfilter->map[RMAP_IMPORT].map; + } + else + { + pfilter->map[RMAP_IMPORT].name = NULL; + pfilter->map[RMAP_IMPORT].map =NULL; + } + + /* Export policy. */ + if (gfilter->map[RMAP_EXPORT].name && ! pfilter->map[RMAP_EXPORT].name) + { + pfilter->map[RMAP_EXPORT].name = strdup (gfilter->map[RMAP_EXPORT].name); + pfilter->map[RMAP_EXPORT].map = gfilter->map[RMAP_EXPORT].map; + } + } + } + return CMD_SUCCESS; +} + +static int +peer_rsclient_unset_vty (struct vty *vty, const char *peer_str, + int afi, int safi) +{ + int ret; + struct bgp *bgp; + struct peer *peer; + struct peer_group *group; + struct listnode *node, *nnode; + + bgp = vty->index; + + peer = peer_and_group_lookup_vty (vty, peer_str); + if ( ! peer ) + return CMD_WARNING; + + /* If it is not a RS-Client, don't do anything. */ + if ( ! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT) ) + return CMD_SUCCESS; + + if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) + { + group = peer->group; + + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + ret = peer_af_flag_unset (peer, afi, safi, PEER_FLAG_RSERVER_CLIENT); + if (ret < 0) + return bgp_vty_return (vty, ret); + + peer->rib[afi][safi] = NULL; + } + + peer = group->conf; + } + + ret = peer_af_flag_unset (peer, afi, safi, PEER_FLAG_RSERVER_CLIENT); + if (ret < 0) + return bgp_vty_return (vty, ret); + + if ( ! peer_rsclient_active (peer) ) + { + bgp_clear_route (peer, afi, safi, BGP_CLEAR_ROUTE_MY_RSCLIENT); + listnode_delete (bgp->rsclient, peer); + peer_unlock (peer); /* peer bgp rsclient reference */ + } + + bgp_table_finish (&peer->rib[bgp_node_afi(vty)][bgp_node_safi(vty)]); + + return CMD_SUCCESS; +} + +/* neighbor route-server-client. */ +DEFUN (neighbor_route_server_client, + neighbor_route_server_client_cmd, + NEIGHBOR_CMD2 "route-server-client", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Configure a neighbor as Route Server client\n") +{ + return peer_rsclient_set_vty (vty, argv[0], bgp_node_afi(vty), + bgp_node_safi(vty)); +} + +DEFUN (no_neighbor_route_server_client, + no_neighbor_route_server_client_cmd, + NO_NEIGHBOR_CMD2 "route-server-client", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Configure a neighbor as Route Server client\n") +{ + 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", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Configure treatment of outgoing link-local nexthop attribute\n" + "Leave link-local nexthop unchanged for this peer\n") +{ + return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty), + 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", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Configure treatment of outgoing link-local-nexthop attribute\n" + "Leave link-local nexthop unchanged for this peer\n") +{ + return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), + PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED ); +} + +DEFUN (neighbor_attr_unchanged, + neighbor_attr_unchanged_cmd, + NEIGHBOR_CMD2 "attribute-unchanged", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "BGP attribute is propagated unchanged to this neighbor\n") +{ + return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), + (PEER_FLAG_AS_PATH_UNCHANGED | + PEER_FLAG_NEXTHOP_UNCHANGED | + PEER_FLAG_MED_UNCHANGED)); +} + +DEFUN (neighbor_attr_unchanged1, + neighbor_attr_unchanged1_cmd, + NEIGHBOR_CMD2 "attribute-unchanged (as-path|next-hop|med)", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "BGP attribute is propagated unchanged to this neighbor\n" + "As-path attribute\n" + "Nexthop attribute\n" + "Med attribute\n") +{ + u_int16_t flags = 0; + + if (strncmp (argv[1], "as-path", 1) == 0) + SET_FLAG (flags, PEER_FLAG_AS_PATH_UNCHANGED); + else if (strncmp (argv[1], "next-hop", 1) == 0) + SET_FLAG (flags, PEER_FLAG_NEXTHOP_UNCHANGED); + else if (strncmp (argv[1], "med", 1) == 0) + SET_FLAG (flags, PEER_FLAG_MED_UNCHANGED); + + return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), flags); +} + +DEFUN (neighbor_attr_unchanged2, + neighbor_attr_unchanged2_cmd, + NEIGHBOR_CMD2 "attribute-unchanged as-path (next-hop|med)", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "BGP attribute is propagated unchanged to this neighbor\n" + "As-path attribute\n" + "Nexthop attribute\n" + "Med attribute\n") +{ + u_int16_t flags = PEER_FLAG_AS_PATH_UNCHANGED; + + if (strncmp (argv[1], "next-hop", 1) == 0) + SET_FLAG (flags, PEER_FLAG_NEXTHOP_UNCHANGED); + else if (strncmp (argv[1], "med", 1) == 0) + SET_FLAG (flags, PEER_FLAG_MED_UNCHANGED); + + return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), flags); + +} + +DEFUN (neighbor_attr_unchanged3, + neighbor_attr_unchanged3_cmd, + NEIGHBOR_CMD2 "attribute-unchanged next-hop (as-path|med)", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "BGP attribute is propagated unchanged to this neighbor\n" + "Nexthop attribute\n" + "As-path attribute\n" + "Med attribute\n") +{ + u_int16_t flags = PEER_FLAG_NEXTHOP_UNCHANGED; + + if (strncmp (argv[1], "as-path", 1) == 0) + SET_FLAG (flags, PEER_FLAG_AS_PATH_UNCHANGED); + else if (strncmp (argv[1], "med", 1) == 0) + SET_FLAG (flags, PEER_FLAG_MED_UNCHANGED); + + return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), flags); +} + +DEFUN (neighbor_attr_unchanged4, + neighbor_attr_unchanged4_cmd, + NEIGHBOR_CMD2 "attribute-unchanged med (as-path|next-hop)", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "BGP attribute is propagated unchanged to this neighbor\n" + "Med attribute\n" + "As-path attribute\n" + "Nexthop attribute\n") +{ + u_int16_t flags = PEER_FLAG_MED_UNCHANGED; + + if (strncmp (argv[1], "as-path", 1) == 0) + SET_FLAG (flags, PEER_FLAG_AS_PATH_UNCHANGED); + else if (strncmp (argv[1], "next-hop", 1) == 0) + SET_FLAG (flags, PEER_FLAG_NEXTHOP_UNCHANGED); + + return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), flags); +} + +ALIAS (neighbor_attr_unchanged, + neighbor_attr_unchanged5_cmd, + NEIGHBOR_CMD2 "attribute-unchanged as-path next-hop med", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "BGP attribute is propagated unchanged to this neighbor\n" + "As-path attribute\n" + "Nexthop attribute\n" + "Med attribute\n") + +ALIAS (neighbor_attr_unchanged, + neighbor_attr_unchanged6_cmd, + NEIGHBOR_CMD2 "attribute-unchanged as-path med next-hop", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "BGP attribute is propagated unchanged to this neighbor\n" + "As-path attribute\n" + "Med attribute\n" + "Nexthop attribute\n") + +ALIAS (neighbor_attr_unchanged, + neighbor_attr_unchanged7_cmd, + NEIGHBOR_CMD2 "attribute-unchanged next-hop med as-path", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "BGP attribute is propagated unchanged to this neighbor\n" + "Nexthop attribute\n" + "Med attribute\n" + "As-path attribute\n") + +ALIAS (neighbor_attr_unchanged, + neighbor_attr_unchanged8_cmd, + NEIGHBOR_CMD2 "attribute-unchanged next-hop as-path med", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "BGP attribute is propagated unchanged to this neighbor\n" + "Nexthop attribute\n" + "As-path attribute\n" + "Med attribute\n") + +ALIAS (neighbor_attr_unchanged, + neighbor_attr_unchanged9_cmd, + NEIGHBOR_CMD2 "attribute-unchanged med next-hop as-path", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "BGP attribute is propagated unchanged to this neighbor\n" + "Med attribute\n" + "Nexthop attribute\n" + "As-path attribute\n") + +ALIAS (neighbor_attr_unchanged, + neighbor_attr_unchanged10_cmd, + NEIGHBOR_CMD2 "attribute-unchanged med as-path next-hop", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "BGP attribute is propagated unchanged to this neighbor\n" + "Med attribute\n" + "As-path attribute\n" + "Nexthop attribute\n") + +DEFUN (no_neighbor_attr_unchanged, + no_neighbor_attr_unchanged_cmd, + NO_NEIGHBOR_CMD2 "attribute-unchanged", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "BGP attribute is propagated unchanged to this neighbor\n") +{ + return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), + (PEER_FLAG_AS_PATH_UNCHANGED | + PEER_FLAG_NEXTHOP_UNCHANGED | + PEER_FLAG_MED_UNCHANGED)); +} + +DEFUN (no_neighbor_attr_unchanged1, + no_neighbor_attr_unchanged1_cmd, + NO_NEIGHBOR_CMD2 "attribute-unchanged (as-path|next-hop|med)", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "BGP attribute is propagated unchanged to this neighbor\n" + "As-path attribute\n" + "Nexthop attribute\n" + "Med attribute\n") +{ + u_int16_t flags = 0; + + if (strncmp (argv[1], "as-path", 1) == 0) + SET_FLAG (flags, PEER_FLAG_AS_PATH_UNCHANGED); + else if (strncmp (argv[1], "next-hop", 1) == 0) + SET_FLAG (flags, PEER_FLAG_NEXTHOP_UNCHANGED); + else if (strncmp (argv[1], "med", 1) == 0) + SET_FLAG (flags, PEER_FLAG_MED_UNCHANGED); + + return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), flags); +} + +DEFUN (no_neighbor_attr_unchanged2, + no_neighbor_attr_unchanged2_cmd, + NO_NEIGHBOR_CMD2 "attribute-unchanged as-path (next-hop|med)", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "BGP attribute is propagated unchanged to this neighbor\n" + "As-path attribute\n" + "Nexthop attribute\n" + "Med attribute\n") +{ + u_int16_t flags = PEER_FLAG_AS_PATH_UNCHANGED; + + if (strncmp (argv[1], "next-hop", 1) == 0) + SET_FLAG (flags, PEER_FLAG_NEXTHOP_UNCHANGED); + else if (strncmp (argv[1], "med", 1) == 0) + SET_FLAG (flags, PEER_FLAG_MED_UNCHANGED); + + return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), flags); +} + +DEFUN (no_neighbor_attr_unchanged3, + no_neighbor_attr_unchanged3_cmd, + NO_NEIGHBOR_CMD2 "attribute-unchanged next-hop (as-path|med)", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "BGP attribute is propagated unchanged to this neighbor\n" + "Nexthop attribute\n" + "As-path attribute\n" + "Med attribute\n") +{ + u_int16_t flags = PEER_FLAG_NEXTHOP_UNCHANGED; + + if (strncmp (argv[1], "as-path", 1) == 0) + SET_FLAG (flags, PEER_FLAG_AS_PATH_UNCHANGED); + else if (strncmp (argv[1], "med", 1) == 0) + SET_FLAG (flags, PEER_FLAG_MED_UNCHANGED); + + return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), flags); +} + +DEFUN (no_neighbor_attr_unchanged4, + no_neighbor_attr_unchanged4_cmd, + NO_NEIGHBOR_CMD2 "attribute-unchanged med (as-path|next-hop)", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "BGP attribute is propagated unchanged to this neighbor\n" + "Med attribute\n" + "As-path attribute\n" + "Nexthop attribute\n") +{ + u_int16_t flags = PEER_FLAG_MED_UNCHANGED; + + if (strncmp (argv[1], "as-path", 1) == 0) + SET_FLAG (flags, PEER_FLAG_AS_PATH_UNCHANGED); + else if (strncmp (argv[1], "next-hop", 1) == 0) + SET_FLAG (flags, PEER_FLAG_NEXTHOP_UNCHANGED); + + return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), flags); +} + +ALIAS (no_neighbor_attr_unchanged, + no_neighbor_attr_unchanged5_cmd, + NO_NEIGHBOR_CMD2 "attribute-unchanged as-path next-hop med", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "BGP attribute is propagated unchanged to this neighbor\n" + "As-path attribute\n" + "Nexthop attribute\n" + "Med attribute\n") + +ALIAS (no_neighbor_attr_unchanged, + no_neighbor_attr_unchanged6_cmd, + NO_NEIGHBOR_CMD2 "attribute-unchanged as-path med next-hop", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "BGP attribute is propagated unchanged to this neighbor\n" + "As-path attribute\n" + "Med attribute\n" + "Nexthop attribute\n") + +ALIAS (no_neighbor_attr_unchanged, + no_neighbor_attr_unchanged7_cmd, + NO_NEIGHBOR_CMD2 "attribute-unchanged next-hop med as-path", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "BGP attribute is propagated unchanged to this neighbor\n" + "Nexthop attribute\n" + "Med attribute\n" + "As-path attribute\n") + +ALIAS (no_neighbor_attr_unchanged, + no_neighbor_attr_unchanged8_cmd, + NO_NEIGHBOR_CMD2 "attribute-unchanged next-hop as-path med", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "BGP attribute is propagated unchanged to this neighbor\n" + "Nexthop attribute\n" + "As-path attribute\n" + "Med attribute\n") + +ALIAS (no_neighbor_attr_unchanged, + no_neighbor_attr_unchanged9_cmd, + NO_NEIGHBOR_CMD2 "attribute-unchanged med next-hop as-path", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "BGP attribute is propagated unchanged to this neighbor\n" + "Med attribute\n" + "Nexthop attribute\n" + "As-path attribute\n") + +ALIAS (no_neighbor_attr_unchanged, + no_neighbor_attr_unchanged10_cmd, + NO_NEIGHBOR_CMD2 "attribute-unchanged med as-path next-hop", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "BGP attribute is propagated unchanged to this neighbor\n" + "Med attribute\n" + "As-path attribute\n" + "Nexthop attribute\n") + +/* For old version Zebra compatibility. */ +DEFUN_DEPRECATED (neighbor_transparent_as, + neighbor_transparent_as_cmd, + NEIGHBOR_CMD "transparent-as", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR + "Do not append my AS number even peer is EBGP peer\n") +{ + return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), + PEER_FLAG_AS_PATH_UNCHANGED); +} + +DEFUN_DEPRECATED (neighbor_transparent_nexthop, + neighbor_transparent_nexthop_cmd, + NEIGHBOR_CMD "transparent-nexthop", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR + "Do not change nexthop even peer is EBGP peer\n") +{ + return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty), + 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, + const char *ttl_str) +{ + struct peer *peer; + unsigned int ttl; + + peer = peer_and_group_lookup_vty (vty, ip_str); + if (! peer) + return CMD_WARNING; + + if (! ttl_str) + ttl = TTL_MAX; + else + VTY_GET_INTEGER_RANGE ("TTL", ttl, ttl_str, 1, 255); + + return bgp_vty_return (vty, peer_ebgp_multihop_set (peer, ttl)); +} + +static int +peer_ebgp_multihop_unset_vty (struct vty *vty, const char *ip_str) +{ + struct peer *peer; + + peer = peer_and_group_lookup_vty (vty, ip_str); + if (! peer) + return CMD_WARNING; + + return bgp_vty_return (vty, peer_ebgp_multihop_set (peer, 0)); +} + +/* neighbor ebgp-multihop. */ +DEFUN (neighbor_ebgp_multihop, + neighbor_ebgp_multihop_cmd, + NEIGHBOR_CMD2 "ebgp-multihop", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Allow EBGP neighbors not on directly connected networks\n") +{ + return peer_ebgp_multihop_set_vty (vty, argv[0], NULL); +} + +DEFUN (neighbor_ebgp_multihop_ttl, + neighbor_ebgp_multihop_ttl_cmd, + NEIGHBOR_CMD2 "ebgp-multihop <1-255>", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Allow EBGP neighbors not on directly connected networks\n" + "maximum hop count\n") +{ + return peer_ebgp_multihop_set_vty (vty, argv[0], argv[1]); +} + +DEFUN (no_neighbor_ebgp_multihop, + no_neighbor_ebgp_multihop_cmd, + NO_NEIGHBOR_CMD2 "ebgp-multihop", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Allow EBGP neighbors not on directly connected networks\n") +{ + return peer_ebgp_multihop_unset_vty (vty, argv[0]); +} + +ALIAS (no_neighbor_ebgp_multihop, + no_neighbor_ebgp_multihop_ttl_cmd, + NO_NEIGHBOR_CMD2 "ebgp-multihop <1-255>", + NO_STR + NEIGHBOR_STR + 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, + NEIGHBOR_CMD2 "disable-connected-check", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "one-hop away EBGP peer using loopback address\n") +{ + return peer_flag_set_vty (vty, argv[0], PEER_FLAG_DISABLE_CONNECTED_CHECK); +} + +DEFUN (no_neighbor_disable_connected_check, + no_neighbor_disable_connected_check_cmd, + NO_NEIGHBOR_CMD2 "disable-connected-check", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "one-hop away EBGP peer using loopback address\n") +{ + return peer_flag_unset_vty (vty, argv[0], PEER_FLAG_DISABLE_CONNECTED_CHECK); +} + +/* Enforce multihop. */ +ALIAS (neighbor_disable_connected_check, + neighbor_enforce_multihop_cmd, + NEIGHBOR_CMD2 "enforce-multihop", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Enforce EBGP neighbors perform multihop\n") + +/* Enforce multihop. */ +ALIAS (no_neighbor_disable_connected_check, + no_neighbor_enforce_multihop_cmd, + NO_NEIGHBOR_CMD2 "enforce-multihop", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Enforce EBGP neighbors perform multihop\n") + +DEFUN (neighbor_description, + neighbor_description_cmd, + NEIGHBOR_CMD2 "description .LINE", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Neighbor specific description\n" + "Up to 80 characters describing this neighbor\n") +{ + struct peer *peer; + char *str; + + peer = peer_and_group_lookup_vty (vty, argv[0]); + if (! peer) + return CMD_WARNING; + + if (argc == 1) + return CMD_SUCCESS; + + str = argv_concat(argv, argc, 1); + + peer_description_set (peer, str); + + XFREE (MTYPE_TMP, str); + + return CMD_SUCCESS; +} + +DEFUN (no_neighbor_description, + no_neighbor_description_cmd, + NO_NEIGHBOR_CMD2 "description", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Neighbor specific description\n") +{ + struct peer *peer; + + peer = peer_and_group_lookup_vty (vty, argv[0]); + if (! peer) + return CMD_WARNING; + + peer_description_unset (peer); + + return CMD_SUCCESS; +} + +ALIAS (no_neighbor_description, + no_neighbor_description_val_cmd, + NO_NEIGHBOR_CMD2 "description .LINE", + NO_STR + NEIGHBOR_STR + 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; + + peer = peer_and_group_lookup_vty (vty, peer_str); + if (! peer) + return CMD_WARNING; + + if (source_str) + { + 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); + } + else + peer_update_source_unset (peer); + + return CMD_SUCCESS; +} + +#define BGP_UPDATE_SOURCE_STR "(A.B.C.D|X:X::X:X|WORD)" +#define BGP_UPDATE_SOURCE_HELP_STR \ + "IPv4 address\n" \ + "IPv6 address\n" \ + "Interface name (requires zebra to be running)\n" + +DEFUN (neighbor_update_source, + neighbor_update_source_cmd, + NEIGHBOR_CMD2 "update-source " BGP_UPDATE_SOURCE_STR, + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Source of routing updates\n" + BGP_UPDATE_SOURCE_HELP_STR) +{ + return peer_update_source_vty (vty, argv[0], argv[1]); +} + +DEFUN (no_neighbor_update_source, + no_neighbor_update_source_cmd, + NO_NEIGHBOR_CMD2 "update-source", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Source of routing updates\n") +{ + 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, + const char *rmap, int set) +{ + int ret; + struct peer *peer; + + peer = peer_and_group_lookup_vty (vty, peer_str); + if (! peer) + return CMD_WARNING; + + if (set) + ret = peer_default_originate_set (peer, afi, safi, rmap); + else + ret = peer_default_originate_unset (peer, afi, safi); + + return bgp_vty_return (vty, ret); +} + +/* neighbor default-originate. */ +DEFUN (neighbor_default_originate, + neighbor_default_originate_cmd, + NEIGHBOR_CMD2 "default-originate", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Originate default route to this neighbor\n") +{ + return peer_default_originate_set_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), NULL, 1); +} + +DEFUN (neighbor_default_originate_rmap, + neighbor_default_originate_rmap_cmd, + NEIGHBOR_CMD2 "default-originate route-map WORD", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Originate default route to this neighbor\n" + "Route-map to specify criteria to originate default\n" + "route-map name\n") +{ + return peer_default_originate_set_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), argv[1], 1); +} + +DEFUN (no_neighbor_default_originate, + no_neighbor_default_originate_cmd, + NO_NEIGHBOR_CMD2 "default-originate", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Originate default route to this neighbor\n") +{ + return peer_default_originate_set_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), NULL, 0); +} + +ALIAS (no_neighbor_default_originate, + no_neighbor_default_originate_rmap_cmd, + NO_NEIGHBOR_CMD2 "default-originate route-map WORD", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "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, + const char *port_str) +{ + struct peer *peer; + u_int16_t port; + struct servent *sp; + + peer = peer_lookup_vty (vty, ip_str); + if (! peer) + return CMD_WARNING; + + if (! port_str) + { + sp = getservbyname ("bgp", "tcp"); + port = (sp == NULL) ? BGP_PORT_DEFAULT : ntohs (sp->s_port); + } + else + { + VTY_GET_INTEGER("port", port, port_str); + } + + peer_port_set (peer, port); + + return CMD_SUCCESS; +} + +/* Set specified peer's BGP port. */ +DEFUN (neighbor_port, + neighbor_port_cmd, + NEIGHBOR_CMD "port <0-65535>", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR + "Neighbor's BGP port\n" + "TCP port number\n") +{ + return peer_port_vty (vty, argv[0], AFI_IP, argv[1]); +} + +DEFUN (no_neighbor_port, + no_neighbor_port_cmd, + NO_NEIGHBOR_CMD "port", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR + "Neighbor's BGP port\n") +{ + return peer_port_vty (vty, argv[0], AFI_IP, NULL); +} + +ALIAS (no_neighbor_port, + no_neighbor_port_val_cmd, + NO_NEIGHBOR_CMD "port <0-65535>", + NO_STR + NEIGHBOR_STR + 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) +{ + struct peer *peer; + unsigned long weight; + + peer = peer_and_group_lookup_vty (vty, ip_str); + if (! peer) + return CMD_WARNING; + + VTY_GET_INTEGER_RANGE("weight", weight, weight_str, 0, 65535); + + return bgp_vty_return (vty, peer_weight_set (peer, weight)); +} + +static int +peer_weight_unset_vty (struct vty *vty, const char *ip_str) +{ + struct peer *peer; + + peer = peer_and_group_lookup_vty (vty, ip_str); + if (! peer) + return CMD_WARNING; + + return bgp_vty_return (vty, peer_weight_unset (peer)); +} + +DEFUN (neighbor_weight, + neighbor_weight_cmd, + NEIGHBOR_CMD2 "weight <0-65535>", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Set default weight for routes from this neighbor\n" + "default weight\n") +{ + return peer_weight_set_vty (vty, argv[0], argv[1]); +} + +DEFUN (no_neighbor_weight, + no_neighbor_weight_cmd, + NO_NEIGHBOR_CMD2 "weight", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Set default weight for routes from this neighbor\n") +{ + return peer_weight_unset_vty (vty, argv[0]); +} + +ALIAS (no_neighbor_weight, + no_neighbor_weight_val_cmd, + NO_NEIGHBOR_CMD2 "weight <0-65535>", + NO_STR + NEIGHBOR_STR + 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, + NEIGHBOR_CMD2 "override-capability", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Override capability negotiation result\n") +{ + return peer_flag_set_vty (vty, argv[0], PEER_FLAG_OVERRIDE_CAPABILITY); +} + +DEFUN (no_neighbor_override_capability, + no_neighbor_override_capability_cmd, + NO_NEIGHBOR_CMD2 "override-capability", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Override capability negotiation result\n") +{ + 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", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR + "Strict capability negotiation match\n") +{ + return peer_flag_set_vty (vty, argv[0], PEER_FLAG_STRICT_CAP_MATCH); +} + +DEFUN (no_neighbor_strict_capability, + no_neighbor_strict_capability_cmd, + NO_NEIGHBOR_CMD "strict-capability-match", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR + "Strict capability negotiation match\n") +{ + 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) +{ + int ret; + struct peer *peer; + u_int32_t keepalive; + u_int32_t holdtime; + + peer = peer_and_group_lookup_vty (vty, ip_str); + if (! peer) + return CMD_WARNING; + + VTY_GET_INTEGER_RANGE ("Keepalive", keepalive, keep_str, 0, 65535); + VTY_GET_INTEGER_RANGE ("Holdtime", holdtime, hold_str, 0, 65535); + + ret = peer_timers_set (peer, keepalive, holdtime); + + return bgp_vty_return (vty, ret); +} + +static int +peer_timers_unset_vty (struct vty *vty, const char *ip_str) +{ + int ret; + struct peer *peer; + + peer = peer_lookup_vty (vty, ip_str); + if (! peer) + return CMD_WARNING; + + ret = peer_timers_unset (peer); + + return bgp_vty_return (vty, ret); +} + +DEFUN (neighbor_timers, + neighbor_timers_cmd, + NEIGHBOR_CMD2 "timers <0-65535> <0-65535>", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "BGP per neighbor timers\n" + "Keepalive interval\n" + "Holdtime\n") +{ + return peer_timers_set_vty (vty, argv[0], argv[1], argv[2]); +} + +DEFUN (no_neighbor_timers, + no_neighbor_timers_cmd, + NO_NEIGHBOR_CMD2 "timers", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "BGP per neighbor timers\n") +{ + 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) +{ + struct peer *peer; + u_int32_t connect; + + 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); + + 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) +{ + struct peer *peer; + + peer = peer_and_group_lookup_vty (vty, ip_str); + if (! peer) + return CMD_WARNING; + + return bgp_vty_return (vty, peer_timers_connect_unset (peer)); +} + +DEFUN (neighbor_timers_connect, + neighbor_timers_connect_cmd, + NEIGHBOR_CMD2 "timers connect <1-65535>", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "BGP per neighbor timers\n" + "BGP connect timer\n" + "Connect timer\n") +{ + return peer_timers_connect_set_vty (vty, argv[0], argv[1]); +} + +DEFUN (no_neighbor_timers_connect, + no_neighbor_timers_connect_cmd, + NO_NEIGHBOR_CMD2 "timers connect", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "BGP per neighbor timers\n" + "BGP connect timer\n") +{ + return peer_timers_connect_unset_vty (vty, argv[0]); +} + +ALIAS (no_neighbor_timers_connect, + no_neighbor_timers_connect_val_cmd, + NO_NEIGHBOR_CMD2 "timers connect <1-65535>", + NO_STR + NEIGHBOR_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) +{ + int ret; + struct peer *peer; + u_int32_t routeadv = 0; + + peer = peer_and_group_lookup_vty (vty, ip_str); + if (! peer) + return CMD_WARNING; + + if (time_str) + VTY_GET_INTEGER_RANGE ("advertise interval", routeadv, time_str, 0, 600); + + if (set) + ret = peer_advertise_interval_set (peer, routeadv); + else + ret = peer_advertise_interval_unset (peer); + + return bgp_vty_return (vty, ret); +} + +DEFUN (neighbor_advertise_interval, + neighbor_advertise_interval_cmd, + NEIGHBOR_CMD2 "advertisement-interval <0-600>", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Minimum interval between sending BGP routing updates\n" + "time in seconds\n") +{ + return peer_advertise_interval_vty (vty, argv[0], argv[1], 1); +} + +DEFUN (no_neighbor_advertise_interval, + no_neighbor_advertise_interval_cmd, + NO_NEIGHBOR_CMD2 "advertisement-interval", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Minimum interval between sending BGP routing updates\n") +{ + return peer_advertise_interval_vty (vty, argv[0], NULL, 0); +} + +ALIAS (no_neighbor_advertise_interval, + no_neighbor_advertise_interval_val_cmd, + NO_NEIGHBOR_CMD2 "advertisement-interval <0-600>", + NO_STR + NEIGHBOR_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) +{ + int ret; + struct peer *peer; + + peer = peer_lookup_vty (vty, ip_str); + if (! peer) + return CMD_WARNING; + + if (str) + ret = peer_interface_set (peer, str); + else + ret = peer_interface_unset (peer); + + return bgp_vty_return (vty, ret); +} + +DEFUN (neighbor_interface, + neighbor_interface_cmd, + NEIGHBOR_CMD "interface WORD", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR + "Interface\n" + "Interface name\n") +{ + return peer_interface_vty (vty, argv[0], argv[1]); +} + +DEFUN (no_neighbor_interface, + no_neighbor_interface_cmd, + NO_NEIGHBOR_CMD "interface WORD", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR + "Interface\n" + "Interface name\n") +{ + 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, + afi_t afi, safi_t safi, + const char *name_str, const char *direct_str) +{ + int ret; + struct peer *peer; + int direct = FILTER_IN; + + peer = peer_and_group_lookup_vty (vty, ip_str); + if (! peer) + return CMD_WARNING; + + /* Check filter direction. */ + if (strncmp (direct_str, "i", 1) == 0) + direct = FILTER_IN; + else if (strncmp (direct_str, "o", 1) == 0) + direct = FILTER_OUT; + + ret = peer_distribute_set (peer, afi, safi, direct, name_str); + + return bgp_vty_return (vty, ret); +} + +static int +peer_distribute_unset_vty (struct vty *vty, const char *ip_str, afi_t afi, + safi_t safi, const char *direct_str) +{ + int ret; + struct peer *peer; + int direct = FILTER_IN; + + peer = peer_and_group_lookup_vty (vty, ip_str); + if (! peer) + return CMD_WARNING; + + /* Check filter direction. */ + if (strncmp (direct_str, "i", 1) == 0) + direct = FILTER_IN; + else if (strncmp (direct_str, "o", 1) == 0) + direct = FILTER_OUT; + + ret = peer_distribute_unset (peer, afi, safi, direct); + + return bgp_vty_return (vty, ret); +} + +DEFUN (neighbor_distribute_list, + neighbor_distribute_list_cmd, + NEIGHBOR_CMD2 "distribute-list (<1-199>|<1300-2699>|WORD) (in|out)", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Filter updates to/from this neighbor\n" + "IP access-list number\n" + "IP access-list number (expanded range)\n" + "IP Access-list name\n" + "Filter incoming updates\n" + "Filter outgoing updates\n") +{ + return peer_distribute_set_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), argv[1], argv[2]); +} + +DEFUN (no_neighbor_distribute_list, + no_neighbor_distribute_list_cmd, + NO_NEIGHBOR_CMD2 "distribute-list (<1-199>|<1300-2699>|WORD) (in|out)", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Filter updates to/from this neighbor\n" + "IP access-list number\n" + "IP access-list number (expanded range)\n" + "IP Access-list name\n" + "Filter incoming updates\n" + "Filter outgoing updates\n") +{ + 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, + safi_t safi, const char *name_str, + const char *direct_str) +{ + int ret; + struct peer *peer; + int direct = FILTER_IN; + + peer = peer_and_group_lookup_vty (vty, ip_str); + if (! peer) + return CMD_WARNING; + + /* Check filter direction. */ + if (strncmp (direct_str, "i", 1) == 0) + direct = FILTER_IN; + else if (strncmp (direct_str, "o", 1) == 0) + direct = FILTER_OUT; + + ret = peer_prefix_list_set (peer, afi, safi, direct, name_str); + + return bgp_vty_return (vty, ret); +} + +static int +peer_prefix_list_unset_vty (struct vty *vty, const char *ip_str, afi_t afi, + safi_t safi, const char *direct_str) +{ + int ret; + struct peer *peer; + int direct = FILTER_IN; + + peer = peer_and_group_lookup_vty (vty, ip_str); + if (! peer) + return CMD_WARNING; + + /* Check filter direction. */ + if (strncmp (direct_str, "i", 1) == 0) + direct = FILTER_IN; + else if (strncmp (direct_str, "o", 1) == 0) + direct = FILTER_OUT; + + ret = peer_prefix_list_unset (peer, afi, safi, direct); + + return bgp_vty_return (vty, ret); +} + +DEFUN (neighbor_prefix_list, + neighbor_prefix_list_cmd, + NEIGHBOR_CMD2 "prefix-list WORD (in|out)", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Filter updates to/from this neighbor\n" + "Name of a prefix list\n" + "Filter incoming updates\n" + "Filter outgoing updates\n") +{ + return peer_prefix_list_set_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), argv[1], argv[2]); +} + +DEFUN (no_neighbor_prefix_list, + no_neighbor_prefix_list_cmd, + NO_NEIGHBOR_CMD2 "prefix-list WORD (in|out)", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Filter updates to/from this neighbor\n" + "Name of a prefix list\n" + "Filter incoming updates\n" + "Filter outgoing updates\n") +{ + 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, + const char *name_str, const char *direct_str) +{ + int ret; + struct peer *peer; + int direct = FILTER_IN; + + peer = peer_and_group_lookup_vty (vty, ip_str); + if (! peer) + return CMD_WARNING; + + /* Check filter direction. */ + if (strncmp (direct_str, "i", 1) == 0) + direct = FILTER_IN; + else if (strncmp (direct_str, "o", 1) == 0) + direct = FILTER_OUT; + + ret = peer_aslist_set (peer, afi, safi, direct, name_str); + + return bgp_vty_return (vty, ret); +} + +static int +peer_aslist_unset_vty (struct vty *vty, const char *ip_str, + afi_t afi, safi_t safi, + const char *direct_str) +{ + int ret; + struct peer *peer; + int direct = FILTER_IN; + + peer = peer_and_group_lookup_vty (vty, ip_str); + if (! peer) + return CMD_WARNING; + + /* Check filter direction. */ + if (strncmp (direct_str, "i", 1) == 0) + direct = FILTER_IN; + else if (strncmp (direct_str, "o", 1) == 0) + direct = FILTER_OUT; + + ret = peer_aslist_unset (peer, afi, safi, direct); + + return bgp_vty_return (vty, ret); +} + +DEFUN (neighbor_filter_list, + neighbor_filter_list_cmd, + NEIGHBOR_CMD2 "filter-list WORD (in|out)", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Establish BGP filters\n" + "AS path access-list name\n" + "Filter incoming routes\n" + "Filter outgoing routes\n") +{ + return peer_aslist_set_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), argv[1], argv[2]); +} + +DEFUN (no_neighbor_filter_list, + no_neighbor_filter_list_cmd, + NO_NEIGHBOR_CMD2 "filter-list WORD (in|out)", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Establish BGP filters\n" + "AS path access-list name\n" + "Filter incoming routes\n" + "Filter outgoing routes\n") +{ + 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, + afi_t afi, safi_t safi, + const char *name_str, const char *direct_str) +{ + int ret; + struct peer *peer; + int direct = RMAP_IN; + + peer = peer_and_group_lookup_vty (vty, ip_str); + if (! peer) + return CMD_WARNING; + + /* Check filter direction. */ + if (strncmp (direct_str, "in", 2) == 0) + direct = RMAP_IN; + else if (strncmp (direct_str, "o", 1) == 0) + direct = RMAP_OUT; + else if (strncmp (direct_str, "im", 2) == 0) + direct = RMAP_IMPORT; + else if (strncmp (direct_str, "e", 1) == 0) + direct = RMAP_EXPORT; + + ret = peer_route_map_set (peer, afi, safi, direct, name_str); + + return bgp_vty_return (vty, ret); +} + +static int +peer_route_map_unset_vty (struct vty *vty, const char *ip_str, afi_t afi, + safi_t safi, const char *direct_str) +{ + int ret; + struct peer *peer; + int direct = RMAP_IN; + + peer = peer_and_group_lookup_vty (vty, ip_str); + if (! peer) + return CMD_WARNING; + + /* Check filter direction. */ + if (strncmp (direct_str, "in", 2) == 0) + direct = RMAP_IN; + else if (strncmp (direct_str, "o", 1) == 0) + direct = RMAP_OUT; + else if (strncmp (direct_str, "im", 2) == 0) + direct = RMAP_IMPORT; + else if (strncmp (direct_str, "e", 1) == 0) + direct = RMAP_EXPORT; + + ret = peer_route_map_unset (peer, afi, safi, direct); + + return bgp_vty_return (vty, ret); +} + +DEFUN (neighbor_route_map, + neighbor_route_map_cmd, + NEIGHBOR_CMD2 "route-map WORD (in|out|import|export)", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Apply route map to neighbor\n" + "Name of route map\n" + "Apply map to incoming routes\n" + "Apply map to outbound routes\n" + "Apply map to routes going into a Route-Server client's table\n" + "Apply map to routes coming from a Route-Server client") +{ + return peer_route_map_set_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), argv[1], argv[2]); +} + +DEFUN (no_neighbor_route_map, + no_neighbor_route_map_cmd, + NO_NEIGHBOR_CMD2 "route-map WORD (in|out|import|export)", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Apply route map to neighbor\n" + "Name of route map\n" + "Apply map to incoming routes\n" + "Apply map to outbound routes\n" + "Apply map to routes going into a Route-Server client's table\n" + "Apply map to routes coming from a Route-Server client") +{ + 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, + safi_t safi, const char *name_str) +{ + int ret; + struct peer *peer; + + peer = peer_and_group_lookup_vty (vty, ip_str); + if (! peer) + return CMD_WARNING; + + ret = peer_unsuppress_map_set (peer, afi, safi, name_str); + + return bgp_vty_return (vty, ret); +} + +/* Unset route-map from the peer. */ +static int +peer_unsuppress_map_unset_vty (struct vty *vty, const char *ip_str, afi_t afi, + safi_t safi) +{ + int ret; + struct peer *peer; + + peer = peer_and_group_lookup_vty (vty, ip_str); + if (! peer) + return CMD_WARNING; + + ret = peer_unsuppress_map_unset (peer, afi, safi); + + return bgp_vty_return (vty, ret); +} + +DEFUN (neighbor_unsuppress_map, + neighbor_unsuppress_map_cmd, + NEIGHBOR_CMD2 "unsuppress-map WORD", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Route-map to selectively unsuppress suppressed routes\n" + "Name of route map\n") +{ + return peer_unsuppress_map_set_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), argv[1]); +} + +DEFUN (no_neighbor_unsuppress_map, + no_neighbor_unsuppress_map_cmd, + NO_NEIGHBOR_CMD2 "unsuppress-map WORD", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Route-map to selectively unsuppress suppressed routes\n" + "Name of route map\n") +{ + 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, + const char *threshold_str, int warning, + const char *restart_str) +{ + int ret; + struct peer *peer; + u_int32_t max; + u_char threshold; + u_int16_t restart; + + peer = peer_and_group_lookup_vty (vty, ip_str); + if (! peer) + return CMD_WARNING; + + VTY_GET_INTEGER ("maximum number", max, num_str); + if (threshold_str) + threshold = atoi (threshold_str); + else + threshold = MAXIMUM_PREFIX_THRESHOLD_DEFAULT; + + if (restart_str) + restart = atoi (restart_str); + else + restart = 0; + + ret = peer_maximum_prefix_set (peer, afi, safi, max, threshold, warning, restart); + + return bgp_vty_return (vty, ret); +} + +static int +peer_maximum_prefix_unset_vty (struct vty *vty, const char *ip_str, afi_t afi, + safi_t safi) +{ + int ret; + struct peer *peer; + + peer = peer_and_group_lookup_vty (vty, ip_str); + if (! peer) + return CMD_WARNING; + + ret = peer_maximum_prefix_unset (peer, afi, safi); + + return bgp_vty_return (vty, ret); +} + +/* Maximum number of prefix configuration. prefix count is different + for each peer configuration. So this configuration can be set for + each peer configuration. */ +DEFUN (neighbor_maximum_prefix, + neighbor_maximum_prefix_cmd, + NEIGHBOR_CMD2 "maximum-prefix <1-4294967295>", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Maximum number of prefix accept from this peer\n" + "maximum no. of prefix limit\n") +{ + return peer_maximum_prefix_set_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), argv[1], NULL, 0, + NULL); +} + +DEFUN (neighbor_maximum_prefix_threshold, + neighbor_maximum_prefix_threshold_cmd, + NEIGHBOR_CMD2 "maximum-prefix <1-4294967295> <1-100>", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Maximum number of prefix accept from this peer\n" + "maximum no. of prefix limit\n" + "Threshold value (%) at which to generate a warning msg\n") +{ + return peer_maximum_prefix_set_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), argv[1], argv[2], 0, + NULL); +} + +DEFUN (neighbor_maximum_prefix_warning, + neighbor_maximum_prefix_warning_cmd, + NEIGHBOR_CMD2 "maximum-prefix <1-4294967295> warning-only", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Maximum number of prefix accept from this peer\n" + "maximum no. of prefix limit\n" + "Only give warning message when limit is exceeded\n") +{ + return peer_maximum_prefix_set_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), argv[1], NULL, 1, + NULL); +} + +DEFUN (neighbor_maximum_prefix_threshold_warning, + neighbor_maximum_prefix_threshold_warning_cmd, + NEIGHBOR_CMD2 "maximum-prefix <1-4294967295> <1-100> warning-only", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Maximum number of prefix accept from this peer\n" + "maximum no. of prefix limit\n" + "Threshold value (%) at which to generate a warning msg\n" + "Only give warning message when limit is exceeded\n") +{ + return peer_maximum_prefix_set_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), argv[1], argv[2], 1, NULL); +} + +DEFUN (neighbor_maximum_prefix_restart, + neighbor_maximum_prefix_restart_cmd, + NEIGHBOR_CMD2 "maximum-prefix <1-4294967295> restart <1-65535>", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Maximum number of prefix accept from this peer\n" + "maximum no. of prefix limit\n" + "Restart bgp connection after limit is exceeded\n" + "Restart interval in minutes") +{ + return peer_maximum_prefix_set_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), argv[1], NULL, 0, argv[2]); +} + +DEFUN (neighbor_maximum_prefix_threshold_restart, + neighbor_maximum_prefix_threshold_restart_cmd, + NEIGHBOR_CMD2 "maximum-prefix <1-4294967295> <1-100> restart <1-65535>", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Maximum number of prefix accept from this peer\n" + "maximum no. of prefix limit\n" + "Threshold value (%) at which to generate a warning msg\n" + "Restart bgp connection after limit is exceeded\n" + "Restart interval in minutes") +{ + return peer_maximum_prefix_set_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), argv[1], argv[2], 0, argv[3]); +} + +DEFUN (no_neighbor_maximum_prefix, + no_neighbor_maximum_prefix_cmd, + NO_NEIGHBOR_CMD2 "maximum-prefix", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Maximum number of prefix accept from this peer\n") +{ + return peer_maximum_prefix_unset_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty)); +} + +ALIAS (no_neighbor_maximum_prefix, + no_neighbor_maximum_prefix_val_cmd, + NO_NEIGHBOR_CMD2 "maximum-prefix <1-4294967295>", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Maximum number of prefix accept from this peer\n" + "maximum no. of prefix limit\n") + +ALIAS (no_neighbor_maximum_prefix, + no_neighbor_maximum_prefix_threshold_cmd, + NO_NEIGHBOR_CMD2 "maximum-prefix <1-4294967295> warning-only", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Maximum number of prefix accept from this peer\n" + "maximum no. of prefix limit\n" + "Threshold value (%) at which to generate a warning msg\n") + +ALIAS (no_neighbor_maximum_prefix, + no_neighbor_maximum_prefix_warning_cmd, + NO_NEIGHBOR_CMD2 "maximum-prefix <1-4294967295> warning-only", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Maximum number of prefix accept from this peer\n" + "maximum no. of prefix limit\n" + "Only give warning message when limit is exceeded\n") + +ALIAS (no_neighbor_maximum_prefix, + no_neighbor_maximum_prefix_threshold_warning_cmd, + NO_NEIGHBOR_CMD2 "maximum-prefix <1-4294967295> <1-100> warning-only", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Maximum number of prefix accept from this peer\n" + "maximum no. of prefix limit\n" + "Threshold value (%) at which to generate a warning msg\n" + "Only give warning message when limit is exceeded\n") + +ALIAS (no_neighbor_maximum_prefix, + no_neighbor_maximum_prefix_restart_cmd, + NO_NEIGHBOR_CMD2 "maximum-prefix <1-4294967295> restart <1-65535>", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Maximum number of prefix accept from this peer\n" + "maximum no. of prefix limit\n" + "Restart bgp connection after limit is exceeded\n" + "Restart interval in minutes") + +ALIAS (no_neighbor_maximum_prefix, + no_neighbor_maximum_prefix_threshold_restart_cmd, + NO_NEIGHBOR_CMD2 "maximum-prefix <1-4294967295> <1-100> restart <1-65535>", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Maximum number of prefix accept from this peer\n" + "maximum no. of prefix limit\n" + "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, + NEIGHBOR_CMD2 "allowas-in", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Accept as-path with my AS present in it\n") +{ + int ret; + struct peer *peer; + unsigned int allow_num; + + peer = peer_and_group_lookup_vty (vty, argv[0]); + if (! peer) + return CMD_WARNING; + + if (argc == 1) + allow_num = 3; + else + VTY_GET_INTEGER_RANGE ("AS number", allow_num, argv[1], 1, 10); + + ret = peer_allowas_in_set (peer, bgp_node_afi (vty), bgp_node_safi (vty), + allow_num); + + return bgp_vty_return (vty, ret); +} + +ALIAS (neighbor_allowas_in, + neighbor_allowas_in_arg_cmd, + NEIGHBOR_CMD2 "allowas-in <1-10>", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Accept as-path with my AS present in it\n" + "Number of occurances of AS number\n") + +DEFUN (no_neighbor_allowas_in, + no_neighbor_allowas_in_cmd, + NO_NEIGHBOR_CMD2 "allowas-in", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "allow local ASN appears in aspath attribute\n") +{ + int ret; + struct peer *peer; + + peer = peer_and_group_lookup_vty (vty, argv[0]); + if (! peer) + return CMD_WARNING; + + ret = peer_allowas_in_unset (peer, bgp_node_afi (vty), bgp_node_safi (vty)); + + return bgp_vty_return (vty, ret); +} + +DEFUN (neighbor_ttl_security, + neighbor_ttl_security_cmd, + NEIGHBOR_CMD2 "ttl-security hops <1-254>", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Specify the maximum number of hops to the BGP peer\n") +{ + struct peer *peer; + int gtsm_hops; + + peer = peer_and_group_lookup_vty (vty, argv[0]); + if (! peer) + return CMD_WARNING; + + VTY_GET_INTEGER_RANGE ("", gtsm_hops, argv[1], 1, 254); + + return bgp_vty_return (vty, peer_ttl_security_hops_set (peer, gtsm_hops)); +} + +DEFUN (no_neighbor_ttl_security, + no_neighbor_ttl_security_cmd, + NO_NEIGHBOR_CMD2 "ttl-security hops <1-254>", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Specify the maximum number of hops to the BGP peer\n") +{ + struct peer *peer; + + peer = peer_and_group_lookup_vty (vty, argv[0]); + if (! peer) + return CMD_WARNING; + + return bgp_vty_return (vty, peer_ttl_security_hops_set (peer, 0)); +} + +/* Address family configuration. */ +DEFUN (address_family_ipv4, + address_family_ipv4_cmd, + "address-family ipv4", + "Enter Address Family command mode\n" + "Address family\n") +{ + vty->node = BGP_IPV4_NODE; + return CMD_SUCCESS; +} + +DEFUN (address_family_ipv4_safi, + address_family_ipv4_safi_cmd, + "address-family ipv4 (unicast|multicast)", + "Enter Address Family command mode\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n") +{ + if (strncmp (argv[0], "m", 1) == 0) + vty->node = BGP_IPV4M_NODE; + else + vty->node = BGP_IPV4_NODE; + + return CMD_SUCCESS; +} + +DEFUN (address_family_ipv6, + address_family_ipv6_cmd, + "address-family ipv6", + "Enter Address Family command mode\n" + "Address family\n") +{ + vty->node = BGP_IPV6_NODE; + return CMD_SUCCESS; +} + +DEFUN (address_family_ipv6_safi, + address_family_ipv6_safi_cmd, + "address-family ipv6 (unicast|multicast)", + "Enter Address Family command mode\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n") +{ + if (strncmp (argv[0], "m", 1) == 0) + vty->node = BGP_IPV6M_NODE; + else + vty->node = BGP_IPV6_NODE; + + return CMD_SUCCESS; +} + +DEFUN (address_family_vpnv4, + address_family_vpnv4_cmd, + "address-family vpnv4", + "Enter Address Family command mode\n" + "Address family\n") +{ + vty->node = BGP_VPNV4_NODE; + return CMD_SUCCESS; +} + +ALIAS (address_family_vpnv4, + address_family_vpnv4_unicast_cmd, + "address-family vpnv4 unicast", + "Enter Address Family command mode\n" + "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 +{ + clear_all, + clear_peer, + clear_group, + clear_external, + clear_as +}; + +static void +bgp_clear_vty_error (struct vty *vty, struct peer *peer, afi_t afi, + safi_t safi, int error) +{ + switch (error) + { + case BGP_ERR_AF_UNCONFIGURED: + vty_out (vty, + "%%BGP: Enable %s %s address family for the neighbor %s%s", + afi == AFI_IP6 ? "IPv6" : safi == SAFI_MPLS_VPN ? "VPNv4" : "IPv4", + safi == SAFI_MULTICAST ? "Multicast" : "Unicast", + peer->host, VTY_NEWLINE); + break; + case BGP_ERR_SOFT_RECONFIG_UNCONFIGURED: + vty_out (vty, "%%BGP: Inbound soft reconfig for %s not possible as it%s has neither refresh capability, nor inbound soft reconfig%s", peer->host, VTY_NEWLINE, VTY_NEWLINE); + break; + default: + break; + } +} + +/* `clear ip bgp' functions. */ +static int +bgp_clear (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, + enum clear_sort sort,enum bgp_clear_type stype, const char *arg) +{ + int ret; + struct peer *peer; + struct listnode *node, *nnode; + + /* Clear all neighbors. */ + if (sort == clear_all) + { + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + if (stype == BGP_CLEAR_SOFT_NONE) + ret = peer_clear (peer); + else + ret = peer_clear_soft (peer, afi, safi, stype); + + if (ret < 0) + bgp_clear_vty_error (vty, peer, afi, safi, ret); + } + return CMD_SUCCESS; + } + + /* Clear specified neighbors. */ + if (sort == clear_peer) + { + union sockunion su; + int ret; + + /* Make sockunion for lookup. */ + ret = str2sockunion (arg, &su); + if (ret < 0) + { + vty_out (vty, "Malformed address: %s%s", arg, VTY_NEWLINE); + return CMD_WARNING; + } + peer = peer_lookup (bgp, &su); + if (! peer) + { + vty_out (vty, "%%BGP: Unknown neighbor - \"%s\"%s", arg, VTY_NEWLINE); + return CMD_WARNING; + } + + if (stype == BGP_CLEAR_SOFT_NONE) + ret = peer_clear (peer); + else + ret = peer_clear_soft (peer, afi, safi, stype); + + if (ret < 0) + bgp_clear_vty_error (vty, peer, afi, safi, ret); + + return CMD_SUCCESS; + } + + /* Clear all peer-group members. */ + if (sort == clear_group) + { + struct peer_group *group; + + group = peer_group_lookup (bgp, arg); + if (! group) + { + vty_out (vty, "%%BGP: No such peer-group %s%s", arg, VTY_NEWLINE); + return CMD_WARNING; + } + + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + if (stype == BGP_CLEAR_SOFT_NONE) + { + ret = peer_clear (peer); + continue; + } + + if (! peer->af_group[afi][safi]) + continue; + + ret = peer_clear_soft (peer, afi, safi, stype); + + if (ret < 0) + bgp_clear_vty_error (vty, peer, afi, safi, ret); + } + return CMD_SUCCESS; + } + + if (sort == clear_external) + { + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + if (peer->sort == BGP_PEER_IBGP) + continue; + + if (stype == BGP_CLEAR_SOFT_NONE) + ret = peer_clear (peer); + else + ret = peer_clear_soft (peer, afi, safi, stype); + + if (ret < 0) + bgp_clear_vty_error (vty, peer, afi, safi, ret); + } + return CMD_SUCCESS; + } + + if (sort == clear_as) + { + as_t as; + int find = 0; + + VTY_GET_INTEGER_RANGE ("AS", as, arg, 1, BGP_AS4_MAX); + + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + if (peer->as != as) + continue; + + find = 1; + if (stype == BGP_CLEAR_SOFT_NONE) + ret = peer_clear (peer); + else + ret = peer_clear_soft (peer, afi, safi, stype); + + if (ret < 0) + bgp_clear_vty_error (vty, peer, afi, safi, ret); + } + if (! find) + vty_out (vty, "%%BGP: No peer is configured with AS %s%s", arg, + VTY_NEWLINE); + return CMD_SUCCESS; + } + + 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, + const char *arg) +{ + struct bgp *bgp; + + /* BGP structure lookup. */ + if (name) + { + bgp = bgp_lookup_by_name (name); + if (bgp == NULL) + { + vty_out (vty, "Can't find BGP view %s%s", 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; + } + } + + return bgp_clear (vty, bgp, afi, safi, sort, stype, arg); +} + +DEFUN (clear_ip_bgp_all, + clear_ip_bgp_all_cmd, + "clear ip bgp *", + CLEAR_STR + IP_STR + BGP_STR + "Clear all peers\n") +{ + if (argc == 1) + return bgp_clear_vty (vty, argv[0], 0, 0, clear_all, BGP_CLEAR_SOFT_NONE, NULL); + + return bgp_clear_vty (vty, NULL, 0, 0, clear_all, BGP_CLEAR_SOFT_NONE, NULL); +} + +ALIAS (clear_ip_bgp_all, + clear_bgp_all_cmd, + "clear bgp *", + CLEAR_STR + BGP_STR + "Clear all peers\n") + +ALIAS (clear_ip_bgp_all, + clear_bgp_ipv6_all_cmd, + "clear bgp ipv6 *", + CLEAR_STR + BGP_STR + "Address family\n" + "Clear all peers\n") + +ALIAS (clear_ip_bgp_all, + clear_ip_bgp_instance_all_cmd, + "clear ip bgp view WORD *", + CLEAR_STR + IP_STR + BGP_STR + "BGP view\n" + "view name\n" + "Clear all peers\n") + +ALIAS (clear_ip_bgp_all, + clear_bgp_instance_all_cmd, + "clear bgp view WORD *", + CLEAR_STR + BGP_STR + "BGP view\n" + "view name\n" + "Clear all peers\n") + +DEFUN (clear_ip_bgp_peer, + clear_ip_bgp_peer_cmd, + "clear ip bgp (A.B.C.D|X:X::X:X)", + CLEAR_STR + IP_STR + BGP_STR + "BGP neighbor IP address to clear\n" + "BGP IPv6 neighbor to clear\n") +{ + return bgp_clear_vty (vty, NULL, 0, 0, clear_peer, BGP_CLEAR_SOFT_NONE, argv[0]); +} + +ALIAS (clear_ip_bgp_peer, + clear_bgp_peer_cmd, + "clear bgp (A.B.C.D|X:X::X:X)", + CLEAR_STR + BGP_STR + "BGP neighbor address to clear\n" + "BGP IPv6 neighbor to clear\n") + +ALIAS (clear_ip_bgp_peer, + clear_bgp_ipv6_peer_cmd, + "clear bgp ipv6 (A.B.C.D|X:X::X:X)", + CLEAR_STR + BGP_STR + "Address family\n" + "BGP neighbor address to clear\n" + "BGP IPv6 neighbor to clear\n") + +DEFUN (clear_ip_bgp_peer_group, + clear_ip_bgp_peer_group_cmd, + "clear ip bgp peer-group WORD", + CLEAR_STR + IP_STR + BGP_STR + "Clear all members of peer-group\n" + "BGP peer-group name\n") +{ + return bgp_clear_vty (vty, NULL, 0, 0, clear_group, BGP_CLEAR_SOFT_NONE, argv[0]); +} + +ALIAS (clear_ip_bgp_peer_group, + clear_bgp_peer_group_cmd, + "clear bgp peer-group WORD", + CLEAR_STR + BGP_STR + "Clear all members of peer-group\n" + "BGP peer-group name\n") + +ALIAS (clear_ip_bgp_peer_group, + clear_bgp_ipv6_peer_group_cmd, + "clear bgp ipv6 peer-group WORD", + CLEAR_STR + BGP_STR + "Address family\n" + "Clear all members of peer-group\n" + "BGP peer-group name\n") + +DEFUN (clear_ip_bgp_external, + clear_ip_bgp_external_cmd, + "clear ip bgp external", + CLEAR_STR + IP_STR + BGP_STR + "Clear all external peers\n") +{ + return bgp_clear_vty (vty, NULL, 0, 0, clear_external, BGP_CLEAR_SOFT_NONE, NULL); +} + +ALIAS (clear_ip_bgp_external, + clear_bgp_external_cmd, + "clear bgp external", + CLEAR_STR + BGP_STR + "Clear all external peers\n") + +ALIAS (clear_ip_bgp_external, + clear_bgp_ipv6_external_cmd, + "clear bgp ipv6 external", + CLEAR_STR + BGP_STR + "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, + CLEAR_STR + IP_STR + BGP_STR + "Clear peers with the AS number\n") +{ + return bgp_clear_vty (vty, NULL, 0, 0, clear_as, BGP_CLEAR_SOFT_NONE, argv[0]); +} + +ALIAS (clear_ip_bgp_as, + clear_bgp_as_cmd, + "clear bgp " CMD_AS_RANGE, + CLEAR_STR + BGP_STR + "Clear peers with the AS number\n") + +ALIAS (clear_ip_bgp_as, + clear_bgp_ipv6_as_cmd, + "clear bgp ipv6 " CMD_AS_RANGE, + CLEAR_STR + 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, + "clear ip bgp * soft out", + CLEAR_STR + IP_STR + BGP_STR + "Clear all peers\n" + BGP_SOFT_STR + BGP_SOFT_OUT_STR) +{ + if (argc == 1) + return bgp_clear_vty (vty, argv[0], AFI_IP, SAFI_UNICAST, clear_all, + BGP_CLEAR_SOFT_OUT, NULL); + + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_all, + BGP_CLEAR_SOFT_OUT, NULL); +} + +ALIAS (clear_ip_bgp_all_soft_out, + clear_ip_bgp_all_out_cmd, + "clear ip bgp * out", + CLEAR_STR + IP_STR + BGP_STR + "Clear all peers\n" + BGP_SOFT_OUT_STR) + +ALIAS (clear_ip_bgp_all_soft_out, + clear_ip_bgp_instance_all_soft_out_cmd, + "clear ip bgp view WORD * soft out", + CLEAR_STR + IP_STR + BGP_STR + "BGP view\n" + "view name\n" + "Clear all peers\n" + BGP_SOFT_STR + BGP_SOFT_OUT_STR) + +DEFUN (clear_ip_bgp_all_ipv4_soft_out, + clear_ip_bgp_all_ipv4_soft_out_cmd, + "clear ip bgp * ipv4 (unicast|multicast) soft out", + CLEAR_STR + IP_STR + BGP_STR + "Clear all peers\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\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, + BGP_CLEAR_SOFT_OUT, NULL); + + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_all, + BGP_CLEAR_SOFT_OUT, NULL); +} + +ALIAS (clear_ip_bgp_all_ipv4_soft_out, + clear_ip_bgp_all_ipv4_out_cmd, + "clear ip bgp * ipv4 (unicast|multicast) out", + CLEAR_STR + IP_STR + BGP_STR + "Clear all peers\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + BGP_SOFT_OUT_STR) + +DEFUN (clear_ip_bgp_instance_all_ipv4_soft_out, + clear_ip_bgp_instance_all_ipv4_soft_out_cmd, + "clear ip bgp view WORD * ipv4 (unicast|multicast) soft out", + CLEAR_STR + IP_STR + BGP_STR + "BGP view\n" + "view name\n" + "Clear all peers\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\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, + BGP_CLEAR_SOFT_OUT, NULL); + + return bgp_clear_vty (vty, argv[0], AFI_IP, SAFI_UNICAST, clear_all, + BGP_CLEAR_SOFT_OUT, NULL); +} + +DEFUN (clear_ip_bgp_all_vpnv4_soft_out, + clear_ip_bgp_all_vpnv4_soft_out_cmd, + "clear ip bgp * vpnv4 unicast soft out", + CLEAR_STR + IP_STR + BGP_STR + "Clear all peers\n" + "Address family\n" + "Address Family Modifier\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); +} + +ALIAS (clear_ip_bgp_all_vpnv4_soft_out, + clear_ip_bgp_all_vpnv4_out_cmd, + "clear ip bgp * vpnv4 unicast out", + CLEAR_STR + IP_STR + BGP_STR + "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, + clear_bgp_all_soft_out_cmd, + "clear bgp * soft out", + CLEAR_STR + BGP_STR + "Clear all peers\n" + BGP_SOFT_STR + BGP_SOFT_OUT_STR) +{ + if (argc == 1) + return bgp_clear_vty (vty, argv[0], AFI_IP6, SAFI_UNICAST, clear_all, + BGP_CLEAR_SOFT_OUT, NULL); + + return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_all, + BGP_CLEAR_SOFT_OUT, NULL); +} + +ALIAS (clear_bgp_all_soft_out, + clear_bgp_instance_all_soft_out_cmd, + "clear bgp view WORD * soft out", + CLEAR_STR + BGP_STR + "BGP view\n" + "view name\n" + "Clear all peers\n" + BGP_SOFT_STR + BGP_SOFT_OUT_STR) + +ALIAS (clear_bgp_all_soft_out, + clear_bgp_all_out_cmd, + "clear bgp * out", + CLEAR_STR + BGP_STR + "Clear all peers\n" + BGP_SOFT_OUT_STR) + +ALIAS (clear_bgp_all_soft_out, + clear_bgp_ipv6_all_soft_out_cmd, + "clear bgp ipv6 * soft out", + CLEAR_STR + BGP_STR + "Address family\n" + "Clear all peers\n" + BGP_SOFT_STR + BGP_SOFT_OUT_STR) + +ALIAS (clear_bgp_all_soft_out, + clear_bgp_ipv6_all_out_cmd, + "clear bgp ipv6 * out", + CLEAR_STR + BGP_STR + "Address family\n" + "Clear all peers\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, + "clear ip bgp A.B.C.D soft out", + CLEAR_STR + IP_STR + BGP_STR + "BGP neighbor address to clear\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]); +} + +ALIAS (clear_ip_bgp_peer_soft_out, + clear_ip_bgp_peer_out_cmd, + "clear ip bgp A.B.C.D out", + CLEAR_STR + IP_STR + BGP_STR + "BGP neighbor address to clear\n" + BGP_SOFT_OUT_STR) + +DEFUN (clear_ip_bgp_peer_ipv4_soft_out, + clear_ip_bgp_peer_ipv4_soft_out_cmd, + "clear ip bgp A.B.C.D ipv4 (unicast|multicast) soft out", + CLEAR_STR + IP_STR + BGP_STR + "BGP neighbor address to clear\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\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, + BGP_CLEAR_SOFT_OUT, argv[0]); + + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_peer, + BGP_CLEAR_SOFT_OUT, argv[0]); +} + +ALIAS (clear_ip_bgp_peer_ipv4_soft_out, + clear_ip_bgp_peer_ipv4_out_cmd, + "clear ip bgp A.B.C.D ipv4 (unicast|multicast) out", + CLEAR_STR + IP_STR + BGP_STR + "BGP neighbor address to clear\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + BGP_SOFT_OUT_STR) + +DEFUN (clear_ip_bgp_peer_vpnv4_soft_out, + clear_ip_bgp_peer_vpnv4_soft_out_cmd, + "clear ip bgp A.B.C.D vpnv4 unicast soft out", + CLEAR_STR + IP_STR + BGP_STR + "BGP neighbor address to clear\n" + "Address family\n" + "Address Family Modifier\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]); +} + +ALIAS (clear_ip_bgp_peer_vpnv4_soft_out, + clear_ip_bgp_peer_vpnv4_out_cmd, + "clear ip bgp A.B.C.D vpnv4 unicast out", + CLEAR_STR + IP_STR + BGP_STR + "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, + clear_bgp_peer_soft_out_cmd, + "clear bgp (A.B.C.D|X:X::X:X) soft out", + CLEAR_STR + BGP_STR + "BGP neighbor address to clear\n" + "BGP IPv6 neighbor to clear\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]); +} + +ALIAS (clear_bgp_peer_soft_out, + clear_bgp_ipv6_peer_soft_out_cmd, + "clear bgp ipv6 (A.B.C.D|X:X::X:X) soft out", + CLEAR_STR + BGP_STR + "Address family\n" + "BGP neighbor address to clear\n" + "BGP IPv6 neighbor to clear\n" + BGP_SOFT_STR + BGP_SOFT_OUT_STR) + +ALIAS (clear_bgp_peer_soft_out, + clear_bgp_peer_out_cmd, + "clear bgp (A.B.C.D|X:X::X:X) out", + CLEAR_STR + BGP_STR + "BGP neighbor address to clear\n" + "BGP IPv6 neighbor to clear\n" + BGP_SOFT_OUT_STR) + +ALIAS (clear_bgp_peer_soft_out, + clear_bgp_ipv6_peer_out_cmd, + "clear bgp ipv6 (A.B.C.D|X:X::X:X) out", + CLEAR_STR + BGP_STR + "Address family\n" + "BGP neighbor address to clear\n" + "BGP IPv6 neighbor to clear\n" + BGP_SOFT_OUT_STR) + +DEFUN (clear_ip_bgp_peer_group_soft_out, + clear_ip_bgp_peer_group_soft_out_cmd, + "clear ip bgp peer-group WORD soft out", + CLEAR_STR + IP_STR + BGP_STR + "Clear all members of peer-group\n" + "BGP peer-group name\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]); +} + +ALIAS (clear_ip_bgp_peer_group_soft_out, + clear_ip_bgp_peer_group_out_cmd, + "clear ip bgp peer-group WORD out", + CLEAR_STR + IP_STR + BGP_STR + "Clear all members of peer-group\n" + "BGP peer-group name\n" + BGP_SOFT_OUT_STR) + +DEFUN (clear_ip_bgp_peer_group_ipv4_soft_out, + clear_ip_bgp_peer_group_ipv4_soft_out_cmd, + "clear ip bgp peer-group WORD ipv4 (unicast|multicast) soft out", + CLEAR_STR + IP_STR + BGP_STR + "Clear all members of peer-group\n" + "BGP peer-group name\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\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, + BGP_CLEAR_SOFT_OUT, argv[0]); + + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_group, + BGP_CLEAR_SOFT_OUT, argv[0]); +} + +ALIAS (clear_ip_bgp_peer_group_ipv4_soft_out, + clear_ip_bgp_peer_group_ipv4_out_cmd, + "clear ip bgp peer-group WORD ipv4 (unicast|multicast) out", + CLEAR_STR + IP_STR + BGP_STR + "Clear all members of peer-group\n" + "BGP peer-group name\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + BGP_SOFT_OUT_STR) + +DEFUN (clear_bgp_peer_group_soft_out, + clear_bgp_peer_group_soft_out_cmd, + "clear bgp peer-group WORD soft out", + CLEAR_STR + BGP_STR + "Clear all members of peer-group\n" + "BGP peer-group name\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]); +} + +ALIAS (clear_bgp_peer_group_soft_out, + clear_bgp_ipv6_peer_group_soft_out_cmd, + "clear bgp ipv6 peer-group WORD soft out", + CLEAR_STR + BGP_STR + "Address family\n" + "Clear all members of peer-group\n" + "BGP peer-group name\n" + BGP_SOFT_STR + BGP_SOFT_OUT_STR) + +ALIAS (clear_bgp_peer_group_soft_out, + clear_bgp_peer_group_out_cmd, + "clear bgp peer-group WORD out", + CLEAR_STR + BGP_STR + "Clear all members of peer-group\n" + "BGP peer-group name\n" + BGP_SOFT_OUT_STR) + +ALIAS (clear_bgp_peer_group_soft_out, + clear_bgp_ipv6_peer_group_out_cmd, + "clear bgp ipv6 peer-group WORD out", + CLEAR_STR + BGP_STR + "Address family\n" + "Clear all members of peer-group\n" + "BGP peer-group name\n" + BGP_SOFT_OUT_STR) + +DEFUN (clear_ip_bgp_external_soft_out, + clear_ip_bgp_external_soft_out_cmd, + "clear ip bgp external soft out", + CLEAR_STR + IP_STR + BGP_STR + "Clear all external peers\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); +} + +ALIAS (clear_ip_bgp_external_soft_out, + clear_ip_bgp_external_out_cmd, + "clear ip bgp external out", + CLEAR_STR + IP_STR + BGP_STR + "Clear all external peers\n" + BGP_SOFT_OUT_STR) + +DEFUN (clear_ip_bgp_external_ipv4_soft_out, + clear_ip_bgp_external_ipv4_soft_out_cmd, + "clear ip bgp external ipv4 (unicast|multicast) soft out", + CLEAR_STR + IP_STR + BGP_STR + "Clear all external peers\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\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, + BGP_CLEAR_SOFT_OUT, NULL); + + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_external, + BGP_CLEAR_SOFT_OUT, NULL); +} + +ALIAS (clear_ip_bgp_external_ipv4_soft_out, + clear_ip_bgp_external_ipv4_out_cmd, + "clear ip bgp external ipv4 (unicast|multicast) out", + CLEAR_STR + IP_STR + BGP_STR + "Clear all external peers\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + BGP_SOFT_OUT_STR) + +DEFUN (clear_bgp_external_soft_out, + clear_bgp_external_soft_out_cmd, + "clear bgp external soft out", + CLEAR_STR + BGP_STR + "Clear all external peers\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); +} + +ALIAS (clear_bgp_external_soft_out, + clear_bgp_ipv6_external_soft_out_cmd, + "clear bgp ipv6 external soft out", + CLEAR_STR + BGP_STR + "Address family\n" + "Clear all external peers\n" + BGP_SOFT_STR + BGP_SOFT_OUT_STR) + +ALIAS (clear_bgp_external_soft_out, + clear_bgp_external_out_cmd, + "clear bgp external out", + CLEAR_STR + BGP_STR + "Clear all external peers\n" + BGP_SOFT_OUT_STR) + +ALIAS (clear_bgp_external_soft_out, + clear_bgp_ipv6_external_out_cmd, + "clear bgp ipv6 external WORD out", + CLEAR_STR + BGP_STR + "Address family\n" + "Clear all external peers\n" + BGP_SOFT_OUT_STR) + +DEFUN (clear_ip_bgp_as_soft_out, + clear_ip_bgp_as_soft_out_cmd, + "clear ip bgp " CMD_AS_RANGE " soft out", + CLEAR_STR + IP_STR + BGP_STR + "Clear peers with the AS number\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]); +} + +ALIAS (clear_ip_bgp_as_soft_out, + clear_ip_bgp_as_out_cmd, + "clear ip bgp " CMD_AS_RANGE " out", + CLEAR_STR + IP_STR + BGP_STR + "Clear peers with the AS number\n" + BGP_SOFT_OUT_STR) + +DEFUN (clear_ip_bgp_as_ipv4_soft_out, + clear_ip_bgp_as_ipv4_soft_out_cmd, + "clear ip bgp " CMD_AS_RANGE " ipv4 (unicast|multicast) soft out", + CLEAR_STR + IP_STR + BGP_STR + "Clear peers with the AS number\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\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, + BGP_CLEAR_SOFT_OUT, argv[0]); + + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_as, + BGP_CLEAR_SOFT_OUT, argv[0]); +} + +ALIAS (clear_ip_bgp_as_ipv4_soft_out, + clear_ip_bgp_as_ipv4_out_cmd, + "clear ip bgp " CMD_AS_RANGE " ipv4 (unicast|multicast) out", + CLEAR_STR + IP_STR + BGP_STR + "Clear peers with the AS number\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + BGP_SOFT_OUT_STR) + +DEFUN (clear_ip_bgp_as_vpnv4_soft_out, + clear_ip_bgp_as_vpnv4_soft_out_cmd, + "clear ip bgp " CMD_AS_RANGE " vpnv4 unicast soft out", + CLEAR_STR + IP_STR + BGP_STR + "Clear peers with the AS number\n" + "Address family\n" + "Address Family modifier\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]); +} + +ALIAS (clear_ip_bgp_as_vpnv4_soft_out, + clear_ip_bgp_as_vpnv4_out_cmd, + "clear ip bgp " CMD_AS_RANGE " vpnv4 unicast out", + CLEAR_STR + IP_STR + BGP_STR + "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, + clear_bgp_as_soft_out_cmd, + "clear bgp " CMD_AS_RANGE " soft out", + CLEAR_STR + BGP_STR + "Clear peers with the AS number\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]); +} + +ALIAS (clear_bgp_as_soft_out, + clear_bgp_ipv6_as_soft_out_cmd, + "clear bgp ipv6 " CMD_AS_RANGE " soft out", + CLEAR_STR + BGP_STR + "Address family\n" + "Clear peers with the AS number\n" + BGP_SOFT_STR + BGP_SOFT_OUT_STR) + +ALIAS (clear_bgp_as_soft_out, + clear_bgp_as_out_cmd, + "clear bgp " CMD_AS_RANGE " out", + CLEAR_STR + BGP_STR + "Clear peers with the AS number\n" + BGP_SOFT_OUT_STR) + +ALIAS (clear_bgp_as_soft_out, + clear_bgp_ipv6_as_out_cmd, + "clear bgp ipv6 " CMD_AS_RANGE " out", + CLEAR_STR + BGP_STR + "Address family\n" + "Clear peers with the AS number\n" + BGP_SOFT_OUT_STR) + +/* Inbound soft-reconfiguration */ +DEFUN (clear_ip_bgp_all_soft_in, + clear_ip_bgp_all_soft_in_cmd, + "clear ip bgp * soft in", + CLEAR_STR + IP_STR + BGP_STR + "Clear all peers\n" + BGP_SOFT_STR + BGP_SOFT_IN_STR) +{ + if (argc == 1) + return bgp_clear_vty (vty, argv[0], AFI_IP, SAFI_UNICAST, clear_all, + BGP_CLEAR_SOFT_IN, NULL); + + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_all, + BGP_CLEAR_SOFT_IN, NULL); +} + +ALIAS (clear_ip_bgp_all_soft_in, + clear_ip_bgp_instance_all_soft_in_cmd, + "clear ip bgp view WORD * soft in", + CLEAR_STR + IP_STR + BGP_STR + "BGP view\n" + "view name\n" + "Clear all peers\n" + BGP_SOFT_STR + BGP_SOFT_IN_STR) + +ALIAS (clear_ip_bgp_all_soft_in, + clear_ip_bgp_all_in_cmd, + "clear ip bgp * in", + CLEAR_STR + IP_STR + BGP_STR + "Clear all peers\n" + BGP_SOFT_IN_STR) + +DEFUN (clear_ip_bgp_all_in_prefix_filter, + clear_ip_bgp_all_in_prefix_filter_cmd, + "clear ip bgp * in prefix-filter", + CLEAR_STR + IP_STR + BGP_STR + "Clear all peers\n" + BGP_SOFT_IN_STR + "Push out prefix-list ORF and do inbound soft reconfig\n") +{ + if (argc== 1) + return bgp_clear_vty (vty, argv[0], AFI_IP, SAFI_UNICAST, clear_all, + BGP_CLEAR_SOFT_IN_ORF_PREFIX, NULL); + + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_all, + BGP_CLEAR_SOFT_IN_ORF_PREFIX, NULL); +} + +ALIAS (clear_ip_bgp_all_in_prefix_filter, + clear_ip_bgp_instance_all_in_prefix_filter_cmd, + "clear ip bgp view WORD * in prefix-filter", + CLEAR_STR + IP_STR + BGP_STR + "BGP view\n" + "view name\n" + "Clear all peers\n" + BGP_SOFT_IN_STR + "Push out prefix-list ORF and do inbound soft reconfig\n") + + +DEFUN (clear_ip_bgp_all_ipv4_soft_in, + clear_ip_bgp_all_ipv4_soft_in_cmd, + "clear ip bgp * ipv4 (unicast|multicast) soft in", + CLEAR_STR + IP_STR + BGP_STR + "Clear all peers\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\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, + BGP_CLEAR_SOFT_IN, NULL); + + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_all, + BGP_CLEAR_SOFT_IN, NULL); +} + +ALIAS (clear_ip_bgp_all_ipv4_soft_in, + clear_ip_bgp_all_ipv4_in_cmd, + "clear ip bgp * ipv4 (unicast|multicast) in", + CLEAR_STR + IP_STR + BGP_STR + "Clear all peers\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + BGP_SOFT_IN_STR) + +DEFUN (clear_ip_bgp_instance_all_ipv4_soft_in, + clear_ip_bgp_instance_all_ipv4_soft_in_cmd, + "clear ip bgp view WORD * ipv4 (unicast|multicast) soft in", + CLEAR_STR + IP_STR + BGP_STR + "BGP view\n" + "view name\n" + "Clear all peers\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\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, + BGP_CLEAR_SOFT_IN, NULL); + + return bgp_clear_vty (vty, argv[0], AFI_IP, SAFI_UNICAST, clear_all, + BGP_CLEAR_SOFT_IN, NULL); +} + +DEFUN (clear_ip_bgp_all_ipv4_in_prefix_filter, + clear_ip_bgp_all_ipv4_in_prefix_filter_cmd, + "clear ip bgp * ipv4 (unicast|multicast) in prefix-filter", + CLEAR_STR + IP_STR + BGP_STR + "Clear all peers\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + BGP_SOFT_IN_STR + "Push out prefix-list ORF and do inbound soft reconfig\n") +{ + if (strncmp (argv[0], "m", 1) == 0) + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_all, + BGP_CLEAR_SOFT_IN_ORF_PREFIX, NULL); + + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_all, + BGP_CLEAR_SOFT_IN_ORF_PREFIX, NULL); +} + +DEFUN (clear_ip_bgp_instance_all_ipv4_in_prefix_filter, + clear_ip_bgp_instance_all_ipv4_in_prefix_filter_cmd, + "clear ip bgp view WORD * ipv4 (unicast|multicast) in prefix-filter", + CLEAR_STR + IP_STR + BGP_STR + "Clear all peers\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + BGP_SOFT_IN_STR + "Push out prefix-list ORF and do inbound soft reconfig\n") +{ + if (strncmp (argv[1], "m", 1) == 0) + return bgp_clear_vty (vty, argv[0], AFI_IP, SAFI_MULTICAST, clear_all, + BGP_CLEAR_SOFT_IN_ORF_PREFIX, NULL); + + return bgp_clear_vty (vty, argv[0], AFI_IP, SAFI_UNICAST, clear_all, + BGP_CLEAR_SOFT_IN_ORF_PREFIX, NULL); +} + +DEFUN (clear_ip_bgp_all_vpnv4_soft_in, + clear_ip_bgp_all_vpnv4_soft_in_cmd, + "clear ip bgp * vpnv4 unicast soft in", + CLEAR_STR + IP_STR + BGP_STR + "Clear all peers\n" + "Address family\n" + "Address Family Modifier\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); +} + +ALIAS (clear_ip_bgp_all_vpnv4_soft_in, + clear_ip_bgp_all_vpnv4_in_cmd, + "clear ip bgp * vpnv4 unicast in", + CLEAR_STR + IP_STR + BGP_STR + "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, + clear_bgp_all_soft_in_cmd, + "clear bgp * soft in", + CLEAR_STR + BGP_STR + "Clear all peers\n" + BGP_SOFT_STR + BGP_SOFT_IN_STR) +{ + if (argc == 1) + return bgp_clear_vty (vty, argv[0], AFI_IP6, SAFI_UNICAST, clear_all, + BGP_CLEAR_SOFT_IN, NULL); + + return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_all, + BGP_CLEAR_SOFT_IN, NULL); +} + +ALIAS (clear_bgp_all_soft_in, + clear_bgp_instance_all_soft_in_cmd, + "clear bgp view WORD * soft in", + CLEAR_STR + BGP_STR + "BGP view\n" + "view name\n" + "Clear all peers\n" + BGP_SOFT_STR + BGP_SOFT_IN_STR) + +ALIAS (clear_bgp_all_soft_in, + clear_bgp_ipv6_all_soft_in_cmd, + "clear bgp ipv6 * soft in", + CLEAR_STR + BGP_STR + "Address family\n" + "Clear all peers\n" + BGP_SOFT_STR + BGP_SOFT_IN_STR) + +ALIAS (clear_bgp_all_soft_in, + clear_bgp_all_in_cmd, + "clear bgp * in", + CLEAR_STR + BGP_STR + "Clear all peers\n" + BGP_SOFT_IN_STR) + +ALIAS (clear_bgp_all_soft_in, + clear_bgp_ipv6_all_in_cmd, + "clear bgp ipv6 * in", + CLEAR_STR + BGP_STR + "Address family\n" + "Clear all peers\n" + BGP_SOFT_IN_STR) + +DEFUN (clear_bgp_all_in_prefix_filter, + clear_bgp_all_in_prefix_filter_cmd, + "clear bgp * in prefix-filter", + CLEAR_STR + BGP_STR + "Clear all peers\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, + BGP_CLEAR_SOFT_IN_ORF_PREFIX, NULL); +} + +ALIAS (clear_bgp_all_in_prefix_filter, + clear_bgp_ipv6_all_in_prefix_filter_cmd, + "clear bgp ipv6 * in prefix-filter", + CLEAR_STR + BGP_STR + "Address family\n" + "Clear all peers\n" + BGP_SOFT_IN_STR + "Push out prefix-list ORF and do inbound soft reconfig\n") + +DEFUN (clear_ip_bgp_peer_soft_in, + clear_ip_bgp_peer_soft_in_cmd, + "clear ip bgp A.B.C.D soft in", + CLEAR_STR + IP_STR + BGP_STR + "BGP neighbor address to clear\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]); +} + +ALIAS (clear_ip_bgp_peer_soft_in, + clear_ip_bgp_peer_in_cmd, + "clear ip bgp A.B.C.D in", + CLEAR_STR + IP_STR + BGP_STR + "BGP neighbor address to clear\n" + BGP_SOFT_IN_STR) + +DEFUN (clear_ip_bgp_peer_in_prefix_filter, + clear_ip_bgp_peer_in_prefix_filter_cmd, + "clear ip bgp A.B.C.D in prefix-filter", + CLEAR_STR + IP_STR + BGP_STR + "BGP neighbor address to clear\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, + BGP_CLEAR_SOFT_IN_ORF_PREFIX, argv[0]); +} + +DEFUN (clear_ip_bgp_peer_ipv4_soft_in, + clear_ip_bgp_peer_ipv4_soft_in_cmd, + "clear ip bgp A.B.C.D ipv4 (unicast|multicast) soft in", + CLEAR_STR + IP_STR + BGP_STR + "BGP neighbor address to clear\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\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, + BGP_CLEAR_SOFT_IN, argv[0]); + + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_peer, + BGP_CLEAR_SOFT_IN, argv[0]); +} + +ALIAS (clear_ip_bgp_peer_ipv4_soft_in, + clear_ip_bgp_peer_ipv4_in_cmd, + "clear ip bgp A.B.C.D ipv4 (unicast|multicast) in", + CLEAR_STR + IP_STR + BGP_STR + "BGP neighbor address to clear\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + BGP_SOFT_IN_STR) + +DEFUN (clear_ip_bgp_peer_ipv4_in_prefix_filter, + clear_ip_bgp_peer_ipv4_in_prefix_filter_cmd, + "clear ip bgp A.B.C.D ipv4 (unicast|multicast) in prefix-filter", + CLEAR_STR + IP_STR + BGP_STR + "BGP neighbor address to clear\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + BGP_SOFT_IN_STR + "Push out the existing ORF prefix-list\n") +{ + if (strncmp (argv[1], "m", 1) == 0) + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_peer, + BGP_CLEAR_SOFT_IN_ORF_PREFIX, argv[0]); + + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_peer, + BGP_CLEAR_SOFT_IN_ORF_PREFIX, argv[0]); +} + +DEFUN (clear_ip_bgp_peer_vpnv4_soft_in, + clear_ip_bgp_peer_vpnv4_soft_in_cmd, + "clear ip bgp A.B.C.D vpnv4 unicast soft in", + CLEAR_STR + IP_STR + BGP_STR + "BGP neighbor address to clear\n" + "Address family\n" + "Address Family Modifier\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]); +} + +ALIAS (clear_ip_bgp_peer_vpnv4_soft_in, + clear_ip_bgp_peer_vpnv4_in_cmd, + "clear ip bgp A.B.C.D vpnv4 unicast in", + CLEAR_STR + IP_STR + BGP_STR + "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, + clear_bgp_peer_soft_in_cmd, + "clear bgp (A.B.C.D|X:X::X:X) soft in", + CLEAR_STR + BGP_STR + "BGP neighbor address to clear\n" + "BGP IPv6 neighbor to clear\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]); +} + +ALIAS (clear_bgp_peer_soft_in, + clear_bgp_ipv6_peer_soft_in_cmd, + "clear bgp ipv6 (A.B.C.D|X:X::X:X) soft in", + CLEAR_STR + BGP_STR + "Address family\n" + "BGP neighbor address to clear\n" + "BGP IPv6 neighbor to clear\n" + BGP_SOFT_STR + BGP_SOFT_IN_STR) + +ALIAS (clear_bgp_peer_soft_in, + clear_bgp_peer_in_cmd, + "clear bgp (A.B.C.D|X:X::X:X) in", + CLEAR_STR + BGP_STR + "BGP neighbor address to clear\n" + "BGP IPv6 neighbor to clear\n" + BGP_SOFT_IN_STR) + +ALIAS (clear_bgp_peer_soft_in, + clear_bgp_ipv6_peer_in_cmd, + "clear bgp ipv6 (A.B.C.D|X:X::X:X) in", + CLEAR_STR + BGP_STR + "Address family\n" + "BGP neighbor address to clear\n" + "BGP IPv6 neighbor to clear\n" + BGP_SOFT_IN_STR) + +DEFUN (clear_bgp_peer_in_prefix_filter, + clear_bgp_peer_in_prefix_filter_cmd, + "clear bgp (A.B.C.D|X:X::X:X) in prefix-filter", + CLEAR_STR + BGP_STR + "BGP neighbor address to clear\n" + "BGP IPv6 neighbor to clear\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, + BGP_CLEAR_SOFT_IN_ORF_PREFIX, argv[0]); +} + +ALIAS (clear_bgp_peer_in_prefix_filter, + clear_bgp_ipv6_peer_in_prefix_filter_cmd, + "clear bgp ipv6 (A.B.C.D|X:X::X:X) in prefix-filter", + CLEAR_STR + BGP_STR + "Address family\n" + "BGP neighbor address to clear\n" + "BGP IPv6 neighbor to clear\n" + BGP_SOFT_IN_STR + "Push out the existing ORF prefix-list\n") + +DEFUN (clear_ip_bgp_peer_group_soft_in, + clear_ip_bgp_peer_group_soft_in_cmd, + "clear ip bgp peer-group WORD soft in", + CLEAR_STR + IP_STR + BGP_STR + "Clear all members of peer-group\n" + "BGP peer-group name\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]); +} + +ALIAS (clear_ip_bgp_peer_group_soft_in, + clear_ip_bgp_peer_group_in_cmd, + "clear ip bgp peer-group WORD in", + CLEAR_STR + IP_STR + BGP_STR + "Clear all members of peer-group\n" + "BGP peer-group name\n" + BGP_SOFT_IN_STR) + +DEFUN (clear_ip_bgp_peer_group_in_prefix_filter, + clear_ip_bgp_peer_group_in_prefix_filter_cmd, + "clear ip bgp peer-group WORD in prefix-filter", + CLEAR_STR + IP_STR + BGP_STR + "Clear all members of peer-group\n" + "BGP peer-group name\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, + BGP_CLEAR_SOFT_IN_ORF_PREFIX, argv[0]); +} + +DEFUN (clear_ip_bgp_peer_group_ipv4_soft_in, + clear_ip_bgp_peer_group_ipv4_soft_in_cmd, + "clear ip bgp peer-group WORD ipv4 (unicast|multicast) soft in", + CLEAR_STR + IP_STR + BGP_STR + "Clear all members of peer-group\n" + "BGP peer-group name\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\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, + BGP_CLEAR_SOFT_IN, argv[0]); + + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_group, + BGP_CLEAR_SOFT_IN, argv[0]); +} + +ALIAS (clear_ip_bgp_peer_group_ipv4_soft_in, + clear_ip_bgp_peer_group_ipv4_in_cmd, + "clear ip bgp peer-group WORD ipv4 (unicast|multicast) in", + CLEAR_STR + IP_STR + BGP_STR + "Clear all members of peer-group\n" + "BGP peer-group name\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\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, + "clear ip bgp peer-group WORD ipv4 (unicast|multicast) in prefix-filter", + CLEAR_STR + IP_STR + BGP_STR + "Clear all members of peer-group\n" + "BGP peer-group name\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + BGP_SOFT_IN_STR + "Push out prefix-list ORF and do inbound soft reconfig\n") +{ + if (strncmp (argv[1], "m", 1) == 0) + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_group, + BGP_CLEAR_SOFT_IN_ORF_PREFIX, argv[0]); + + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_group, + BGP_CLEAR_SOFT_IN_ORF_PREFIX, argv[0]); +} + +DEFUN (clear_bgp_peer_group_soft_in, + clear_bgp_peer_group_soft_in_cmd, + "clear bgp peer-group WORD soft in", + CLEAR_STR + BGP_STR + "Clear all members of peer-group\n" + "BGP peer-group name\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]); +} + +ALIAS (clear_bgp_peer_group_soft_in, + clear_bgp_ipv6_peer_group_soft_in_cmd, + "clear bgp ipv6 peer-group WORD soft in", + CLEAR_STR + BGP_STR + "Address family\n" + "Clear all members of peer-group\n" + "BGP peer-group name\n" + BGP_SOFT_STR + BGP_SOFT_IN_STR) + +ALIAS (clear_bgp_peer_group_soft_in, + clear_bgp_peer_group_in_cmd, + "clear bgp peer-group WORD in", + CLEAR_STR + BGP_STR + "Clear all members of peer-group\n" + "BGP peer-group name\n" + BGP_SOFT_IN_STR) + +ALIAS (clear_bgp_peer_group_soft_in, + clear_bgp_ipv6_peer_group_in_cmd, + "clear bgp ipv6 peer-group WORD in", + CLEAR_STR + BGP_STR + "Address family\n" + "Clear all members of peer-group\n" + "BGP peer-group name\n" + BGP_SOFT_IN_STR) + +DEFUN (clear_bgp_peer_group_in_prefix_filter, + clear_bgp_peer_group_in_prefix_filter_cmd, + "clear bgp peer-group WORD in prefix-filter", + CLEAR_STR + BGP_STR + "Clear all members of peer-group\n" + "BGP peer-group name\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, + BGP_CLEAR_SOFT_IN_ORF_PREFIX, argv[0]); +} + +ALIAS (clear_bgp_peer_group_in_prefix_filter, + clear_bgp_ipv6_peer_group_in_prefix_filter_cmd, + "clear bgp ipv6 peer-group WORD in prefix-filter", + CLEAR_STR + BGP_STR + "Address family\n" + "Clear all members of peer-group\n" + "BGP peer-group name\n" + BGP_SOFT_IN_STR + "Push out prefix-list ORF and do inbound soft reconfig\n") + +DEFUN (clear_ip_bgp_external_soft_in, + clear_ip_bgp_external_soft_in_cmd, + "clear ip bgp external soft in", + CLEAR_STR + IP_STR + BGP_STR + "Clear all external peers\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); +} + +ALIAS (clear_ip_bgp_external_soft_in, + clear_ip_bgp_external_in_cmd, + "clear ip bgp external in", + CLEAR_STR + IP_STR + BGP_STR + "Clear all external peers\n" + BGP_SOFT_IN_STR) + +DEFUN (clear_ip_bgp_external_in_prefix_filter, + clear_ip_bgp_external_in_prefix_filter_cmd, + "clear ip bgp external in prefix-filter", + CLEAR_STR + IP_STR + BGP_STR + "Clear all external peers\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, + BGP_CLEAR_SOFT_IN_ORF_PREFIX, NULL); +} + +DEFUN (clear_ip_bgp_external_ipv4_soft_in, + clear_ip_bgp_external_ipv4_soft_in_cmd, + "clear ip bgp external ipv4 (unicast|multicast) soft in", + CLEAR_STR + IP_STR + BGP_STR + "Clear all external peers\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\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, + BGP_CLEAR_SOFT_IN, NULL); + + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_external, + BGP_CLEAR_SOFT_IN, NULL); +} + +ALIAS (clear_ip_bgp_external_ipv4_soft_in, + clear_ip_bgp_external_ipv4_in_cmd, + "clear ip bgp external ipv4 (unicast|multicast) in", + CLEAR_STR + IP_STR + BGP_STR + "Clear all external peers\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + BGP_SOFT_IN_STR) + +DEFUN (clear_ip_bgp_external_ipv4_in_prefix_filter, + clear_ip_bgp_external_ipv4_in_prefix_filter_cmd, + "clear ip bgp external ipv4 (unicast|multicast) in prefix-filter", + CLEAR_STR + IP_STR + BGP_STR + "Clear all external peers\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + BGP_SOFT_IN_STR + "Push out prefix-list ORF and do inbound soft reconfig\n") +{ + if (strncmp (argv[0], "m", 1) == 0) + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_external, + BGP_CLEAR_SOFT_IN_ORF_PREFIX, NULL); + + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_external, + BGP_CLEAR_SOFT_IN_ORF_PREFIX, NULL); +} + +DEFUN (clear_bgp_external_soft_in, + clear_bgp_external_soft_in_cmd, + "clear bgp external soft in", + CLEAR_STR + BGP_STR + "Clear all external peers\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); +} + +ALIAS (clear_bgp_external_soft_in, + clear_bgp_ipv6_external_soft_in_cmd, + "clear bgp ipv6 external soft in", + CLEAR_STR + BGP_STR + "Address family\n" + "Clear all external peers\n" + BGP_SOFT_STR + BGP_SOFT_IN_STR) + +ALIAS (clear_bgp_external_soft_in, + clear_bgp_external_in_cmd, + "clear bgp external in", + CLEAR_STR + BGP_STR + "Clear all external peers\n" + BGP_SOFT_IN_STR) + +ALIAS (clear_bgp_external_soft_in, + clear_bgp_ipv6_external_in_cmd, + "clear bgp ipv6 external WORD in", + CLEAR_STR + BGP_STR + "Address family\n" + "Clear all external peers\n" + BGP_SOFT_IN_STR) + +DEFUN (clear_bgp_external_in_prefix_filter, + clear_bgp_external_in_prefix_filter_cmd, + "clear bgp external in prefix-filter", + CLEAR_STR + BGP_STR + "Clear all external peers\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, + BGP_CLEAR_SOFT_IN_ORF_PREFIX, NULL); +} + +ALIAS (clear_bgp_external_in_prefix_filter, + clear_bgp_ipv6_external_in_prefix_filter_cmd, + "clear bgp ipv6 external in prefix-filter", + CLEAR_STR + BGP_STR + "Address family\n" + "Clear all external peers\n" + BGP_SOFT_IN_STR + "Push out prefix-list ORF and do inbound soft reconfig\n") + +DEFUN (clear_ip_bgp_as_soft_in, + clear_ip_bgp_as_soft_in_cmd, + "clear ip bgp " CMD_AS_RANGE " soft in", + CLEAR_STR + IP_STR + BGP_STR + "Clear peers with the AS number\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]); +} + +ALIAS (clear_ip_bgp_as_soft_in, + clear_ip_bgp_as_in_cmd, + "clear ip bgp " CMD_AS_RANGE " in", + CLEAR_STR + IP_STR + BGP_STR + "Clear peers with the AS number\n" + BGP_SOFT_IN_STR) + +DEFUN (clear_ip_bgp_as_in_prefix_filter, + clear_ip_bgp_as_in_prefix_filter_cmd, + "clear ip bgp " CMD_AS_RANGE " in prefix-filter", + CLEAR_STR + IP_STR + BGP_STR + "Clear peers with the AS number\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, + BGP_CLEAR_SOFT_IN_ORF_PREFIX, argv[0]); +} + +DEFUN (clear_ip_bgp_as_ipv4_soft_in, + clear_ip_bgp_as_ipv4_soft_in_cmd, + "clear ip bgp " CMD_AS_RANGE " ipv4 (unicast|multicast) soft in", + CLEAR_STR + IP_STR + BGP_STR + "Clear peers with the AS number\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\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, + BGP_CLEAR_SOFT_IN, argv[0]); + + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_as, + BGP_CLEAR_SOFT_IN, argv[0]); +} + +ALIAS (clear_ip_bgp_as_ipv4_soft_in, + clear_ip_bgp_as_ipv4_in_cmd, + "clear ip bgp " CMD_AS_RANGE " ipv4 (unicast|multicast) in", + CLEAR_STR + IP_STR + BGP_STR + "Clear peers with the AS number\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + BGP_SOFT_IN_STR) + +DEFUN (clear_ip_bgp_as_ipv4_in_prefix_filter, + clear_ip_bgp_as_ipv4_in_prefix_filter_cmd, + "clear ip bgp " CMD_AS_RANGE " ipv4 (unicast|multicast) in prefix-filter", + CLEAR_STR + IP_STR + BGP_STR + "Clear peers with the AS number\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + BGP_SOFT_IN_STR + "Push out prefix-list ORF and do inbound soft reconfig\n") +{ + if (strncmp (argv[1], "m", 1) == 0) + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_as, + BGP_CLEAR_SOFT_IN_ORF_PREFIX, argv[0]); + + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_as, + BGP_CLEAR_SOFT_IN_ORF_PREFIX, argv[0]); +} + +DEFUN (clear_ip_bgp_as_vpnv4_soft_in, + clear_ip_bgp_as_vpnv4_soft_in_cmd, + "clear ip bgp " CMD_AS_RANGE " vpnv4 unicast soft in", + CLEAR_STR + IP_STR + BGP_STR + "Clear peers with the AS number\n" + "Address family\n" + "Address Family modifier\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]); +} + +ALIAS (clear_ip_bgp_as_vpnv4_soft_in, + clear_ip_bgp_as_vpnv4_in_cmd, + "clear ip bgp " CMD_AS_RANGE " vpnv4 unicast in", + CLEAR_STR + IP_STR + BGP_STR + "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, + clear_bgp_as_soft_in_cmd, + "clear bgp " CMD_AS_RANGE " soft in", + CLEAR_STR + BGP_STR + "Clear peers with the AS number\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]); +} + +ALIAS (clear_bgp_as_soft_in, + clear_bgp_ipv6_as_soft_in_cmd, + "clear bgp ipv6 " CMD_AS_RANGE " soft in", + CLEAR_STR + BGP_STR + "Address family\n" + "Clear peers with the AS number\n" + BGP_SOFT_STR + BGP_SOFT_IN_STR) + +ALIAS (clear_bgp_as_soft_in, + clear_bgp_as_in_cmd, + "clear bgp " CMD_AS_RANGE " in", + CLEAR_STR + BGP_STR + "Clear peers with the AS number\n" + BGP_SOFT_IN_STR) + +ALIAS (clear_bgp_as_soft_in, + clear_bgp_ipv6_as_in_cmd, + "clear bgp ipv6 " CMD_AS_RANGE " in", + CLEAR_STR + BGP_STR + "Address family\n" + "Clear peers with the AS number\n" + BGP_SOFT_IN_STR) + +DEFUN (clear_bgp_as_in_prefix_filter, + clear_bgp_as_in_prefix_filter_cmd, + "clear bgp " CMD_AS_RANGE " in prefix-filter", + CLEAR_STR + BGP_STR + "Clear peers with the AS number\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, + BGP_CLEAR_SOFT_IN_ORF_PREFIX, argv[0]); +} + +ALIAS (clear_bgp_as_in_prefix_filter, + clear_bgp_ipv6_as_in_prefix_filter_cmd, + "clear bgp ipv6 " CMD_AS_RANGE " in prefix-filter", + CLEAR_STR + BGP_STR + "Address family\n" + "Clear peers with the AS number\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, + "clear ip bgp * soft", + CLEAR_STR + IP_STR + BGP_STR + "Clear all peers\n" + BGP_SOFT_STR) +{ + if (argc == 1) + return bgp_clear_vty (vty, argv[0], AFI_IP, SAFI_UNICAST, clear_all, + BGP_CLEAR_SOFT_BOTH, NULL); + + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_all, + BGP_CLEAR_SOFT_BOTH, NULL); +} + +ALIAS (clear_ip_bgp_all_soft, + clear_ip_bgp_instance_all_soft_cmd, + "clear ip bgp view WORD * soft", + CLEAR_STR + IP_STR + BGP_STR + "BGP view\n" + "view name\n" + "Clear all peers\n" + BGP_SOFT_STR) + + +DEFUN (clear_ip_bgp_all_ipv4_soft, + clear_ip_bgp_all_ipv4_soft_cmd, + "clear ip bgp * ipv4 (unicast|multicast) soft", + CLEAR_STR + IP_STR + BGP_STR + "Clear all peers\n" + "Address family\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + BGP_SOFT_STR) +{ + if (strncmp (argv[0], "m", 1) == 0) + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_all, + BGP_CLEAR_SOFT_BOTH, NULL); + + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_all, + BGP_CLEAR_SOFT_BOTH, NULL); +} + +DEFUN (clear_ip_bgp_instance_all_ipv4_soft, + clear_ip_bgp_instance_all_ipv4_soft_cmd, + "clear ip bgp view WORD * ipv4 (unicast|multicast) soft", + CLEAR_STR + IP_STR + BGP_STR + "BGP view\n" + "view name\n" + "Clear all peers\n" + "Address family\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + BGP_SOFT_STR) +{ + if (strncmp (argv[1], "m", 1) == 0) + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_all, + BGP_CLEAR_SOFT_BOTH, NULL); + + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_all, + BGP_CLEAR_SOFT_BOTH, NULL); +} + +DEFUN (clear_ip_bgp_all_vpnv4_soft, + clear_ip_bgp_all_vpnv4_soft_cmd, + "clear ip bgp * vpnv4 unicast soft", + CLEAR_STR + IP_STR + BGP_STR + "Clear all peers\n" + "Address family\n" + "Address Family Modifier\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" + BGP_SOFT_STR) +{ + if (argc == 1) + return bgp_clear_vty (vty, argv[0], AFI_IP6, SAFI_UNICAST, clear_all, + BGP_CLEAR_SOFT_BOTH, argv[0]); + + return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_all, + BGP_CLEAR_SOFT_BOTH, argv[0]); +} + +ALIAS (clear_bgp_all_soft, + clear_bgp_instance_all_soft_cmd, + "clear bgp view WORD * soft", + CLEAR_STR + BGP_STR + "BGP view\n" + "view name\n" + "Clear all peers\n" + BGP_SOFT_STR) + +ALIAS (clear_bgp_all_soft, + clear_bgp_ipv6_all_soft_cmd, + "clear bgp ipv6 * soft", + CLEAR_STR + BGP_STR + "Address family\n" + "Clear all peers\n" + BGP_SOFT_STR) + +DEFUN (clear_ip_bgp_peer_soft, + clear_ip_bgp_peer_soft_cmd, + "clear ip bgp A.B.C.D soft", + CLEAR_STR + IP_STR + BGP_STR + "BGP neighbor address to clear\n" + BGP_SOFT_STR) +{ + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_peer, + BGP_CLEAR_SOFT_BOTH, argv[0]); +} + +DEFUN (clear_ip_bgp_peer_ipv4_soft, + clear_ip_bgp_peer_ipv4_soft_cmd, + "clear ip bgp A.B.C.D ipv4 (unicast|multicast) soft", + CLEAR_STR + IP_STR + BGP_STR + "BGP neighbor address to clear\n" + "Address family\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + BGP_SOFT_STR) +{ + if (strncmp (argv[1], "m", 1) == 0) + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_peer, + BGP_CLEAR_SOFT_BOTH, argv[0]); + + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_peer, + BGP_CLEAR_SOFT_BOTH, argv[0]); +} + +DEFUN (clear_ip_bgp_peer_vpnv4_soft, + clear_ip_bgp_peer_vpnv4_soft_cmd, + "clear ip bgp A.B.C.D vpnv4 unicast soft", + CLEAR_STR + IP_STR + BGP_STR + "BGP neighbor address to clear\n" + "Address family\n" + "Address Family Modifier\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", + CLEAR_STR + BGP_STR + "BGP neighbor address to clear\n" + "BGP IPv6 neighbor to clear\n" + BGP_SOFT_STR) +{ + return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_peer, + BGP_CLEAR_SOFT_BOTH, argv[0]); +} + +ALIAS (clear_bgp_peer_soft, + clear_bgp_ipv6_peer_soft_cmd, + "clear bgp ipv6 (A.B.C.D|X:X::X:X) soft", + CLEAR_STR + BGP_STR + "Address family\n" + "BGP neighbor address to clear\n" + "BGP IPv6 neighbor to clear\n" + BGP_SOFT_STR) + +DEFUN (clear_ip_bgp_peer_group_soft, + clear_ip_bgp_peer_group_soft_cmd, + "clear ip bgp peer-group WORD soft", + CLEAR_STR + IP_STR + BGP_STR + "Clear all members of peer-group\n" + "BGP peer-group name\n" + BGP_SOFT_STR) +{ + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_group, + BGP_CLEAR_SOFT_BOTH, argv[0]); +} + +DEFUN (clear_ip_bgp_peer_group_ipv4_soft, + clear_ip_bgp_peer_group_ipv4_soft_cmd, + "clear ip bgp peer-group WORD ipv4 (unicast|multicast) soft", + CLEAR_STR + IP_STR + BGP_STR + "Clear all members of peer-group\n" + "BGP peer-group name\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + BGP_SOFT_STR) +{ + if (strncmp (argv[1], "m", 1) == 0) + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_group, + BGP_CLEAR_SOFT_BOTH, argv[0]); + + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_group, + BGP_CLEAR_SOFT_BOTH, argv[0]); +} + +DEFUN (clear_bgp_peer_group_soft, + clear_bgp_peer_group_soft_cmd, + "clear bgp peer-group WORD soft", + CLEAR_STR + BGP_STR + "Clear all members of peer-group\n" + "BGP peer-group name\n" + BGP_SOFT_STR) +{ + return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_group, + BGP_CLEAR_SOFT_BOTH, argv[0]); +} + +ALIAS (clear_bgp_peer_group_soft, + clear_bgp_ipv6_peer_group_soft_cmd, + "clear bgp ipv6 peer-group WORD soft", + CLEAR_STR + BGP_STR + "Address family\n" + "Clear all members of peer-group\n" + "BGP peer-group name\n" + BGP_SOFT_STR) + +DEFUN (clear_ip_bgp_external_soft, + clear_ip_bgp_external_soft_cmd, + "clear ip bgp external soft", + CLEAR_STR + IP_STR + BGP_STR + "Clear all external peers\n" + BGP_SOFT_STR) +{ + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_external, + BGP_CLEAR_SOFT_BOTH, NULL); +} + +DEFUN (clear_ip_bgp_external_ipv4_soft, + clear_ip_bgp_external_ipv4_soft_cmd, + "clear ip bgp external ipv4 (unicast|multicast) soft", + CLEAR_STR + IP_STR + BGP_STR + "Clear all external peers\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + BGP_SOFT_STR) +{ + if (strncmp (argv[0], "m", 1) == 0) + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_external, + BGP_CLEAR_SOFT_BOTH, NULL); + + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_external, + BGP_CLEAR_SOFT_BOTH, NULL); +} + +DEFUN (clear_bgp_external_soft, + clear_bgp_external_soft_cmd, + "clear bgp external soft", + CLEAR_STR + BGP_STR + "Clear all external peers\n" + BGP_SOFT_STR) +{ + return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_external, + BGP_CLEAR_SOFT_BOTH, NULL); +} + +ALIAS (clear_bgp_external_soft, + clear_bgp_ipv6_external_soft_cmd, + "clear bgp ipv6 external soft", + CLEAR_STR + BGP_STR + "Address family\n" + "Clear all external peers\n" + BGP_SOFT_STR) + +DEFUN (clear_ip_bgp_as_soft, + clear_ip_bgp_as_soft_cmd, + "clear ip bgp " CMD_AS_RANGE " soft", + CLEAR_STR + IP_STR + BGP_STR + "Clear peers with the AS number\n" + BGP_SOFT_STR) +{ + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_as, + BGP_CLEAR_SOFT_BOTH, argv[0]); +} + +DEFUN (clear_ip_bgp_as_ipv4_soft, + clear_ip_bgp_as_ipv4_soft_cmd, + "clear ip bgp " CMD_AS_RANGE " ipv4 (unicast|multicast) soft", + CLEAR_STR + IP_STR + BGP_STR + "Clear peers with the AS number\n" + "Address family\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + BGP_SOFT_STR) +{ + if (strncmp (argv[1], "m", 1) == 0) + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_as, + BGP_CLEAR_SOFT_BOTH, argv[0]); + + return bgp_clear_vty (vty, NULL,AFI_IP, SAFI_UNICAST, clear_as, + BGP_CLEAR_SOFT_BOTH, argv[0]); +} + +DEFUN (clear_ip_bgp_as_vpnv4_soft, + clear_ip_bgp_as_vpnv4_soft_cmd, + "clear ip bgp " CMD_AS_RANGE " vpnv4 unicast soft", + CLEAR_STR + IP_STR + BGP_STR + "Clear peers with the AS number\n" + "Address family\n" + "Address Family Modifier\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" + BGP_SOFT_STR) +{ + return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_as, + BGP_CLEAR_SOFT_BOTH, argv[0]); +} + +ALIAS (clear_bgp_as_soft, + clear_bgp_ipv6_as_soft_cmd, + "clear bgp ipv6 " CMD_AS_RANGE " soft", + CLEAR_STR + BGP_STR + "Address family\n" + "Clear peers with the AS number\n" + BGP_SOFT_STR) + +/* RS-client soft reconfiguration. */ +DEFUN (clear_bgp_all_rsclient, + clear_bgp_all_rsclient_cmd, + "clear bgp * rsclient", + CLEAR_STR + BGP_STR + "Clear all peers\n" + BGP_SOFT_RSCLIENT_RIB_STR) +{ + if (argc == 1) + return bgp_clear_vty (vty, argv[0], AFI_IP6, SAFI_UNICAST, clear_all, + BGP_CLEAR_SOFT_RSCLIENT, NULL); + + return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_all, + BGP_CLEAR_SOFT_RSCLIENT, NULL); +} + +ALIAS (clear_bgp_all_rsclient, + clear_bgp_ipv6_all_rsclient_cmd, + "clear bgp ipv6 * rsclient", + CLEAR_STR + BGP_STR + "Address family\n" + "Clear all peers\n" + BGP_SOFT_RSCLIENT_RIB_STR) + +ALIAS (clear_bgp_all_rsclient, + clear_bgp_instance_all_rsclient_cmd, + "clear bgp view WORD * rsclient", + CLEAR_STR + BGP_STR + "BGP view\n" + "view name\n" + "Clear all peers\n" + BGP_SOFT_RSCLIENT_RIB_STR) + +ALIAS (clear_bgp_all_rsclient, + clear_bgp_ipv6_instance_all_rsclient_cmd, + "clear bgp ipv6 view WORD * rsclient", + CLEAR_STR + BGP_STR + "Address family\n" + "BGP view\n" + "view name\n" + "Clear all peers\n" + BGP_SOFT_RSCLIENT_RIB_STR) + +DEFUN (clear_ip_bgp_all_rsclient, + clear_ip_bgp_all_rsclient_cmd, + "clear ip bgp * rsclient", + CLEAR_STR + IP_STR + BGP_STR + "Clear all peers\n" + BGP_SOFT_RSCLIENT_RIB_STR) +{ + if (argc == 1) + return bgp_clear_vty (vty, argv[0], AFI_IP, SAFI_UNICAST, clear_all, + BGP_CLEAR_SOFT_RSCLIENT, NULL); + + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_all, + BGP_CLEAR_SOFT_RSCLIENT, NULL); +} + +ALIAS (clear_ip_bgp_all_rsclient, + clear_ip_bgp_instance_all_rsclient_cmd, + "clear ip bgp view WORD * rsclient", + CLEAR_STR + IP_STR + BGP_STR + "BGP view\n" + "view name\n" + "Clear all peers\n" + BGP_SOFT_RSCLIENT_RIB_STR) + +DEFUN (clear_bgp_peer_rsclient, + clear_bgp_peer_rsclient_cmd, + "clear bgp (A.B.C.D|X:X::X:X) rsclient", + CLEAR_STR + BGP_STR + "BGP neighbor IP address to clear\n" + "BGP IPv6 neighbor to clear\n" + BGP_SOFT_RSCLIENT_RIB_STR) +{ + if (argc == 2) + return bgp_clear_vty (vty, argv[0], AFI_IP6, SAFI_UNICAST, clear_peer, + BGP_CLEAR_SOFT_RSCLIENT, argv[1]); + + return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_peer, + BGP_CLEAR_SOFT_RSCLIENT, argv[0]); +} + +ALIAS (clear_bgp_peer_rsclient, + clear_bgp_ipv6_peer_rsclient_cmd, + "clear bgp ipv6 (A.B.C.D|X:X::X:X) rsclient", + CLEAR_STR + BGP_STR + "Address family\n" + "BGP neighbor IP address to clear\n" + "BGP IPv6 neighbor to clear\n" + BGP_SOFT_RSCLIENT_RIB_STR) + +ALIAS (clear_bgp_peer_rsclient, + clear_bgp_instance_peer_rsclient_cmd, + "clear bgp view WORD (A.B.C.D|X:X::X:X) rsclient", + CLEAR_STR + BGP_STR + "BGP view\n" + "view name\n" + "BGP neighbor IP address to clear\n" + "BGP IPv6 neighbor to clear\n" + BGP_SOFT_RSCLIENT_RIB_STR) + +ALIAS (clear_bgp_peer_rsclient, + clear_bgp_ipv6_instance_peer_rsclient_cmd, + "clear bgp ipv6 view WORD (A.B.C.D|X:X::X:X) rsclient", + CLEAR_STR + BGP_STR + "Address family\n" + "BGP view\n" + "view name\n" + "BGP neighbor IP address to clear\n" + "BGP IPv6 neighbor to clear\n" + BGP_SOFT_RSCLIENT_RIB_STR) + +DEFUN (clear_ip_bgp_peer_rsclient, + clear_ip_bgp_peer_rsclient_cmd, + "clear ip bgp (A.B.C.D|X:X::X:X) rsclient", + CLEAR_STR + IP_STR + BGP_STR + "BGP neighbor IP address to clear\n" + "BGP IPv6 neighbor to clear\n" + BGP_SOFT_RSCLIENT_RIB_STR) +{ + if (argc == 2) + return bgp_clear_vty (vty, argv[0], AFI_IP, SAFI_UNICAST, clear_peer, + BGP_CLEAR_SOFT_RSCLIENT, argv[1]); + + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_peer, + BGP_CLEAR_SOFT_RSCLIENT, argv[0]); +} + +ALIAS (clear_ip_bgp_peer_rsclient, + clear_ip_bgp_instance_peer_rsclient_cmd, + "clear ip bgp view WORD (A.B.C.D|X:X::X:X) rsclient", + CLEAR_STR + IP_STR + BGP_STR + "BGP view\n" + "view name\n" + "BGP neighbor IP address to clear\n" + "BGP IPv6 neighbor to clear\n" + BGP_SOFT_RSCLIENT_RIB_STR) + +DEFUN (show_bgp_views, + show_bgp_views_cmd, + "show bgp views", + SHOW_STR + BGP_STR + "Show the defined BGP views\n") +{ + struct list *inst = bm->bgp; + struct listnode *node; + struct bgp *bgp; + + if (!bgp_option_check (BGP_OPT_MULTIPLE_INSTANCE)) + { + vty_out (vty, "Multiple BGP views are not defined%s", VTY_NEWLINE); + return CMD_WARNING; + } + + vty_out (vty, "Defined BGP views:%s", VTY_NEWLINE); + for (ALL_LIST_ELEMENTS_RO(inst, node, bgp)) + vty_out (vty, "\t%s (AS%u)%s", + bgp->name ? bgp->name : "(null)", + bgp->as, VTY_NEWLINE); + + return CMD_SUCCESS; +} + +DEFUN (show_bgp_memory, + show_bgp_memory_cmd, + "show bgp memory", + SHOW_STR + BGP_STR + "Global BGP memory statistics\n") +{ + char memstrbuf[MTYPE_MEMSTR_LEN]; + unsigned long count; + + /* RIB related usage stats */ + count = mtype_stats_alloc (MTYPE_BGP_NODE); + vty_out (vty, "%ld RIB nodes, using %s of memory%s", count, + mtype_memstr (memstrbuf, sizeof (memstrbuf), + count * sizeof (struct bgp_node)), + VTY_NEWLINE); + + count = mtype_stats_alloc (MTYPE_BGP_ROUTE); + vty_out (vty, "%ld BGP routes, using %s of memory%s", count, + mtype_memstr (memstrbuf, sizeof (memstrbuf), + count * sizeof (struct bgp_info)), + VTY_NEWLINE); + if ((count = mtype_stats_alloc (MTYPE_BGP_ROUTE_EXTRA))) + vty_out (vty, "%ld BGP route ancillaries, using %s of memory%s", count, + mtype_memstr (memstrbuf, sizeof (memstrbuf), + count * sizeof (struct bgp_info_extra)), + VTY_NEWLINE); + + if ((count = mtype_stats_alloc (MTYPE_BGP_STATIC))) + vty_out (vty, "%ld Static routes, using %s of memory%s", count, + mtype_memstr (memstrbuf, sizeof (memstrbuf), + count * sizeof (struct bgp_static)), + VTY_NEWLINE); + + /* Adj-In/Out */ + if ((count = mtype_stats_alloc (MTYPE_BGP_ADJ_IN))) + vty_out (vty, "%ld Adj-In entries, using %s of memory%s", count, + mtype_memstr (memstrbuf, sizeof (memstrbuf), + count * sizeof (struct bgp_adj_in)), + VTY_NEWLINE); + if ((count = mtype_stats_alloc (MTYPE_BGP_ADJ_OUT))) + vty_out (vty, "%ld Adj-Out entries, using %s of memory%s", count, + mtype_memstr (memstrbuf, sizeof (memstrbuf), + count * sizeof (struct bgp_adj_out)), + VTY_NEWLINE); + + if ((count = mtype_stats_alloc (MTYPE_BGP_NEXTHOP_CACHE))) + vty_out (vty, "%ld Nexthop cache entries, using %s of memory%s", count, + mtype_memstr (memstrbuf, sizeof (memstrbuf), + count * sizeof (struct bgp_nexthop_cache)), + VTY_NEWLINE); + + if ((count = mtype_stats_alloc (MTYPE_BGP_DAMP_INFO))) + vty_out (vty, "%ld Dampening entries, using %s of memory%s", count, + mtype_memstr (memstrbuf, sizeof (memstrbuf), + count * sizeof (struct bgp_damp_info)), + VTY_NEWLINE); + + /* Attributes */ + count = attr_count(); + vty_out (vty, "%ld BGP attributes, using %s of memory%s", count, + mtype_memstr (memstrbuf, sizeof (memstrbuf), + count * sizeof(struct attr)), + VTY_NEWLINE); + if ((count = mtype_stats_alloc (MTYPE_ATTR_EXTRA))) + vty_out (vty, "%ld BGP extra attributes, using %s of memory%s", count, + mtype_memstr (memstrbuf, sizeof (memstrbuf), + count * sizeof(struct attr_extra)), + VTY_NEWLINE); + + if ((count = attr_unknown_count())) + vty_out (vty, "%ld unknown attributes%s", count, VTY_NEWLINE); + + /* AS_PATH attributes */ + count = aspath_count (); + vty_out (vty, "%ld BGP AS-PATH entries, using %s of memory%s", count, + mtype_memstr (memstrbuf, sizeof (memstrbuf), + count * sizeof (struct aspath)), + VTY_NEWLINE); + + count = mtype_stats_alloc (MTYPE_AS_SEG); + vty_out (vty, "%ld BGP AS-PATH segments, using %s of memory%s", count, + mtype_memstr (memstrbuf, sizeof (memstrbuf), + count * sizeof (struct assegment)), + VTY_NEWLINE); + + /* Other attributes */ + if ((count = community_count ())) + vty_out (vty, "%ld BGP community entries, using %s of memory%s", count, + mtype_memstr (memstrbuf, sizeof (memstrbuf), + count * sizeof (struct community)), + VTY_NEWLINE); + if ((count = mtype_stats_alloc (MTYPE_ECOMMUNITY))) + vty_out (vty, "%ld BGP community entries, using %s of memory%s", count, + 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), + count * sizeof (struct cluster_list)), + VTY_NEWLINE); + + /* Peer related usage */ + count = mtype_stats_alloc (MTYPE_BGP_PEER); + vty_out (vty, "%ld peers, using %s of memory%s", count, + mtype_memstr (memstrbuf, sizeof (memstrbuf), + count * sizeof (struct peer)), + VTY_NEWLINE); + + if ((count = mtype_stats_alloc (MTYPE_PEER_GROUP))) + vty_out (vty, "%ld peer groups, using %s of memory%s", count, + mtype_memstr (memstrbuf, sizeof (memstrbuf), + count * sizeof (struct peer_group)), + VTY_NEWLINE); + + /* Other */ + if ((count = mtype_stats_alloc (MTYPE_HASH))) + vty_out (vty, "%ld hash tables, using %s of memory%s", count, + mtype_memstr (memstrbuf, sizeof (memstrbuf), + count * sizeof (struct hash)), + VTY_NEWLINE); + if ((count = mtype_stats_alloc (MTYPE_HASH_BACKET))) + vty_out (vty, "%ld hash buckets, using %s of memory%s", count, + mtype_memstr (memstrbuf, sizeof (memstrbuf), + count * sizeof (struct hash_backet)), + VTY_NEWLINE); + if ((count = mtype_stats_alloc (MTYPE_BGP_REGEXP))) + vty_out (vty, "%ld compiled regexes, using %s of memory%s", count, + mtype_memstr (memstrbuf, sizeof (memstrbuf), + count * sizeof (regex_t)), + VTY_NEWLINE); + return CMD_SUCCESS; +} + +/* Show BGP peer's summary information. */ +static int +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"; + + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + if (peer->afc[afi][safi]) + { + if (!count) + { + unsigned long ents; + char memstrbuf[MTYPE_MEMSTR_LEN]; + + /* Usage summary and header */ + vty_out (vty, + "BGP router identifier %s, local AS number %u%s", + inet_ntoa (bgp->router_id), bgp->as, VTY_NEWLINE); + + ents = bgp_table_count (bgp->rib[afi][safi]); + vty_out (vty, "RIB entries %ld, using %s of memory%s", ents, + mtype_memstr (memstrbuf, sizeof (memstrbuf), + ents * sizeof (struct bgp_node)), + VTY_NEWLINE); + + /* Peer related usage */ + ents = listcount (bgp->peer); + vty_out (vty, "Peers %ld, using %s of memory%s", + ents, + mtype_memstr (memstrbuf, sizeof (memstrbuf), + ents * sizeof (struct peer)), + VTY_NEWLINE); + + if ((ents = listcount (bgp->rsclient))) + vty_out (vty, "RS-Client peers %ld, using %s of memory%s", + ents, + mtype_memstr (memstrbuf, sizeof (memstrbuf), + ents * sizeof (struct peer)), + VTY_NEWLINE); + + if ((ents = listcount (bgp->group))) + vty_out (vty, "Peer groups %ld, using %s of memory%s", ents, + mtype_memstr (memstrbuf, sizeof (memstrbuf), + ents * sizeof (struct peer_group)), + VTY_NEWLINE); + + if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)) + vty_out (vty, "Dampening enabled.%s", VTY_NEWLINE); + vty_out (vty, "%s", VTY_NEWLINE); + vty_out (vty, "%s%s", header, VTY_NEWLINE); + } + + count++; + + len = vty_out (vty, "%s", peer->host); + len = 16 - len; + if (len < 1) + vty_out (vty, "%s%*s", VTY_NEWLINE, 16, " "); + else + vty_out (vty, "%*s", len, " "); + + vty_out (vty, "4 "); + + 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, + peer->sync[afi][safi]->update.count + + peer->sync[afi][safi]->withdraw.count); + + vty_out (vty, "%8s", + peer_uptime (peer->uptime, timebuf, BGP_UPTIME_LEN)); + + if (peer->status == Established) + { + vty_out (vty, " %8ld", peer->pcount[afi][safi]); + totrcount += peer->pcount[afi][safi]; + totecount++; + } + else + { + if (CHECK_FLAG (peer->flags, PEER_FLAG_SHUTDOWN)) + vty_out (vty, " Idle (Admin)"); + else if (CHECK_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW)) + vty_out (vty, " Idle (PfxCt)"); + else + vty_out (vty, " %-11s", LOOKUP(bgp_status_msg, peer->status)); + } + + vty_out (vty, "%s", VTY_NEWLINE); + } + } + + if (count) + { + 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); + return CMD_SUCCESS; +} + +static int +bgp_show_summary_vty (struct vty *vty, const char *name, + afi_t afi, safi_t safi) +{ + struct bgp *bgp; + + if (name) + { + bgp = bgp_lookup_by_name (name); + + if (! bgp) + { + vty_out (vty, "%% No such BGP instance exist%s", VTY_NEWLINE); + return CMD_WARNING; + } + + bgp_show_summary (vty, bgp, afi, safi); + return CMD_SUCCESS; + } + + bgp = bgp_get_default (); + + if (bgp) + bgp_show_summary (vty, bgp, afi, safi); + + return CMD_SUCCESS; +} + +/* `show ip bgp summary' commands. */ +DEFUN (show_ip_bgp_summary, + show_ip_bgp_summary_cmd, + "show ip bgp summary", + SHOW_STR + IP_STR + BGP_STR + "Summary of BGP neighbor status\n") +{ + return bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_UNICAST); +} + +DEFUN (show_ip_bgp_instance_summary, + show_ip_bgp_instance_summary_cmd, + "show ip bgp view WORD summary", + SHOW_STR + IP_STR + BGP_STR + "BGP view\n" + "View name\n" + "Summary of BGP neighbor status\n") +{ + return bgp_show_summary_vty (vty, argv[0], AFI_IP, SAFI_UNICAST); +} + +DEFUN (show_ip_bgp_ipv4_summary, + show_ip_bgp_ipv4_summary_cmd, + "show ip bgp ipv4 (unicast|multicast) summary", + SHOW_STR + IP_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Summary of BGP neighbor status\n") +{ + 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_ip_bgp_instance_ipv4_summary, + show_ip_bgp_instance_ipv4_summary_cmd, + "show ip bgp view WORD ipv4 (unicast|multicast) summary", + SHOW_STR + IP_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_ip_bgp_vpnv4_all_summary, + show_ip_bgp_vpnv4_all_summary_cmd, + "show ip bgp vpnv4 all summary", + SHOW_STR + IP_STR + BGP_STR + "Display VPNv4 NLRI specific information\n" + "Display information about all VPNv4 NLRIs\n" + "Summary of BGP neighbor status\n") +{ + return bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN); +} + +DEFUN (show_ip_bgp_vpnv4_rd_summary, + show_ip_bgp_vpnv4_rd_summary_cmd, + "show ip bgp vpnv4 rd ASN:nn_or_IP-address:nn summary", + SHOW_STR + IP_STR + BGP_STR + "Display VPNv4 NLRI specific information\n" + "Display information for a route distinguisher\n" + "VPN Route Distinguisher\n" + "Summary of BGP neighbor status\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_summary_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN); +} + +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") +{ + 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, + show_bgp_instance_summary_cmd, + "show bgp view WORD summary", + SHOW_STR + BGP_STR + "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); + + 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_instance_ipv4_summary, + show_bgp_instance_ipv4_summary_cmd, + "show bgp view WORD ipv4 summary", + SHOW_STR + BGP_STR + 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; +} + +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" + "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, + "show bgp ipv6 (unicast|multicast) summary", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Summary of BGP neighbor status\n") +{ + if (strncmp (argv[0], "m", 1) == 0) + return bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_MULTICAST); + + return bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_UNICAST); +} + +DEFUN (show_bgp_instance_ipv6_safi_summary, + show_bgp_instance_ipv6_safi_summary_cmd, + "show bgp view WORD ipv6 (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_IP6, SAFI_MULTICAST); + + return bgp_show_summary_vty (vty, argv[0], AFI_IP6, SAFI_UNICAST); +} + +/* old command */ +DEFUN (show_ipv6_bgp_summary, + show_ipv6_bgp_summary_cmd, + "show ipv6 bgp summary", + SHOW_STR + IPV6_STR + BGP_STR + "Summary of BGP neighbor status\n") +{ + return bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_UNICAST); +} + +/* old command */ +DEFUN (show_ipv6_mbgp_summary, + show_ipv6_mbgp_summary_cmd, + "show ipv6 mbgp summary", + SHOW_STR + IPV6_STR + MBGP_STR + "Summary of BGP neighbor status\n") +{ + return bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_MULTICAST); +} + +/* 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) +{ + if (afi == AFI_IP && safi == SAFI_UNICAST) + return "IPv4 Unicast"; + else if (afi == AFI_IP && safi == SAFI_MULTICAST) + return "IPv4 Multicast"; + else if (afi == AFI_IP && safi == SAFI_MPLS_VPN) + 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"; +} + +/* Show BGP peer's information. */ +enum show_type +{ + show_all, + show_peer +}; + +static void +bgp_show_peer_afi_orf_cap (struct vty *vty, struct peer *p, + afi_t afi, safi_t safi, + u_int16_t adv_smcap, u_int16_t adv_rmcap, + u_int16_t rcv_smcap, u_int16_t rcv_rmcap) +{ + /* Send-Mode */ + if (CHECK_FLAG (p->af_cap[afi][safi], adv_smcap) + || CHECK_FLAG (p->af_cap[afi][safi], rcv_smcap)) + { + vty_out (vty, " Send-mode: "); + if (CHECK_FLAG (p->af_cap[afi][safi], adv_smcap)) + vty_out (vty, "advertised"); + if (CHECK_FLAG (p->af_cap[afi][safi], rcv_smcap)) + vty_out (vty, "%sreceived", + CHECK_FLAG (p->af_cap[afi][safi], adv_smcap) ? + ", " : ""); + vty_out (vty, "%s", VTY_NEWLINE); + } + + /* Receive-Mode */ + if (CHECK_FLAG (p->af_cap[afi][safi], adv_rmcap) + || CHECK_FLAG (p->af_cap[afi][safi], rcv_rmcap)) + { + vty_out (vty, " Receive-mode: "); + if (CHECK_FLAG (p->af_cap[afi][safi], adv_rmcap)) + vty_out (vty, "advertised"); + if (CHECK_FLAG (p->af_cap[afi][safi], rcv_rmcap)) + vty_out (vty, "%sreceived", + CHECK_FLAG (p->af_cap[afi][safi], adv_rmcap) ? + ", " : ""); + vty_out (vty, "%s", VTY_NEWLINE); + } +} + +static void +bgp_show_peer_afi (struct vty *vty, struct peer *p, afi_t afi, safi_t safi) +{ + struct bgp_filter *filter; + char orf_pfx_name[BUFSIZ]; + int orf_pfx_count; + + filter = &p->filter[afi][safi]; + + vty_out (vty, " For address family: %s%s", afi_safi_print (afi, safi), + VTY_NEWLINE); + + if (p->af_group[afi][safi]) + vty_out (vty, " %s peer-group member%s", p->group->name, VTY_NEWLINE); + + if (CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV) + || CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_RCV) + || CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_OLD_RCV) + || CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_ADV) + || CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_RCV) + || CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_OLD_RCV)) + vty_out (vty, " AF-dependant capabilities:%s", VTY_NEWLINE); + + if (CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV) + || CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_RCV) + || CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_ADV) + || CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_RCV)) + { + vty_out (vty, " Outbound Route Filter (ORF) type (%d) Prefix-list:%s", + ORF_TYPE_PREFIX, VTY_NEWLINE); + bgp_show_peer_afi_orf_cap (vty, p, afi, safi, + PEER_CAP_ORF_PREFIX_SM_ADV, + PEER_CAP_ORF_PREFIX_RM_ADV, + PEER_CAP_ORF_PREFIX_SM_RCV, + PEER_CAP_ORF_PREFIX_RM_RCV); + } + if (CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV) + || CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_OLD_RCV) + || CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_ADV) + || CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_OLD_RCV)) + { + vty_out (vty, " Outbound Route Filter (ORF) type (%d) Prefix-list:%s", + ORF_TYPE_PREFIX_OLD, VTY_NEWLINE); + bgp_show_peer_afi_orf_cap (vty, p, afi, safi, + PEER_CAP_ORF_PREFIX_SM_ADV, + PEER_CAP_ORF_PREFIX_RM_ADV, + PEER_CAP_ORF_PREFIX_SM_OLD_RCV, + PEER_CAP_ORF_PREFIX_RM_OLD_RCV); + } + + sprintf (orf_pfx_name, "%s.%d.%d", p->host, afi, safi); + orf_pfx_count = prefix_bgp_show_prefix_list (NULL, afi, orf_pfx_name); + + if (CHECK_FLAG (p->af_sflags[afi][safi], PEER_STATUS_ORF_PREFIX_SEND) + || orf_pfx_count) + { + vty_out (vty, " Outbound Route Filter (ORF):"); + if (CHECK_FLAG (p->af_sflags[afi][safi], PEER_STATUS_ORF_PREFIX_SEND)) + vty_out (vty, " sent;"); + if (orf_pfx_count) + vty_out (vty, " received (%d entries)", orf_pfx_count); + vty_out (vty, "%s", VTY_NEWLINE); + } + if (CHECK_FLAG (p->af_sflags[afi][safi], PEER_STATUS_ORF_WAIT_REFRESH)) + vty_out (vty, " First update is deferred until ORF or ROUTE-REFRESH is received%s", VTY_NEWLINE); + + if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_REFLECTOR_CLIENT)) + vty_out (vty, " Route-Reflector Client%s", VTY_NEWLINE); + if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)) + vty_out (vty, " Route-Server Client%s", VTY_NEWLINE); + if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG)) + vty_out (vty, " Inbound soft reconfiguration allowed%s", VTY_NEWLINE); + if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_REMOVE_PRIVATE_AS)) + vty_out (vty, " Private AS number removed from updates to this neighbor%s", VTY_NEWLINE); + if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_NEXTHOP_SELF)) + vty_out (vty, " NEXT_HOP is always this router%s", VTY_NEWLINE); + if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_AS_PATH_UNCHANGED)) + vty_out (vty, " AS_PATH is propagated unchanged to this neighbor%s", VTY_NEWLINE); + if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_NEXTHOP_UNCHANGED)) + vty_out (vty, " NEXT_HOP is propagated unchanged to this neighbor%s", VTY_NEWLINE); + 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_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) + && 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); + } + if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE)) + { + vty_out (vty, " Default information originate,"); + + if (p->default_rmap[afi][safi].name) + vty_out (vty, " default route-map %s%s,", + p->default_rmap[afi][safi].map ? "*" : "", + p->default_rmap[afi][safi].name); + if (CHECK_FLAG (p->af_sflags[afi][safi], PEER_STATUS_DEFAULT_ORIGINATE)) + vty_out (vty, " default sent%s", VTY_NEWLINE); + else + vty_out (vty, " default not sent%s", VTY_NEWLINE); + } + + if (filter->plist[FILTER_IN].name + || filter->dlist[FILTER_IN].name + || filter->aslist[FILTER_IN].name + || filter->map[RMAP_IN].name) + vty_out (vty, " Inbound path policy configured%s", VTY_NEWLINE); + if (filter->plist[FILTER_OUT].name + || filter->dlist[FILTER_OUT].name + || filter->aslist[FILTER_OUT].name + || filter->map[RMAP_OUT].name + || filter->usmap.name) + vty_out (vty, " Outbound path policy configured%s", VTY_NEWLINE); + if (filter->map[RMAP_IMPORT].name) + vty_out (vty, " Import policy for this RS-client configured%s", VTY_NEWLINE); + if (filter->map[RMAP_EXPORT].name) + vty_out (vty, " Export policy for this RS-client configured%s", VTY_NEWLINE); + + /* prefix-list */ + if (filter->plist[FILTER_IN].name) + vty_out (vty, " Incoming update prefix filter list is %s%s%s", + filter->plist[FILTER_IN].plist ? "*" : "", + filter->plist[FILTER_IN].name, + VTY_NEWLINE); + if (filter->plist[FILTER_OUT].name) + vty_out (vty, " Outgoing update prefix filter list is %s%s%s", + filter->plist[FILTER_OUT].plist ? "*" : "", + filter->plist[FILTER_OUT].name, + VTY_NEWLINE); + + /* distribute-list */ + if (filter->dlist[FILTER_IN].name) + vty_out (vty, " Incoming update network filter list is %s%s%s", + filter->dlist[FILTER_IN].alist ? "*" : "", + filter->dlist[FILTER_IN].name, + VTY_NEWLINE); + if (filter->dlist[FILTER_OUT].name) + vty_out (vty, " Outgoing update network filter list is %s%s%s", + filter->dlist[FILTER_OUT].alist ? "*" : "", + filter->dlist[FILTER_OUT].name, + VTY_NEWLINE); + + /* filter-list. */ + if (filter->aslist[FILTER_IN].name) + vty_out (vty, " Incoming update AS path filter list is %s%s%s", + filter->aslist[FILTER_IN].aslist ? "*" : "", + filter->aslist[FILTER_IN].name, + VTY_NEWLINE); + if (filter->aslist[FILTER_OUT].name) + vty_out (vty, " Outgoing update AS path filter list is %s%s%s", + filter->aslist[FILTER_OUT].aslist ? "*" : "", + filter->aslist[FILTER_OUT].name, + VTY_NEWLINE); + + /* route-map. */ + if (filter->map[RMAP_IN].name) + vty_out (vty, " Route map for incoming advertisements is %s%s%s", + filter->map[RMAP_IN].map ? "*" : "", + filter->map[RMAP_IN].name, + VTY_NEWLINE); + if (filter->map[RMAP_OUT].name) + vty_out (vty, " Route map for outgoing advertisements is %s%s%s", + filter->map[RMAP_OUT].map ? "*" : "", + filter->map[RMAP_OUT].name, + VTY_NEWLINE); + if (filter->map[RMAP_IMPORT].name) + vty_out (vty, " Route map for advertisements going into this RS-client's table is %s%s%s", + filter->map[RMAP_IMPORT].map ? "*" : "", + filter->map[RMAP_IMPORT].name, + VTY_NEWLINE); + if (filter->map[RMAP_EXPORT].name) + vty_out (vty, " Route map for advertisements coming from this RS-client is %s%s%s", + filter->map[RMAP_EXPORT].map ? "*" : "", + filter->map[RMAP_EXPORT].name, + VTY_NEWLINE); + + /* unsuppress-map */ + if (filter->usmap.name) + vty_out (vty, " Route map for selective unsuppress is %s%s%s", + filter->usmap.map ? "*" : "", + filter->usmap.name, VTY_NEWLINE); + + /* Receive prefix count */ + vty_out (vty, " %ld accepted prefixes%s", p->pcount[afi][safi], VTY_NEWLINE); + + /* Maximum prefix */ + if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX)) + { + vty_out (vty, " Maximum prefixes allowed %ld%s%s", p->pmax[afi][safi], + CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING) + ? " (warning-only)" : "", VTY_NEWLINE); + vty_out (vty, " Threshold for warning message %d%%", + p->pmax_threshold[afi][safi]); + if (p->pmax_restart[afi][safi]) + vty_out (vty, ", restart interval %d min", p->pmax_restart[afi][safi]); + vty_out (vty, "%s", VTY_NEWLINE); + } + + vty_out (vty, "%s", VTY_NEWLINE); +} + +static void +bgp_show_peer (struct vty *vty, struct peer *p) +{ + struct bgp *bgp; + char buf1[BUFSIZ]; + 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%s, ", + p->change_local_as ? p->change_local_as : p->local_as, + CHECK_FLAG (p->flags, PEER_FLAG_LOCAL_AS_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); + + /* Description. */ + if (p->desc) + vty_out (vty, " Description: %s%s", p->desc, VTY_NEWLINE); + + /* Peer-group */ + if (p->group) + vty_out (vty, " Member of peer-group %s for session parameters%s", + p->group->name, VTY_NEWLINE); + + /* Administrative shutdown. */ + if (CHECK_FLAG (p->flags, PEER_FLAG_SHUTDOWN)) + vty_out (vty, " Administratively shut down%s", VTY_NEWLINE); + + /* BGP Version. */ + vty_out (vty, " BGP version 4"); + vty_out (vty, ", remote router ID %s%s", + inet_ntop (AF_INET, &p->remote_id, buf1, BUFSIZ), + VTY_NEWLINE); + + /* Confederation */ + if (CHECK_FLAG (bgp->config, BGP_CONFIG_CONFEDERATION) + && bgp_confederation_peers_check (bgp, p->as)) + vty_out (vty, " Neighbor under common administration%s", VTY_NEWLINE); + + /* Status. */ + vty_out (vty, " BGP state = %s", + LOOKUP (bgp_status_msg, p->status)); + if (p->status == Established) + vty_out (vty, ", up for %8s", + peer_uptime (p->uptime, timebuf, BGP_UPTIME_LEN)); + else if (p->status == Active) + { + if (CHECK_FLAG (p->flags, PEER_FLAG_PASSIVE)) + vty_out (vty, " (passive)"); + else if (CHECK_FLAG (p->sflags, PEER_STATUS_NSF_WAIT)) + vty_out (vty, " (NSF passive)"); + } + vty_out (vty, "%s", VTY_NEWLINE); + + /* read timer */ + vty_out (vty, " Last read %s", peer_uptime (p->readtime, timebuf, BGP_UPTIME_LEN)); + + /* Configured timer values. */ + vty_out (vty, ", hold time is %d, keepalive interval is %d seconds%s", + p->v_holdtime, p->v_keepalive, VTY_NEWLINE); + if (CHECK_FLAG (p->config, PEER_CONFIG_TIMER)) + { + vty_out (vty, " Configured hold time is %d", p->holdtime); + vty_out (vty, ", keepalive interval is %d seconds%s", + p->keepalive, VTY_NEWLINE); + } + + /* Capability. */ + if (p->status == Established) + { + if (p->cap + || p->afc_adv[AFI_IP][SAFI_UNICAST] + || p->afc_recv[AFI_IP][SAFI_UNICAST] + || p->afc_adv[AFI_IP][SAFI_MULTICAST] + || p->afc_recv[AFI_IP][SAFI_MULTICAST] + || 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] + || 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]) + { + vty_out (vty, " Neighbor capabilities:%s", VTY_NEWLINE); + + /* AS4 */ + if (CHECK_FLAG (p->cap, PEER_CAP_AS4_RCV) + || CHECK_FLAG (p->cap, PEER_CAP_AS4_ADV)) + { + vty_out (vty, " 4 Byte AS:"); + if (CHECK_FLAG (p->cap, PEER_CAP_AS4_ADV)) + vty_out (vty, " advertised"); + if (CHECK_FLAG (p->cap, PEER_CAP_AS4_RCV)) + vty_out (vty, " %sreceived", + CHECK_FLAG (p->cap, PEER_CAP_AS4_ADV) ? "and " : ""); + vty_out (vty, "%s", VTY_NEWLINE); + } + /* Dynamic */ + if (CHECK_FLAG (p->cap, PEER_CAP_DYNAMIC_RCV) + || CHECK_FLAG (p->cap, PEER_CAP_DYNAMIC_ADV)) + { + vty_out (vty, " Dynamic:"); + if (CHECK_FLAG (p->cap, PEER_CAP_DYNAMIC_ADV)) + vty_out (vty, " advertised"); + if (CHECK_FLAG (p->cap, PEER_CAP_DYNAMIC_RCV)) + vty_out (vty, " %sreceived", + CHECK_FLAG (p->cap, PEER_CAP_DYNAMIC_ADV) ? "and " : ""); + vty_out (vty, "%s", VTY_NEWLINE); + } + + /* Route Refresh */ + if (CHECK_FLAG (p->cap, PEER_CAP_REFRESH_ADV) + || CHECK_FLAG (p->cap, PEER_CAP_REFRESH_NEW_RCV) + || CHECK_FLAG (p->cap, PEER_CAP_REFRESH_OLD_RCV)) + { + vty_out (vty, " Route refresh:"); + if (CHECK_FLAG (p->cap, PEER_CAP_REFRESH_ADV)) + vty_out (vty, " advertised"); + if (CHECK_FLAG (p->cap, PEER_CAP_REFRESH_NEW_RCV) + || CHECK_FLAG (p->cap, PEER_CAP_REFRESH_OLD_RCV)) + vty_out (vty, " %sreceived(%s)", + CHECK_FLAG (p->cap, PEER_CAP_REFRESH_ADV) ? "and " : "", + (CHECK_FLAG (p->cap, PEER_CAP_REFRESH_OLD_RCV) + && CHECK_FLAG (p->cap, PEER_CAP_REFRESH_NEW_RCV)) ? + "old & new" : CHECK_FLAG (p->cap, PEER_CAP_REFRESH_OLD_RCV) ? "old" : "new"); + + vty_out (vty, "%s", VTY_NEWLINE); + } + + /* Multiprotocol Extensions */ + for (afi = AFI_IP ; afi < AFI_MAX ; afi++) + for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++) + if (p->afc_adv[afi][safi] || p->afc_recv[afi][safi]) + { + vty_out (vty, " Address family %s:", afi_safi_print (afi, safi)); + if (p->afc_adv[afi][safi]) + vty_out (vty, " advertised"); + if (p->afc_recv[afi][safi]) + vty_out (vty, " %sreceived", p->afc_adv[afi][safi] ? "and " : ""); + vty_out (vty, "%s", VTY_NEWLINE); + } + + /* Gracefull Restart */ + if (CHECK_FLAG (p->cap, PEER_CAP_RESTART_RCV) + || CHECK_FLAG (p->cap, PEER_CAP_RESTART_ADV)) + { + vty_out (vty, " Graceful Restart Capabilty:"); + if (CHECK_FLAG (p->cap, PEER_CAP_RESTART_ADV)) + vty_out (vty, " advertised"); + if (CHECK_FLAG (p->cap, PEER_CAP_RESTART_RCV)) + vty_out (vty, " %sreceived", + CHECK_FLAG (p->cap, PEER_CAP_RESTART_ADV) ? "and " : ""); + vty_out (vty, "%s", VTY_NEWLINE); + + if (CHECK_FLAG (p->cap, PEER_CAP_RESTART_RCV)) + { + int restart_af_count = 0; + + vty_out (vty, " Remote Restart timer is %d seconds%s", + p->v_gr_restart, VTY_NEWLINE); + vty_out (vty, " Address families by peer:%s ", VTY_NEWLINE); + + for (afi = AFI_IP ; afi < AFI_MAX ; afi++) + for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++) + if (CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_RESTART_AF_RCV)) + { + vty_out (vty, "%s%s(%s)", restart_af_count ? ", " : "", + afi_safi_print (afi, safi), + CHECK_FLAG (p->af_cap[afi][safi], PEER_CAP_RESTART_AF_PRESERVE_RCV) ? + "preserved" : "not preserved"); + restart_af_count++; + } + if (! restart_af_count) + vty_out (vty, "none"); + vty_out (vty, "%s", VTY_NEWLINE); + } + } + } + } + + /* graceful restart information */ + if (CHECK_FLAG (p->cap, PEER_CAP_RESTART_RCV) + || p->t_gr_restart + || p->t_gr_stale) + { + int eor_send_af_count = 0; + int eor_receive_af_count = 0; + + vty_out (vty, " Graceful restart informations:%s", VTY_NEWLINE); + if (p->status == Established) + { + vty_out (vty, " End-of-RIB send: "); + for (afi = AFI_IP ; afi < AFI_MAX ; afi++) + for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++) + if (CHECK_FLAG (p->af_sflags[afi][safi], PEER_STATUS_EOR_SEND)) + { + vty_out (vty, "%s%s", eor_send_af_count ? ", " : "", + afi_safi_print (afi, safi)); + eor_send_af_count++; + } + vty_out (vty, "%s", VTY_NEWLINE); + + vty_out (vty, " End-of-RIB received: "); + for (afi = AFI_IP ; afi < AFI_MAX ; afi++) + for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++) + if (CHECK_FLAG (p->af_sflags[afi][safi], PEER_STATUS_EOR_RECEIVED)) + { + vty_out (vty, "%s%s", eor_receive_af_count ? ", " : "", + afi_safi_print (afi, safi)); + eor_receive_af_count++; + } + vty_out (vty, "%s", VTY_NEWLINE); + } + + if (p->t_gr_restart) + vty_out (vty, " The remaining time of restart timer is %ld%s", + thread_timer_remain_second (p->t_gr_restart), VTY_NEWLINE); + + if (p->t_gr_stale) + vty_out (vty, " The remaining time of stalepath timer is %ld%s", + thread_timer_remain_second (p->t_gr_stale), VTY_NEWLINE); + } + + /* Packet counts. */ + vty_out (vty, " Message statistics:%s", VTY_NEWLINE); + vty_out (vty, " Inq depth is 0%s", VTY_NEWLINE); + vty_out (vty, " Outq depth is %lu%s", (unsigned long) p->obuf->count, VTY_NEWLINE); + vty_out (vty, " Sent Rcvd%s", VTY_NEWLINE); + vty_out (vty, " Opens: %10d %10d%s", p->open_out, p->open_in, VTY_NEWLINE); + vty_out (vty, " Notifications: %10d %10d%s", p->notify_out, p->notify_in, VTY_NEWLINE); + vty_out (vty, " Updates: %10d %10d%s", p->update_out, p->update_in, VTY_NEWLINE); + vty_out (vty, " Keepalives: %10d %10d%s", p->keepalive_out, p->keepalive_in, VTY_NEWLINE); + vty_out (vty, " Route Refresh: %10d %10d%s", p->refresh_out, p->refresh_in, VTY_NEWLINE); + vty_out (vty, " Capability: %10d %10d%s", p->dynamic_cap_out, p->dynamic_cap_in, VTY_NEWLINE); + vty_out (vty, " Total: %10d %10d%s", p->open_out + p->notify_out + + p->update_out + p->keepalive_out + p->refresh_out + p->dynamic_cap_out, + p->open_in + p->notify_in + p->update_in + p->keepalive_in + p->refresh_in + + p->dynamic_cap_in, VTY_NEWLINE); + + /* advertisement-interval */ + vty_out (vty, " Minimum time between advertisement runs is %d seconds%s", + p->v_routeadv, VTY_NEWLINE); + + /* Update-source. */ + if (p->update_if || p->update_source) + { + vty_out (vty, " Update source is "); + if (p->update_if) + vty_out (vty, "%s", p->update_if); + else if (p->update_source) + vty_out (vty, "%s", + sockunion2str (p->update_source, buf1, SU_ADDRSTRLEN)); + vty_out (vty, "%s", VTY_NEWLINE); + } + + /* Default weight */ + if (CHECK_FLAG (p->config, PEER_CONFIG_WEIGHT)) + vty_out (vty, " Default weight %d%s", p->weight, + VTY_NEWLINE); + + vty_out (vty, "%s", VTY_NEWLINE); + + /* Address Family Information */ + for (afi = AFI_IP ; afi < AFI_MAX ; afi++) + for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++) + if (p->afc[afi][safi]) + bgp_show_peer_afi (vty, p, afi, safi); + + vty_out (vty, " Connections established %d; dropped %d%s", + p->established, p->dropped, + VTY_NEWLINE); + + if (! p->dropped) + vty_out (vty, " Last reset never%s", VTY_NEWLINE); + else + vty_out (vty, " Last reset %s, due to %s%s", + peer_uptime (p->resettime, timebuf, BGP_UPTIME_LEN), + peer_down_str[(int) p->last_reset], VTY_NEWLINE); + + if (CHECK_FLAG (p->sflags, PEER_STATUS_PREFIX_OVERFLOW)) + { + vty_out (vty, " Peer had exceeded the max. no. of prefixes configured.%s", VTY_NEWLINE); + + if (p->t_pmax_restart) + vty_out (vty, " Reduce the no. of prefix from %s, will restart in %ld seconds%s", + p->host, thread_timer_remain_second (p->t_pmax_restart), + VTY_NEWLINE); + else + vty_out (vty, " Reduce the no. of prefix and clear ip bgp %s to restore peering%s", + p->host, VTY_NEWLINE); + } + + /* EBGP Multihop and GTSM */ + 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) + { + vty_out (vty, "Local host: %s, Local port: %d%s", + sockunion2str (p->su_local, buf1, SU_ADDRSTRLEN), + ntohs (p->su_local->sin.sin_port), + VTY_NEWLINE); + } + + /* Remote address. */ + if (p->su_remote) + { + vty_out (vty, "Foreign host: %s, Foreign port: %d%s", + sockunion2str (p->su_remote, buf1, SU_ADDRSTRLEN), + ntohs (p->su_remote->sin.sin_port), + VTY_NEWLINE); + } + + /* Nexthop display. */ + if (p->su_local) + { + vty_out (vty, "Nexthop: %s%s", + inet_ntop (AF_INET, &p->nexthop.v4, buf1, BUFSIZ), + VTY_NEWLINE); + vty_out (vty, "Nexthop global: %s%s", + inet_ntop (AF_INET6, &p->nexthop.v6_global, buf1, BUFSIZ), + VTY_NEWLINE); + vty_out (vty, "Nexthop local: %s%s", + inet_ntop (AF_INET6, &p->nexthop.v6_local, buf1, BUFSIZ), + VTY_NEWLINE); + vty_out (vty, "BGP connection: %s%s", + p->shared_network ? "shared network" : "non shared network", + VTY_NEWLINE); + } + + /* 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", + thread_timer_remain_second (p->t_start), VTY_NEWLINE); + if (p->t_connect) + vty_out (vty, "Next connect timer due in %ld seconds%s", + thread_timer_remain_second (p->t_connect), VTY_NEWLINE); + + vty_out (vty, "Read thread: %s Write thread: %s%s", + p->t_read ? "on" : "off", + p->t_write ? "on" : "off", + VTY_NEWLINE); + + if (p->notify.code == BGP_NOTIFY_OPEN_ERR + && p->notify.subcode == BGP_NOTIFY_OPEN_UNSUP_CAPBL) + bgp_capability_vty_out (vty, p); + + vty_out (vty, "%s", VTY_NEWLINE); +} + +static int +bgp_show_neighbor (struct vty *vty, struct bgp *bgp, + enum show_type type, union sockunion *su) +{ + struct listnode *node, *nnode; + struct peer *peer; + int find = 0; + + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + switch (type) + { + case show_all: + bgp_show_peer (vty, peer); + break; + case show_peer: + if (sockunion_same (&peer->su, su)) + { + find = 1; + bgp_show_peer (vty, peer); + } + break; + } + } + + if (type == show_peer && ! find) + vty_out (vty, "%% No such neighbor%s", VTY_NEWLINE); + + return CMD_SUCCESS; +} + +static int +bgp_show_neighbor_vty (struct vty *vty, const char *name, + enum show_type type, const char *ip_str) +{ + int ret; + struct bgp *bgp; + union sockunion su; + + if (ip_str) + { + ret = str2sockunion (ip_str, &su); + if (ret < 0) + { + vty_out (vty, "%% Malformed address: %s%s", ip_str, VTY_NEWLINE); + return CMD_WARNING; + } + } + + if (name) + { + bgp = bgp_lookup_by_name (name); + + if (! bgp) + { + vty_out (vty, "%% No such BGP instance exist%s", VTY_NEWLINE); + return CMD_WARNING; + } + + bgp_show_neighbor (vty, bgp, type, &su); + + return CMD_SUCCESS; + } + + bgp = bgp_get_default (); + + if (bgp) + bgp_show_neighbor (vty, bgp, type, &su); + + return CMD_SUCCESS; +} + +/* "show ip bgp neighbors" commands. */DEFUN (show_ip_bgp_neighbors, + show_ip_bgp_neighbors_cmd, + "show ip bgp neighbors", + SHOW_STR + IP_STR + BGP_STR + "Detailed information on TCP and BGP neighbor connections\n") +{ + return bgp_show_neighbor_vty (vty, NULL, show_all, NULL); +} + +ALIAS (show_ip_bgp_neighbors, + show_ip_bgp_ipv4_neighbors_cmd, + "show ip bgp ipv4 (unicast|multicast) neighbors", + 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") + +ALIAS (show_ip_bgp_neighbors, + show_ip_bgp_vpnv4_all_neighbors_cmd, + "show ip bgp vpnv4 all neighbors", + SHOW_STR + IP_STR + BGP_STR + "Display VPNv4 NLRI specific information\n" + "Display information about all VPNv4 NLRIs\n" + "Detailed information on TCP and BGP neighbor connections\n") + +ALIAS (show_ip_bgp_neighbors, + show_ip_bgp_vpnv4_rd_neighbors_cmd, + "show ip bgp vpnv4 rd ASN:nn_or_IP-address:nn neighbors", + SHOW_STR + IP_STR + BGP_STR + "Display VPNv4 NLRI specific information\n" + "Display information for a route distinguisher\n" + "VPN Route Distinguisher\n" + "Detailed information on TCP and BGP neighbor connections\n") + +ALIAS (show_ip_bgp_neighbors, + show_bgp_ipv6_neighbors_cmd, + "show bgp ipv6 neighbors", + SHOW_STR + BGP_STR + "Address family\n" + "Detailed information on TCP and BGP neighbor connections\n") + +DEFUN (show_ip_bgp_neighbors_peer, + show_ip_bgp_neighbors_peer_cmd, + "show ip bgp neighbors (A.B.C.D|X:X::X:X)", + 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") +{ + return bgp_show_neighbor_vty (vty, NULL, show_peer, argv[argc - 1]); +} + +ALIAS (show_ip_bgp_neighbors_peer, + show_ip_bgp_ipv4_neighbors_peer_cmd, + "show ip bgp ipv4 (unicast|multicast) neighbors (A.B.C.D|X:X::X:X)", + 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") + +ALIAS (show_ip_bgp_neighbors_peer, + show_ip_bgp_vpnv4_all_neighbors_peer_cmd, + "show ip bgp vpnv4 all neighbors A.B.C.D", + SHOW_STR + IP_STR + BGP_STR + "Display VPNv4 NLRI specific information\n" + "Display information about all VPNv4 NLRIs\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n") + +ALIAS (show_ip_bgp_neighbors_peer, + show_ip_bgp_vpnv4_rd_neighbors_peer_cmd, + "show ip bgp vpnv4 rd ASN:nn_or_IP-address:nn neighbors A.B.C.D", + SHOW_STR + IP_STR + BGP_STR + "Display VPNv4 NLRI specific information\n" + "Display information about all VPNv4 NLRIs\n" + "Detailed information on TCP and BGP neighbor connections\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)", + 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") + +DEFUN (show_ip_bgp_instance_neighbors, + show_ip_bgp_instance_neighbors_cmd, + "show ip bgp view WORD neighbors", + SHOW_STR + IP_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); +} + +ALIAS (show_ip_bgp_instance_neighbors, + show_bgp_instance_ipv6_neighbors_cmd, + "show bgp view WORD ipv6 neighbors", + SHOW_STR + BGP_STR + "BGP view\n" + "View name\n" + "Address family\n" + "Detailed information on TCP and BGP neighbor connections\n") + +DEFUN (show_ip_bgp_instance_neighbors_peer, + show_ip_bgp_instance_neighbors_peer_cmd, + "show ip bgp view WORD neighbors (A.B.C.D|X:X::X:X)", + 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") +{ + return bgp_show_neighbor_vty (vty, argv[0], show_peer, argv[1]); +} + +/* 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_ip_bgp_paths, + show_ip_bgp_paths_cmd, + "show ip bgp paths", + SHOW_STR + IP_STR + BGP_STR + "Path information\n") +{ + vty_out (vty, "Address Refcnt Path%s", VTY_NEWLINE); + aspath_print_all_vty (vty); + return CMD_SUCCESS; +} + +DEFUN (show_ip_bgp_ipv4_paths, + show_ip_bgp_ipv4_paths_cmd, + "show ip bgp ipv4 (unicast|multicast) paths", + SHOW_STR + IP_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Path information\n") +{ + vty_out (vty, "Address Refcnt Path\r\n"); + aspath_print_all_vty (vty); + + 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 +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", (void *)backet, com->refcnt, + community_str (com), VTY_NEWLINE); +} + +/* Show BGP's community internal data. */ +DEFUN (show_ip_bgp_community_info, + show_ip_bgp_community_info_cmd, + "show ip bgp community-info", + SHOW_STR + IP_STR + BGP_STR + "List all bgp community information\n") +{ + vty_out (vty, "Address Refcnt Community%s", VTY_NEWLINE); + + hash_iterate (community_hash (), + (void (*) (struct hash_backet *, void *)) + community_show_all_iterator, + vty); + + return CMD_SUCCESS; +} + +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 large-community information\n") +{ + 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) +{ + char timebuf[BGP_UPTIME_LEN]; + char rmbuf[14]; + const char *rmname; + struct peer *peer; + struct listnode *node, *nnode; + int len; + int count = 0; + + if (CHECK_FLAG (rsclient->sflags, PEER_STATUS_GROUP)) + { + for (ALL_LIST_ELEMENTS (rsclient->group->peer, node, nnode, peer)) + { + count++; + bgp_write_rsclient_summary (vty, peer, afi, safi); + } + return count; + } + + len = vty_out (vty, "%s", rsclient->host); + len = 16 - len; + + if (len < 1) + vty_out (vty, "%s%*s", VTY_NEWLINE, 16, " "); + else + vty_out (vty, "%*s", len, " "); + + vty_out (vty, "4 "); + + vty_out (vty, "%10u ", rsclient->as); + + rmname = ROUTE_MAP_EXPORT_NAME(&rsclient->filter[afi][safi]); + if ( rmname && strlen (rmname) > 13 ) + { + sprintf (rmbuf, "%13s", "..."); + rmname = strncpy (rmbuf, rmname, 10); + } + else if (! rmname) + rmname = ""; + vty_out (vty, " %13s ", rmname); + + rmname = ROUTE_MAP_IMPORT_NAME(&rsclient->filter[afi][safi]); + if ( rmname && strlen (rmname) > 13 ) + { + sprintf (rmbuf, "%13s", "..."); + rmname = strncpy (rmbuf, rmname, 10); + } + else if (! rmname) + rmname = ""; + vty_out (vty, " %13s ", rmname); + + vty_out (vty, "%8s", peer_uptime (rsclient->uptime, timebuf, BGP_UPTIME_LEN)); + + if (CHECK_FLAG (rsclient->flags, PEER_FLAG_SHUTDOWN)) + vty_out (vty, " Idle (Admin)"); + else if (CHECK_FLAG (rsclient->sflags, PEER_STATUS_PREFIX_OVERFLOW)) + vty_out (vty, " Idle (PfxCt)"); + else + vty_out (vty, " %-11s", LOOKUP(bgp_status_msg, rsclient->status)); + + vty_out (vty, "%s", VTY_NEWLINE); + + return 1; +} + +static int +bgp_show_rsclient_summary (struct vty *vty, struct bgp *bgp, + afi_t afi, safi_t safi) +{ + struct peer *peer; + struct listnode *node, *nnode; + int count = 0; + + /* Header string for each address family. */ + static char header[] = "Neighbor V AS Export-Policy Import-Policy Up/Down State"; + + for (ALL_LIST_ELEMENTS (bgp->rsclient, node, nnode, peer)) + { + if (peer->afc[afi][safi] && + CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)) + { + if (! count) + { + vty_out (vty, + "Route Server's BGP router identifier %s%s", + inet_ntoa (bgp->router_id), VTY_NEWLINE); + vty_out (vty, + "Route Server's local AS number %u%s", bgp->as, + VTY_NEWLINE); + + vty_out (vty, "%s", VTY_NEWLINE); + vty_out (vty, "%s%s", header, VTY_NEWLINE); + } + + count += bgp_write_rsclient_summary (vty, peer, afi, safi); + } + } + + if (count) + vty_out (vty, "%sTotal number of Route Server Clients %d%s", VTY_NEWLINE, + count, VTY_NEWLINE); + else + vty_out (vty, "No %s Route Server Client is configured%s", + afi == AFI_IP ? "IPv4" : "IPv6", VTY_NEWLINE); + + return CMD_SUCCESS; +} + +static int +bgp_show_rsclient_summary_vty (struct vty *vty, const char *name, + afi_t afi, safi_t safi) +{ + struct bgp *bgp; + + if (name) + { + bgp = bgp_lookup_by_name (name); + + if (! bgp) + { + vty_out (vty, "%% No such BGP instance exist%s", VTY_NEWLINE); + return CMD_WARNING; + } + + bgp_show_rsclient_summary (vty, bgp, afi, safi); + return CMD_SUCCESS; + } + + bgp = bgp_get_default (); + + if (bgp) + bgp_show_rsclient_summary (vty, bgp, afi, safi); + + return CMD_SUCCESS; +} + +/* 'show bgp rsclient' commands. */ +DEFUN (show_ip_bgp_rsclient_summary, + show_ip_bgp_rsclient_summary_cmd, + "show ip bgp rsclient summary", + SHOW_STR + IP_STR + BGP_STR + "Information about Route Server Clients\n" + "Summary of all Route Server Clients\n") +{ + return bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP, SAFI_UNICAST); +} + +DEFUN (show_ip_bgp_instance_rsclient_summary, + show_ip_bgp_instance_rsclient_summary_cmd, + "show ip bgp view WORD rsclient summary", + SHOW_STR + IP_STR + BGP_STR + "BGP view\n" + "View name\n" + "Information about Route Server Clients\n" + "Summary of all Route Server Clients\n") +{ + return bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP, SAFI_UNICAST); +} + +DEFUN (show_ip_bgp_ipv4_rsclient_summary, + show_ip_bgp_ipv4_rsclient_summary_cmd, + "show ip bgp ipv4 (unicast|multicast) rsclient summary", + SHOW_STR + IP_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Information about Route Server Clients\n" + "Summary of all Route Server Clients\n") +{ + if (strncmp (argv[0], "m", 1) == 0) + return bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP, SAFI_MULTICAST); + + return bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP, SAFI_UNICAST); +} + +DEFUN (show_ip_bgp_instance_ipv4_rsclient_summary, + show_ip_bgp_instance_ipv4_rsclient_summary_cmd, + "show ip bgp view WORD ipv4 (unicast|multicast) rsclient summary", + SHOW_STR + IP_STR + BGP_STR + "BGP view\n" + "View name\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Information about Route Server Clients\n" + "Summary of all Route Server Clients\n") +{ + if (strncmp (argv[1], "m", 1) == 0) + return bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP, SAFI_MULTICAST); + + return bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP, SAFI_UNICAST); +} + +DEFUN (show_bgp_instance_ipv4_safi_rsclient_summary, + show_bgp_instance_ipv4_safi_rsclient_summary_cmd, + "show bgp view WORD ipv4 (unicast|multicast) rsclient summary", + 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 Clients\n" + "Summary of all Route Server Clients\n") +{ + safi_t safi; + + if (argc == 2) { + safi = (strncmp (argv[1], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST; + return bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP, safi); + } else { + safi = (strncmp (argv[0], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST; + return bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP, safi); + } +} + +ALIAS (show_bgp_instance_ipv4_safi_rsclient_summary, + show_bgp_ipv4_safi_rsclient_summary_cmd, + "show bgp ipv4 (unicast|multicast) rsclient summary", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Information about Route Server Clients\n" + "Summary of all Route Server Clients\n") + +DEFUN (show_bgp_rsclient_summary, + show_bgp_rsclient_summary_cmd, + "show bgp rsclient summary", + SHOW_STR + BGP_STR + "Information about Route Server Clients\n" + "Summary of all Route Server Clients\n") +{ + 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, + show_bgp_instance_rsclient_summary_cmd, + "show bgp view WORD rsclient summary", + SHOW_STR + BGP_STR + "BGP view\n" + "View name\n" + "Information about Route Server Clients\n" + "Summary of all Route Server Clients\n") +{ + 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; +} + +DEFUN (show_bgp_ipv6_rsclient_summary, + show_bgp_ipv6_rsclient_summary_cmd, + "show bgp ipv6 rsclient summary", + SHOW_STR + BGP_STR + "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; +} + +DEFUN (show_bgp_instance_ipv6_rsclient_summary, + show_bgp_instance_ipv6_rsclient_summary_cmd, + "show bgp view WORD ipv6 rsclient summary", + SHOW_STR + BGP_STR + "BGP view\n" + "View name\n" + "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, + "show bgp view WORD ipv6 (unicast|multicast) rsclient summary", + 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 Clients\n" + "Summary of all Route Server Clients\n") +{ + safi_t safi; + + if (argc == 2) { + safi = (strncmp (argv[1], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST; + return bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP6, safi); + } else { + safi = (strncmp (argv[0], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST; + return bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP6, safi); + } +} + +ALIAS (show_bgp_instance_ipv6_safi_rsclient_summary, + show_bgp_ipv6_safi_rsclient_summary_cmd, + "show bgp ipv6 (unicast|multicast) rsclient summary", + SHOW_STR + BGP_STR + IPV6_STR + "Address Family modifier\n" + "Address Family modifier\n" + "Information about Route Server Clients\n" + "Summary of all Route Server Clients\n") + +/* Redistribute VTY commands. */ + +DEFUN (bgp_redistribute_ipv4, + bgp_redistribute_ipv4_cmd, + "redistribute " QUAGGA_IP_REDIST_STR_BGPD, + "Redistribute information from another routing protocol\n" + QUAGGA_IP_REDIST_HELP_STR_BGPD) +{ + 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; + } + return bgp_redistribute_set (vty->index, AFI_IP, type); +} + +DEFUN (bgp_redistribute_ipv4_rmap, + bgp_redistribute_ipv4_rmap_cmd, + "redistribute " QUAGGA_IP_REDIST_STR_BGPD " route-map WORD", + "Redistribute information from another routing protocol\n" + 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_rmap_set (vty->index, AFI_IP, type, argv[1]); + return bgp_redistribute_set (vty->index, AFI_IP, type); +} + +DEFUN (bgp_redistribute_ipv4_metric, + bgp_redistribute_ipv4_metric_cmd, + "redistribute " QUAGGA_IP_REDIST_STR_BGPD " metric <0-4294967295>", + "Redistribute information from another routing protocol\n" + QUAGGA_IP_REDIST_HELP_STR_BGPD + "Metric for redistributed routes\n" + "Default metric\n") +{ + int type; + u_int32_t metric; + + 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; + } + VTY_GET_INTEGER ("metric", metric, argv[1]); + + bgp_redistribute_metric_set (vty->index, AFI_IP, type, metric); + return bgp_redistribute_set (vty->index, AFI_IP, type); +} + +DEFUN (bgp_redistribute_ipv4_rmap_metric, + bgp_redistribute_ipv4_rmap_metric_cmd, + "redistribute " QUAGGA_IP_REDIST_STR_BGPD " route-map WORD metric <0-4294967295>", + "Redistribute information from another routing protocol\n" + QUAGGA_IP_REDIST_HELP_STR_BGPD + "Route map reference\n" + "Pointer to route-map entries\n" + "Metric for redistributed routes\n" + "Default metric\n") +{ + int type; + u_int32_t metric; + + 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; + } + VTY_GET_INTEGER ("metric", metric, argv[2]); + + bgp_redistribute_rmap_set (vty->index, AFI_IP, type, argv[1]); + bgp_redistribute_metric_set (vty->index, AFI_IP, type, metric); + return bgp_redistribute_set (vty->index, AFI_IP, type); +} + +DEFUN (bgp_redistribute_ipv4_metric_rmap, + bgp_redistribute_ipv4_metric_rmap_cmd, + "redistribute " QUAGGA_IP_REDIST_STR_BGPD " metric <0-4294967295> route-map WORD", + "Redistribute information from another routing protocol\n" + QUAGGA_IP_REDIST_HELP_STR_BGPD + "Metric for redistributed routes\n" + "Default metric\n" + "Route map reference\n" + "Pointer to route-map entries\n") +{ + int type; + u_int32_t metric; + + 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; + } + VTY_GET_INTEGER ("metric", metric, argv[1]); + + bgp_redistribute_metric_set (vty->index, AFI_IP, type, metric); + bgp_redistribute_rmap_set (vty->index, AFI_IP, type, argv[2]); + return bgp_redistribute_set (vty->index, AFI_IP, type); +} + +DEFUN (no_bgp_redistribute_ipv4, + no_bgp_redistribute_ipv4_cmd, + "no redistribute " QUAGGA_IP_REDIST_STR_BGPD, + NO_STR + "Redistribute information from another routing protocol\n" + QUAGGA_IP_REDIST_HELP_STR_BGPD) +{ + 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; + } + + return bgp_redistribute_unset (vty->index, AFI_IP, type); +} + +ALIAS (no_bgp_redistribute_ipv4, + no_bgp_redistribute_ipv4_rmap_cmd, + "no redistribute " QUAGGA_IP_REDIST_STR_BGPD " route-map WORD", + NO_STR + "Redistribute information from another routing protocol\n" + QUAGGA_IP_REDIST_HELP_STR_BGPD + "Route map reference\n" + "Pointer to route-map entries\n") + +ALIAS (no_bgp_redistribute_ipv4, + no_bgp_redistribute_ipv4_metric_cmd, + "no redistribute " QUAGGA_IP_REDIST_STR_BGPD " metric <0-4294967295>", + NO_STR + "Redistribute information from another routing protocol\n" + QUAGGA_IP_REDIST_HELP_STR_BGPD + "Metric for redistributed routes\n" + "Default metric\n") + +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 + "Redistribute information from another routing protocol\n" + QUAGGA_IP_REDIST_HELP_STR_BGPD + "Route map reference\n" + "Pointer to route-map entries\n" + "Metric for redistributed routes\n" + "Default metric\n") + +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 + "Redistribute information from another routing protocol\n" + QUAGGA_IP_REDIST_HELP_STR_BGPD + "Metric for redistributed routes\n" + "Default metric\n" + "Route map reference\n" + "Pointer to route-map entries\n") + +DEFUN (bgp_redistribute_ipv6, + bgp_redistribute_ipv6_cmd, + "redistribute " QUAGGA_IP6_REDIST_STR_BGPD, + "Redistribute information from another routing protocol\n" + QUAGGA_IP6_REDIST_HELP_STR_BGPD) +{ + 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; + } + + return bgp_redistribute_set (vty->index, AFI_IP6, type); +} + +DEFUN (bgp_redistribute_ipv6_rmap, + bgp_redistribute_ipv6_rmap_cmd, + "redistribute " QUAGGA_IP6_REDIST_STR_BGPD " route-map WORD", + "Redistribute information from another routing protocol\n" + 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_rmap_set (vty->index, AFI_IP6, type, argv[1]); + return bgp_redistribute_set (vty->index, AFI_IP6, type); +} + +DEFUN (bgp_redistribute_ipv6_metric, + bgp_redistribute_ipv6_metric_cmd, + "redistribute " QUAGGA_IP6_REDIST_STR_BGPD " metric <0-4294967295>", + "Redistribute information from another routing protocol\n" + QUAGGA_IP6_REDIST_HELP_STR_BGPD + "Metric for redistributed routes\n" + "Default metric\n") +{ + int type; + u_int32_t metric; + + 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; + } + VTY_GET_INTEGER ("metric", metric, argv[1]); + + bgp_redistribute_metric_set (vty->index, AFI_IP6, type, metric); + return bgp_redistribute_set (vty->index, AFI_IP6, type); +} + +DEFUN (bgp_redistribute_ipv6_rmap_metric, + bgp_redistribute_ipv6_rmap_metric_cmd, + "redistribute " QUAGGA_IP6_REDIST_STR_BGPD " route-map WORD metric <0-4294967295>", + "Redistribute information from another routing protocol\n" + QUAGGA_IP6_REDIST_HELP_STR_BGPD + "Route map reference\n" + "Pointer to route-map entries\n" + "Metric for redistributed routes\n" + "Default metric\n") +{ + int type; + u_int32_t metric; + + 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; + } + VTY_GET_INTEGER ("metric", metric, argv[2]); + + bgp_redistribute_rmap_set (vty->index, AFI_IP6, type, argv[1]); + bgp_redistribute_metric_set (vty->index, AFI_IP6, type, metric); + return bgp_redistribute_set (vty->index, AFI_IP6, type); +} + +DEFUN (bgp_redistribute_ipv6_metric_rmap, + bgp_redistribute_ipv6_metric_rmap_cmd, + "redistribute " QUAGGA_IP6_REDIST_STR_BGPD " metric <0-4294967295> route-map WORD", + "Redistribute information from another routing protocol\n" + QUAGGA_IP6_REDIST_HELP_STR_BGPD + "Metric for redistributed routes\n" + "Default metric\n" + "Route map reference\n" + "Pointer to route-map entries\n") +{ + int type; + u_int32_t metric; + + 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; + } + VTY_GET_INTEGER ("metric", metric, argv[1]); + + bgp_redistribute_metric_set (vty->index, AFI_IP6, type, metric); + bgp_redistribute_rmap_set (vty->index, AFI_IP6, type, argv[2]); + return bgp_redistribute_set (vty->index, AFI_IP6, type); +} + +DEFUN (no_bgp_redistribute_ipv6, + no_bgp_redistribute_ipv6_cmd, + "no redistribute " QUAGGA_IP6_REDIST_STR_BGPD, + NO_STR + "Redistribute information from another routing protocol\n" + QUAGGA_IP6_REDIST_HELP_STR_BGPD) +{ + 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; + } + + return bgp_redistribute_unset (vty->index, AFI_IP6, type); +} + +ALIAS (no_bgp_redistribute_ipv6, + no_bgp_redistribute_ipv6_rmap_cmd, + "no redistribute " QUAGGA_IP6_REDIST_STR_BGPD " route-map WORD", + NO_STR + "Redistribute information from another routing protocol\n" + QUAGGA_IP6_REDIST_HELP_STR_BGPD + "Route map reference\n" + "Pointer to route-map entries\n") + +ALIAS (no_bgp_redistribute_ipv6, + no_bgp_redistribute_ipv6_metric_cmd, + "no redistribute " QUAGGA_IP6_REDIST_STR_BGPD " metric <0-4294967295>", + NO_STR + "Redistribute information from another routing protocol\n" + QUAGGA_IP6_REDIST_HELP_STR_BGPD + "Metric for redistributed routes\n" + "Default metric\n") + +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 + "Redistribute information from another routing protocol\n" + QUAGGA_IP6_REDIST_HELP_STR_BGPD + "Route map reference\n" + "Pointer to route-map entries\n" + "Metric for redistributed routes\n" + "Default metric\n") + +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 + "Redistribute information from another routing protocol\n" + QUAGGA_IP6_REDIST_HELP_STR_BGPD + "Metric for redistributed routes\n" + "Default metric\n" + "Route map reference\n" + "Pointer to route-map entries\n") + +int +bgp_config_write_redistribute (struct vty *vty, struct bgp *bgp, afi_t afi, + safi_t safi, int *write) +{ + int i; + + /* Unicast redistribution only. */ + if (safi != SAFI_UNICAST) + return 0; + + for (i = 0; i < ZEBRA_ROUTE_MAX; i++) + { + /* Redistribute BGP does not make sense. */ + if (bgp->redist[afi][i] && i != ZEBRA_ROUTE_BGP) + { + /* Display "address-family" when it is not yet diplayed. */ + bgp_config_write_family_header (vty, afi, safi, write); + + /* "redistribute" configuration. */ + vty_out (vty, " redistribute %s", zebra_route_string(i)); + + if (bgp->redist_metric_flag[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); + + vty_out (vty, "%s", VTY_NEWLINE); + } + } + return *write; +} + +/* BGP node structure. */ +static struct cmd_node bgp_node = +{ + BGP_NODE, + "%s(config-router)# ", + 1, +}; + +static struct cmd_node bgp_ipv4_unicast_node = +{ + BGP_IPV4_NODE, + "%s(config-router-af)# ", + 1, +}; + +static struct cmd_node bgp_ipv4_multicast_node = +{ + BGP_IPV4M_NODE, + "%s(config-router-af)# ", + 1, +}; + +static struct cmd_node bgp_ipv6_unicast_node = +{ + BGP_IPV6_NODE, + "%s(config-router-af)# ", + 1, +}; + +static struct cmd_node bgp_ipv6_multicast_node = +{ + BGP_IPV6M_NODE, + "%s(config-router-af)# ", + 1, +}; + +static struct cmd_node bgp_vpnv4_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 +bgp_vty_init (void) +{ + /* Install bgp top node. */ + install_node (&bgp_node, bgp_config_write); + install_node (&bgp_ipv4_unicast_node, NULL); + install_node (&bgp_ipv4_multicast_node, NULL); + 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); + install_default (BGP_IPV4_NODE); + install_default (BGP_IPV4M_NODE); + 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); + install_element (CONFIG_NODE, &no_bgp_multiple_instance_cmd); + + /* "bgp config-type" commands. */ + install_element (CONFIG_NODE, &bgp_config_type_cmd); + install_element (CONFIG_NODE, &no_bgp_config_type_cmd); + + /* Dummy commands (Currently not supported) */ + install_element (BGP_NODE, &no_synchronization_cmd); + install_element (BGP_NODE, &no_auto_summary_cmd); + + /* "router bgp" commands. */ + install_element (CONFIG_NODE, &router_bgp_cmd); + install_element (CONFIG_NODE, &router_bgp_view_cmd); + + /* "no router bgp" commands. */ + install_element (CONFIG_NODE, &no_router_bgp_cmd); + install_element (CONFIG_NODE, &no_router_bgp_view_cmd); + + /* "bgp router-id" commands. */ + install_element (BGP_NODE, &bgp_router_id_cmd); + install_element (BGP_NODE, &no_bgp_router_id_cmd); + install_element (BGP_NODE, &no_bgp_router_id_val_cmd); + + /* "bgp cluster-id" commands. */ + install_element (BGP_NODE, &bgp_cluster_id_cmd); + install_element (BGP_NODE, &bgp_cluster_id32_cmd); + install_element (BGP_NODE, &no_bgp_cluster_id_cmd); + install_element (BGP_NODE, &no_bgp_cluster_id_arg_cmd); + + /* "bgp confederation" commands. */ + install_element (BGP_NODE, &bgp_confederation_identifier_cmd); + install_element (BGP_NODE, &no_bgp_confederation_identifier_cmd); + install_element (BGP_NODE, &no_bgp_confederation_identifier_arg_cmd); + + /* "bgp confederation peers" commands. */ + 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); + install_element (BGP_NODE, &no_bgp_timers_arg_cmd); + + /* "bgp client-to-client reflection" commands */ + install_element (BGP_NODE, &no_bgp_client_to_client_reflection_cmd); + install_element (BGP_NODE, &bgp_client_to_client_reflection_cmd); + + /* "bgp always-compare-med" commands */ + install_element (BGP_NODE, &bgp_always_compare_med_cmd); + install_element (BGP_NODE, &no_bgp_always_compare_med_cmd); + + /* "bgp deterministic-med" commands */ + install_element (BGP_NODE, &bgp_deterministic_med_cmd); + install_element (BGP_NODE, &no_bgp_deterministic_med_cmd); + + /* "bgp graceful-restart" commands */ + install_element (BGP_NODE, &bgp_graceful_restart_cmd); + install_element (BGP_NODE, &no_bgp_graceful_restart_cmd); + 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); + install_element (BGP_NODE, &no_bgp_fast_external_failover_cmd); + + /* "bgp enforce-first-as" commands */ + install_element (BGP_NODE, &bgp_enforce_first_as_cmd); + install_element (BGP_NODE, &no_bgp_enforce_first_as_cmd); + + /* "bgp bestpath compare-routerid" commands */ + install_element (BGP_NODE, &bgp_bestpath_compare_router_id_cmd); + install_element (BGP_NODE, &no_bgp_bestpath_compare_router_id_cmd); + + /* "bgp bestpath as-path ignore" commands */ + install_element (BGP_NODE, &bgp_bestpath_aspath_ignore_cmd); + install_element (BGP_NODE, &no_bgp_bestpath_aspath_ignore_cmd); + + /* "bgp bestpath as-path confed" commands */ + 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); + + /* "bgp bestpath med" commands */ + install_element (BGP_NODE, &bgp_bestpath_med_cmd); + install_element (BGP_NODE, &bgp_bestpath_med2_cmd); + install_element (BGP_NODE, &bgp_bestpath_med3_cmd); + install_element (BGP_NODE, &no_bgp_bestpath_med_cmd); + install_element (BGP_NODE, &no_bgp_bestpath_med2_cmd); + install_element (BGP_NODE, &no_bgp_bestpath_med3_cmd); + + /* "no bgp default ipv4-unicast" commands. */ + install_element (BGP_NODE, &no_bgp_default_ipv4_unicast_cmd); + install_element (BGP_NODE, &bgp_default_ipv4_unicast_cmd); + + /* "bgp network import-check" commands. */ + install_element (BGP_NODE, &bgp_network_import_check_cmd); + install_element (BGP_NODE, &no_bgp_network_import_check_cmd); + + /* "bgp default local-preference" commands. */ + install_element (BGP_NODE, &bgp_default_local_preference_cmd); + 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); + install_element (BGP_NODE, &no_neighbor_remote_as_cmd); + + /* "neighbor peer-group" commands. */ + install_element (BGP_NODE, &neighbor_peer_group_cmd); + install_element (BGP_NODE, &no_neighbor_peer_group_cmd); + install_element (BGP_NODE, &no_neighbor_peer_group_remote_as_cmd); + + /* "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); + install_element (BGP_NODE, &no_neighbor_password_cmd); + + /* "neighbor activate" commands. */ + install_element (BGP_NODE, &neighbor_activate_cmd); + install_element (BGP_IPV4_NODE, &neighbor_activate_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_activate_cmd); + 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); + install_element (BGP_IPV4_NODE, &no_neighbor_activate_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_activate_cmd); + 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); + install_element (BGP_IPV4_NODE, &neighbor_set_peer_group_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_set_peer_group_cmd); + 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); + install_element (BGP_IPV4_NODE, &no_neighbor_set_peer_group_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_set_peer_group_cmd); + 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); + install_element (BGP_NODE, &no_neighbor_soft_reconfiguration_cmd); + install_element (BGP_IPV4_NODE, &neighbor_soft_reconfiguration_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_soft_reconfiguration_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_soft_reconfiguration_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_soft_reconfiguration_cmd); + install_element (BGP_IPV6_NODE, &neighbor_soft_reconfiguration_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_soft_reconfiguration_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_soft_reconfiguration_cmd); + 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); + install_element (BGP_NODE, &neighbor_attr_unchanged1_cmd); + install_element (BGP_NODE, &neighbor_attr_unchanged2_cmd); + install_element (BGP_NODE, &neighbor_attr_unchanged3_cmd); + install_element (BGP_NODE, &neighbor_attr_unchanged4_cmd); + install_element (BGP_NODE, &neighbor_attr_unchanged5_cmd); + install_element (BGP_NODE, &neighbor_attr_unchanged6_cmd); + install_element (BGP_NODE, &neighbor_attr_unchanged7_cmd); + install_element (BGP_NODE, &neighbor_attr_unchanged8_cmd); + install_element (BGP_NODE, &neighbor_attr_unchanged9_cmd); + install_element (BGP_NODE, &neighbor_attr_unchanged10_cmd); + install_element (BGP_NODE, &no_neighbor_attr_unchanged_cmd); + install_element (BGP_NODE, &no_neighbor_attr_unchanged1_cmd); + install_element (BGP_NODE, &no_neighbor_attr_unchanged2_cmd); + install_element (BGP_NODE, &no_neighbor_attr_unchanged3_cmd); + install_element (BGP_NODE, &no_neighbor_attr_unchanged4_cmd); + install_element (BGP_NODE, &no_neighbor_attr_unchanged5_cmd); + install_element (BGP_NODE, &no_neighbor_attr_unchanged6_cmd); + install_element (BGP_NODE, &no_neighbor_attr_unchanged7_cmd); + install_element (BGP_NODE, &no_neighbor_attr_unchanged8_cmd); + install_element (BGP_NODE, &no_neighbor_attr_unchanged9_cmd); + install_element (BGP_NODE, &no_neighbor_attr_unchanged10_cmd); + install_element (BGP_IPV4_NODE, &neighbor_attr_unchanged_cmd); + install_element (BGP_IPV4_NODE, &neighbor_attr_unchanged1_cmd); + install_element (BGP_IPV4_NODE, &neighbor_attr_unchanged2_cmd); + install_element (BGP_IPV4_NODE, &neighbor_attr_unchanged3_cmd); + install_element (BGP_IPV4_NODE, &neighbor_attr_unchanged4_cmd); + install_element (BGP_IPV4_NODE, &neighbor_attr_unchanged5_cmd); + install_element (BGP_IPV4_NODE, &neighbor_attr_unchanged6_cmd); + install_element (BGP_IPV4_NODE, &neighbor_attr_unchanged7_cmd); + install_element (BGP_IPV4_NODE, &neighbor_attr_unchanged8_cmd); + install_element (BGP_IPV4_NODE, &neighbor_attr_unchanged9_cmd); + install_element (BGP_IPV4_NODE, &neighbor_attr_unchanged10_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_attr_unchanged_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_attr_unchanged1_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_attr_unchanged2_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_attr_unchanged3_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_attr_unchanged4_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_attr_unchanged5_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_attr_unchanged6_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_attr_unchanged7_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_attr_unchanged8_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_attr_unchanged9_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_attr_unchanged10_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_attr_unchanged_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_attr_unchanged1_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_attr_unchanged2_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_attr_unchanged3_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_attr_unchanged4_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_attr_unchanged5_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_attr_unchanged6_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_attr_unchanged7_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_attr_unchanged8_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_attr_unchanged9_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_attr_unchanged10_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_attr_unchanged_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_attr_unchanged1_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_attr_unchanged2_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_attr_unchanged3_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_attr_unchanged4_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_attr_unchanged5_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_attr_unchanged6_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_attr_unchanged7_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_attr_unchanged8_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_attr_unchanged9_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_attr_unchanged10_cmd); + install_element (BGP_IPV6_NODE, &neighbor_attr_unchanged_cmd); + install_element (BGP_IPV6_NODE, &neighbor_attr_unchanged1_cmd); + install_element (BGP_IPV6_NODE, &neighbor_attr_unchanged2_cmd); + install_element (BGP_IPV6_NODE, &neighbor_attr_unchanged3_cmd); + install_element (BGP_IPV6_NODE, &neighbor_attr_unchanged4_cmd); + install_element (BGP_IPV6_NODE, &neighbor_attr_unchanged5_cmd); + install_element (BGP_IPV6_NODE, &neighbor_attr_unchanged6_cmd); + install_element (BGP_IPV6_NODE, &neighbor_attr_unchanged7_cmd); + install_element (BGP_IPV6_NODE, &neighbor_attr_unchanged8_cmd); + install_element (BGP_IPV6_NODE, &neighbor_attr_unchanged9_cmd); + install_element (BGP_IPV6_NODE, &neighbor_attr_unchanged10_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_attr_unchanged_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_attr_unchanged1_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_attr_unchanged2_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_attr_unchanged3_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_attr_unchanged4_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_attr_unchanged5_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_attr_unchanged6_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_attr_unchanged7_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_attr_unchanged8_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_attr_unchanged9_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_attr_unchanged10_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_attr_unchanged_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_attr_unchanged1_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_attr_unchanged2_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_attr_unchanged3_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_attr_unchanged4_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_attr_unchanged5_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_attr_unchanged6_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_attr_unchanged7_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_attr_unchanged8_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_attr_unchanged9_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_attr_unchanged10_cmd); + install_element (BGP_IPV6M_NODE, &no_neighbor_attr_unchanged_cmd); + install_element (BGP_IPV6M_NODE, &no_neighbor_attr_unchanged1_cmd); + install_element (BGP_IPV6M_NODE, &no_neighbor_attr_unchanged2_cmd); + install_element (BGP_IPV6M_NODE, &no_neighbor_attr_unchanged3_cmd); + install_element (BGP_IPV6M_NODE, &no_neighbor_attr_unchanged4_cmd); + install_element (BGP_IPV6M_NODE, &no_neighbor_attr_unchanged5_cmd); + install_element (BGP_IPV6M_NODE, &no_neighbor_attr_unchanged6_cmd); + install_element (BGP_IPV6M_NODE, &no_neighbor_attr_unchanged7_cmd); + install_element (BGP_IPV6M_NODE, &no_neighbor_attr_unchanged8_cmd); + install_element (BGP_IPV6M_NODE, &no_neighbor_attr_unchanged9_cmd); + install_element (BGP_IPV6M_NODE, &no_neighbor_attr_unchanged10_cmd); + install_element (BGP_VPNV4_NODE, &neighbor_attr_unchanged_cmd); + install_element (BGP_VPNV4_NODE, &neighbor_attr_unchanged1_cmd); + install_element (BGP_VPNV4_NODE, &neighbor_attr_unchanged2_cmd); + install_element (BGP_VPNV4_NODE, &neighbor_attr_unchanged3_cmd); + install_element (BGP_VPNV4_NODE, &neighbor_attr_unchanged4_cmd); + install_element (BGP_VPNV4_NODE, &neighbor_attr_unchanged5_cmd); + install_element (BGP_VPNV4_NODE, &neighbor_attr_unchanged6_cmd); + install_element (BGP_VPNV4_NODE, &neighbor_attr_unchanged7_cmd); + install_element (BGP_VPNV4_NODE, &neighbor_attr_unchanged8_cmd); + install_element (BGP_VPNV4_NODE, &neighbor_attr_unchanged9_cmd); + install_element (BGP_VPNV4_NODE, &neighbor_attr_unchanged10_cmd); + install_element (BGP_VPNV4_NODE, &no_neighbor_attr_unchanged_cmd); + install_element (BGP_VPNV4_NODE, &no_neighbor_attr_unchanged1_cmd); + install_element (BGP_VPNV4_NODE, &no_neighbor_attr_unchanged2_cmd); + install_element (BGP_VPNV4_NODE, &no_neighbor_attr_unchanged3_cmd); + install_element (BGP_VPNV4_NODE, &no_neighbor_attr_unchanged4_cmd); + install_element (BGP_VPNV4_NODE, &no_neighbor_attr_unchanged5_cmd); + install_element (BGP_VPNV4_NODE, &no_neighbor_attr_unchanged6_cmd); + install_element (BGP_VPNV4_NODE, &no_neighbor_attr_unchanged7_cmd); + install_element (BGP_VPNV4_NODE, &no_neighbor_attr_unchanged8_cmd); + 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); + + /* "transparent-as" and "transparent-nexthop" for old version + compatibility. */ + install_element (BGP_NODE, &neighbor_transparent_as_cmd); + install_element (BGP_NODE, &neighbor_transparent_nexthop_cmd); + + /* "neighbor next-hop-self" commands. */ + install_element (BGP_NODE, &neighbor_nexthop_self_cmd); + install_element (BGP_NODE, &no_neighbor_nexthop_self_cmd); + install_element (BGP_IPV4_NODE, &neighbor_nexthop_self_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_nexthop_self_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_nexthop_self_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_nexthop_self_cmd); + install_element (BGP_IPV6_NODE, &neighbor_nexthop_self_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_nexthop_self_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_nexthop_self_cmd); + 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); + install_element (BGP_NODE, &no_neighbor_remove_private_as_cmd); + install_element (BGP_IPV4_NODE, &neighbor_remove_private_as_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_remove_private_as_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_remove_private_as_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_remove_private_as_cmd); + install_element (BGP_IPV6_NODE, &neighbor_remove_private_as_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_remove_private_as_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_remove_private_as_cmd); + 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); + install_element (BGP_NODE, &neighbor_send_community_type_cmd); + install_element (BGP_NODE, &no_neighbor_send_community_cmd); + install_element (BGP_NODE, &no_neighbor_send_community_type_cmd); + install_element (BGP_IPV4_NODE, &neighbor_send_community_cmd); + install_element (BGP_IPV4_NODE, &neighbor_send_community_type_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_send_community_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_send_community_type_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_send_community_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_send_community_type_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_send_community_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_send_community_type_cmd); + install_element (BGP_IPV6_NODE, &neighbor_send_community_cmd); + install_element (BGP_IPV6_NODE, &neighbor_send_community_type_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_send_community_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_send_community_type_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_send_community_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_send_community_type_cmd); + install_element (BGP_IPV6M_NODE, &no_neighbor_send_community_cmd); + install_element (BGP_IPV6M_NODE, &no_neighbor_send_community_type_cmd); + install_element (BGP_VPNV4_NODE, &neighbor_send_community_cmd); + 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); + install_element (BGP_NODE, &no_neighbor_route_reflector_client_cmd); + install_element (BGP_IPV4_NODE, &neighbor_route_reflector_client_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_route_reflector_client_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_route_reflector_client_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_route_reflector_client_cmd); + install_element (BGP_IPV6_NODE, &neighbor_route_reflector_client_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_route_reflector_client_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_route_reflector_client_cmd); + 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); + install_element (BGP_NODE, &no_neighbor_route_server_client_cmd); + install_element (BGP_IPV4_NODE, &neighbor_route_server_client_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_route_server_client_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_route_server_client_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_route_server_client_cmd); + install_element (BGP_IPV6_NODE, &neighbor_route_server_client_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_route_server_client_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_route_server_client_cmd); + 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); + install_element (BGP_NODE, &no_neighbor_passive_cmd); + + /* "neighbor shutdown" commands. */ + install_element (BGP_NODE, &neighbor_shutdown_cmd); + install_element (BGP_NODE, &no_neighbor_shutdown_cmd); + + /* Deprecated "neighbor capability route-refresh" commands.*/ + install_element (BGP_NODE, &neighbor_capability_route_refresh_cmd); + install_element (BGP_NODE, &no_neighbor_capability_route_refresh_cmd); + + /* "neighbor capability orf prefix-list" commands.*/ + install_element (BGP_NODE, &neighbor_capability_orf_prefix_cmd); + install_element (BGP_NODE, &no_neighbor_capability_orf_prefix_cmd); + install_element (BGP_IPV4_NODE, &neighbor_capability_orf_prefix_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_capability_orf_prefix_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_capability_orf_prefix_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_capability_orf_prefix_cmd); + install_element (BGP_IPV6_NODE, &neighbor_capability_orf_prefix_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_capability_orf_prefix_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_capability_orf_prefix_cmd); + install_element (BGP_IPV6M_NODE, &no_neighbor_capability_orf_prefix_cmd); + + /* "neighbor capability dynamic" commands.*/ + install_element (BGP_NODE, &neighbor_capability_dynamic_cmd); + install_element (BGP_NODE, &no_neighbor_capability_dynamic_cmd); + + /* "neighbor dont-capability-negotiate" commands. */ + install_element (BGP_NODE, &neighbor_dont_capability_negotiate_cmd); + install_element (BGP_NODE, &no_neighbor_dont_capability_negotiate_cmd); + + /* "neighbor ebgp-multihop" commands. */ + install_element (BGP_NODE, &neighbor_ebgp_multihop_cmd); + install_element (BGP_NODE, &neighbor_ebgp_multihop_ttl_cmd); + install_element (BGP_NODE, &no_neighbor_ebgp_multihop_cmd); + install_element (BGP_NODE, &no_neighbor_ebgp_multihop_ttl_cmd); + + /* "neighbor disable-connected-check" commands. */ + install_element (BGP_NODE, &neighbor_disable_connected_check_cmd); + install_element (BGP_NODE, &no_neighbor_disable_connected_check_cmd); + install_element (BGP_NODE, &neighbor_enforce_multihop_cmd); + install_element (BGP_NODE, &no_neighbor_enforce_multihop_cmd); + + /* "neighbor description" commands. */ + install_element (BGP_NODE, &neighbor_description_cmd); + install_element (BGP_NODE, &no_neighbor_description_cmd); + install_element (BGP_NODE, &no_neighbor_description_val_cmd); + + /* "neighbor update-source" commands. "*/ + install_element (BGP_NODE, &neighbor_update_source_cmd); + install_element (BGP_NODE, &no_neighbor_update_source_cmd); + + /* "neighbor default-originate" commands. */ + install_element (BGP_NODE, &neighbor_default_originate_cmd); + install_element (BGP_NODE, &neighbor_default_originate_rmap_cmd); + install_element (BGP_NODE, &no_neighbor_default_originate_cmd); + install_element (BGP_NODE, &no_neighbor_default_originate_rmap_cmd); + install_element (BGP_IPV4_NODE, &neighbor_default_originate_cmd); + install_element (BGP_IPV4_NODE, &neighbor_default_originate_rmap_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_default_originate_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_default_originate_rmap_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_default_originate_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_default_originate_rmap_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_default_originate_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_default_originate_rmap_cmd); + install_element (BGP_IPV6_NODE, &neighbor_default_originate_cmd); + install_element (BGP_IPV6_NODE, &neighbor_default_originate_rmap_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_default_originate_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_default_originate_rmap_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_default_originate_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_default_originate_rmap_cmd); + install_element (BGP_IPV6M_NODE, &no_neighbor_default_originate_cmd); + install_element (BGP_IPV6M_NODE, &no_neighbor_default_originate_rmap_cmd); + + /* "neighbor port" commands. */ + install_element (BGP_NODE, &neighbor_port_cmd); + install_element (BGP_NODE, &no_neighbor_port_cmd); + install_element (BGP_NODE, &no_neighbor_port_val_cmd); + + /* "neighbor weight" commands. */ + install_element (BGP_NODE, &neighbor_weight_cmd); + install_element (BGP_NODE, &no_neighbor_weight_cmd); + install_element (BGP_NODE, &no_neighbor_weight_val_cmd); + + /* "neighbor override-capability" commands. */ + install_element (BGP_NODE, &neighbor_override_capability_cmd); + install_element (BGP_NODE, &no_neighbor_override_capability_cmd); + + /* "neighbor strict-capability-match" commands. */ + install_element (BGP_NODE, &neighbor_strict_capability_cmd); + install_element (BGP_NODE, &no_neighbor_strict_capability_cmd); + + /* "neighbor timers" commands. */ + install_element (BGP_NODE, &neighbor_timers_cmd); + install_element (BGP_NODE, &no_neighbor_timers_cmd); + + /* "neighbor timers connect" commands. */ + install_element (BGP_NODE, &neighbor_timers_connect_cmd); + install_element (BGP_NODE, &no_neighbor_timers_connect_cmd); + install_element (BGP_NODE, &no_neighbor_timers_connect_val_cmd); + + /* "neighbor advertisement-interval" commands. */ + install_element (BGP_NODE, &neighbor_advertise_interval_cmd); + install_element (BGP_NODE, &no_neighbor_advertise_interval_cmd); + install_element (BGP_NODE, &no_neighbor_advertise_interval_val_cmd); + + /* "neighbor version" commands. */ + install_element (BGP_NODE, &neighbor_version_cmd); + + /* "neighbor interface" commands. */ + install_element (BGP_NODE, &neighbor_interface_cmd); + install_element (BGP_NODE, &no_neighbor_interface_cmd); + + /* "neighbor distribute" commands. */ + install_element (BGP_NODE, &neighbor_distribute_list_cmd); + install_element (BGP_NODE, &no_neighbor_distribute_list_cmd); + install_element (BGP_IPV4_NODE, &neighbor_distribute_list_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_distribute_list_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_distribute_list_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_distribute_list_cmd); + install_element (BGP_IPV6_NODE, &neighbor_distribute_list_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_distribute_list_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_distribute_list_cmd); + 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); + install_element (BGP_NODE, &no_neighbor_prefix_list_cmd); + install_element (BGP_IPV4_NODE, &neighbor_prefix_list_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_prefix_list_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_prefix_list_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_prefix_list_cmd); + install_element (BGP_IPV6_NODE, &neighbor_prefix_list_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_prefix_list_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_prefix_list_cmd); + 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); + install_element (BGP_NODE, &no_neighbor_filter_list_cmd); + install_element (BGP_IPV4_NODE, &neighbor_filter_list_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_filter_list_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_filter_list_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_filter_list_cmd); + install_element (BGP_IPV6_NODE, &neighbor_filter_list_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_filter_list_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_filter_list_cmd); + 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); + install_element (BGP_NODE, &no_neighbor_route_map_cmd); + install_element (BGP_IPV4_NODE, &neighbor_route_map_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_route_map_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_route_map_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_route_map_cmd); + install_element (BGP_IPV6_NODE, &neighbor_route_map_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_route_map_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_route_map_cmd); + 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); + install_element (BGP_NODE, &no_neighbor_unsuppress_map_cmd); + install_element (BGP_IPV4_NODE, &neighbor_unsuppress_map_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_unsuppress_map_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_unsuppress_map_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_unsuppress_map_cmd); + install_element (BGP_IPV6_NODE, &neighbor_unsuppress_map_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_unsuppress_map_cmd); + 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_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); + install_element (BGP_NODE, &neighbor_maximum_prefix_threshold_cmd); + install_element (BGP_NODE, &neighbor_maximum_prefix_warning_cmd); + install_element (BGP_NODE, &neighbor_maximum_prefix_threshold_warning_cmd); + install_element (BGP_NODE, &neighbor_maximum_prefix_restart_cmd); + install_element (BGP_NODE, &neighbor_maximum_prefix_threshold_restart_cmd); + install_element (BGP_NODE, &no_neighbor_maximum_prefix_cmd); + install_element (BGP_NODE, &no_neighbor_maximum_prefix_val_cmd); + install_element (BGP_NODE, &no_neighbor_maximum_prefix_threshold_cmd); + install_element (BGP_NODE, &no_neighbor_maximum_prefix_warning_cmd); + install_element (BGP_NODE, &no_neighbor_maximum_prefix_threshold_warning_cmd); + install_element (BGP_NODE, &no_neighbor_maximum_prefix_restart_cmd); + install_element (BGP_NODE, &no_neighbor_maximum_prefix_threshold_restart_cmd); + install_element (BGP_IPV4_NODE, &neighbor_maximum_prefix_cmd); + install_element (BGP_IPV4_NODE, &neighbor_maximum_prefix_threshold_cmd); + install_element (BGP_IPV4_NODE, &neighbor_maximum_prefix_warning_cmd); + install_element (BGP_IPV4_NODE, &neighbor_maximum_prefix_threshold_warning_cmd); + install_element (BGP_IPV4_NODE, &neighbor_maximum_prefix_restart_cmd); + install_element (BGP_IPV4_NODE, &neighbor_maximum_prefix_threshold_restart_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_maximum_prefix_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_maximum_prefix_val_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_maximum_prefix_threshold_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_maximum_prefix_warning_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_maximum_prefix_threshold_warning_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_maximum_prefix_restart_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_maximum_prefix_threshold_restart_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_maximum_prefix_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_maximum_prefix_threshold_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_maximum_prefix_warning_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_maximum_prefix_threshold_warning_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_maximum_prefix_restart_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_maximum_prefix_threshold_restart_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_maximum_prefix_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_maximum_prefix_val_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_maximum_prefix_threshold_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_maximum_prefix_warning_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_maximum_prefix_threshold_warning_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_maximum_prefix_restart_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_maximum_prefix_threshold_restart_cmd); + install_element (BGP_IPV6_NODE, &neighbor_maximum_prefix_cmd); + install_element (BGP_IPV6_NODE, &neighbor_maximum_prefix_threshold_cmd); + install_element (BGP_IPV6_NODE, &neighbor_maximum_prefix_warning_cmd); + install_element (BGP_IPV6_NODE, &neighbor_maximum_prefix_threshold_warning_cmd); + install_element (BGP_IPV6_NODE, &neighbor_maximum_prefix_restart_cmd); + install_element (BGP_IPV6_NODE, &neighbor_maximum_prefix_threshold_restart_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_maximum_prefix_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_maximum_prefix_val_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_maximum_prefix_threshold_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_maximum_prefix_warning_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_maximum_prefix_threshold_warning_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_maximum_prefix_restart_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_maximum_prefix_threshold_restart_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_maximum_prefix_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_maximum_prefix_threshold_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_maximum_prefix_warning_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_maximum_prefix_threshold_warning_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_maximum_prefix_restart_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_maximum_prefix_threshold_restart_cmd); + install_element (BGP_IPV6M_NODE, &no_neighbor_maximum_prefix_cmd); + install_element (BGP_IPV6M_NODE, &no_neighbor_maximum_prefix_val_cmd); + install_element (BGP_IPV6M_NODE, &no_neighbor_maximum_prefix_threshold_cmd); + install_element (BGP_IPV6M_NODE, &no_neighbor_maximum_prefix_warning_cmd); + install_element (BGP_IPV6M_NODE, &no_neighbor_maximum_prefix_threshold_warning_cmd); + install_element (BGP_IPV6M_NODE, &no_neighbor_maximum_prefix_restart_cmd); + install_element (BGP_IPV6M_NODE, &no_neighbor_maximum_prefix_threshold_restart_cmd); + install_element (BGP_VPNV4_NODE, &neighbor_maximum_prefix_cmd); + install_element (BGP_VPNV4_NODE, &neighbor_maximum_prefix_threshold_cmd); + install_element (BGP_VPNV4_NODE, &neighbor_maximum_prefix_warning_cmd); + install_element (BGP_VPNV4_NODE, &neighbor_maximum_prefix_threshold_warning_cmd); + install_element (BGP_VPNV4_NODE, &neighbor_maximum_prefix_restart_cmd); + install_element (BGP_VPNV4_NODE, &neighbor_maximum_prefix_threshold_restart_cmd); + install_element (BGP_VPNV4_NODE, &no_neighbor_maximum_prefix_cmd); + install_element (BGP_VPNV4_NODE, &no_neighbor_maximum_prefix_val_cmd); + install_element (BGP_VPNV4_NODE, &no_neighbor_maximum_prefix_threshold_cmd); + install_element (BGP_VPNV4_NODE, &no_neighbor_maximum_prefix_warning_cmd); + install_element (BGP_VPNV4_NODE, &no_neighbor_maximum_prefix_threshold_warning_cmd); + 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); + install_element (BGP_NODE, &no_neighbor_allowas_in_cmd); + install_element (BGP_IPV4_NODE, &neighbor_allowas_in_cmd); + install_element (BGP_IPV4_NODE, &neighbor_allowas_in_arg_cmd); + install_element (BGP_IPV4_NODE, &no_neighbor_allowas_in_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_allowas_in_cmd); + install_element (BGP_IPV4M_NODE, &neighbor_allowas_in_arg_cmd); + install_element (BGP_IPV4M_NODE, &no_neighbor_allowas_in_cmd); + install_element (BGP_IPV6_NODE, &neighbor_allowas_in_cmd); + install_element (BGP_IPV6_NODE, &neighbor_allowas_in_arg_cmd); + install_element (BGP_IPV6_NODE, &no_neighbor_allowas_in_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_allowas_in_cmd); + install_element (BGP_IPV6M_NODE, &neighbor_allowas_in_arg_cmd); + install_element (BGP_IPV6M_NODE, &no_neighbor_allowas_in_cmd); + 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); + install_element (BGP_NODE, &address_family_ipv6_cmd); + install_element (BGP_NODE, &address_family_ipv6_safi_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_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); + install_element (ENABLE_NODE, &clear_ip_bgp_instance_all_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_as_cmd); + 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); + 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); + install_element (ENABLE_NODE, &clear_bgp_peer_cmd); + install_element (ENABLE_NODE, &clear_bgp_ipv6_peer_cmd); + install_element (ENABLE_NODE, &clear_bgp_peer_group_cmd); + install_element (ENABLE_NODE, &clear_bgp_ipv6_peer_group_cmd); + install_element (ENABLE_NODE, &clear_bgp_external_cmd); + 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); + + /* "clear ip bgp neighbor soft in" */ + install_element (ENABLE_NODE, &clear_ip_bgp_all_soft_in_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_instance_all_soft_in_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_all_in_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_all_in_prefix_filter_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_instance_all_in_prefix_filter_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_peer_soft_in_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_peer_in_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_peer_in_prefix_filter_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_peer_group_soft_in_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_peer_group_in_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_peer_group_in_prefix_filter_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_external_soft_in_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_external_in_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_external_in_prefix_filter_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_as_soft_in_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_as_in_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_as_in_prefix_filter_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_all_ipv4_soft_in_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_instance_all_ipv4_soft_in_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_all_ipv4_in_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_all_ipv4_in_prefix_filter_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_instance_all_ipv4_in_prefix_filter_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_peer_ipv4_soft_in_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_peer_ipv4_in_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_peer_ipv4_in_prefix_filter_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_peer_group_ipv4_soft_in_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_peer_group_ipv4_in_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_peer_group_ipv4_in_prefix_filter_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_external_ipv4_soft_in_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_external_ipv4_in_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_external_ipv4_in_prefix_filter_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_as_ipv4_soft_in_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_as_ipv4_in_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_as_ipv4_in_prefix_filter_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_all_vpnv4_soft_in_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_all_vpnv4_in_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_peer_vpnv4_soft_in_cmd); + 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); + 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); + install_element (ENABLE_NODE, &clear_bgp_all_in_prefix_filter_cmd); + install_element (ENABLE_NODE, &clear_bgp_peer_soft_in_cmd); + install_element (ENABLE_NODE, &clear_bgp_peer_in_cmd); + install_element (ENABLE_NODE, &clear_bgp_peer_in_prefix_filter_cmd); + install_element (ENABLE_NODE, &clear_bgp_peer_group_soft_in_cmd); + install_element (ENABLE_NODE, &clear_bgp_peer_group_in_cmd); + install_element (ENABLE_NODE, &clear_bgp_peer_group_in_prefix_filter_cmd); + install_element (ENABLE_NODE, &clear_bgp_external_soft_in_cmd); + install_element (ENABLE_NODE, &clear_bgp_external_in_cmd); + install_element (ENABLE_NODE, &clear_bgp_external_in_prefix_filter_cmd); + install_element (ENABLE_NODE, &clear_bgp_as_soft_in_cmd); + install_element (ENABLE_NODE, &clear_bgp_as_in_cmd); + install_element (ENABLE_NODE, &clear_bgp_as_in_prefix_filter_cmd); + install_element (ENABLE_NODE, &clear_bgp_ipv6_all_soft_in_cmd); + install_element (ENABLE_NODE, &clear_bgp_ipv6_all_in_cmd); + install_element (ENABLE_NODE, &clear_bgp_ipv6_all_in_prefix_filter_cmd); + install_element (ENABLE_NODE, &clear_bgp_ipv6_peer_soft_in_cmd); + install_element (ENABLE_NODE, &clear_bgp_ipv6_peer_in_cmd); + install_element (ENABLE_NODE, &clear_bgp_ipv6_peer_in_prefix_filter_cmd); + install_element (ENABLE_NODE, &clear_bgp_ipv6_peer_group_soft_in_cmd); + install_element (ENABLE_NODE, &clear_bgp_ipv6_peer_group_in_cmd); + install_element (ENABLE_NODE, &clear_bgp_ipv6_peer_group_in_prefix_filter_cmd); + install_element (ENABLE_NODE, &clear_bgp_ipv6_external_soft_in_cmd); + install_element (ENABLE_NODE, &clear_bgp_ipv6_external_in_cmd); + install_element (ENABLE_NODE, &clear_bgp_ipv6_external_in_prefix_filter_cmd); + 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); + + /* 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); + install_element (ENABLE_NODE, &clear_ip_bgp_instance_all_soft_out_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_all_out_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_peer_soft_out_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_peer_out_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_peer_group_soft_out_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_peer_group_out_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_external_soft_out_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_external_out_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_as_soft_out_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_as_out_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_all_ipv4_soft_out_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_instance_all_ipv4_soft_out_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_all_ipv4_out_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_peer_ipv4_soft_out_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_peer_ipv4_out_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_peer_group_ipv4_soft_out_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_peer_group_ipv4_out_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_external_ipv4_soft_out_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_external_ipv4_out_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_as_ipv4_soft_out_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_as_ipv4_out_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_all_vpnv4_soft_out_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_all_vpnv4_out_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_peer_vpnv4_soft_out_cmd); + 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); + 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); + install_element (ENABLE_NODE, &clear_bgp_peer_soft_out_cmd); + install_element (ENABLE_NODE, &clear_bgp_peer_out_cmd); + install_element (ENABLE_NODE, &clear_bgp_peer_group_soft_out_cmd); + install_element (ENABLE_NODE, &clear_bgp_peer_group_out_cmd); + install_element (ENABLE_NODE, &clear_bgp_external_soft_out_cmd); + install_element (ENABLE_NODE, &clear_bgp_external_out_cmd); + install_element (ENABLE_NODE, &clear_bgp_as_soft_out_cmd); + install_element (ENABLE_NODE, &clear_bgp_as_out_cmd); + install_element (ENABLE_NODE, &clear_bgp_ipv6_all_soft_out_cmd); + install_element (ENABLE_NODE, &clear_bgp_ipv6_all_out_cmd); + install_element (ENABLE_NODE, &clear_bgp_ipv6_peer_soft_out_cmd); + install_element (ENABLE_NODE, &clear_bgp_ipv6_peer_out_cmd); + install_element (ENABLE_NODE, &clear_bgp_ipv6_peer_group_soft_out_cmd); + install_element (ENABLE_NODE, &clear_bgp_ipv6_peer_group_out_cmd); + install_element (ENABLE_NODE, &clear_bgp_ipv6_external_soft_out_cmd); + 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); + + /* "clear ip bgp neighbor soft" */ + install_element (ENABLE_NODE, &clear_ip_bgp_all_soft_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_instance_all_soft_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_peer_soft_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_peer_group_soft_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_external_soft_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_as_soft_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_all_ipv4_soft_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_instance_all_ipv4_soft_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_peer_ipv4_soft_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_peer_group_ipv4_soft_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_external_ipv4_soft_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_as_ipv4_soft_cmd); + 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); + 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); + install_element (ENABLE_NODE, &clear_bgp_peer_group_soft_cmd); + install_element (ENABLE_NODE, &clear_bgp_external_soft_cmd); + install_element (ENABLE_NODE, &clear_bgp_as_soft_cmd); + install_element (ENABLE_NODE, &clear_bgp_ipv6_all_soft_cmd); + install_element (ENABLE_NODE, &clear_bgp_ipv6_peer_soft_cmd); + 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); + + /* "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); + 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); + install_element (ENABLE_NODE, &clear_bgp_ipv6_instance_all_rsclient_cmd); + install_element (ENABLE_NODE, &clear_bgp_peer_rsclient_cmd); + 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); + + /* "show ip bgp summary" commands. */ + 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_bgp_instance_ipv4_safi_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_safi_summary_cmd); + install_element (VIEW_NODE, &show_bgp_instance_ipv6_summary_cmd); + install_element (VIEW_NODE, &show_bgp_instance_ipv6_safi_summary_cmd); + + install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_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_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_safi_summary_cmd); + install_element (RESTRICTED_NODE, &show_bgp_instance_ipv6_summary_cmd); + install_element (RESTRICTED_NODE, &show_bgp_instance_ipv6_safi_summary_cmd); + + /* "show ip bgp neighbors" commands. */ + install_element (VIEW_NODE, &show_bgp_instance_neighbors_cmd); + + install_element (VIEW_NODE, &show_bgp_neighbors_cmd); + install_element (VIEW_NODE, &show_bgp_neighbors_peer_cmd); + install_element (VIEW_NODE, &show_bgp_instance_neighbors_peer_cmd); + install_element (RESTRICTED_NODE, &show_bgp_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); + + /* "show ip bgp rsclient" commands. */ + 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_bgp_instance_ipv4_safi_rsclient_summary_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_rsclient_summary_cmd); + + install_element (VIEW_NODE, &show_bgp_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_ipv6_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); + + /* "show ip bgp paths" commands. */ + install_element (VIEW_NODE, &show_bgp_ipv4_paths_cmd); + + /* "show ip bgp community" commands. */ + install_element (VIEW_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); + + /* "redistribute" commands. */ + install_element (BGP_NODE, &bgp_redistribute_ipv4_cmd); + install_element (BGP_NODE, &no_bgp_redistribute_ipv4_cmd); + install_element (BGP_NODE, &bgp_redistribute_ipv4_rmap_cmd); + install_element (BGP_NODE, &no_bgp_redistribute_ipv4_rmap_cmd); + install_element (BGP_NODE, &bgp_redistribute_ipv4_metric_cmd); + install_element (BGP_NODE, &no_bgp_redistribute_ipv4_metric_cmd); + install_element (BGP_NODE, &bgp_redistribute_ipv4_rmap_metric_cmd); + 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); + 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); + install_element (BGP_IPV6_NODE, &no_bgp_redistribute_ipv6_rmap_cmd); + install_element (BGP_IPV6_NODE, &bgp_redistribute_ipv6_metric_cmd); + install_element (BGP_IPV6_NODE, &no_bgp_redistribute_ipv6_metric_cmd); + install_element (BGP_IPV6_NODE, &bgp_redistribute_ipv6_rmap_metric_cmd); + 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); + + /* ttl_security commands */ + install_element (BGP_NODE, &neighbor_ttl_security_cmd); + install_element (BGP_NODE, &no_neighbor_ttl_security_cmd); + + /* "show bgp memory" commands. */ + install_element (VIEW_NODE, &show_bgp_memory_cmd); + install_element (RESTRICTED_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); + + /* 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" +#include "bgp_ecommunity.h" + +/* VTY functions. */ + +/* Direction value to string conversion. */ +static const char * +community_direct_str (int direct) +{ + switch (direct) + { + case COMMUNITY_DENY: + return "deny"; + case COMMUNITY_PERMIT: + return "permit"; + default: + return "unknown"; + } +} + +/* Display error string. */ +static void +community_list_perror (struct vty *vty, int ret) +{ + switch (ret) + { + case COMMUNITY_LIST_ERR_CANT_FIND_LIST: + vty_out (vty, "%% Can't find community-list%s", VTY_NEWLINE); + break; + case COMMUNITY_LIST_ERR_MALFORMED_VAL: + vty_out (vty, "%% Malformed community-list value%s", VTY_NEWLINE); + break; + case COMMUNITY_LIST_ERR_STANDARD_CONFLICT: + vty_out (vty, "%% Community name conflict, previously defined as standard community%s", VTY_NEWLINE); + break; + case COMMUNITY_LIST_ERR_EXPANDED_CONFLICT: + vty_out (vty, "%% Community name conflict, previously defined as expanded community%s", VTY_NEWLINE); + break; + } +} + +/* VTY interface for community_set() function. */ +static int +community_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; + + /* When community_list_set() return nevetive value, it means + malformed community string. */ + ret = community_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) + { + /* Display error string. */ + community_list_perror (vty, ret); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +/* Communiyt-list entry delete. */ +static int +community_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 = community_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; +} + +/* "community-list" keyword help string. */ +#define COMMUNITY_LIST_STR "Add a community list entry\n" +#define COMMUNITY_VAL_STR "Community number in aa:nn format or internet|local-AS|no-advertise|no-export\n" + +DEFUN (ip_community_list_standard, + ip_community_list_standard_cmd, + "ip community-list <1-99> (deny|permit) .AA:NN", + IP_STR + COMMUNITY_LIST_STR + "Community list number (standard)\n" + "Specify community to reject\n" + "Specify community to accept\n" + COMMUNITY_VAL_STR) +{ + return community_list_set_vty (vty, argc, argv, COMMUNITY_LIST_STANDARD, 0); +} + +ALIAS (ip_community_list_standard, + ip_community_list_standard2_cmd, + "ip community-list <1-99> (deny|permit)", + IP_STR + COMMUNITY_LIST_STR + "Community list number (standard)\n" + "Specify community to reject\n" + "Specify community to accept\n") + +DEFUN (ip_community_list_expanded, + ip_community_list_expanded_cmd, + "ip community-list <100-500> (deny|permit) .LINE", + IP_STR + COMMUNITY_LIST_STR + "Community list number (expanded)\n" + "Specify community to reject\n" + "Specify community to accept\n" + "An ordered list as a regular-expression\n") +{ + return community_list_set_vty (vty, argc, argv, COMMUNITY_LIST_EXPANDED, 0); +} + +DEFUN (ip_community_list_name_standard, + ip_community_list_name_standard_cmd, + "ip community-list standard WORD (deny|permit) .AA:NN", + IP_STR + COMMUNITY_LIST_STR + "Add a standard community-list entry\n" + "Community list name\n" + "Specify community to reject\n" + "Specify community to accept\n" + COMMUNITY_VAL_STR) +{ + return community_list_set_vty (vty, argc, argv, COMMUNITY_LIST_STANDARD, 1); +} + +ALIAS (ip_community_list_name_standard, + ip_community_list_name_standard2_cmd, + "ip community-list standard WORD (deny|permit)", + IP_STR + COMMUNITY_LIST_STR + "Add a standard community-list entry\n" + "Community list name\n" + "Specify community to reject\n" + "Specify community to accept\n") + +DEFUN (ip_community_list_name_expanded, + ip_community_list_name_expanded_cmd, + "ip community-list expanded WORD (deny|permit) .LINE", + IP_STR + COMMUNITY_LIST_STR + "Add an expanded community-list entry\n" + "Community list name\n" + "Specify community to reject\n" + "Specify community to accept\n" + "An ordered list as a regular-expression\n") +{ + return community_list_set_vty (vty, argc, argv, COMMUNITY_LIST_EXPANDED, 1); +} + +DEFUN (no_ip_community_list_standard_all, + no_ip_community_list_standard_all_cmd, + "no ip community-list <1-99>", + NO_STR + IP_STR + COMMUNITY_LIST_STR + "Community list number (standard)\n") +{ + return community_list_unset_vty (vty, argc, argv, COMMUNITY_LIST_STANDARD); +} + +DEFUN (no_ip_community_list_expanded_all, + no_ip_community_list_expanded_all_cmd, + "no ip community-list <100-500>", + NO_STR + IP_STR + COMMUNITY_LIST_STR + "Community list number (expanded)\n") +{ + return community_list_unset_vty (vty, argc, argv, COMMUNITY_LIST_EXPANDED); +} + +DEFUN (no_ip_community_list_name_standard_all, + no_ip_community_list_name_standard_all_cmd, + "no ip community-list standard WORD", + NO_STR + IP_STR + COMMUNITY_LIST_STR + "Add a standard community-list entry\n" + "Community list name\n") +{ + return community_list_unset_vty (vty, argc, argv, COMMUNITY_LIST_STANDARD); +} + +DEFUN (no_ip_community_list_name_expanded_all, + no_ip_community_list_name_expanded_all_cmd, + "no ip community-list expanded WORD", + NO_STR + IP_STR + COMMUNITY_LIST_STR + "Add an expanded community-list entry\n" + "Community list name\n") +{ + return community_list_unset_vty (vty, argc, argv, COMMUNITY_LIST_EXPANDED); +} + +DEFUN (no_ip_community_list_standard, + no_ip_community_list_standard_cmd, + "no ip community-list <1-99> (deny|permit) .AA:NN", + NO_STR + IP_STR + COMMUNITY_LIST_STR + "Community list number (standard)\n" + "Specify community to reject\n" + "Specify community to accept\n" + COMMUNITY_VAL_STR) +{ + return community_list_unset_vty (vty, argc, argv, COMMUNITY_LIST_STANDARD); +} + +DEFUN (no_ip_community_list_expanded, + no_ip_community_list_expanded_cmd, + "no ip community-list <100-500> (deny|permit) .LINE", + NO_STR + IP_STR + COMMUNITY_LIST_STR + "Community list number (expanded)\n" + "Specify community to reject\n" + "Specify community to accept\n" + "An ordered list as a regular-expression\n") +{ + return community_list_unset_vty (vty, argc, argv, COMMUNITY_LIST_EXPANDED); +} + +DEFUN (no_ip_community_list_name_standard, + no_ip_community_list_name_standard_cmd, + "no ip community-list standard WORD (deny|permit) .AA:NN", + NO_STR + IP_STR + COMMUNITY_LIST_STR + "Specify a standard community-list\n" + "Community list name\n" + "Specify community to reject\n" + "Specify community to accept\n" + COMMUNITY_VAL_STR) +{ + return community_list_unset_vty (vty, argc, argv, COMMUNITY_LIST_STANDARD); +} + +DEFUN (no_ip_community_list_name_expanded, + no_ip_community_list_name_expanded_cmd, + "no ip community-list expanded WORD (deny|permit) .LINE", + NO_STR + IP_STR + COMMUNITY_LIST_STR + "Specify an expanded community-list\n" + "Community list name\n" + "Specify community to reject\n" + "Specify community to accept\n" + "An ordered list as a regular-expression\n") +{ + return community_list_unset_vty (vty, argc, argv, COMMUNITY_LIST_EXPANDED); +} + +static void +community_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, "Community %s list %s%s", + entry->style == COMMUNITY_LIST_STANDARD ? + "standard" : "(expanded) access", + list->name, VTY_NEWLINE); + else + vty_out (vty, "Named Community %s list %s%s", + entry->style == COMMUNITY_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 == COMMUNITY_LIST_STANDARD + ? community_str (entry->u.com) : entry->config, + VTY_NEWLINE); + } +} + +DEFUN (show_ip_community_list, + show_ip_community_list_cmd, + "show ip community-list", + SHOW_STR + IP_STR + "List community-list\n") +{ + struct community_list *list; + struct community_list_master *cm; + + cm = community_list_master_lookup (bgp_clist, COMMUNITY_LIST_MASTER); + if (! cm) + return CMD_SUCCESS; + + for (list = cm->num.head; list; list = list->next) + community_list_show (vty, list); + + for (list = cm->str.head; list; list = list->next) + community_list_show (vty, list); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_community_list_arg, + show_ip_community_list_arg_cmd, + "show ip community-list (<1-500>|WORD)", + SHOW_STR + IP_STR + "List community-list\n" + "Community-list number\n" + "Community-list name\n") +{ + struct community_list *list; + + list = community_list_lookup (bgp_clist, argv[0], COMMUNITY_LIST_MASTER); + if (! list) + { + vty_out (vty, "%% Can't find community-list%s", VTY_NEWLINE); + return CMD_WARNING; + } + + community_list_show (vty, list); + + 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) +{ + 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 = extcommunity_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 +extcommunity_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 = extcommunity_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; +} + +/* "extcommunity-list" keyword help string. */ +#define EXTCOMMUNITY_LIST_STR "Add a extended community list entry\n" +#define EXTCOMMUNITY_VAL_STR "Extended community attribute in 'rt aa:nn_or_IPaddr:nn' OR 'soo aa:nn_or_IPaddr:nn' format\n" + +DEFUN (ip_extcommunity_list_standard, + ip_extcommunity_list_standard_cmd, + "ip extcommunity-list <1-99> (deny|permit) .AA:NN", + IP_STR + EXTCOMMUNITY_LIST_STR + "Extended Community list number (standard)\n" + "Specify community to reject\n" + "Specify community to accept\n" + EXTCOMMUNITY_VAL_STR) +{ + return extcommunity_list_set_vty (vty, argc, argv, EXTCOMMUNITY_LIST_STANDARD, 0); +} + +ALIAS (ip_extcommunity_list_standard, + ip_extcommunity_list_standard2_cmd, + "ip extcommunity-list <1-99> (deny|permit)", + IP_STR + EXTCOMMUNITY_LIST_STR + "Extended Community list number (standard)\n" + "Specify community to reject\n" + "Specify community to accept\n") + +DEFUN (ip_extcommunity_list_expanded, + ip_extcommunity_list_expanded_cmd, + "ip extcommunity-list <100-500> (deny|permit) .LINE", + IP_STR + EXTCOMMUNITY_LIST_STR + "Extended Community list number (expanded)\n" + "Specify community to reject\n" + "Specify community to accept\n" + "An ordered list as a regular-expression\n") +{ + return extcommunity_list_set_vty (vty, argc, argv, EXTCOMMUNITY_LIST_EXPANDED, 0); +} + +DEFUN (ip_extcommunity_list_name_standard, + ip_extcommunity_list_name_standard_cmd, + "ip extcommunity-list standard WORD (deny|permit) .AA:NN", + IP_STR + EXTCOMMUNITY_LIST_STR + "Specify standard extcommunity-list\n" + "Extended Community list name\n" + "Specify community to reject\n" + "Specify community to accept\n" + EXTCOMMUNITY_VAL_STR) +{ + return extcommunity_list_set_vty (vty, argc, argv, EXTCOMMUNITY_LIST_STANDARD, 1); +} + +ALIAS (ip_extcommunity_list_name_standard, + ip_extcommunity_list_name_standard2_cmd, + "ip extcommunity-list standard WORD (deny|permit)", + IP_STR + EXTCOMMUNITY_LIST_STR + "Specify standard extcommunity-list\n" + "Extended Community list name\n" + "Specify community to reject\n" + "Specify community to accept\n") + +DEFUN (ip_extcommunity_list_name_expanded, + ip_extcommunity_list_name_expanded_cmd, + "ip extcommunity-list expanded WORD (deny|permit) .LINE", + IP_STR + EXTCOMMUNITY_LIST_STR + "Specify expanded extcommunity-list\n" + "Extended Community list name\n" + "Specify community to reject\n" + "Specify community to accept\n" + "An ordered list as a regular-expression\n") +{ + return extcommunity_list_set_vty (vty, argc, argv, EXTCOMMUNITY_LIST_EXPANDED, 1); +} + +DEFUN (no_ip_extcommunity_list_standard_all, + no_ip_extcommunity_list_standard_all_cmd, + "no ip extcommunity-list <1-99>", + NO_STR + IP_STR + EXTCOMMUNITY_LIST_STR + "Extended Community list number (standard)\n") +{ + return extcommunity_list_unset_vty (vty, argc, argv, EXTCOMMUNITY_LIST_STANDARD); +} + +DEFUN (no_ip_extcommunity_list_expanded_all, + no_ip_extcommunity_list_expanded_all_cmd, + "no ip extcommunity-list <100-500>", + NO_STR + IP_STR + EXTCOMMUNITY_LIST_STR + "Extended Community list number (expanded)\n") +{ + return extcommunity_list_unset_vty (vty, argc, argv, EXTCOMMUNITY_LIST_EXPANDED); +} + +DEFUN (no_ip_extcommunity_list_name_standard_all, + no_ip_extcommunity_list_name_standard_all_cmd, + "no ip extcommunity-list standard WORD", + NO_STR + IP_STR + EXTCOMMUNITY_LIST_STR + "Specify standard extcommunity-list\n" + "Extended Community list name\n") +{ + return extcommunity_list_unset_vty (vty, argc, argv, EXTCOMMUNITY_LIST_STANDARD); +} + +DEFUN (no_ip_extcommunity_list_name_expanded_all, + no_ip_extcommunity_list_name_expanded_all_cmd, + "no ip extcommunity-list expanded WORD", + NO_STR + IP_STR + EXTCOMMUNITY_LIST_STR + "Specify expanded extcommunity-list\n" + "Extended Community list name\n") +{ + return extcommunity_list_unset_vty (vty, argc, argv, EXTCOMMUNITY_LIST_EXPANDED); +} + +DEFUN (no_ip_extcommunity_list_standard, + no_ip_extcommunity_list_standard_cmd, + "no ip extcommunity-list <1-99> (deny|permit) .AA:NN", + NO_STR + IP_STR + EXTCOMMUNITY_LIST_STR + "Extended Community list number (standard)\n" + "Specify community to reject\n" + "Specify community to accept\n" + EXTCOMMUNITY_VAL_STR) +{ + return extcommunity_list_unset_vty (vty, argc, argv, EXTCOMMUNITY_LIST_STANDARD); +} + +DEFUN (no_ip_extcommunity_list_expanded, + no_ip_extcommunity_list_expanded_cmd, + "no ip extcommunity-list <100-500> (deny|permit) .LINE", + NO_STR + IP_STR + EXTCOMMUNITY_LIST_STR + "Extended Community list number (expanded)\n" + "Specify community to reject\n" + "Specify community to accept\n" + "An ordered list as a regular-expression\n") +{ + return extcommunity_list_unset_vty (vty, argc, argv, EXTCOMMUNITY_LIST_EXPANDED); +} + +DEFUN (no_ip_extcommunity_list_name_standard, + no_ip_extcommunity_list_name_standard_cmd, + "no ip extcommunity-list standard WORD (deny|permit) .AA:NN", + NO_STR + IP_STR + EXTCOMMUNITY_LIST_STR + "Specify standard extcommunity-list\n" + "Extended Community list name\n" + "Specify community to reject\n" + "Specify community to accept\n" + EXTCOMMUNITY_VAL_STR) +{ + return extcommunity_list_unset_vty (vty, argc, argv, EXTCOMMUNITY_LIST_STANDARD); +} + +DEFUN (no_ip_extcommunity_list_name_expanded, + no_ip_extcommunity_list_name_expanded_cmd, + "no ip extcommunity-list expanded WORD (deny|permit) .LINE", + NO_STR + IP_STR + EXTCOMMUNITY_LIST_STR + "Specify expanded extcommunity-list\n" + "Community list name\n" + "Specify community to reject\n" + "Specify community to accept\n" + "An ordered list as a regular-expression\n") +{ + return extcommunity_list_unset_vty (vty, argc, argv, EXTCOMMUNITY_LIST_EXPANDED); +} + +static void +extcommunity_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, "Extended community %s list %s%s", + entry->style == EXTCOMMUNITY_LIST_STANDARD ? + "standard" : "(expanded) access", + list->name, VTY_NEWLINE); + else + vty_out (vty, "Named extended 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_extcommunity_list, + show_ip_extcommunity_list_cmd, + "show ip extcommunity-list", + SHOW_STR + IP_STR + "List extended-community list\n") +{ + struct community_list *list; + struct community_list_master *cm; + + cm = community_list_master_lookup (bgp_clist, EXTCOMMUNITY_LIST_MASTER); + if (! cm) + return CMD_SUCCESS; + + for (list = cm->num.head; list; list = list->next) + extcommunity_list_show (vty, list); + + for (list = cm->str.head; list; list = list->next) + extcommunity_list_show (vty, list); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_extcommunity_list_arg, + show_ip_extcommunity_list_arg_cmd, + "show ip extcommunity-list (<1-500>|WORD)", + SHOW_STR + IP_STR + "List extended-community list\n" + "Extcommunity-list number\n" + "Extcommunity-list name\n") +{ + struct community_list *list; + + list = community_list_lookup (bgp_clist, argv[0], EXTCOMMUNITY_LIST_MASTER); + if (! list) + { + vty_out (vty, "%% Can't find extcommunity-list%s", VTY_NEWLINE); + return CMD_WARNING; + } + + extcommunity_list_show (vty, list); + + return CMD_SUCCESS; +} + +/* Return configuration string of community-list entry. */ +static const char * +community_list_config_str (struct community_entry *entry) +{ + const char *str; + + if (entry->any) + str = ""; + else + { + if (entry->style == COMMUNITY_LIST_STANDARD) + str = community_str (entry->u.com); + else + str = entry->config; + } + return str; +} + +/* Display community-list and extcommunity-list configuration. */ +static int +community_list_config_write (struct vty *vty) +{ + struct community_list *list; + struct community_entry *entry; + struct community_list_master *cm; + int write = 0; + + /* Community-list. */ + cm = community_list_master_lookup (bgp_clist, COMMUNITY_LIST_MASTER); + + for (list = cm->num.head; list; list = list->next) + for (entry = list->head; entry; entry = entry->next) + { + vty_out (vty, "ip 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 community-list %s %s %s %s%s", + entry->style == COMMUNITY_LIST_STANDARD + ? "standard" : "expanded", + list->name, community_direct_str (entry->direct), + community_list_config_str (entry), + VTY_NEWLINE); + write++; + } + + /* Extcommunity-list. */ + cm = community_list_master_lookup (bgp_clist, EXTCOMMUNITY_LIST_MASTER); + + for (list = cm->num.head; list; list = list->next) + for (entry = list->head; entry; entry = entry->next) + { + vty_out (vty, "ip extcommunity-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 extcommunity-list %s %s %s %s%s", + entry->style == EXTCOMMUNITY_LIST_STANDARD + ? "standard" : "expanded", + list->name, community_direct_str (entry->direct), + 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; +} + +static struct cmd_node community_list_node = +{ + COMMUNITY_LIST_NODE, + "", + 1 /* Export to vtysh. */ +}; + +static void +community_list_vty (void) +{ + install_node (&community_list_node, community_list_config_write); + + /* Community-list. */ + install_element (CONFIG_NODE, &ip_community_list_standard_cmd); + install_element (CONFIG_NODE, &ip_community_list_standard2_cmd); + install_element (CONFIG_NODE, &ip_community_list_expanded_cmd); + install_element (CONFIG_NODE, &ip_community_list_name_standard_cmd); + install_element (CONFIG_NODE, &ip_community_list_name_standard2_cmd); + install_element (CONFIG_NODE, &ip_community_list_name_expanded_cmd); + install_element (CONFIG_NODE, &no_ip_community_list_standard_all_cmd); + install_element (CONFIG_NODE, &no_ip_community_list_expanded_all_cmd); + install_element (CONFIG_NODE, &no_ip_community_list_name_standard_all_cmd); + install_element (CONFIG_NODE, &no_ip_community_list_name_expanded_all_cmd); + install_element (CONFIG_NODE, &no_ip_community_list_standard_cmd); + install_element (CONFIG_NODE, &no_ip_community_list_expanded_cmd); + install_element (CONFIG_NODE, &no_ip_community_list_name_standard_cmd); + 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); + + /* Extcommunity-list. */ + install_element (CONFIG_NODE, &ip_extcommunity_list_standard_cmd); + install_element (CONFIG_NODE, &ip_extcommunity_list_standard2_cmd); + install_element (CONFIG_NODE, &ip_extcommunity_list_expanded_cmd); + install_element (CONFIG_NODE, &ip_extcommunity_list_name_standard_cmd); + install_element (CONFIG_NODE, &ip_extcommunity_list_name_standard2_cmd); + install_element (CONFIG_NODE, &ip_extcommunity_list_name_expanded_cmd); + install_element (CONFIG_NODE, &no_ip_extcommunity_list_standard_all_cmd); + install_element (CONFIG_NODE, &no_ip_extcommunity_list_expanded_all_cmd); + install_element (CONFIG_NODE, &no_ip_extcommunity_list_name_standard_all_cmd); + install_element (CONFIG_NODE, &no_ip_extcommunity_list_name_expanded_all_cmd); + install_element (CONFIG_NODE, &no_ip_extcommunity_list_standard_cmd); + install_element (CONFIG_NODE, &no_ip_extcommunity_list_expanded_cmd); + install_element (CONFIG_NODE, &no_ip_extcommunity_list_name_standard_cmd); + 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); + + /* 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 new file mode 100644 index 0000000..7329c5f --- /dev/null +++ b/bgpd/bgp_vty.h @@ -0,0 +1,35 @@ +/* BGP VTY interface. + Copyright (C) 1996, 97, 98, 99, 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. */ + +#ifndef _QUAGGA_BGP_VTY_H +#define _QUAGGA_BGP_VTY_H + +#define CMD_AS_RANGE "<1-4294967295>" + +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 new file mode 100644 index 0000000..40ecbce --- /dev/null +++ b/bgpd/bgp_zebra.c @@ -0,0 +1,1222 @@ +/* zebra client + 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. */ + +#include + +#include "command.h" +#include "stream.h" +#include "network.h" +#include "prefix.h" +#include "log.h" +#include "sockunion.h" +#include "zclient.h" +#include "routemap.h" +#include "thread.h" +#include "filter.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_nexthop.h" +#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, + vrf_id_t vrf_id) +{ + struct prefix router_id; + + zebra_router_id_update_read(zclient->ibuf,&router_id); + + if (BGP_DEBUG(zebra, ZEBRA)) + { + char buf[128]; + prefix2str(&router_id, buf, sizeof(buf)); + zlog_debug("Zebra rcvd: router id update %s", buf); + } + + router_id_zebra = 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, + vrf_id_t vrf_id) +{ + struct interface *ifp; + + ifp = zebra_interface_add_read (zclient->ibuf, vrf_id); + + if (BGP_DEBUG(zebra, ZEBRA) && ifp) + zlog_debug("Zebra rcvd: interface add %s", ifp->name); + + return 0; +} + +static int +bgp_interface_delete (int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct stream *s; + struct interface *ifp; + + s = zclient->ibuf; + ifp = zebra_interface_state_read (s, vrf_id); + if (! ifp) + return 0; + + ifp->ifindex = IFINDEX_INTERNAL; + + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("Zebra rcvd: interface delete %s", ifp->name); + + return 0; +} + +static int +bgp_interface_up (int command, struct zclient *zclient, zebra_size_t length, + vrf_id_t vrf_id) +{ + struct stream *s; + struct interface *ifp; + struct connected *c; + struct listnode *node, *nnode; + + s = zclient->ibuf; + ifp = zebra_interface_state_read (s, vrf_id); + + if (! ifp) + return 0; + + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("Zebra rcvd: interface %s up", ifp->name); + + for (ALL_LIST_ELEMENTS (ifp->connected, node, nnode, c)) + bgp_connected_add (c); + + return 0; +} + +static int +bgp_interface_down (int command, struct zclient *zclient, zebra_size_t length, + vrf_id_t vrf_id) +{ + struct stream *s; + struct interface *ifp; + struct connected *c; + struct listnode *node, *nnode; + + s = zclient->ibuf; + ifp = zebra_interface_state_read (s, vrf_id); + if (! ifp) + return 0; + + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("Zebra rcvd: interface %s down", ifp->name); + + for (ALL_LIST_ELEMENTS (ifp->connected, node, nnode, c)) + bgp_connected_delete (c); + + /* Fast external-failover */ + { + struct listnode *mnode; + struct bgp *bgp; + struct peer *peer; + + for (ALL_LIST_ELEMENTS_RO (bm->bgp, mnode, bgp)) + { + if (CHECK_FLAG (bgp->flags, BGP_FLAG_NO_FAST_EXT_FAILOVER)) + continue; + + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + if (peer->gtsm_hops != 1 && peer_ttl (peer) != 1) + continue; + if (ifp == peer->nexthop.ifp) + BGP_EVENT_ADD (peer, BGP_Stop); + } + } + } + + return 0; +} + +static int +bgp_interface_address_add (int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct connected *ifc; + + ifc = zebra_interface_address_read (command, zclient->ibuf, vrf_id); + + if (ifc == NULL) + return 0; + + if (BGP_DEBUG(zebra, ZEBRA)) + { + char buf[128]; + prefix2str(ifc->address, buf, sizeof(buf)); + zlog_debug("Zebra rcvd: interface %s address add %s", + ifc->ifp->name, buf); + } + + if (if_is_operative (ifc->ifp)) + bgp_connected_add (ifc); + + return 0; +} + +static int +bgp_interface_address_delete (int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct connected *ifc; + + ifc = zebra_interface_address_read (command, zclient->ibuf, vrf_id); + + if (ifc == NULL) + return 0; + + if (BGP_DEBUG(zebra, ZEBRA)) + { + char buf[128]; + prefix2str(ifc->address, buf, sizeof(buf)); + zlog_debug("Zebra rcvd: interface %s address delete %s", + ifc->ifp->name, buf); + } + + if (if_is_operative (ifc->ifp)) + bgp_connected_delete (ifc); + + connected_free (ifc); + + return 0; +} + +/* Zebra route add and delete treatment. */ +static int +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; + + /* Type, flags, message. */ + api.type = stream_getc (s); + api.flags = stream_getc (s); + api.message = stream_getc (s); + + /* IPv4 prefix. */ + memset (&p, 0, sizeof (struct prefix_ipv4)); + p.family = AF_INET; + plength = stream_getc (s); + p.prefixlen = MIN(IPV4_MAX_PREFIXLEN, plength); + 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); + stream_getl (s); /* ifindex, unused */ + } + if (CHECK_FLAG (api.message, ZAPI_MESSAGE_DISTANCE)) + api.distance = stream_getc (s); + if (CHECK_FLAG (api.message, ZAPI_MESSAGE_METRIC)) + api.metric = stream_getl (s); + 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 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.tag); + } + bgp_redistribute_add ((struct prefix *)&p, &nexthop, NULL, + api.metric, api.type, api.tag); + } + else + { + if (BGP_DEBUG(zebra, ZEBRA)) + { + char buf[2][INET_ADDRSTRLEN]; + zlog_debug("Zebra rcvd: IPv4 route delete %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.tag); + } + bgp_redistribute_delete((struct prefix *)&p, api.type); + } + + return 0; +} + +/* Zebra route add and delete treatment. */ +static int +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)); + + /* Type, flags, message. */ + api.type = stream_getc (s); + api.flags = stream_getc (s); + api.message = stream_getc (s); + + /* IPv6 prefix. */ + memset (&p, 0, sizeof (struct prefix_ipv6)); + p.family = AF_INET6; + plength = stream_getc (s); + p.prefixlen = MIN(IPV6_MAX_PREFIXLEN, plength); + 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); + stream_get (&nexthop, s, 16); + } + if (CHECK_FLAG (api.message, ZAPI_MESSAGE_IFINDEX)) + { + api.ifindex_num = stream_getc (s); + stream_getl (s); /* ifindex, unused */ + } + 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 (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; + + if (command == ZEBRA_IPV6_ROUTE_ADD) + { + if (BGP_DEBUG(zebra, ZEBRA)) + { + char buf[2][INET6_ADDRSTRLEN]; + 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.tag); + } + bgp_redistribute_add ((struct prefix *)&p, NULL, &nexthop, + api.metric, api.type, api.tag); + } + else + { + if (BGP_DEBUG(zebra, ZEBRA)) + { + char buf[2][INET6_ADDRSTRLEN]; + zlog_debug("Zebra rcvd: IPv6 route delete %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_INET6, &nexthop, buf[1], sizeof(buf[1])), + api.metric, + api.tag); + } + bgp_redistribute_delete ((struct prefix *) &p, api.type); + } + + return 0; +} + +struct interface * +if_lookup_by_ipv4 (struct in_addr *addr) +{ + struct listnode *ifnode; + struct listnode *cnode; + struct interface *ifp; + struct connected *connected; + struct prefix_ipv4 p; + struct prefix *cp; + + p.family = AF_INET; + p.prefix = *addr; + p.prefixlen = IPV4_MAX_BITLEN; + + for (ALL_LIST_ELEMENTS_RO (iflist, ifnode, ifp)) + { + for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, connected)) + { + cp = connected->address; + + if (cp->family == AF_INET) + if (prefix_match (cp, (struct prefix *)&p)) + return ifp; + } + } + return NULL; +} + +struct interface * +if_lookup_by_ipv4_exact (struct in_addr *addr) +{ + struct listnode *ifnode; + struct listnode *cnode; + struct interface *ifp; + struct connected *connected; + struct prefix *cp; + + for (ALL_LIST_ELEMENTS_RO (iflist, ifnode, ifp)) + { + for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, connected)) + { + cp = connected->address; + + if (cp->family == AF_INET) + if (IPV4_ADDR_SAME (&cp->u.prefix4, addr)) + return ifp; + } + } + return NULL; +} + +struct interface * +if_lookup_by_ipv6 (struct in6_addr *addr) +{ + struct listnode *ifnode; + struct listnode *cnode; + struct interface *ifp; + struct connected *connected; + struct prefix_ipv6 p; + struct prefix *cp; + + p.family = AF_INET6; + p.prefix = *addr; + p.prefixlen = IPV6_MAX_BITLEN; + + for (ALL_LIST_ELEMENTS_RO (iflist, ifnode, ifp)) + { + for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, connected)) + { + cp = connected->address; + + if (cp->family == AF_INET6) + if (prefix_match (cp, (struct prefix *)&p)) + return ifp; + } + } + return NULL; +} + +struct interface * +if_lookup_by_ipv6_exact (struct in6_addr *addr) +{ + struct listnode *ifnode; + struct listnode *cnode; + struct interface *ifp; + struct connected *connected; + struct prefix *cp; + + for (ALL_LIST_ELEMENTS_RO (iflist, ifnode, ifp)) + { + for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, connected)) + { + cp = connected->address; + + if (cp->family == AF_INET6) + if (IPV6_ADDR_SAME (&cp->u.prefix6, addr)) + return ifp; + } + } + return NULL; +} + +static int +if_get_ipv6_global (struct interface *ifp, struct in6_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_INET6) + if (! IN6_IS_ADDR_LINKLOCAL (&cp->u.prefix6)) + { + memcpy (addr, &cp->u.prefix6, IPV6_MAX_BYTELEN); + return 1; + } + } + return 0; +} + +static int +if_get_ipv6_local (struct interface *ifp, struct in6_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_INET6) + if (IN6_IS_ADDR_LINKLOCAL (&cp->u.prefix6)) + { + memcpy (addr, &cp->u.prefix6, IPV6_MAX_BYTELEN); + return 1; + } + } + return 0; +} + +static int +if_get_ipv4_address (struct interface *ifp, struct in_addr *addr) +{ + struct listnode *cnode; + struct connected *connected; + struct prefix *cp; + + for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, connected)) + { + cp = connected->address; + if ((cp->family == AF_INET) && !ipv4_martian(&(cp->u.prefix4))) + { + *addr = cp->u.prefix4; + return 1; + } + } + return 0; +} + +int +bgp_nexthop_set (union sockunion *local, union sockunion *remote, + struct bgp_nexthop *nexthop, struct peer *peer) +{ + int ret = 0; + struct interface *ifp = NULL; + + memset (nexthop, 0, sizeof (struct bgp_nexthop)); + + if (!local) + return -1; + if (!remote) + return -1; + + if (local->sa.sa_family == AF_INET) + { + nexthop->v4 = 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); + } + if (local->sa.sa_family == AF_INET6) + { + if (IN6_IS_ADDR_LINKLOCAL (&local->sin6.sin6_addr)) + { + if (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_exact (&local->sin6.sin6_addr); + } + + if (!ifp) + return -1; + + nexthop->ifp = ifp; + + /* IPv4 connection. */ + if (local->sa.sa_family == AF_INET) + { + /* IPv6 nexthop*/ + ret = if_get_ipv6_global (ifp, &nexthop->v6_global); + + /* There is no global nexthop. */ + if (!ret) + if_get_ipv6_local (ifp, &nexthop->v6_global); + else + if_get_ipv6_local (ifp, &nexthop->v6_local); + } + + /* IPv6 connection. */ + if (local->sa.sa_family == AF_INET6) + { + struct interface *direct = NULL; + + /* IPv4 nexthop. */ + ret = if_get_ipv4_address(ifp, &nexthop->v4); + if (!ret && peer->local_id.s_addr) + nexthop->v4 = peer->local_id; + + /* Global address*/ + if (! IN6_IS_ADDR_LINKLOCAL (&local->sin6.sin6_addr)) + { + memcpy (&nexthop->v6_global, &local->sin6.sin6_addr, + IPV6_MAX_BYTELEN); + + /* If directory connected set link-local address. */ + direct = if_lookup_by_ipv6 (&remote->sin6.sin6_addr); + if (direct) + if_get_ipv6_local (ifp, &nexthop->v6_local); + } + else + /* Link-local address. */ + { + ret = if_get_ipv6_global (ifp, &nexthop->v6_global); + + /* If there is no global address. Set link-local address as + global. I know this break RFC specification... */ + if (!ret) + memcpy (&nexthop->v6_global, &local->sin6.sin6_addr, + IPV6_MAX_BYTELEN); + else + memcpy (&nexthop->v6_local, &local->sin6.sin6_addr, + IPV6_MAX_BYTELEN); + } + } + + if (IN6_IS_ADDR_LINKLOCAL (&local->sin6.sin6_addr) || + if_lookup_by_ipv6 (&remote->sin6.sin6_addr)) + peer->shared_network = 1; + else + peer->shared_network = 0; + + /* KAME stack specific treatment. */ +#ifdef KAME + if (IN6_IS_ADDR_LINKLOCAL (&nexthop->v6_global) + && IN6_LINKLOCAL_IFINDEX (nexthop->v6_global)) + { + SET_IN6_LINKLOCAL_IFINDEX (nexthop->v6_global, 0); + } + if (IN6_IS_ADDR_LINKLOCAL (&nexthop->v6_local) + && IN6_LINKLOCAL_IFINDEX (nexthop->v6_local)) + { + SET_IN6_LINKLOCAL_IFINDEX (nexthop->v6_local, 0); + } +#endif /* KAME */ + return ret; +} + +void +bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, safi_t safi) +{ + 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 (! vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_BGP], VRF_DEFAULT)) + return; + + flags = 0; + peer = info->peer; + + 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 == 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 = 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) + { + SET_FLAG (api.message, ZAPI_MESSAGE_DISTANCE); + api.distance = distance; + } + + 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" + " tag %u count %d", + inet_ntop(AF_INET, &p->u.prefix4, buf[0], sizeof(buf[0])), + p->prefixlen, + 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); + } + + /* We have to think about a IPv6 link-local address curse. */ + if (p->family == AF_INET6) + { + 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. */ + 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) + { + /* Workaround for Cisco's nexthop bug. */ + if (IN6_IS_ADDR_UNSPECIFIED (&info->attr->extra->mp_nexthop_global) + && peer->su_remote->sa.sa_family == AF_INET6) + nexthop = &peer->su_remote->sin6.sin6_addr; + else + nexthop = &info->attr->extra->mp_nexthop_local; + + if (info->peer->nexthop.ifp) + ifindex = info->peer->nexthop.ifp->ifindex; + } + + if (nexthop == NULL) + return; + + if (!ifindex) + { + if (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 = valid_nh_count; + api.nexthop = (struct in6_addr **)STREAM_DATA (bgp_nexthop_buf); + SET_FLAG (api.message, ZAPI_MESSAGE_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" + " 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.tag); + } + + zapi_ipv6_route (ZEBRA_IPV6_ROUTE_ADD, zclient, + (struct prefix_ipv6 *) p, &api); + } +} + +void +bgp_zebra_withdraw (struct prefix *p, struct bgp_info *info, safi_t safi) +{ + int flags; + struct peer *peer; + + if (zclient->sock < 0) + return; + + if (! vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_BGP], VRF_DEFAULT)) + return; + + peer = info->peer; + flags = 0; + + if (peer->sort == BGP_PEER_IBGP) + { + SET_FLAG (flags, ZEBRA_FLAG_INTERNAL); + SET_FLAG (flags, ZEBRA_FLAG_IBGP); + } + + 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; + + api.vrf_id = VRF_DEFAULT; + api.flags = flags; + + api.type = ZEBRA_ROUTE_BGP; + api.message = 0; + api.safi = safi; + 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 metric %u tag %d", + inet_ntop(AF_INET, &p->u.prefix4, buf[0], sizeof(buf[0])), + p->prefixlen, + api.metric, + api.tag); + } + + zapi_ipv4_route (ZEBRA_IPV4_ROUTE_DELETE, zclient, + (struct prefix_ipv4 *) p, &api); + } + + /* We have to think about a IPv6 link-local address curse. */ + if (p->family == AF_INET6) + { + struct zapi_ipv6 api; + + api.vrf_id = VRF_DEFAULT; + api.flags = flags; + api.type = ZEBRA_ROUTE_BGP; + api.message = 0; + api.safi = safi; + 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 metric %u tag %d", + inet_ntop(AF_INET6, &p->u.prefix6, buf[0], sizeof(buf[0])), + p->prefixlen, + api.metric, + api.tag); + } + + zapi_ipv6_route (ZEBRA_IPV6_ROUTE_DELETE, zclient, + (struct prefix_ipv6 *) p, &api); + } +} + +/* Other routes redistribution into BGP. */ +int +bgp_redistribute_set (struct bgp *bgp, afi_t afi, int type) +{ + /* Set flag to BGP instance. */ + bgp->redist[afi][type] = 1; + + /* Return if already redistribute flag is set. */ + if (vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT)) + return CMD_WARNING; + + vrf_bitmap_set (zclient->redist[type], VRF_DEFAULT); + + /* Return if zebra connection is not established. */ + if (zclient->sock < 0) + return CMD_WARNING; + + if (BGP_DEBUG(zebra, ZEBRA)) + 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, VRF_DEFAULT); + + return CMD_SUCCESS; +} + +/* Redistribute with route-map specification. */ +int +bgp_redistribute_rmap_set (struct bgp *bgp, afi_t afi, int type, + const char *name) +{ + if (bgp->rmap[afi][type].name + && (strcmp (bgp->rmap[afi][type].name, name) == 0)) + return 0; + + if (bgp->rmap[afi][type].name) + free (bgp->rmap[afi][type].name); + bgp->rmap[afi][type].name = strdup (name); + bgp->rmap[afi][type].map = route_map_lookup_by_name (name); + + return 1; +} + +/* Redistribute with metric specification. */ +int +bgp_redistribute_metric_set (struct bgp *bgp, afi_t afi, int type, + u_int32_t metric) +{ + if (bgp->redist_metric_flag[afi][type] + && bgp->redist_metric[afi][type] == metric) + return 0; + + bgp->redist_metric_flag[afi][type] = 1; + bgp->redist_metric[afi][type] = metric; + + return 1; +} + +/* Unset redistribution. */ +int +bgp_redistribute_unset (struct bgp *bgp, afi_t afi, int type) +{ + /* Unset flag from BGP instance. */ + bgp->redist[afi][type] = 0; + + /* Unset route-map. */ + if (bgp->rmap[afi][type].name) + free (bgp->rmap[afi][type].name); + bgp->rmap[afi][type].name = NULL; + bgp->rmap[afi][type].map = NULL; + + /* Unset metric. */ + bgp->redist_metric_flag[afi][type] = 0; + bgp->redist_metric[afi][type] = 0; + + /* Return if zebra connection is disabled. */ + if (! vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT)) + return CMD_WARNING; + vrf_bitmap_unset (zclient->redist[type], VRF_DEFAULT); + + if (bgp->redist[AFI_IP][type] == 0 + && bgp->redist[AFI_IP6][type] == 0 + && zclient->sock >= 0) + { + /* Send distribute delete message to zebra. */ + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("Zebra send: redistribute delete %s", + zebra_route_string(type)); + zebra_redistribute_send (ZEBRA_REDISTRIBUTE_DELETE, zclient, type, + VRF_DEFAULT); + } + + /* Withdraw redistributed routes from current BGP's routing table. */ + bgp_redistribute_withdraw (bgp, afi, type); + + return CMD_SUCCESS; +} + +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 (struct thread_master *master) +{ + zclient_num_connects = 0; + + /* Set default values. */ + 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; + zclient->interface_address_add = bgp_interface_address_add; + zclient->interface_address_delete = bgp_interface_address_delete; + zclient->ipv4_route_add = zebra_read_ipv4; + zclient->ipv4_route_delete = zebra_read_ipv4; + zclient->interface_up = bgp_interface_up; + zclient->interface_down = bgp_interface_down; + zclient->ipv6_route_add = zebra_read_ipv6; + zclient->ipv6_route_delete = zebra_read_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); +} + +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 new file mode 100644 index 0000000..5d4ed62 --- /dev/null +++ b/bgpd/bgp_zebra.h @@ -0,0 +1,53 @@ +/* zebra connection and redistribute fucntions. + 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_BGP_ZEBRA_H +#define _QUAGGA_BGP_ZEBRA_H + +#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); +extern void bgp_zebra_withdraw (struct prefix *, struct bgp_info *, safi_t); + +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 struct interface *if_lookup_by_ipv4 (struct in_addr *); +extern struct interface *if_lookup_by_ipv4_exact (struct in_addr *); +extern struct interface *if_lookup_by_ipv6 (struct in6_addr *); +extern struct interface *if_lookup_by_ipv6_exact (struct in6_addr *); + +extern int bgp_zebra_num_connects(void); + +#endif /* _QUAGGA_BGP_ZEBRA_H */ diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c new file mode 100644 index 0000000..6aeecb1 --- /dev/null +++ b/bgpd/bgpd.c @@ -0,0 +1,5614 @@ +/* BGP-4, BGP-4+ daemon program + Copyright (C) 1996, 97, 98, 99, 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 "prefix.h" +#include "thread.h" +#include "buffer.h" +#include "stream.h" +#include "command.h" +#include "sockunion.h" +#include "sockopt.h" +#include "network.h" +#include "memory.h" +#include "filter.h" +#include "routemap.h" +#include "str.h" +#include "log.h" +#include "plist.h" +#include "linklist.h" +#include "workqueue.h" +#include "table.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_table.h" +#include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_dump.h" +#include "bgpd/bgp_debug.h" +#include "bgpd/bgp_community.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_regex.h" +#include "bgpd/bgp_clist.h" +#include "bgpd/bgp_fsm.h" +#include "bgpd/bgp_packet.h" +#include "bgpd/bgp_zebra.h" +#include "bgpd/bgp_open.h" +#include "bgpd/bgp_filter.h" +#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; + +extern struct in_addr router_id_zebra; + +/* BGP process wide configuration pointer to export. */ +struct bgp_master *bm; + +/* BGP community-list. */ +struct community_list_handler *bgp_clist; + +/* BGP global flag manipulation. */ +int +bgp_option_set (int flag) +{ + switch (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: + return BGP_ERR_INVALID_FLAG; + } + return 0; +} + +int +bgp_option_unset (int flag) +{ + switch (flag) + { + case BGP_OPT_MULTIPLE_INSTANCE: + if (listcount (bm->bgp) > 1) + return BGP_ERR_MULTIPLE_INSTANCE_USED; + /* Fall through. */ + case BGP_OPT_NO_FIB: + case BGP_OPT_CONFIG_CISCO: + UNSET_FLAG (bm->options, flag); + break; + default: + return BGP_ERR_INVALID_FLAG; + } + return 0; +} + +int +bgp_option_check (int flag) +{ + return CHECK_FLAG (bm->options, flag); +} + +/* BGP flag manipulation. */ +int +bgp_flag_set (struct bgp *bgp, int flag) +{ + SET_FLAG (bgp->flags, flag); + return 0; +} + +int +bgp_flag_unset (struct bgp *bgp, int flag) +{ + UNSET_FLAG (bgp->flags, flag); + return 0; +} + +int +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) +{ + SET_FLAG (bgp->config, config); +} + +static void +bgp_config_unset (struct bgp *bgp, int config) +{ + UNSET_FLAG (bgp->config, config); +} + +static int +bgp_config_check (struct bgp *bgp, int config) +{ + return CHECK_FLAG (bgp->config, config); +} + +/* Set BGP router identifier. */ +static int +bgp_router_id_set (struct bgp *bgp, struct in_addr *id) +{ + struct peer *peer; + struct listnode *node, *nnode; + + if (bgp_config_check (bgp, BGP_CONFIG_ROUTER_ID) + && IPV4_ADDR_SAME (&bgp->router_id, id)) + return 0; + + IPV4_ADDR_COPY (&bgp->router_id, id); + bgp_config_set (bgp, BGP_CONFIG_ROUTER_ID); + + /* Set all peer's local identifier with this value. */ + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + IPV4_ADDR_COPY (&peer->local_id, id); + + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + { + peer->last_reset = PEER_DOWN_RID_CHANGE; + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + } + 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) +{ + struct peer *peer; + struct listnode *node, *nnode; + + if (bgp_config_check (bgp, BGP_CONFIG_CLUSTER_ID) + && IPV4_ADDR_SAME (&bgp->cluster_id, cluster_id)) + return 0; + + IPV4_ADDR_COPY (&bgp->cluster_id, cluster_id); + bgp_config_set (bgp, BGP_CONFIG_CLUSTER_ID); + + /* Clear all IBGP peer. */ + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + if (peer->sort != BGP_PEER_IBGP) + continue; + + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + { + peer->last_reset = PEER_DOWN_CLID_CHANGE; + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + } + return 0; +} + +int +bgp_cluster_id_unset (struct bgp *bgp) +{ + struct peer *peer; + struct listnode *node, *nnode; + + if (! bgp_config_check (bgp, BGP_CONFIG_CLUSTER_ID)) + return 0; + + bgp->cluster_id.s_addr = 0; + bgp_config_unset (bgp, BGP_CONFIG_CLUSTER_ID); + + /* Clear all IBGP peer. */ + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + if (peer->sort != BGP_PEER_IBGP) + continue; + + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + { + peer->last_reset = PEER_DOWN_CLID_CHANGE; + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + } + return 0; +} + +/* time_t value that is monotonicly increasing + * and uneffected by adjustments to system clock + */ +time_t bgp_clock (void) +{ + struct timeval tv; + + quagga_gettime(QUAGGA_CLK_MONOTONIC, &tv); + return tv.tv_sec; +} + +/* BGP timer configuration. */ +int +bgp_timers_set (struct bgp *bgp, u_int32_t keepalive, u_int32_t holdtime) +{ + bgp->default_keepalive = (keepalive < holdtime / 3 + ? keepalive : holdtime / 3); + bgp->default_holdtime = holdtime; + + return 0; +} + +int +bgp_timers_unset (struct bgp *bgp) +{ + bgp->default_keepalive = BGP_DEFAULT_KEEPALIVE; + bgp->default_holdtime = BGP_DEFAULT_HOLDTIME; + + return 0; +} + +/* BGP confederation configuration. */ +int +bgp_confederation_id_set (struct bgp *bgp, as_t as) +{ + struct peer *peer; + struct listnode *node, *nnode; + int already_confed; + + if (as == 0) + return BGP_ERR_INVALID_AS; + + /* Remember - were we doing confederation before? */ + already_confed = bgp_config_check (bgp, BGP_CONFIG_CONFEDERATION); + bgp->confed_id = as; + bgp_config_set (bgp, BGP_CONFIG_CONFEDERATION); + + /* If we were doing confederation already, this is just an external + AS change. Just Reset EBGP sessions, not CONFED sessions. If we + were not doing confederation before, reset all EBGP sessions. */ + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + /* We're looking for peers who's AS is not local or part of our + confederation. */ + if (already_confed) + { + if (peer_sort (peer) == BGP_PEER_EBGP) + { + peer->local_as = as; + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + { + peer->last_reset = PEER_DOWN_CONFED_ID_CHANGE; + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + + else + BGP_EVENT_ADD (peer, BGP_Stop); + } + } + else + { + /* Not doign confederation before, so reset every non-local + session */ + if (peer_sort (peer) != BGP_PEER_IBGP) + { + /* Reset the local_as to be our EBGP one */ + if (peer_sort (peer) == BGP_PEER_EBGP) + peer->local_as = as; + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + { + peer->last_reset = PEER_DOWN_CONFED_ID_CHANGE; + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + else + BGP_EVENT_ADD (peer, BGP_Stop); + } + } + } + return 0; +} + +int +bgp_confederation_id_unset (struct bgp *bgp) +{ + struct peer *peer; + struct listnode *node, *nnode; + + bgp->confed_id = 0; + bgp_config_unset (bgp, BGP_CONFIG_CONFEDERATION); + + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + /* We're looking for peers who's AS is not local */ + if (peer_sort (peer) != BGP_PEER_IBGP) + { + peer->local_as = bgp->as; + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + { + peer->last_reset = PEER_DOWN_CONFED_ID_CHANGE; + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + + else + BGP_EVENT_ADD (peer, BGP_Stop); + } + } + return 0; +} + +/* Is an AS part of the confed or not? */ +int +bgp_confederation_peers_check (struct bgp *bgp, as_t as) +{ + int i; + + if (! bgp) + return 0; + + for (i = 0; i < bgp->confed_peers_cnt; i++) + if (bgp->confed_peers[i] == as) + return 1; + + return 0; +} + +/* Add an AS to the confederation set. */ +int +bgp_confederation_peers_add (struct bgp *bgp, as_t as) +{ + struct peer *peer; + struct listnode *node, *nnode; + + if (! bgp) + return BGP_ERR_INVALID_BGP; + + if (bgp->as == as) + return BGP_ERR_INVALID_AS; + + if (bgp_confederation_peers_check (bgp, as)) + return -1; + + if (bgp->confed_peers) + bgp->confed_peers = XREALLOC (MTYPE_BGP_CONFED_LIST, + bgp->confed_peers, + (bgp->confed_peers_cnt + 1) * sizeof (as_t)); + else + bgp->confed_peers = XMALLOC (MTYPE_BGP_CONFED_LIST, + (bgp->confed_peers_cnt + 1) * sizeof (as_t)); + + bgp->confed_peers[bgp->confed_peers_cnt] = as; + bgp->confed_peers_cnt++; + + if (bgp_config_check (bgp, BGP_CONFIG_CONFEDERATION)) + { + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + if (peer->as == as) + { + peer->local_as = bgp->as; + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + { + peer->last_reset = PEER_DOWN_CONFED_PEER_CHANGE; + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + else + BGP_EVENT_ADD (peer, BGP_Stop); + } + } + } + return 0; +} + +/* Delete an AS from the confederation set. */ +int +bgp_confederation_peers_remove (struct bgp *bgp, as_t as) +{ + int i; + int j; + struct peer *peer; + struct listnode *node, *nnode; + + if (! bgp) + return -1; + + if (! bgp_confederation_peers_check (bgp, as)) + return -1; + + for (i = 0; i < bgp->confed_peers_cnt; i++) + if (bgp->confed_peers[i] == as) + for(j = i + 1; j < bgp->confed_peers_cnt; j++) + bgp->confed_peers[j - 1] = bgp->confed_peers[j]; + + bgp->confed_peers_cnt--; + + if (bgp->confed_peers_cnt == 0) + { + if (bgp->confed_peers) + XFREE (MTYPE_BGP_CONFED_LIST, bgp->confed_peers); + bgp->confed_peers = NULL; + } + else + bgp->confed_peers = XREALLOC (MTYPE_BGP_CONFED_LIST, + bgp->confed_peers, + bgp->confed_peers_cnt * sizeof (as_t)); + + /* Now reset any peer who's remote AS has just been removed from the + CONFED */ + if (bgp_config_check (bgp, BGP_CONFIG_CONFEDERATION)) + { + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + if (peer->as == as) + { + peer->local_as = bgp->confed_id; + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + { + peer->last_reset = PEER_DOWN_CONFED_PEER_CHANGE; + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + else + BGP_EVENT_ADD (peer, BGP_Stop); + } + } + } + + return 0; +} + +/* Local preference configuration. */ +int +bgp_default_local_preference_set (struct bgp *bgp, u_int32_t local_pref) +{ + if (! bgp) + return -1; + + bgp->default_local_pref = local_pref; + + return 0; +} + +int +bgp_default_local_preference_unset (struct bgp *bgp) +{ + if (! bgp) + return -1; + + bgp->default_local_pref = BGP_DEFAULT_LOCAL_PREF; + + 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. */ +int +peer_rsclient_active (struct peer *peer) +{ + int i; + int j; + + for (i=AFI_IP; i < AFI_MAX; i++) + for (j=SAFI_UNICAST; j < SAFI_MAX; j++) + if (CHECK_FLAG(peer->af_flags[i][j], PEER_FLAG_RSERVER_CLIENT) + && ! peer->af_group[i][j]) + return 1; + return 0; +} + +/* Peer comparison function for sorting. */ +static int +peer_cmp (struct peer *p1, struct peer *p2) +{ + return sockunion_cmp (&p1->su, &p2->su); +} + +int +peer_af_flag_check (struct peer *peer, afi_t afi, safi_t safi, u_int32_t flag) +{ + return CHECK_FLAG (peer->af_flags[afi][safi], flag); +} + +/* Reset all address family specific configuration. */ +static void +peer_af_flag_reset (struct peer *peer, afi_t afi, safi_t safi) +{ + int i; + struct bgp_filter *filter; + char orf_name[BUFSIZ]; + + filter = &peer->filter[afi][safi]; + + /* Clear neighbor filter and route-map */ + for (i = FILTER_IN; i < FILTER_MAX; i++) + { + if (filter->dlist[i].name) + { + free (filter->dlist[i].name); + filter->dlist[i].name = NULL; + } + if (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->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; + } + } + + /* Clear unsuppress map. */ + if (filter->usmap.name) + free (filter->usmap.name); + filter->usmap.name = NULL; + filter->usmap.map = NULL; + + /* Clear neighbor's all address family flags. */ + peer->af_flags[afi][safi] = 0; + + /* Clear neighbor's all address family sflags. */ + peer->af_sflags[afi][safi] = 0; + + /* Clear neighbor's all address family capabilities. */ + peer->af_cap[afi][safi] = 0; + + /* Clear ORF info */ + peer->orf_plist[afi][safi] = NULL; + sprintf (orf_name, "%s.%d.%d", peer->host, afi, safi); + 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 */ + if (peer->default_rmap[afi][safi].name) + free (peer->default_rmap[afi][safi].name); + peer->default_rmap[afi][safi].name = NULL; + peer->default_rmap[afi][safi].map = NULL; + + /* Clear neighbor maximum-prefix */ + peer->pmax[afi][safi] = 0; + peer->pmax_threshold[afi][safi] = MAXIMUM_PREFIX_THRESHOLD_DEFAULT; +} + +/* peer global config reset */ +static void +peer_global_config_reset (struct peer *peer) +{ + peer->weight = 0; + peer->change_local_as = 0; + peer->ttl = 0; + peer->gtsm_hops = 0; + if (peer->update_source) + { + sockunion_free (peer->update_source); + peer->update_source = NULL; + } + if (peer->update_if) + { + XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if); + peer->update_if = NULL; + } + + if (peer_sort (peer) == BGP_PEER_IBGP) + peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV; + else + peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV; + + peer->flags = 0; + peer->config = 0; + peer->holdtime = 0; + peer->keepalive = 0; + peer->connect = 0; + peer->v_connect = BGP_DEFAULT_CONNECT_RETRY; +} + +/* 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; + + bgp = peer->bgp; + + /* Peer-group */ + if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + if (peer->as) + return (bgp->as == peer->as ? BGP_PEER_IBGP : BGP_PEER_EBGP); + else + { + struct peer *peer1; + peer1 = listnode_head (peer->group->peer); + if (peer1) + return (peer1->local_as == peer1->as + ? BGP_PEER_IBGP : BGP_PEER_EBGP); + } + return BGP_PEER_INTERNAL; + } + + /* Normal peer */ + if (bgp && CHECK_FLAG (bgp->config, BGP_CONFIG_CONFEDERATION)) + { + if (peer->local_as == 0) + return BGP_PEER_INTERNAL; + + if (peer->local_as == peer->as) + { + if (peer->local_as == bgp->confed_id) + return BGP_PEER_EBGP; + else + return BGP_PEER_IBGP; + } + + if (bgp_confederation_peers_check (bgp, peer->as)) + return BGP_PEER_CONFED; + + return BGP_PEER_EBGP; + } + else + { + return (peer->local_as == 0 + ? BGP_PEER_INTERNAL : peer->local_as == peer->as + ? BGP_PEER_IBGP : BGP_PEER_EBGP); + } +} + +/* 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); + + /* this /ought/ to have been done already through bgp_stop earlier, + * but just to be sure.. + */ + bgp_timer_set (peer); + BGP_READ_OFF (peer->t_read); + BGP_WRITE_OFF (peer->t_write); + BGP_EVENT_FLUSH (peer); + + if (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); + peer->host = NULL; + } + + /* Update source configuration. */ + if (peer->update_source) + { + sockunion_free (peer->update_source); + peer->update_source = NULL; + } + + if (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); + 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); +} + +/* increase reference count on a struct peer */ +struct 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; +} + +/* decrease reference count on a struct peer + * struct peer is freed and NULL returned if last reference + */ +struct 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) + { + peer_free (peer); + return NULL; + } + + return peer; +} + +/* Allocate new peer object, implicitely locked. */ +static struct peer * +peer_new (struct bgp *bgp) +{ + afi_t afi; + safi_t safi; + struct peer *peer; + struct servent *sp; + + /* bgp argument is absolutely required */ + assert (bgp); + if (!bgp) + return NULL; + + /* Allocate new peer. */ + peer = XCALLOC (MTYPE_BGP_PEER, sizeof (struct peer)); + + /* Set default value. */ + peer->fd = -1; + peer->v_start = BGP_INIT_START_TIMER; + peer->v_connect = BGP_DEFAULT_CONNECT_RETRY; + peer->status = Idle; + peer->ostatus = Idle; + peer->weight = 0; + peer->password = NULL; + peer->bgp = bgp; + peer = peer_lock (peer); /* initial reference */ + bgp_lock (bgp); + + /* Set default flags. */ + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + { + 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); + } + peer->orf_plist[afi][safi] = NULL; + } + SET_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN); + + /* Create buffers. */ + peer->ibuf = stream_new (BGP_MAX_PACKET_SIZE); + peer->obuf = stream_fifo_new (); + + /* 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); + + /* Get service port number. */ + sp = getservbyname ("bgp", "tcp"); + peer->port = (sp == NULL) ? BGP_PORT_DEFAULT : ntohs (sp->s_port); + + return peer; +} + +/* Create new BGP peer. */ +static struct peer * +peer_create (union sockunion *su, struct bgp *bgp, as_t local_as, + as_t remote_as, afi_t afi, safi_t safi) +{ + int active; + struct peer *peer; + char buf[SU_ADDRSTRLEN]; + + peer = peer_new (bgp); + peer->su = *su; + peer->local_as = local_as; + peer->as = remote_as; + peer->local_id = bgp->router_id; + peer->v_holdtime = bgp->default_holdtime; + peer->v_keepalive = bgp->default_keepalive; + if (peer_sort (peer) == BGP_PEER_IBGP) + peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV; + else + peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV; + + peer = peer_lock (peer); /* bgp peer list reference */ + listnode_add_sort (bgp->peer, peer); + + active = peer_active (peer); + + if (afi && safi) + peer->afc[afi][safi] = 1; + + /* Last read and reset time set */ + peer->readtime = peer->resettime = bgp_clock (); + + /* Make peer's address string. */ + sockunion2str (su, buf, SU_ADDRSTRLEN); + peer->host = XSTRDUP (MTYPE_BGP_PEER_HOST, buf); + + /* Set up peer's events and timers. */ + if (! active && peer_active (peer)) + bgp_timer_set (peer); + + return peer; +} + +/* Make accept BGP peer. Called from bgp_accept (). */ +struct peer * +peer_create_accept (struct bgp *bgp) +{ + struct peer *peer; + + peer = peer_new (bgp); + + peer = peer_lock (peer); /* bgp peer list reference */ + listnode_add_sort (bgp->peer, peer); + + return peer; +} + +/* Change peer's AS number. */ +static void +peer_as_change (struct peer *peer, as_t as) +{ + struct peer *conf; + + /* Stop peer. */ + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + { + peer->last_reset = PEER_DOWN_REMOTE_AS_CHANGE; + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + else + BGP_EVENT_ADD (peer, BGP_Stop); + } + peer->as = as; + + if (bgp_config_check (peer->bgp, BGP_CONFIG_CONFEDERATION) + && ! bgp_confederation_peers_check (peer->bgp, as) + && peer->bgp->as != as) + peer->local_as = peer->bgp->confed_id; + else + peer->local_as = peer->bgp->as; + + /* Advertisement-interval reset */ + conf = NULL; + if (peer->group) + conf = peer->group->conf; + + 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) + { + UNSET_FLAG (peer->af_flags[AFI_IP][SAFI_UNICAST], + PEER_FLAG_REFLECTOR_CLIENT); + UNSET_FLAG (peer->af_flags[AFI_IP][SAFI_MULTICAST], + 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 */ + if (peer_sort (peer) != BGP_PEER_EBGP) + { + 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 does not exist, create new one. If peer already exists, + set AS number to the peer. */ +int +peer_remote_as (struct bgp *bgp, union sockunion *su, as_t *as, + afi_t afi, safi_t safi) +{ + struct peer *peer; + as_t local_as; + + peer = peer_lookup (bgp, su); + + if (peer) + { + /* When this peer is a member of peer-group. */ + if (peer->group) + { + if (peer->group->conf->as) + { + /* Return peer group's AS number. */ + *as = peer->group->conf->as; + return BGP_ERR_PEER_GROUP_MEMBER; + } + if (peer_sort (peer->group->conf) == BGP_PEER_IBGP) + { + if (bgp->as != *as) + { + *as = peer->as; + return BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT; + } + } + else + { + if (bgp->as == *as) + { + *as = peer->as; + return BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT; + } + } + } + + /* Existing peer's AS number change. */ + if (peer->as != *as) + peer_as_change (peer, *as); + } + else + { + + /* If the peer is not part of our confederation, and its not an + iBGP peer then spoof the source AS */ + if (bgp_config_check (bgp, BGP_CONFIG_CONFEDERATION) + && ! bgp_confederation_peers_check (bgp, *as) + && bgp->as != *as) + local_as = bgp->confed_id; + else + local_as = bgp->as; + + /* If this is IPv4 unicast configuration and "no bgp default + ipv4-unicast" is specified. */ + + if (bgp_flag_check (bgp, BGP_FLAG_NO_DEFAULT_IPV4) + && afi == AFI_IP && safi == SAFI_UNICAST) + peer_create (su, bgp, local_as, *as, 0, 0); + else + peer_create (su, bgp, local_as, *as, afi, safi); + } + + return 0; +} + +/* Activate the peer or peer group for specified AFI and SAFI. */ +int +peer_activate (struct peer *peer, afi_t afi, safi_t safi) +{ + int active; + + if (peer->afc[afi][safi]) + return 0; + + /* Activate the address family configuration. */ + if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + peer->afc[afi][safi] = 1; + else + { + active = peer_active (peer); + + peer->afc[afi][safi] = 1; + + if (! active && peer_active (peer)) + bgp_timer_set (peer); + else + { + if (peer->status == Established) + { + if (CHECK_FLAG (peer->cap, PEER_CAP_DYNAMIC_RCV)) + { + peer->afc_adv[afi][safi] = 1; + bgp_capability_send (peer, afi, safi, + CAPABILITY_CODE_MP, + CAPABILITY_ACTION_SET); + if (peer->afc_recv[afi][safi]) + { + peer->afc_nego[afi][safi] = 1; + bgp_announce_route (peer, afi, safi); + } + } + else + { + peer->last_reset = PEER_DOWN_AF_ACTIVATE; + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + } + } + } + return 0; +} + +int +peer_deactivate (struct peer *peer, afi_t afi, safi_t safi) +{ + struct peer_group *group; + struct peer *peer1; + struct listnode *node, *nnode; + + if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + group = peer->group; + + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer1)) + { + if (peer1->af_group[afi][safi]) + return BGP_ERR_PEER_GROUP_MEMBER_EXISTS; + } + } + else + { + if (peer->af_group[afi][safi]) + return BGP_ERR_PEER_BELONGS_TO_GROUP; + } + + if (! peer->afc[afi][safi]) + return 0; + + /* De-activate the address family configuration. */ + peer->afc[afi][safi] = 0; + peer_af_flag_reset (peer, afi, safi); + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + if (peer->status == Established) + { + if (CHECK_FLAG (peer->cap, PEER_CAP_DYNAMIC_RCV)) + { + peer->afc_adv[afi][safi] = 0; + peer->afc_nego[afi][safi] = 0; + + if (peer_active_nego (peer)) + { + bgp_capability_send (peer, afi, safi, + CAPABILITY_CODE_MP, + CAPABILITY_ACTION_UNSET); + bgp_clear_route (peer, afi, safi, BGP_CLEAR_ROUTE_NORMAL); + peer->pcount[afi][safi] = 0; + } + else + { + peer->last_reset = PEER_DOWN_NEIGHBOR_DELETE; + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + } + else + { + peer->last_reset = PEER_DOWN_NEIGHBOR_DELETE; + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + } + } + 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) +{ + afi_t afi; + safi_t safi; + + UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT); + UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_MODE); + + for (afi = AFI_IP ; afi < AFI_MAX ; afi++) + for (safi = SAFI_UNICAST ; safi < SAFI_RESERVED_3 ; safi++) + peer->nsf[afi][safi] = 0; + + if (peer->t_gr_restart) + { + BGP_TIMER_OFF (peer->t_gr_restart); + if (BGP_DEBUG (events, EVENTS)) + zlog_debug ("%s graceful restart timer stopped", peer->host); + } + if (peer->t_gr_stale) + { + BGP_TIMER_OFF (peer->t_gr_stale); + if (BGP_DEBUG (events, EVENTS)) + zlog_debug ("%s graceful restart stalepath timer stopped", peer->host); + } + bgp_clear_route_all (peer); +} + +/* Delete peer from confguration. + * + * The peer is moved to a dead-end "Deleted" neighbour-state, to allow + * it to "cool off" and refcounts to hit 0, at which state it is freed. + * + * This function /should/ take care to be idempotent, to guard against + * it being called multiple times through stray events that come in + * that happen to result in this function being called again. That + * said, getting here for a "Deleted" peer is a bug in the neighbour + * FSM. + */ +int +peer_delete (struct peer *peer) +{ + int i; + afi_t afi; + safi_t safi; + struct bgp *bgp; + struct bgp_filter *filter; + struct listnode *pn; + + assert (peer->status != Deleted); + + bgp = peer->bgp; + + if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT)) + peer_nsf_stop (peer); + + /* If this peer belongs to peer group, clear up the + relationship. */ + if (peer->group) + { + if ((pn = listnode_lookup (peer->group->peer, peer))) + { + peer = peer_unlock (peer); /* group->peer list reference */ + list_delete_node (peer->group->peer, pn); + } + peer->group = NULL; + } + + /* Withdraw all information from routing table. We can not use + * BGP_EVENT_ADD (peer, BGP_Stop) at here. Because the event is + * executed after peer structure is deleted. + */ + 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) + { + XFREE (MTYPE_PEER_PASSWORD, peer->password); + peer->password = NULL; + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + bgp_md5_set (peer); + } + + bgp_timer_set (peer); /* stops all timers for Deleted */ + + /* Delete from all peer list. */ + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP) + && (pn = listnode_lookup (bgp->peer, peer))) + { + peer_unlock (peer); /* bgp peer list reference */ + list_delete_node (bgp->peer, pn); + } + + if (peer_rsclient_active (peer) + && (pn = listnode_lookup (bgp->rsclient, peer))) + { + peer_unlock (peer); /* rsclient list reference */ + list_delete_node (bgp->rsclient, pn); + + /* Clear our own rsclient ribs. */ + 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_RSERVER_CLIENT)) + bgp_clear_route (peer, afi, safi, BGP_CLEAR_ROUTE_MY_RSCLIENT); + } + + /* Free RIB for any family in which peer is RSERVER_CLIENT, and is not + member of a peer_group. */ + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + if (peer->rib[afi][safi] && ! peer->af_group[afi][safi]) + bgp_table_finish (&peer->rib[afi][safi]); + + /* Buffers. */ + if (peer->ibuf) + { + stream_free (peer->ibuf); + peer->ibuf = NULL; + } + + if (peer->obuf) + { + stream_fifo_free (peer->obuf); + peer->obuf = NULL; + } + + if (peer->work) + { + 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); + peer->su_local = NULL; + } + + if (peer->su_remote) + { + sockunion_free (peer->su_remote); + peer->su_remote = NULL; + } + + /* Free filter related memory. */ + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + { + filter = &peer->filter[afi][safi]; + + for (i = FILTER_IN; i < FILTER_MAX; i++) + { + if (filter->dlist[i].name) + { + free(filter->dlist[i].name); + filter->dlist[i].name = NULL; + } + + if (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->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; + } + } + + if (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); + 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) +{ + return strcmp (g1->name, g2->name); +} + +/* If peer is configured at least one address family return 1. */ +static int +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_MPLS_VPN] + || peer->af_group[AFI_IP6][SAFI_ENCAP]) + return 1; + return 0; +} + +/* Peer group cofiguration. */ +static struct peer_group * +peer_group_new (void) +{ + return (struct peer_group *) XCALLOC (MTYPE_PEER_GROUP, + sizeof (struct peer_group)); +} + +static void +peer_group_free (struct peer_group *group) +{ + XFREE (MTYPE_PEER_GROUP, group); +} + +struct peer_group * +peer_group_lookup (struct bgp *bgp, const char *name) +{ + struct peer_group *group; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS (bgp->group, node, nnode, group)) + { + if (strcmp (group->name, name) == 0) + return group; + } + return NULL; +} + +struct peer_group * +peer_group_get (struct bgp *bgp, const char *name) +{ + struct peer_group *group; + + group = peer_group_lookup (bgp, name); + if (group) + return group; + + group = peer_group_new (); + group->bgp = bgp; + group->name = strdup (name); + group->peer = list_new (); + group->conf = peer_new (bgp); + if (! bgp_flag_check (bgp, BGP_FLAG_NO_DEFAULT_IPV4)) + group->conf->afc[AFI_IP][SAFI_UNICAST] = 1; + group->conf->host = XSTRDUP (MTYPE_BGP_PEER_HOST, name); + group->conf->group = group; + group->conf->as = 0; + 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); + UNSET_FLAG (group->conf->config, PEER_CONFIG_CONNECT); + group->conf->keepalive = 0; + group->conf->holdtime = 0; + group->conf->connect = 0; + SET_FLAG (group->conf->sflags, PEER_STATUS_GROUP); + listnode_add_sort (bgp->group, group); + + return 0; +} + +static void +peer_group2peer_config_copy (struct peer_group *group, struct peer *peer, + afi_t afi, safi_t safi) +{ + int in = FILTER_IN; + int out = FILTER_OUT; + struct peer *conf; + struct bgp_filter *pfilter; + struct bgp_filter *gfilter; + + conf = group->conf; + pfilter = &peer->filter[afi][safi]; + gfilter = &conf->filter[afi][safi]; + + /* remote-as */ + if (conf->as) + peer->as = conf->as; + + /* remote-as */ + if (conf->change_local_as) + peer->change_local_as = conf->change_local_as; + + /* TTL */ + peer->ttl = conf->ttl; + + /* GTSM hops */ + peer->gtsm_hops = conf->gtsm_hops; + + /* Weight */ + peer->weight = conf->weight; + + /* peer flags apply */ + peer->flags = conf->flags; + /* peer af_flags apply */ + peer->af_flags[afi][safi] = conf->af_flags[afi][safi]; + /* peer config apply */ + peer->config = conf->config; + + /* peer timers apply */ + peer->holdtime = conf->holdtime; + peer->keepalive = conf->keepalive; + peer->connect = conf->connect; + if (CHECK_FLAG (conf->config, PEER_CONFIG_CONNECT)) + peer->v_connect = conf->connect; + else + peer->v_connect = BGP_DEFAULT_CONNECT_RETRY; + + /* advertisement-interval reset */ + if (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; + + /* password apply */ + if (conf->password && !peer->password) + peer->password = XSTRDUP (MTYPE_PEER_PASSWORD, conf->password); + + bgp_md5_set (peer); + + /* maximum-prefix */ + peer->pmax[afi][safi] = conf->pmax[afi][safi]; + peer->pmax_threshold[afi][safi] = conf->pmax_threshold[afi][safi]; + peer->pmax_restart[afi][safi] = conf->pmax_restart[afi][safi]; + + /* allowas-in */ + peer->allowas_in[afi][safi] = conf->allowas_in[afi][safi]; + + /* route-server-client */ + if (CHECK_FLAG(conf->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)) + { + /* Make peer's RIB point to group's RIB. */ + peer->rib[afi][safi] = group->conf->rib[afi][safi]; + + /* Import policy. */ + if (pfilter->map[RMAP_IMPORT].name) + free (pfilter->map[RMAP_IMPORT].name); + if (gfilter->map[RMAP_IMPORT].name) + { + pfilter->map[RMAP_IMPORT].name = strdup (gfilter->map[RMAP_IMPORT].name); + pfilter->map[RMAP_IMPORT].map = gfilter->map[RMAP_IMPORT].map; + } + else + { + pfilter->map[RMAP_IMPORT].name = NULL; + pfilter->map[RMAP_IMPORT].map = NULL; + } + + /* Export policy. */ + if (gfilter->map[RMAP_EXPORT].name && ! pfilter->map[RMAP_EXPORT].name) + { + pfilter->map[RMAP_EXPORT].name = strdup (gfilter->map[RMAP_EXPORT].name); + pfilter->map[RMAP_EXPORT].map = gfilter->map[RMAP_EXPORT].map; + } + } + + /* default-originate route-map */ + if (conf->default_rmap[afi][safi].name) + { + if (peer->default_rmap[afi][safi].name) + free (peer->default_rmap[afi][safi].name); + peer->default_rmap[afi][safi].name = strdup (conf->default_rmap[afi][safi].name); + peer->default_rmap[afi][safi].map = conf->default_rmap[afi][safi].map; + } + + /* update-source apply */ + if (conf->update_source) + { + if (peer->update_source) + sockunion_free (peer->update_source); + if (peer->update_if) + { + XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if); + peer->update_if = NULL; + } + peer->update_source = sockunion_dup (conf->update_source); + } + else if (conf->update_if) + { + if (peer->update_if) + XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if); + if (peer->update_source) + { + sockunion_free (peer->update_source); + peer->update_source = NULL; + } + peer->update_if = XSTRDUP (MTYPE_PEER_UPDATE_SOURCE, conf->update_if); + } + + /* inbound filter apply */ + if (gfilter->dlist[in].name && ! pfilter->dlist[in].name) + { + if (pfilter->dlist[in].name) + free (pfilter->dlist[in].name); + pfilter->dlist[in].name = strdup (gfilter->dlist[in].name); + pfilter->dlist[in].alist = gfilter->dlist[in].alist; + } + if (gfilter->plist[in].name && ! pfilter->plist[in].name) + { + if (pfilter->plist[in].name) + free (pfilter->plist[in].name); + pfilter->plist[in].name = strdup (gfilter->plist[in].name); + pfilter->plist[in].plist = gfilter->plist[in].plist; + } + if (gfilter->aslist[in].name && ! pfilter->aslist[in].name) + { + if (pfilter->aslist[in].name) + free (pfilter->aslist[in].name); + pfilter->aslist[in].name = strdup (gfilter->aslist[in].name); + pfilter->aslist[in].aslist = gfilter->aslist[in].aslist; + } + if (gfilter->map[RMAP_IN].name && ! pfilter->map[RMAP_IN].name) + { + if (pfilter->map[RMAP_IN].name) + free (pfilter->map[RMAP_IN].name); + pfilter->map[RMAP_IN].name = strdup (gfilter->map[RMAP_IN].name); + pfilter->map[RMAP_IN].map = gfilter->map[RMAP_IN].map; + } + + /* outbound filter apply */ + if (gfilter->dlist[out].name) + { + if (pfilter->dlist[out].name) + free (pfilter->dlist[out].name); + pfilter->dlist[out].name = strdup (gfilter->dlist[out].name); + pfilter->dlist[out].alist = gfilter->dlist[out].alist; + } + else + { + if (pfilter->dlist[out].name) + free (pfilter->dlist[out].name); + pfilter->dlist[out].name = NULL; + pfilter->dlist[out].alist = NULL; + } + if (gfilter->plist[out].name) + { + if (pfilter->plist[out].name) + free (pfilter->plist[out].name); + pfilter->plist[out].name = strdup (gfilter->plist[out].name); + pfilter->plist[out].plist = gfilter->plist[out].plist; + } + else + { + if (pfilter->plist[out].name) + free (pfilter->plist[out].name); + pfilter->plist[out].name = NULL; + pfilter->plist[out].plist = NULL; + } + if (gfilter->aslist[out].name) + { + if (pfilter->aslist[out].name) + free (pfilter->aslist[out].name); + pfilter->aslist[out].name = strdup (gfilter->aslist[out].name); + pfilter->aslist[out].aslist = gfilter->aslist[out].aslist; + } + else + { + if (pfilter->aslist[out].name) + free (pfilter->aslist[out].name); + pfilter->aslist[out].name = NULL; + pfilter->aslist[out].aslist = NULL; + } + if (gfilter->map[RMAP_OUT].name) + { + if (pfilter->map[RMAP_OUT].name) + free (pfilter->map[RMAP_OUT].name); + pfilter->map[RMAP_OUT].name = strdup (gfilter->map[RMAP_OUT].name); + pfilter->map[RMAP_OUT].map = gfilter->map[RMAP_OUT].map; + } + else + { + if (pfilter->map[RMAP_OUT].name) + free (pfilter->map[RMAP_OUT].name); + pfilter->map[RMAP_OUT].name = NULL; + pfilter->map[RMAP_OUT].map = NULL; + } + + /* RS-client's import/export route-maps. */ + if (gfilter->map[RMAP_IMPORT].name) + { + if (pfilter->map[RMAP_IMPORT].name) + free (pfilter->map[RMAP_IMPORT].name); + pfilter->map[RMAP_IMPORT].name = strdup (gfilter->map[RMAP_IMPORT].name); + pfilter->map[RMAP_IMPORT].map = gfilter->map[RMAP_IMPORT].map; + } + else + { + if (pfilter->map[RMAP_IMPORT].name) + free (pfilter->map[RMAP_IMPORT].name); + pfilter->map[RMAP_IMPORT].name = NULL; + pfilter->map[RMAP_IMPORT].map = NULL; + } + if (gfilter->map[RMAP_EXPORT].name && ! pfilter->map[RMAP_EXPORT].name) + { + if (pfilter->map[RMAP_EXPORT].name) + free (pfilter->map[RMAP_EXPORT].name); + pfilter->map[RMAP_EXPORT].name = strdup (gfilter->map[RMAP_EXPORT].name); + pfilter->map[RMAP_EXPORT].map = gfilter->map[RMAP_EXPORT].map; + } + + if (gfilter->usmap.name) + { + if (pfilter->usmap.name) + free (pfilter->usmap.name); + pfilter->usmap.name = strdup (gfilter->usmap.name); + pfilter->usmap.map = gfilter->usmap.map; + } + else + { + if (pfilter->usmap.name) + free (pfilter->usmap.name); + pfilter->usmap.name = NULL; + pfilter->usmap.map = NULL; + } +} + +/* Peer group's remote AS configuration. */ +int +peer_group_remote_as (struct bgp *bgp, const char *group_name, as_t *as) +{ + struct peer_group *group; + struct peer *peer; + struct listnode *node, *nnode; + + group = peer_group_lookup (bgp, group_name); + if (! group) + return -1; + + if (group->conf->as == *as) + return 0; + + /* When we setup peer-group AS number all peer group member's AS + number must be updated to same number. */ + peer_as_change (group->conf, *as); + + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + if (peer->as != *as) + peer_as_change (peer, *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) +{ + struct bgp *bgp; + struct peer *peer; + struct listnode *node, *nnode; + + bgp = group->bgp; + + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + peer_delete (peer); + } + list_delete (group->peer); + + free (group->name); + group->name = NULL; + + group->conf->group = NULL; + peer_delete (group->conf); + + /* Delete from all peer_group list. */ + listnode_delete (bgp->group, group); + + peer_group_free (group); + + return 0; +} + +int +peer_group_remote_as_delete (struct peer_group *group) +{ + struct peer *peer; + struct listnode *node, *nnode; + + if (! group->conf->as) + return 0; + + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + peer_delete (peer); + } + list_delete_all_node (group->peer); + + group->conf->as = 0; + + return 0; +} + +/* Bind specified peer to peer group. */ +int +peer_group_bind (struct bgp *bgp, union sockunion *su, + struct peer_group *group, afi_t afi, safi_t safi, as_t *as) +{ + struct peer *peer; + int first_member = 0; + + /* Check peer group's address family. */ + if (! group->conf->afc[afi][safi]) + return BGP_ERR_PEER_GROUP_AF_UNCONFIGURED; + + /* Lookup the peer. */ + peer = peer_lookup (bgp, su); + + /* Create a new peer. */ + if (! peer) + { + if (! group->conf->as) + return BGP_ERR_PEER_GROUP_NO_REMOTE_AS; + + peer = peer_create (su, bgp, bgp->as, group->conf->as, afi, safi); + peer->group = group; + peer->af_group[afi][safi] = 1; + + peer = peer_lock (peer); /* group->peer list reference */ + listnode_add (group->peer, peer); + peer_group2peer_config_copy (group, peer, afi, safi); + + return 0; + } + + /* When the peer already belongs to peer group, check the consistency. */ + if (peer->af_group[afi][safi]) + { + if (strcmp (peer->group->name, group->name) != 0) + return BGP_ERR_PEER_GROUP_CANT_CHANGE; + + return 0; + } + + /* Check current peer group configuration. */ + if (peer_group_active (peer) + && strcmp (peer->group->name, group->name) != 0) + return BGP_ERR_PEER_GROUP_MISMATCH; + + if (! group->conf->as) + { + if (peer_sort (group->conf) != BGP_PEER_INTERNAL + && peer_sort (group->conf) != peer_sort (peer)) + { + if (as) + *as = peer->as; + return BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT; + } + + if (peer_sort (group->conf) == BGP_PEER_INTERNAL) + first_member = 1; + } + + peer->af_group[afi][safi] = 1; + peer->afc[afi][safi] = 1; + if (! peer->group) + { + peer->group = group; + + peer = peer_lock (peer); /* group->peer list reference */ + listnode_add (group->peer, peer); + } + else + assert (group && peer->group == group); + + if (first_member) + { + /* Advertisement-interval reset */ + 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); + } + } + + if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)) + { + struct listnode *pn; + + /* If it's not configured as RSERVER_CLIENT in any other address + family, without being member of a peer_group, remove it from + list bgp->rsclient.*/ + if (! peer_rsclient_active (peer) + && (pn = listnode_lookup (bgp->rsclient, peer))) + { + peer_unlock (peer); /* peer rsclient reference */ + list_delete_node (bgp->rsclient, pn); + + /* Clear our own rsclient rib for this afi/safi. */ + bgp_clear_route (peer, afi, safi, BGP_CLEAR_ROUTE_MY_RSCLIENT); + } + + bgp_table_finish (&peer->rib[afi][safi]); + + /* Import policy. */ + if (peer->filter[afi][safi].map[RMAP_IMPORT].name) + { + free (peer->filter[afi][safi].map[RMAP_IMPORT].name); + peer->filter[afi][safi].map[RMAP_IMPORT].name = NULL; + peer->filter[afi][safi].map[RMAP_IMPORT].map = NULL; + } + + /* Export policy. */ + if (! CHECK_FLAG(group->conf->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT) + && peer->filter[afi][safi].map[RMAP_EXPORT].name) + { + free (peer->filter[afi][safi].map[RMAP_EXPORT].name); + peer->filter[afi][safi].map[RMAP_EXPORT].name = NULL; + peer->filter[afi][safi].map[RMAP_EXPORT].map = NULL; + } + } + + peer_group2peer_config_copy (group, peer, afi, safi); + + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + { + peer->last_reset = PEER_DOWN_RMAP_BIND; + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + else + BGP_EVENT_ADD (peer, BGP_Stop); + + return 0; +} + +int +peer_group_unbind (struct bgp *bgp, struct peer *peer, + struct peer_group *group, afi_t afi, safi_t safi) +{ + if (! peer->af_group[afi][safi]) + return 0; + + if (group != peer->group) + return BGP_ERR_PEER_GROUP_MISMATCH; + + peer->af_group[afi][safi] = 0; + peer->afc[afi][safi] = 0; + peer_af_flag_reset (peer, afi, safi); + + if (peer->rib[afi][safi]) + peer->rib[afi][safi] = NULL; + + if (! peer_group_active (peer)) + { + assert (listnode_lookup (group->peer, peer)); + peer_unlock (peer); /* peer group list reference */ + listnode_delete (group->peer, peer); + peer->group = NULL; + if (group->conf->as) + { + peer_delete (peer); + return 0; + } + peer_global_config_reset (peer); + } + + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + { + peer->last_reset = PEER_DOWN_RMAP_UNBIND; + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + else + BGP_EVENT_ADD (peer, BGP_Stop); + + 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) +{ + 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_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; +} + +/* Return first entry of BGP. */ +struct bgp * +bgp_get_default (void) +{ + if (bm && bm->bgp && bm->bgp->head) + return (listgetdata (listhead (bm->bgp))); + return NULL; +} + +/* Lookup BGP entry. */ +struct bgp * +bgp_lookup (as_t as, const char *name) +{ + struct bgp *bgp; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS (bm->bgp, node, nnode, bgp)) + if (bgp->as == as + && ((bgp->name == NULL && name == NULL) + || (bgp->name && name && strcmp (bgp->name, name) == 0))) + return bgp; + return NULL; +} + +/* Lookup BGP structure by view name. */ +struct bgp * +bgp_lookup_by_name (const char *name) +{ + struct bgp *bgp; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS (bm->bgp, node, nnode, bgp)) + if ((bgp->name == NULL && name == NULL) + || (bgp->name && name && strcmp (bgp->name, name) == 0)) + return bgp; + return NULL; +} + +/* Called from VTY commands. */ +int +bgp_get (struct bgp **bgp_val, as_t *as, const char *name) +{ + struct bgp *bgp; + + /* Multiple instance check. */ + if (bgp_option_check (BGP_OPT_MULTIPLE_INSTANCE)) + { + if (name) + bgp = bgp_lookup_by_name (name); + else + bgp = bgp_get_default (); + + /* Already exists. */ + if (bgp) + { + if (bgp->as != *as) + { + *as = bgp->as; + return BGP_ERR_INSTANCE_MISMATCH; + } + *bgp_val = bgp; + return 0; + } + } + else + { + /* BGP instance name can not be specified for single instance. */ + if (name) + return BGP_ERR_MULTIPLE_INSTANCE_NOT_SET; + + /* Get default BGP structure if exists. */ + bgp = bgp_get_default (); + + if (bgp) + { + if (bgp->as != *as) + { + *as = bgp->as; + return BGP_ERR_AS_MISMATCH; + } + *bgp_val = bgp; + return 0; + } + } + + bgp = bgp_create (as, name); + bgp_router_id_set(bgp, &router_id_zebra); + *bgp_val = bgp; + + /* Create BGP server socket, if first instance. */ + 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; + } + + listnode_add (bm->bgp, bgp); + + return 0; +} + +/* Delete BGP instance. */ +int +bgp_delete (struct bgp *bgp) +{ + struct peer *peer; + struct peer_group *group; + 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); + + /* Unset redistribution. */ + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (i = 0; i < ZEBRA_ROUTE_MAX; i++) + if (i != ZEBRA_ROUTE_BGP) + bgp_redistribute_unset (bgp, afi, i); + + for (ALL_LIST_ELEMENTS (bgp->peer, node, next, peer)) + { + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + { + /* Send notify to remote peer. */ + bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN); + } + + peer_delete (peer); + } + + for (ALL_LIST_ELEMENTS (bgp->group, node, next, 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); + + if (bgp->peer_self) { + 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. + */ + listnode_delete (bm->bgp, bgp); + if (list_isempty(bm->bgp)) + bgp_close (); + + bgp_unlock(bgp); /* initial reference */ + + return 0; +} + +static void bgp_free (struct bgp *); + +void +bgp_lock (struct bgp *bgp) +{ + ++bgp->lock; +} + +void +bgp_unlock(struct bgp *bgp) +{ + assert(bgp->lock > 0); + if (--bgp->lock == 0) + bgp_free (bgp); +} + +static void +bgp_free (struct bgp *bgp) +{ + afi_t afi; + safi_t safi; + + list_delete (bgp->group); + list_delete (bgp->peer); + list_delete (bgp->rsclient); + + if (bgp->name) + free (bgp->name); + + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + { + if (bgp->route[afi][safi]) + bgp_table_finish (&bgp->route[afi][safi]); + if (bgp->aggregate[afi][safi]) + bgp_table_finish (&bgp->aggregate[afi][safi]) ; + if (bgp->rib[afi][safi]) + bgp_table_finish (&bgp->rib[afi][safi]); + } + XFREE (MTYPE_BGP, bgp); +} + +struct peer * +peer_lookup (struct bgp *bgp, union sockunion *su) +{ + struct peer *peer; + struct listnode *node, *nnode; + + if (bgp != NULL) + { + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + if (sockunion_same (&peer->su, su) + && ! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) + return peer; + } + else if (bm->bgp != NULL) + { + struct listnode *bgpnode, *nbgpnode; + + for (ALL_LIST_ELEMENTS (bm->bgp, bgpnode, nbgpnode, bgp)) + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + if (sockunion_same (&peer->su, su) + && ! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) + return peer; + } + return NULL; +} + +struct peer * +peer_lookup_with_open (union sockunion *su, as_t remote_as, + struct in_addr *remote_id, int *as) +{ + struct peer *peer; + struct listnode *node; + struct listnode *bgpnode; + struct bgp *bgp; + + if (! bm->bgp) + return NULL; + + for (ALL_LIST_ELEMENTS_RO (bm->bgp, bgpnode, bgp)) + { + for (ALL_LIST_ELEMENTS_RO (bgp->peer, node, peer)) + { + if (sockunion_same (&peer->su, su) + && ! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) + { + if (peer->as == remote_as + && peer->remote_id.s_addr == remote_id->s_addr) + return peer; + if (peer->as == remote_as) + *as = 1; + } + } + + for (ALL_LIST_ELEMENTS_RO (bgp->peer, node, peer)) + { + if (sockunion_same (&peer->su, su) + && ! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) + { + if (peer->as == remote_as + && peer->remote_id.s_addr == 0) + return peer; + if (peer->as == remote_as) + *as = 1; + } + } + } + return NULL; +} + +/* If peer is configured at least one address family return 1. */ +int +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_MPLS_VPN] + || peer->afc[AFI_IP6][SAFI_ENCAP]) + return 1; + return 0; +} + +/* If peer is negotiated at least one address family return 1. */ +int +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_MPLS_VPN] + || peer->afc_nego[AFI_IP6][SAFI_ENCAP]) + return 1; + return 0; +} + +/* peer_flag_change_type. */ +enum peer_change_type +{ + peer_change_none, + peer_change_reset, + peer_change_reset_in, + peer_change_reset_out, +}; + +static void +peer_change_action (struct peer *peer, afi_t afi, safi_t safi, + enum peer_change_type type) +{ + 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); + else if (type == peer_change_reset_in) + { + if (CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_OLD_RCV) + || CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_NEW_RCV)) + bgp_route_refresh_send (peer, afi, safi, 0, 0, 0); + else + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + else if (type == peer_change_reset_out) + bgp_announce_route (peer, afi, safi); +} + +struct peer_flag_action +{ + /* Peer's flag. */ + u_int32_t flag; + + /* This flag can be set for peer-group member. */ + u_char not_for_member; + + /* Action when the flag is changed. */ + enum peer_change_type type; + + /* Peer down cause */ + u_char peer_down; +}; + +static const struct peer_flag_action peer_flag_action_list[] = + { + { PEER_FLAG_PASSIVE, 0, peer_change_reset }, + { PEER_FLAG_SHUTDOWN, 0, peer_change_reset }, + { PEER_FLAG_DONT_CAPABILITY, 0, peer_change_none }, + { PEER_FLAG_OVERRIDE_CAPABILITY, 0, peer_change_none }, + { PEER_FLAG_STRICT_CAP_MATCH, 0, peer_change_none }, + { PEER_FLAG_DYNAMIC_CAPABILITY, 0, peer_change_reset }, + { PEER_FLAG_DISABLE_CONNECTED_CHECK, 0, peer_change_reset }, + { 0, 0, 0 } + }; + +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 }, + { PEER_FLAG_AS_PATH_UNCHANGED, 1, peer_change_reset_out }, + { PEER_FLAG_NEXTHOP_UNCHANGED, 1, peer_change_reset_out }, + { PEER_FLAG_MED_UNCHANGED, 1, peer_change_reset_out }, + { PEER_FLAG_REMOVE_PRIVATE_AS, 1, peer_change_reset_out }, + { PEER_FLAG_ALLOWAS_IN, 0, peer_change_reset_in }, + { 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 } + }; + +/* Proper action set. */ +static int +peer_flag_action_set (const struct peer_flag_action *action_list, int size, + struct peer_flag_action *action, u_int32_t flag) +{ + int i; + int found = 0; + int reset_in = 0; + int reset_out = 0; + const struct peer_flag_action *match = NULL; + + /* Check peer's frag action. */ + for (i = 0; i < size; i++) + { + match = &action_list[i]; + + if (match->flag == 0) + break; + + if (match->flag & flag) + { + found = 1; + + if (match->type == peer_change_reset_in) + reset_in = 1; + if (match->type == peer_change_reset_out) + reset_out = 1; + if (match->type == peer_change_reset) + { + reset_in = 1; + reset_out = 1; + } + if (match->not_for_member) + action->not_for_member = 1; + } + } + + /* Set peer clear type. */ + if (reset_in && reset_out) + action->type = peer_change_reset; + else if (reset_in) + action->type = peer_change_reset_in; + else if (reset_out) + action->type = peer_change_reset_out; + else + action->type = peer_change_none; + + return found; +} + +static void +peer_flag_modify_action (struct peer *peer, u_int32_t flag) +{ + if (flag == PEER_FLAG_SHUTDOWN) + { + if (CHECK_FLAG (peer->flags, flag)) + { + if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT)) + peer_nsf_stop (peer); + + UNSET_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW); + if (peer->t_pmax_restart) + { + BGP_TIMER_OFF (peer->t_pmax_restart); + if (BGP_DEBUG (events, EVENTS)) + zlog_debug ("%s Maximum-prefix restart timer canceled", + peer->host); + } + + if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT)) + peer_nsf_stop (peer); + + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN); + else + BGP_EVENT_ADD (peer, BGP_Stop); + } + else + { + peer->v_start = BGP_INIT_START_TIMER; + BGP_EVENT_ADD (peer, BGP_Stop); + } + } + else if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + { + if (flag == PEER_FLAG_DYNAMIC_CAPABILITY) + peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE; + else if (flag == PEER_FLAG_PASSIVE) + peer->last_reset = PEER_DOWN_PASSIVE_CHANGE; + else if (flag == PEER_FLAG_DISABLE_CONNECTED_CHECK) + peer->last_reset = PEER_DOWN_MULTIHOP_CHANGE; + + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + else + BGP_EVENT_ADD (peer, BGP_Stop); +} + +/* Change specified peer flag. */ +static int +peer_flag_modify (struct peer *peer, u_int32_t flag, int set) +{ + int found; + int size; + struct peer_group *group; + struct listnode *node, *nnode; + struct peer_flag_action action; + + memset (&action, 0, sizeof (struct peer_flag_action)); + size = sizeof peer_flag_action_list / sizeof (struct peer_flag_action); + + found = peer_flag_action_set (peer_flag_action_list, size, &action, flag); + + /* No flag action is found. */ + if (! found) + return BGP_ERR_INVALID_FLAG; + + /* Not for peer-group member. */ + if (action.not_for_member && peer_group_active (peer)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + /* When unset the peer-group member's flag we have to check + peer-group configuration. */ + if (! set && peer_group_active (peer)) + if (CHECK_FLAG (peer->group->conf->flags, flag)) + { + if (flag == PEER_FLAG_SHUTDOWN) + return BGP_ERR_PEER_GROUP_SHUTDOWN; + else + return BGP_ERR_PEER_GROUP_HAS_THE_FLAG; + } + + /* Flag conflict check. */ + if (set + && CHECK_FLAG (peer->flags | flag, PEER_FLAG_STRICT_CAP_MATCH) + && CHECK_FLAG (peer->flags | flag, PEER_FLAG_OVERRIDE_CAPABILITY)) + return BGP_ERR_PEER_FLAG_CONFLICT; + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + if (set && CHECK_FLAG (peer->flags, flag) == flag) + return 0; + if (! set && ! CHECK_FLAG (peer->flags, flag)) + return 0; + } + + if (set) + SET_FLAG (peer->flags, flag); + else + UNSET_FLAG (peer->flags, flag); + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + if (action.type == peer_change_reset) + peer_flag_modify_action (peer, flag); + + return 0; + } + + /* peer-group member updates. */ + group = peer->group; + + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + if (set && CHECK_FLAG (peer->flags, flag) == flag) + continue; + + if (! set && ! CHECK_FLAG (peer->flags, flag)) + continue; + + if (set) + SET_FLAG (peer->flags, flag); + else + UNSET_FLAG (peer->flags, flag); + + if (action.type == peer_change_reset) + peer_flag_modify_action (peer, flag); + } + return 0; +} + +int +peer_flag_set (struct peer *peer, u_int32_t flag) +{ + return peer_flag_modify (peer, flag, 1); +} + +int +peer_flag_unset (struct peer *peer, u_int32_t flag) +{ + return peer_flag_modify (peer, flag, 0); +} + +static int +peer_is_group_member (struct peer *peer, afi_t afi, safi_t safi) +{ + if (peer->af_group[afi][safi]) + return 1; + return 0; +} + +static int +peer_af_flag_modify (struct peer *peer, afi_t afi, safi_t safi, u_int32_t flag, + int set) +{ + int found; + int size; + struct listnode *node, *nnode; + struct peer_group *group; + struct peer_flag_action action; + + memset (&action, 0, sizeof (struct peer_flag_action)); + size = sizeof peer_af_flag_action_list / sizeof (struct peer_flag_action); + + found = peer_flag_action_set (peer_af_flag_action_list, size, &action, flag); + + /* No flag action is found. */ + if (! found) + return BGP_ERR_INVALID_FLAG; + + /* Adress family must be activated. */ + if (! peer->afc[afi][safi]) + return BGP_ERR_PEER_INACTIVE; + + /* Not for peer-group member. */ + if (action.not_for_member && peer_is_group_member (peer, afi, safi)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + /* Spcecial check for reflector client. */ + if (flag & PEER_FLAG_REFLECTOR_CLIENT + && peer_sort (peer) != BGP_PEER_IBGP) + return BGP_ERR_NOT_INTERNAL_PEER; + + /* Spcecial check for remove-private-AS. */ + if (flag & PEER_FLAG_REMOVE_PRIVATE_AS + && peer_sort (peer) == BGP_PEER_IBGP) + return BGP_ERR_REMOVE_PRIVATE_AS; + + /* When unset the peer-group member's flag we have to check + peer-group configuration. */ + if (! set && peer->af_group[afi][safi]) + if (CHECK_FLAG (peer->group->conf->af_flags[afi][safi], flag)) + return BGP_ERR_PEER_GROUP_HAS_THE_FLAG; + + /* When current flag configuration is same as requested one. */ + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + if (set && CHECK_FLAG (peer->af_flags[afi][safi], flag) == flag) + return 0; + if (! set && ! CHECK_FLAG (peer->af_flags[afi][safi], flag)) + return 0; + } + + if (set) + SET_FLAG (peer->af_flags[afi][safi], flag); + else + UNSET_FLAG (peer->af_flags[afi][safi], flag); + + /* Execute action when peer is established. */ + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP) + && peer->status == Established) + { + if (! set && flag == PEER_FLAG_SOFT_RECONFIG) + bgp_clear_adj_in (peer, afi, safi); + else + { + if (flag == PEER_FLAG_REFLECTOR_CLIENT) + peer->last_reset = PEER_DOWN_RR_CLIENT_CHANGE; + else if (flag == PEER_FLAG_RSERVER_CLIENT) + peer->last_reset = PEER_DOWN_RS_CLIENT_CHANGE; + else if (flag == PEER_FLAG_ORF_PREFIX_SM) + peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE; + else if (flag == PEER_FLAG_ORF_PREFIX_RM) + peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE; + + peer_change_action (peer, afi, safi, action.type); + } + + } + + /* Peer group member updates. */ + if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + group = peer->group; + + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + if (! peer->af_group[afi][safi]) + continue; + + if (set && CHECK_FLAG (peer->af_flags[afi][safi], flag) == flag) + continue; + + if (! set && ! CHECK_FLAG (peer->af_flags[afi][safi], flag)) + continue; + + if (set) + SET_FLAG (peer->af_flags[afi][safi], flag); + else + UNSET_FLAG (peer->af_flags[afi][safi], flag); + + if (peer->status == Established) + { + if (! set && flag == PEER_FLAG_SOFT_RECONFIG) + bgp_clear_adj_in (peer, afi, safi); + else + { + if (flag == PEER_FLAG_REFLECTOR_CLIENT) + peer->last_reset = PEER_DOWN_RR_CLIENT_CHANGE; + else if (flag == PEER_FLAG_RSERVER_CLIENT) + peer->last_reset = PEER_DOWN_RS_CLIENT_CHANGE; + else if (flag == PEER_FLAG_ORF_PREFIX_SM) + peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE; + else if (flag == PEER_FLAG_ORF_PREFIX_RM) + peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE; + + peer_change_action (peer, afi, safi, action.type); + } + } + } + } + return 0; +} + +int +peer_af_flag_set (struct peer *peer, afi_t afi, safi_t safi, u_int32_t flag) +{ + return peer_af_flag_modify (peer, afi, safi, flag, 1); +} + +int +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) +{ + struct peer_group *group; + struct listnode *node, *nnode; + struct peer *peer1; + + if (peer->sort == BGP_PEER_IBGP) + return BGP_ERR_NO_IBGP_WITH_TTLHACK; + + if (peer->gtsm_hops != 0) + return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK; + + if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer1)) + { + if (peer1->gtsm_hops != 0) + return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK; + } + } + + peer->ttl = ttl; + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + bgp_set_socket_ttl (peer, peer->fd); + } + else + { + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + peer->ttl = ttl; + bgp_set_socket_ttl (peer, peer->fd); + } + } + + return 0; +} + +/* Neighbor description. */ +int +peer_description_set (struct peer *peer, const char *desc) +{ + if (peer->desc) + XFREE (MTYPE_PEER_DESC, peer->desc); + + peer->desc = XSTRDUP (MTYPE_PEER_DESC, desc); + + return 0; +} + +int +peer_description_unset (struct peer *peer) +{ + if (peer->desc) + XFREE (MTYPE_PEER_DESC, peer->desc); + + peer->desc = NULL; + + return 0; +} + +/* Neighbor update-source. */ +int +peer_update_source_if_set (struct peer *peer, const char *ifname) +{ + struct peer_group *group; + struct listnode *node, *nnode; + + if (peer->update_if) + { + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP) + && strcmp (peer->update_if, ifname) == 0) + return 0; + + XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if); + peer->update_if = NULL; + } + + if (peer->update_source) + { + sockunion_free (peer->update_source); + peer->update_source = NULL; + } + + peer->update_if = XSTRDUP (MTYPE_PEER_UPDATE_SOURCE, ifname); + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + { + peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE; + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + else + BGP_EVENT_ADD (peer, BGP_Stop); + return 0; + } + + /* peer-group member updates. */ + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + if (peer->update_if) + { + if (strcmp (peer->update_if, ifname) == 0) + continue; + + XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if); + peer->update_if = NULL; + } + + if (peer->update_source) + { + sockunion_free (peer->update_source); + peer->update_source = NULL; + } + + peer->update_if = XSTRDUP (MTYPE_PEER_UPDATE_SOURCE, ifname); + + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + { + peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE; + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + else + BGP_EVENT_ADD (peer, BGP_Stop); + } + return 0; +} + +int +peer_update_source_addr_set (struct peer *peer, const union sockunion *su) +{ + struct peer_group *group; + struct listnode *node, *nnode; + + if (peer->update_source) + { + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP) + && sockunion_cmp (peer->update_source, su) == 0) + return 0; + sockunion_free (peer->update_source); + peer->update_source = NULL; + } + + if (peer->update_if) + { + XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if); + peer->update_if = NULL; + } + + peer->update_source = sockunion_dup (su); + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + { + peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE; + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + else + BGP_EVENT_ADD (peer, BGP_Stop); + return 0; + } + + /* peer-group member updates. */ + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + if (peer->update_source) + { + if (sockunion_cmp (peer->update_source, su) == 0) + continue; + sockunion_free (peer->update_source); + peer->update_source = NULL; + } + + if (peer->update_if) + { + XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if); + peer->update_if = NULL; + } + + peer->update_source = sockunion_dup (su); + + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + { + peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE; + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + else + BGP_EVENT_ADD (peer, BGP_Stop); + } + return 0; +} + +int +peer_update_source_unset (struct peer *peer) +{ + union sockunion *su; + struct peer_group *group; + struct listnode *node, *nnode; + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP) + && ! peer->update_source + && ! peer->update_if) + return 0; + + if (peer->update_source) + { + sockunion_free (peer->update_source); + peer->update_source = NULL; + } + if (peer->update_if) + { + XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if); + peer->update_if = NULL; + } + + if (peer_group_active (peer)) + { + group = peer->group; + + if (group->conf->update_source) + { + su = sockunion_dup (group->conf->update_source); + peer->update_source = su; + } + else if (group->conf->update_if) + peer->update_if = + XSTRDUP (MTYPE_PEER_UPDATE_SOURCE, group->conf->update_if); + } + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + { + peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE; + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + else + BGP_EVENT_ADD (peer, BGP_Stop); + return 0; + } + + /* peer-group member updates. */ + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + if (! peer->update_source && ! peer->update_if) + continue; + + if (peer->update_source) + { + sockunion_free (peer->update_source); + peer->update_source = NULL; + } + + if (peer->update_if) + { + XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if); + peer->update_if = NULL; + } + + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + { + peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE; + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + else + BGP_EVENT_ADD (peer, BGP_Stop); + } + return 0; +} + +int +peer_default_originate_set (struct peer *peer, afi_t afi, safi_t safi, + const char *rmap) +{ + struct peer_group *group; + struct listnode *node, *nnode; + + /* Adress family must be activated. */ + if (! peer->afc[afi][safi]) + return BGP_ERR_PEER_INACTIVE; + + /* Default originate can't be used for peer group memeber. */ + if (peer_is_group_member (peer, afi, safi)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + if (! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE) + || (rmap && ! peer->default_rmap[afi][safi].name) + || (rmap && strcmp (rmap, peer->default_rmap[afi][safi].name) != 0)) + { + SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE); + + if (rmap) + { + if (peer->default_rmap[afi][safi].name) + free (peer->default_rmap[afi][safi].name); + peer->default_rmap[afi][safi].name = strdup (rmap); + peer->default_rmap[afi][safi].map = route_map_lookup_by_name (rmap); + } + } + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + if (peer->status == Established && peer->afc_nego[afi][safi]) + bgp_default_originate (peer, afi, safi, 0); + return 0; + } + + /* peer-group member updates. */ + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE); + + if (rmap) + { + if (peer->default_rmap[afi][safi].name) + free (peer->default_rmap[afi][safi].name); + peer->default_rmap[afi][safi].name = strdup (rmap); + peer->default_rmap[afi][safi].map = route_map_lookup_by_name (rmap); + } + + if (peer->status == Established && peer->afc_nego[afi][safi]) + bgp_default_originate (peer, afi, safi, 0); + } + return 0; +} + +int +peer_default_originate_unset (struct peer *peer, afi_t afi, safi_t safi) +{ + struct peer_group *group; + struct listnode *node, *nnode; + + /* Adress family must be activated. */ + if (! peer->afc[afi][safi]) + return BGP_ERR_PEER_INACTIVE; + + /* Default originate can't be used for peer group memeber. */ + if (peer_is_group_member (peer, afi, safi)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE)) + { + UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE); + + if (peer->default_rmap[afi][safi].name) + free (peer->default_rmap[afi][safi].name); + peer->default_rmap[afi][safi].name = NULL; + peer->default_rmap[afi][safi].map = NULL; + } + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + if (peer->status == Established && peer->afc_nego[afi][safi]) + bgp_default_originate (peer, afi, safi, 1); + return 0; + } + + /* peer-group member updates. */ + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE); + + if (peer->default_rmap[afi][safi].name) + free (peer->default_rmap[afi][safi].name); + peer->default_rmap[afi][safi].name = NULL; + peer->default_rmap[afi][safi].map = NULL; + + if (peer->status == Established && peer->afc_nego[afi][safi]) + bgp_default_originate (peer, afi, safi, 1); + } + return 0; +} + +int +peer_port_set (struct peer *peer, u_int16_t port) +{ + peer->port = port; + return 0; +} + +int +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) +{ + struct peer_group *group; + struct listnode *node, *nnode; + + SET_FLAG (peer->config, PEER_CONFIG_WEIGHT); + peer->weight = weight; + + 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)) + { + peer->weight = group->conf->weight; + } + return 0; +} + +int +peer_weight_unset (struct peer *peer) +{ + struct peer_group *group; + struct listnode *node, *nnode; + + /* Set default weight. */ + if (peer_group_active (peer)) + peer->weight = peer->group->conf->weight; + else + peer->weight = 0; + + UNSET_FLAG (peer->config, PEER_CONFIG_WEIGHT); + + 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)) + { + peer->weight = 0; + } + return 0; +} + +int +peer_timers_set (struct peer *peer, u_int32_t keepalive, u_int32_t holdtime) +{ + struct peer_group *group; + struct listnode *node, *nnode; + + /* Not for peer group memeber. */ + if (peer_group_active (peer)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + /* keepalive value check. */ + if (keepalive > 65535) + return BGP_ERR_INVALID_VALUE; + + /* Holdtime value check. */ + if (holdtime > 65535) + return BGP_ERR_INVALID_VALUE; + + /* Holdtime value must be either 0 or greater than 3. */ + if (holdtime < 3 && holdtime != 0) + return BGP_ERR_INVALID_VALUE; + + /* Set value to the configuration. */ + SET_FLAG (peer->config, PEER_CONFIG_TIMER); + peer->holdtime = holdtime; + peer->keepalive = (keepalive < holdtime / 3 ? keepalive : holdtime / 3); + + 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_TIMER); + peer->holdtime = group->conf->holdtime; + peer->keepalive = group->conf->keepalive; + } + return 0; +} + +int +peer_timers_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; + + /* Clear configuration. */ + UNSET_FLAG (peer->config, PEER_CONFIG_TIMER); + peer->keepalive = 0; + peer->holdtime = 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_TIMER); + peer->holdtime = 0; + peer->keepalive = 0; + } + + 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; + + if (connect > 65535) + return BGP_ERR_INVALID_VALUE; + + /* Set value to the configuration. */ + SET_FLAG (peer->config, PEER_CONFIG_CONNECT); + peer->connect = 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; + + /* Clear configuration. */ + UNSET_FLAG (peer->config, PEER_CONFIG_CONNECT); + peer->connect = 0; + + /* Set timer setting to default value. */ + peer->v_connect = BGP_DEFAULT_CONNECT_RETRY; + + 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; + + if (routeadv > 600) + return BGP_ERR_INVALID_VALUE; + + SET_FLAG (peer->config, PEER_CONFIG_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 == 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) +{ + if (peer->ifname) + free (peer->ifname); + peer->ifname = strdup (str); + + return 0; +} + +int +peer_interface_unset (struct peer *peer) +{ + if (peer->ifname) + free (peer->ifname); + peer->ifname = NULL; + + return 0; +} + +/* Allow-as in. */ +int +peer_allowas_in_set (struct peer *peer, afi_t afi, safi_t safi, int allow_num) +{ + struct peer_group *group; + struct listnode *node, *nnode; + + if (allow_num < 1 || allow_num > 10) + return BGP_ERR_INVALID_VALUE; + + if (peer->allowas_in[afi][safi] != allow_num) + { + peer->allowas_in[afi][safi] = allow_num; + SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN); + peer_change_action (peer, afi, safi, peer_change_reset_in); + } + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + return 0; + + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + if (peer->allowas_in[afi][safi] != allow_num) + { + peer->allowas_in[afi][safi] = allow_num; + SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN); + peer_change_action (peer, afi, safi, peer_change_reset_in); + } + + } + return 0; +} + +int +peer_allowas_in_unset (struct peer *peer, afi_t afi, safi_t safi) +{ + struct peer_group *group; + struct listnode *node, *nnode; + + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN)) + { + peer->allowas_in[afi][safi] = 0; + peer_af_flag_unset (peer, afi, safi, PEER_FLAG_ALLOWAS_IN); + } + + 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->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN)) + { + peer->allowas_in[afi][safi] = 0; + peer_af_flag_unset (peer, afi, safi, PEER_FLAG_ALLOWAS_IN); + } + } + return 0; +} + +int +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; + struct listnode *node, *nnode; + + if (peer_sort (peer) != BGP_PEER_EBGP + && peer_sort (peer) != BGP_PEER_INTERNAL) + return BGP_ERR_LOCAL_AS_ALLOWED_ONLY_FOR_EBGP; + + if (bgp->as == as) + return BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS; + + 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_REPLACE_AS) && replace_as) + || (! CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS) && ! replace_as))) + return 0; + + peer->change_local_as = as; + if (no_prepend) + SET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_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 (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + { + peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE; + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + else + BGP_EVENT_ADD (peer, BGP_Stop); + + return 0; + } + + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + peer->change_local_as = as; + if (no_prepend) + SET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_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 (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + { + peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE; + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + else + BGP_EVENT_ADD (peer, BGP_Stop); + } + + return 0; +} + +int +peer_local_as_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; + + if (! peer->change_local_as) + return 0; + + 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 (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + { + peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE; + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + else + BGP_EVENT_ADD (peer, BGP_Stop); + + return 0; + } + + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, 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 (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + { + peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE; + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + else + BGP_EVENT_ADD (peer, BGP_Stop); + } + return 0; +} + +/* Set password for authenticating with the peer. */ +int +peer_password_set (struct peer *peer, const char *password) +{ + struct listnode *nn, *nnode; + int len = password ? strlen(password) : 0; + int ret = BGP_SUCCESS; + + if ((len < PEER_PASSWORD_MINLEN) || (len > PEER_PASSWORD_MAXLEN)) + return BGP_ERR_INVALID_VALUE; + + if (peer->password && strcmp (peer->password, password) == 0 + && ! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + return 0; + + if (peer->password) + XFREE (MTYPE_PEER_PASSWORD, peer->password); + + peer->password = XSTRDUP (MTYPE_PEER_PASSWORD, password); + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + 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); + + return (bgp_md5_set (peer) >= 0) ? BGP_SUCCESS : BGP_ERR_TCPSIG_FAILED; + } + + for (ALL_LIST_ELEMENTS (peer->group->peer, nn, nnode, peer)) + { + if (peer->password && strcmp (peer->password, password) == 0) + continue; + + if (peer->password) + XFREE (MTYPE_PEER_PASSWORD, peer->password); + + peer->password = XSTRDUP(MTYPE_PEER_PASSWORD, password); + + 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); + + if (bgp_md5_set (peer) < 0) + ret = BGP_ERR_TCPSIG_FAILED; + } + + return ret; +} + +int +peer_password_unset (struct peer *peer) +{ + struct listnode *nn, *nnode; + + if (!peer->password + && !CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + return 0; + + if (!CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + if (peer_group_active (peer) + && peer->group->conf->password + && strcmp (peer->group->conf->password, peer->password) == 0) + return BGP_ERR_PEER_GROUP_HAS_THE_FLAG; + + 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); + + if (peer->password) + XFREE (MTYPE_PEER_PASSWORD, peer->password); + + peer->password = NULL; + + bgp_md5_set (peer); + + return 0; + } + + XFREE (MTYPE_PEER_PASSWORD, peer->password); + peer->password = NULL; + + for (ALL_LIST_ELEMENTS (peer->group->peer, nn, nnode, peer)) + { + if (!peer->password) + continue; + + 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); + + XFREE (MTYPE_PEER_PASSWORD, peer->password); + peer->password = NULL; + + bgp_md5_set (peer); + } + + return 0; +} + +/* Set distribute list to the peer. */ +int +peer_distribute_set (struct peer *peer, afi_t afi, safi_t safi, int direct, + const char *name) +{ + struct bgp_filter *filter; + struct peer_group *group; + struct listnode *node, *nnode; + + if (! peer->afc[afi][safi]) + return BGP_ERR_PEER_INACTIVE; + + if (direct != FILTER_IN && direct != FILTER_OUT) + return BGP_ERR_INVALID_VALUE; + + if (direct == FILTER_OUT && peer_is_group_member (peer, afi, safi)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + filter = &peer->filter[afi][safi]; + + if (filter->plist[direct].name) + return BGP_ERR_PEER_FILTER_CONFLICT; + + if (filter->dlist[direct].name) + free (filter->dlist[direct].name); + filter->dlist[direct].name = strdup (name); + filter->dlist[direct].alist = access_list_lookup (afi, name); + + 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]; + + if (! peer->af_group[afi][safi]) + continue; + + if (filter->dlist[direct].name) + free (filter->dlist[direct].name); + filter->dlist[direct].name = strdup (name); + filter->dlist[direct].alist = access_list_lookup (afi, name); + } + + return 0; +} + +int +peer_distribute_unset (struct peer *peer, afi_t afi, safi_t safi, int direct) +{ + struct bgp_filter *filter; + struct bgp_filter *gfilter; + struct peer_group *group; + struct listnode *node, *nnode; + + if (! peer->afc[afi][safi]) + return BGP_ERR_PEER_INACTIVE; + + if (direct != FILTER_IN && direct != FILTER_OUT) + return BGP_ERR_INVALID_VALUE; + + if (direct == FILTER_OUT && peer_is_group_member (peer, afi, safi)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + filter = &peer->filter[afi][safi]; + + /* apply peer-group filter */ + if (peer->af_group[afi][safi]) + { + gfilter = &peer->group->conf->filter[afi][safi]; + + if (gfilter->dlist[direct].name) + { + if (filter->dlist[direct].name) + free (filter->dlist[direct].name); + filter->dlist[direct].name = strdup (gfilter->dlist[direct].name); + filter->dlist[direct].alist = gfilter->dlist[direct].alist; + return 0; + } + } + + if (filter->dlist[direct].name) + free (filter->dlist[direct].name); + filter->dlist[direct].name = NULL; + filter->dlist[direct].alist = NULL; + + 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]; + + 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; + } + + return 0; +} + +/* Update distribute list. */ +static void +peer_distribute_update (struct access_list *access) +{ + afi_t afi; + safi_t safi; + int direct; + struct listnode *mnode, *mnnode; + struct listnode *node, *nnode; + struct bgp *bgp; + struct peer *peer; + struct peer_group *group; + struct bgp_filter *filter; + + for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp)) + { + 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++) + { + filter = &peer->filter[afi][safi]; + + for (direct = FILTER_IN; direct < FILTER_MAX; direct++) + { + if (filter->dlist[direct].name) + filter->dlist[direct].alist = + access_list_lookup (afi, filter->dlist[direct].name); + else + filter->dlist[direct].alist = NULL; + } + } + } + for (ALL_LIST_ELEMENTS (bgp->group, node, nnode, group)) + { + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + { + filter = &group->conf->filter[afi][safi]; + + for (direct = FILTER_IN; direct < FILTER_MAX; direct++) + { + if (filter->dlist[direct].name) + filter->dlist[direct].alist = + access_list_lookup (afi, filter->dlist[direct].name); + else + filter->dlist[direct].alist = NULL; + } + } + } + } +} + +/* Set prefix list to the peer. */ +int +peer_prefix_list_set (struct peer *peer, afi_t afi, safi_t safi, int direct, + const char *name) +{ + struct bgp_filter *filter; + struct peer_group *group; + struct listnode *node, *nnode; + + if (! peer->afc[afi][safi]) + return BGP_ERR_PEER_INACTIVE; + + if (direct != FILTER_IN && direct != FILTER_OUT) + return BGP_ERR_INVALID_VALUE; + + if (direct == FILTER_OUT && peer_is_group_member (peer, afi, safi)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + filter = &peer->filter[afi][safi]; + + if (filter->dlist[direct].name) + return BGP_ERR_PEER_FILTER_CONFLICT; + + if (filter->plist[direct].name) + free (filter->plist[direct].name); + filter->plist[direct].name = strdup (name); + filter->plist[direct].plist = prefix_list_lookup (afi, name); + + 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]; + + if (! peer->af_group[afi][safi]) + continue; + + if (filter->plist[direct].name) + free (filter->plist[direct].name); + filter->plist[direct].name = strdup (name); + filter->plist[direct].plist = prefix_list_lookup (afi, name); + } + return 0; +} + +int +peer_prefix_list_unset (struct peer *peer, afi_t afi, safi_t safi, int direct) +{ + struct bgp_filter *filter; + struct bgp_filter *gfilter; + struct peer_group *group; + struct listnode *node, *nnode; + + if (! peer->afc[afi][safi]) + return BGP_ERR_PEER_INACTIVE; + + if (direct != FILTER_IN && direct != FILTER_OUT) + return BGP_ERR_INVALID_VALUE; + + if (direct == FILTER_OUT && peer_is_group_member (peer, afi, safi)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + filter = &peer->filter[afi][safi]; + + /* apply peer-group filter */ + if (peer->af_group[afi][safi]) + { + gfilter = &peer->group->conf->filter[afi][safi]; + + if (gfilter->plist[direct].name) + { + if (filter->plist[direct].name) + free (filter->plist[direct].name); + filter->plist[direct].name = strdup (gfilter->plist[direct].name); + filter->plist[direct].plist = gfilter->plist[direct].plist; + return 0; + } + } + + if (filter->plist[direct].name) + free (filter->plist[direct].name); + filter->plist[direct].name = NULL; + filter->plist[direct].plist = NULL; + + 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]; + + if (! peer->af_group[afi][safi]) + continue; + + if (filter->plist[direct].name) + free (filter->plist[direct].name); + filter->plist[direct].name = NULL; + filter->plist[direct].plist = NULL; + } + + return 0; +} + +/* Update prefix-list list. */ +static void +peer_prefix_list_update (struct prefix_list *plist) +{ + struct listnode *mnode, *mnnode; + struct listnode *node, *nnode; + struct bgp *bgp; + struct peer *peer; + struct peer_group *group; + struct bgp_filter *filter; + afi_t afi; + safi_t safi; + int direct; + + for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp)) + { + 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++) + { + filter = &peer->filter[afi][safi]; + + for (direct = FILTER_IN; direct < FILTER_MAX; direct++) + { + if (filter->plist[direct].name) + filter->plist[direct].plist = + prefix_list_lookup (afi, filter->plist[direct].name); + else + filter->plist[direct].plist = NULL; + } + } + } + for (ALL_LIST_ELEMENTS (bgp->group, node, nnode, group)) + { + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + { + filter = &group->conf->filter[afi][safi]; + + for (direct = FILTER_IN; direct < FILTER_MAX; direct++) + { + if (filter->plist[direct].name) + filter->plist[direct].plist = + prefix_list_lookup (afi, filter->plist[direct].name); + else + filter->plist[direct].plist = NULL; + } + } + } + } +} + +int +peer_aslist_set (struct peer *peer, afi_t afi, safi_t safi, int direct, + const char *name) +{ + struct bgp_filter *filter; + struct peer_group *group; + struct listnode *node, *nnode; + + if (! peer->afc[afi][safi]) + return BGP_ERR_PEER_INACTIVE; + + if (direct != FILTER_IN && direct != FILTER_OUT) + return BGP_ERR_INVALID_VALUE; + + if (direct == FILTER_OUT && peer_is_group_member (peer, afi, safi)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + filter = &peer->filter[afi][safi]; + + if (filter->aslist[direct].name) + free (filter->aslist[direct].name); + filter->aslist[direct].name = strdup (name); + filter->aslist[direct].aslist = as_list_lookup (name); + + 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]; + + if (! peer->af_group[afi][safi]) + continue; + + if (filter->aslist[direct].name) + free (filter->aslist[direct].name); + filter->aslist[direct].name = strdup (name); + filter->aslist[direct].aslist = as_list_lookup (name); + } + return 0; +} + +int +peer_aslist_unset (struct peer *peer,afi_t afi, safi_t safi, int direct) +{ + struct bgp_filter *filter; + struct bgp_filter *gfilter; + struct peer_group *group; + struct listnode *node, *nnode; + + if (! peer->afc[afi][safi]) + return BGP_ERR_PEER_INACTIVE; + + if (direct != FILTER_IN && direct != FILTER_OUT) + return BGP_ERR_INVALID_VALUE; + + if (direct == FILTER_OUT && peer_is_group_member (peer, afi, safi)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + filter = &peer->filter[afi][safi]; + + /* apply peer-group filter */ + if (peer->af_group[afi][safi]) + { + gfilter = &peer->group->conf->filter[afi][safi]; + + if (gfilter->aslist[direct].name) + { + if (filter->aslist[direct].name) + free (filter->aslist[direct].name); + filter->aslist[direct].name = strdup (gfilter->aslist[direct].name); + filter->aslist[direct].aslist = gfilter->aslist[direct].aslist; + return 0; + } + } + + if (filter->aslist[direct].name) + free (filter->aslist[direct].name); + filter->aslist[direct].name = NULL; + filter->aslist[direct].aslist = NULL; + + 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]; + + if (! peer->af_group[afi][safi]) + continue; + + if (filter->aslist[direct].name) + free (filter->aslist[direct].name); + filter->aslist[direct].name = NULL; + filter->aslist[direct].aslist = NULL; + } + + return 0; +} + +static void +peer_aslist_update (void) +{ + afi_t afi; + safi_t safi; + int direct; + struct listnode *mnode, *mnnode; + struct listnode *node, *nnode; + struct bgp *bgp; + struct peer *peer; + struct peer_group *group; + struct bgp_filter *filter; + + for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp)) + { + 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++) + { + filter = &peer->filter[afi][safi]; + + for (direct = FILTER_IN; direct < FILTER_MAX; direct++) + { + if (filter->aslist[direct].name) + filter->aslist[direct].aslist = + as_list_lookup (filter->aslist[direct].name); + else + filter->aslist[direct].aslist = NULL; + } + } + } + for (ALL_LIST_ELEMENTS (bgp->group, node, nnode, group)) + { + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + { + filter = &group->conf->filter[afi][safi]; + + for (direct = FILTER_IN; direct < FILTER_MAX; direct++) + { + if (filter->aslist[direct].name) + filter->aslist[direct].aslist = + as_list_lookup (filter->aslist[direct].name); + else + filter->aslist[direct].aslist = NULL; + } + } + } + } +} + +/* Set route-map to the peer. */ +int +peer_route_map_set (struct peer *peer, afi_t afi, safi_t safi, int direct, + const char *name) +{ + struct bgp_filter *filter; + struct peer_group *group; + struct listnode *node, *nnode; + + if (! peer->afc[afi][safi]) + return BGP_ERR_PEER_INACTIVE; + + if (direct != RMAP_IN && direct != RMAP_OUT && + direct != RMAP_IMPORT && direct != RMAP_EXPORT) + return BGP_ERR_INVALID_VALUE; + + if ( (direct == RMAP_OUT || direct == RMAP_IMPORT) + && peer_is_group_member (peer, afi, safi)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + filter = &peer->filter[afi][safi]; + + if (filter->map[direct].name) + free (filter->map[direct].name); + + filter->map[direct].name = strdup (name); + filter->map[direct].map = route_map_lookup_by_name (name); + + 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]; + + if (! peer->af_group[afi][safi]) + continue; + + if (filter->map[direct].name) + free (filter->map[direct].name); + filter->map[direct].name = strdup (name); + filter->map[direct].map = route_map_lookup_by_name (name); + } + return 0; +} + +/* Unset route-map from the peer. */ +int +peer_route_map_unset (struct peer *peer, afi_t afi, safi_t safi, int direct) +{ + struct bgp_filter *filter; + struct bgp_filter *gfilter; + struct peer_group *group; + struct listnode *node, *nnode; + + if (! peer->afc[afi][safi]) + return BGP_ERR_PEER_INACTIVE; + + if (direct != RMAP_IN && direct != RMAP_OUT && + direct != RMAP_IMPORT && direct != RMAP_EXPORT) + return BGP_ERR_INVALID_VALUE; + + if ( (direct == RMAP_OUT || direct == RMAP_IMPORT) + && peer_is_group_member (peer, afi, safi)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + filter = &peer->filter[afi][safi]; + + /* apply peer-group filter */ + if (peer->af_group[afi][safi]) + { + gfilter = &peer->group->conf->filter[afi][safi]; + + if (gfilter->map[direct].name) + { + if (filter->map[direct].name) + free (filter->map[direct].name); + filter->map[direct].name = strdup (gfilter->map[direct].name); + filter->map[direct].map = gfilter->map[direct].map; + return 0; + } + } + + if (filter->map[direct].name) + free (filter->map[direct].name); + filter->map[direct].name = NULL; + filter->map[direct].map = NULL; + + 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]; + + if (! peer->af_group[afi][safi]) + continue; + + if (filter->map[direct].name) + free (filter->map[direct].name); + filter->map[direct].name = NULL; + filter->map[direct].map = NULL; + } + return 0; +} + +/* Set unsuppress-map to the peer. */ +int +peer_unsuppress_map_set (struct peer *peer, afi_t afi, safi_t safi, + const char *name) +{ + struct bgp_filter *filter; + struct peer_group *group; + struct listnode *node, *nnode; + + if (! peer->afc[afi][safi]) + return BGP_ERR_PEER_INACTIVE; + + if (peer_is_group_member (peer, afi, safi)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + filter = &peer->filter[afi][safi]; + + if (filter->usmap.name) + free (filter->usmap.name); + + filter->usmap.name = strdup (name); + filter->usmap.map = route_map_lookup_by_name (name); + + 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]; + + if (! peer->af_group[afi][safi]) + continue; + + if (filter->usmap.name) + free (filter->usmap.name); + filter->usmap.name = strdup (name); + filter->usmap.map = route_map_lookup_by_name (name); + } + return 0; +} + +/* Unset route-map from the peer. */ +int +peer_unsuppress_map_unset (struct peer *peer, afi_t afi, safi_t safi) +{ + struct bgp_filter *filter; + struct peer_group *group; + struct listnode *node, *nnode; + + if (! peer->afc[afi][safi]) + return BGP_ERR_PEER_INACTIVE; + + if (peer_is_group_member (peer, afi, safi)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + filter = &peer->filter[afi][safi]; + + if (filter->usmap.name) + free (filter->usmap.name); + filter->usmap.name = NULL; + filter->usmap.map = NULL; + + 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]; + + if (! peer->af_group[afi][safi]) + continue; + + if (filter->usmap.name) + free (filter->usmap.name); + filter->usmap.name = NULL; + filter->usmap.map = NULL; + } + return 0; +} + +int +peer_maximum_prefix_set (struct peer *peer, afi_t afi, safi_t safi, + u_int32_t max, u_char threshold, + int warning, u_int16_t restart) +{ + struct peer_group *group; + struct listnode *node, *nnode; + + if (! peer->afc[afi][safi]) + return BGP_ERR_PEER_INACTIVE; + + 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 (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + 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); + + 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; +} + +int +peer_maximum_prefix_unset (struct peer *peer, afi_t afi, safi_t safi) +{ + struct peer_group *group; + struct listnode *node, *nnode; + + if (! peer->afc[afi][safi]) + return BGP_ERR_PEER_INACTIVE; + + /* apply peer-group config */ + if (peer->af_group[afi][safi]) + { + if (CHECK_FLAG (peer->group->conf->af_flags[afi][safi], + PEER_FLAG_MAX_PREFIX)) + SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX); + else + UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX); + + if (CHECK_FLAG (peer->group->conf->af_flags[afi][safi], + PEER_FLAG_MAX_PREFIX_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); + + peer->pmax[afi][safi] = peer->group->conf->pmax[afi][safi]; + peer->pmax_threshold[afi][safi] = peer->group->conf->pmax_threshold[afi][safi]; + peer->pmax_restart[afi][safi] = peer->group->conf->pmax_restart[afi][safi]; + return 0; + } + + UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX); + UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING); + peer->pmax[afi][safi] = 0; + peer->pmax_threshold[afi][safi] = 0; + peer->pmax_restart[afi][safi] = 0; + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + return 0; + + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + if (! peer->af_group[afi][safi]) + continue; + + UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX); + UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING); + peer->pmax[afi][safi] = 0; + peer->pmax_threshold[afi][safi] = 0; + peer->pmax_restart[afi][safi] = 0; + } + return 0; +} + +/* Set # of hops between us and BGP peer. */ +int +peer_ttl_security_hops_set (struct peer *peer, int gtsm_hops) +{ + struct peer_group *group; + struct listnode *node, *nnode; + struct peer *peer1; + + zlog_debug ("peer_ttl_security_hops_set: set gtsm_hops to %d for %s", gtsm_hops, peer->host); + + if (peer->ttl != 0) + return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK; + + 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; + } + } + + peer->gtsm_hops = gtsm_hops; + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + bgp_set_socket_ttl (peer, peer->fd); + } + else + { + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + peer->gtsm_hops = gtsm_hops; + + /* Change setting of existing peer + * established then change value (may break connectivity) + * not established yet (teardown session and restart) + * no session then do nothing (will get handled by next connection) + */ + if (peer->status == Established) + { + bgp_set_socket_ttl (peer, peer->fd); + } + else if (peer->status < Established) + { + if (BGP_DEBUG (events, EVENTS)) + zlog_debug ("%s Min-ttl changed", peer->host); + BGP_EVENT_ADD (peer, BGP_Stop); + } + } + } + + return 0; +} + +int +peer_clear (struct peer *peer) +{ + if (! CHECK_FLAG (peer->flags, PEER_FLAG_SHUTDOWN)) + { + if (CHECK_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW)) + { + UNSET_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW); + if (peer->t_pmax_restart) + { + BGP_TIMER_OFF (peer->t_pmax_restart); + if (BGP_DEBUG (events, EVENTS)) + zlog_debug ("%s Maximum-prefix restart timer canceled", + peer->host); + } + BGP_EVENT_ADD (peer, BGP_Start); + return 0; + } + + peer->v_start = BGP_INIT_START_TIMER; + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_ADMIN_RESET); + else + BGP_EVENT_ADD (peer, BGP_Stop); + } + return 0; +} + +int +peer_clear_soft (struct peer *peer, afi_t afi, safi_t safi, + enum bgp_clear_type stype) +{ + if (peer->status != Established) + return 0; + + 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)) + return 0; + bgp_check_local_routes_rsclient (peer, afi, safi); + bgp_soft_reconfig_rsclient (peer, afi, safi); + } + + if (stype == BGP_CLEAR_SOFT_OUT || stype == BGP_CLEAR_SOFT_BOTH) + bgp_announce_route (peer, afi, safi); + + if (stype == BGP_CLEAR_SOFT_IN_ORF_PREFIX) + { + if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV) + && (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_RCV) + || CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_OLD_RCV))) + { + struct bgp_filter *filter = &peer->filter[afi][safi]; + u_char prefix_type; + + if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_RCV)) + prefix_type = ORF_TYPE_PREFIX; + else + prefix_type = ORF_TYPE_PREFIX_OLD; + + if (filter->plist[FILTER_IN].plist) + { + if (CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_PREFIX_SEND)) + bgp_route_refresh_send (peer, afi, safi, + prefix_type, REFRESH_DEFER, 1); + bgp_route_refresh_send (peer, afi, safi, prefix_type, + REFRESH_IMMEDIATE, 0); + } + else + { + if (CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_PREFIX_SEND)) + bgp_route_refresh_send (peer, afi, safi, + prefix_type, REFRESH_IMMEDIATE, 1); + else + bgp_route_refresh_send (peer, afi, safi, 0, 0, 0); + } + return 0; + } + } + + if (stype == BGP_CLEAR_SOFT_IN || stype == BGP_CLEAR_SOFT_BOTH + || stype == BGP_CLEAR_SOFT_IN_ORF_PREFIX) + { + /* If neighbor has soft reconfiguration inbound flag. + Use Adj-RIB-In database. */ + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG)) + bgp_soft_reconfig_in (peer, afi, safi); + else + { + /* If neighbor has route refresh capability, send route refresh + message to the peer. */ + if (CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_OLD_RCV) + || CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_NEW_RCV)) + bgp_route_refresh_send (peer, afi, safi, 0, 0, 0); + else + return BGP_ERR_SOFT_RECONFIG_UNCONFIGURED; + } + } + return 0; +} + +/* Display peer uptime.*/ +/* XXX: why does this function return char * when it takes buffer? */ +char * +peer_uptime (time_t uptime2, char *buf, size_t len) +{ + time_t uptime1; + struct tm *tm; + + /* Check buffer length. */ + if (len < BGP_UPTIME_LEN) + { + zlog_warn ("peer_uptime (): buffer shortage %lu", (u_long)len); + /* XXX: should return status instead of buf... */ + snprintf (buf, len, " "); + return buf; + } + + /* If there is no connection has been done before print `never'. */ + if (uptime2 == 0) + { + snprintf (buf, len, "never "); + return buf; + } + + /* Get current time. */ + uptime1 = bgp_clock (); + uptime1 -= uptime2; + tm = gmtime (&uptime1); + + /* Making formatted timer strings. */ +#define ONE_DAY_SECOND 60*60*24 +#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", + tm->tm_hour, tm->tm_min, tm->tm_sec); + else if (uptime1 < ONE_WEEK_SECOND) + snprintf (buf, len, "%dd%02dh%02dm", + tm->tm_yday, tm->tm_hour, tm->tm_min); + 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) +{ + struct bgp_filter *filter; + struct bgp_filter *gfilter = NULL; + char *addr; + int in = FILTER_IN; + int out = FILTER_OUT; + + addr = peer->host; + filter = &peer->filter[afi][safi]; + if (peer->af_group[afi][safi]) + gfilter = &peer->group->conf->filter[afi][safi]; + + /* distribute-list. */ + if (filter->dlist[in].name) + if (! gfilter || ! gfilter->dlist[in].name + || strcmp (filter->dlist[in].name, gfilter->dlist[in].name) != 0) + vty_out (vty, " neighbor %s distribute-list %s in%s", addr, + filter->dlist[in].name, VTY_NEWLINE); + if (filter->dlist[out].name && ! gfilter) + vty_out (vty, " neighbor %s distribute-list %s out%s", addr, + filter->dlist[out].name, VTY_NEWLINE); + + /* prefix-list. */ + if (filter->plist[in].name) + if (! gfilter || ! gfilter->plist[in].name + || strcmp (filter->plist[in].name, gfilter->plist[in].name) != 0) + vty_out (vty, " neighbor %s prefix-list %s in%s", addr, + filter->plist[in].name, VTY_NEWLINE); + if (filter->plist[out].name && ! gfilter) + vty_out (vty, " neighbor %s prefix-list %s out%s", addr, + filter->plist[out].name, VTY_NEWLINE); + + /* route-map. */ + if (filter->map[RMAP_IN].name) + if (! gfilter || ! gfilter->map[RMAP_IN].name + || strcmp (filter->map[RMAP_IN].name, gfilter->map[RMAP_IN].name) != 0) + vty_out (vty, " neighbor %s route-map %s in%s", addr, + filter->map[RMAP_IN].name, VTY_NEWLINE); + if (filter->map[RMAP_OUT].name && ! gfilter) + vty_out (vty, " neighbor %s route-map %s out%s", addr, + filter->map[RMAP_OUT].name, VTY_NEWLINE); + if (filter->map[RMAP_IMPORT].name && ! gfilter) + vty_out (vty, " neighbor %s route-map %s import%s", addr, + filter->map[RMAP_IMPORT].name, VTY_NEWLINE); + if (filter->map[RMAP_EXPORT].name) + if (! gfilter || ! gfilter->map[RMAP_EXPORT].name + || strcmp (filter->map[RMAP_EXPORT].name, + gfilter->map[RMAP_EXPORT].name) != 0) + vty_out (vty, " neighbor %s route-map %s export%s", addr, + filter->map[RMAP_EXPORT].name, VTY_NEWLINE); + + /* unsuppress-map */ + if (filter->usmap.name && ! gfilter) + vty_out (vty, " neighbor %s unsuppress-map %s%s", addr, + filter->usmap.name, VTY_NEWLINE); + + /* filter-list. */ + if (filter->aslist[in].name) + if (! gfilter || ! gfilter->aslist[in].name + || strcmp (filter->aslist[in].name, gfilter->aslist[in].name) != 0) + vty_out (vty, " neighbor %s filter-list %s in%s", addr, + filter->aslist[in].name, VTY_NEWLINE); + if (filter->aslist[out].name && ! gfilter) + vty_out (vty, " neighbor %s filter-list %s out%s", addr, + filter->aslist[out].name, VTY_NEWLINE); +} + +/* BGP peer configuration display function. */ +static void +bgp_config_write_peer (struct vty *vty, struct bgp *bgp, + struct peer *peer, afi_t afi, safi_t safi) +{ + struct peer *g_peer = NULL; + char buf[SU_ADDRSTRLEN]; + char *addr; + + addr = peer->host; + if (peer_group_active (peer)) + g_peer = peer->group->conf; + + /************************************ + ****** Global to the neighbor ****** + ************************************/ + if (afi == AFI_IP && safi == SAFI_UNICAST) + { + /* remote-as. */ + if (! peer_group_active (peer)) + { + if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + vty_out (vty, " neighbor %s peer-group%s", addr, + VTY_NEWLINE); + if (peer->as) + vty_out (vty, " neighbor %s remote-as %u%s", addr, peer->as, + VTY_NEWLINE); + } + else + { + if (! g_peer->as) + vty_out (vty, " neighbor %s remote-as %u%s", addr, peer->as, + VTY_NEWLINE); + if (peer->af_group[AFI_IP][SAFI_UNICAST]) + vty_out (vty, " neighbor %s peer-group %s%s", addr, + peer->group->name, VTY_NEWLINE); + } + + /* local-as. */ + if (peer->change_local_as) + if (! peer_group_active (peer)) + 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" : "", + CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS) ? + " replace-as" : "", VTY_NEWLINE); + + /* Description. */ + if (peer->desc) + vty_out (vty, " neighbor %s description %s%s", addr, peer->desc, + VTY_NEWLINE); + + /* Shutdown. */ + if (CHECK_FLAG (peer->flags, PEER_FLAG_SHUTDOWN)) + if (! peer_group_active (peer) || + ! CHECK_FLAG (g_peer->flags, PEER_FLAG_SHUTDOWN)) + vty_out (vty, " neighbor %s shutdown%s", addr, VTY_NEWLINE); + + /* Password. */ + if (peer->password) + if (!peer_group_active (peer) + || ! g_peer->password + || strcmp (peer->password, g_peer->password) != 0) + vty_out (vty, " neighbor %s password %s%s", addr, peer->password, + VTY_NEWLINE); + + /* BGP port. */ + if (peer->port != BGP_PORT_DEFAULT) + vty_out (vty, " neighbor %s port %d%s", addr, peer->port, + VTY_NEWLINE); + + /* Local interface name. */ + if (peer->ifname) + vty_out (vty, " neighbor %s interface %s%s", addr, peer->ifname, + VTY_NEWLINE); + + /* Passive. */ + if (CHECK_FLAG (peer->flags, PEER_FLAG_PASSIVE)) + if (! peer_group_active (peer) || + ! CHECK_FLAG (g_peer->flags, PEER_FLAG_PASSIVE)) + vty_out (vty, " neighbor %s passive%s", addr, 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)) + if (! peer_group_active (peer) || + ! CHECK_FLAG (g_peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)) + vty_out (vty, " neighbor %s disable-connected-check%s", addr, VTY_NEWLINE); + + /* Update-source. */ + if (peer->update_if) + if (! peer_group_active (peer) || ! g_peer->update_if + || strcmp (g_peer->update_if, peer->update_if) != 0) + vty_out (vty, " neighbor %s update-source %s%s", addr, + peer->update_if, VTY_NEWLINE); + if (peer->update_source) + if (! peer_group_active (peer) || ! g_peer->update_source + || sockunion_cmp (g_peer->update_source, + peer->update_source) != 0) + vty_out (vty, " neighbor %s update-source %s%s", addr, + sockunion2str (peer->update_source, buf, SU_ADDRSTRLEN), + VTY_NEWLINE); + + /* advertisement-interval */ + 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); + + /* timers. */ + if (CHECK_FLAG (peer->config, PEER_CONFIG_TIMER) + && ! peer_group_active (peer)) + vty_out (vty, " neighbor %s timers %d %d%s", addr, + peer->keepalive, peer->holdtime, VTY_NEWLINE); + + 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); + + /* Default weight. */ + if (CHECK_FLAG (peer->config, PEER_CONFIG_WEIGHT)) + if (! peer_group_active (peer) || + g_peer->weight != peer->weight) + vty_out (vty, " neighbor %s weight %d%s", addr, peer->weight, + VTY_NEWLINE); + + /* Dynamic capability. */ + if (CHECK_FLAG (peer->flags, PEER_FLAG_DYNAMIC_CAPABILITY)) + if (! peer_group_active (peer) || + ! CHECK_FLAG (g_peer->flags, PEER_FLAG_DYNAMIC_CAPABILITY)) + vty_out (vty, " neighbor %s capability dynamic%s", addr, + VTY_NEWLINE); + + /* dont capability negotiation. */ + if (CHECK_FLAG (peer->flags, PEER_FLAG_DONT_CAPABILITY)) + if (! peer_group_active (peer) || + ! CHECK_FLAG (g_peer->flags, PEER_FLAG_DONT_CAPABILITY)) + vty_out (vty, " neighbor %s dont-capability-negotiate%s", addr, + VTY_NEWLINE); + + /* override capability negotiation. */ + if (CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY)) + if (! peer_group_active (peer) || + ! CHECK_FLAG (g_peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY)) + vty_out (vty, " neighbor %s override-capability%s", addr, + VTY_NEWLINE); + + /* strict capability negotiation. */ + if (CHECK_FLAG (peer->flags, PEER_FLAG_STRICT_CAP_MATCH)) + if (! peer_group_active (peer) || + ! CHECK_FLAG (g_peer->flags, PEER_FLAG_STRICT_CAP_MATCH)) + vty_out (vty, " neighbor %s strict-capability-match%s", addr, + VTY_NEWLINE); + + if (! peer->af_group[AFI_IP][SAFI_UNICAST]) + { + if (bgp_flag_check (bgp, BGP_FLAG_NO_DEFAULT_IPV4)) + { + if (peer->afc[AFI_IP][SAFI_UNICAST]) + vty_out (vty, " neighbor %s activate%s", addr, VTY_NEWLINE); + } + else + { + if (! peer->afc[AFI_IP][SAFI_UNICAST]) + vty_out (vty, " no neighbor %s activate%s", addr, VTY_NEWLINE); + } + } + } + + + /************************************ + ****** Per AF to the neighbor ****** + ************************************/ + + if (! (afi == AFI_IP && safi == SAFI_UNICAST)) + { + if (peer->af_group[afi][safi]) + vty_out (vty, " neighbor %s peer-group %s%s", addr, + peer->group->name, VTY_NEWLINE); + else + vty_out (vty, " neighbor %s activate%s", addr, VTY_NEWLINE); + } + + /* ORF capability. */ + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_SM) + || CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_RM)) + if (! peer->af_group[afi][safi]) + { + vty_out (vty, " neighbor %s capability orf prefix-list", addr); + + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_SM) + && CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_RM)) + vty_out (vty, " both"); + else if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_SM)) + vty_out (vty, " send"); + else + vty_out (vty, " receive"); + vty_out (vty, "%s", VTY_NEWLINE); + } + + /* Route reflector client. */ + if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_REFLECTOR_CLIENT) + && ! peer->af_group[afi][safi]) + vty_out (vty, " neighbor %s route-reflector-client%s", addr, + VTY_NEWLINE); + + /* 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%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) + && ! peer->af_group[afi][safi]) + vty_out (vty, " neighbor %s remove-private-AS%s", + addr, VTY_NEWLINE); + + /* send-community print. */ + if (! peer->af_group[afi][safi]) + { + 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) + && 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) + && ! 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); + } + } + + /* Default information */ + if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_DEFAULT_ORIGINATE) + && ! peer->af_group[afi][safi]) + { + vty_out (vty, " neighbor %s default-originate", addr); + if (peer->default_rmap[afi][safi].name) + vty_out (vty, " route-map %s", peer->default_rmap[afi][safi].name); + vty_out (vty, "%s", VTY_NEWLINE); + } + + /* Soft reconfiguration inbound. */ + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG)) + if (! peer->af_group[afi][safi] || + ! CHECK_FLAG (g_peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG)) + vty_out (vty, " neighbor %s soft-reconfiguration inbound%s", addr, + VTY_NEWLINE); + + /* maximum-prefix. */ + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX)) + if (! peer->af_group[afi][safi] + || g_peer->pmax[afi][safi] != peer->pmax[afi][safi] + || g_peer->pmax_threshold[afi][safi] != peer->pmax_threshold[afi][safi] + || CHECK_FLAG (g_peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING) + != CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING)) + { + vty_out (vty, " neighbor %s maximum-prefix %ld", addr, peer->pmax[afi][safi]); + if (peer->pmax_threshold[afi][safi] != MAXIMUM_PREFIX_THRESHOLD_DEFAULT) + vty_out (vty, " %d", peer->pmax_threshold[afi][safi]); + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING)) + vty_out (vty, " warning-only"); + if (peer->pmax_restart[afi][safi]) + vty_out (vty, " restart %d", peer->pmax_restart[afi][safi]); + vty_out (vty, "%s", VTY_NEWLINE); + } + + /* Route server client. */ + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT) + && ! peer->af_group[afi][safi]) + vty_out (vty, " neighbor %s route-server-client%s", addr, VTY_NEWLINE); + + /* Nexthop-local unchanged. */ + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED) + && ! peer->af_group[afi][safi]) + vty_out (vty, " neighbor %s nexthop-local unchanged%s", addr, VTY_NEWLINE); + + /* Allow AS in. */ + if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_ALLOWAS_IN)) + if (! peer_group_active (peer) + || ! peer_af_flag_check (g_peer, afi, safi, PEER_FLAG_ALLOWAS_IN) + || peer->allowas_in[afi][safi] != g_peer->allowas_in[afi][safi]) + { + if (peer->allowas_in[afi][safi] == 3) + vty_out (vty, " neighbor %s allowas-in%s", addr, VTY_NEWLINE); + else + vty_out (vty, " neighbor %s allowas-in %d%s", addr, + peer->allowas_in[afi][safi], VTY_NEWLINE); + } + + /* Filter. */ + bgp_config_write_filter (vty, peer, afi, safi); + + /* atribute-unchanged. */ + if ((CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_AS_PATH_UNCHANGED) + || CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_UNCHANGED) + || CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MED_UNCHANGED)) + && ! peer->af_group[afi][safi]) + { + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_AS_PATH_UNCHANGED) + && CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_UNCHANGED) + && CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MED_UNCHANGED)) + vty_out (vty, " neighbor %s attribute-unchanged%s", addr, VTY_NEWLINE); + else + vty_out (vty, " neighbor %s attribute-unchanged%s%s%s%s", addr, + (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_AS_PATH_UNCHANGED)) ? + " as-path" : "", + (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_UNCHANGED)) ? + " next-hop" : "", + (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MED_UNCHANGED)) ? + " med" : "", VTY_NEWLINE); + } +} + +/* Display "address-family" configuration header. */ +void +bgp_config_write_family_header (struct vty *vty, afi_t afi, safi_t safi, + int *write) +{ + if (*write) + return; + + if (afi == AFI_IP && safi == SAFI_UNICAST) + return; + + vty_out (vty, "!%s address-family ", VTY_NEWLINE); + + if (afi == AFI_IP) + { + if (safi == SAFI_MULTICAST) + vty_out (vty, "ipv4 multicast"); + else if (safi == SAFI_MPLS_VPN) + vty_out (vty, "vpnv4"); + else if (safi == SAFI_ENCAP) + vty_out (vty, "encap"); + } + else if (afi == AFI_IP6) + { + 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); + + *write = 1; +} + +/* Address family based peer configuration display. */ +static int +bgp_config_write_family (struct vty *vty, struct bgp *bgp, afi_t afi, + safi_t safi) +{ + int write = 0; + struct peer *peer; + struct peer_group *group; + struct listnode *node, *nnode; + + bgp_config_write_network (vty, bgp, afi, safi, &write); + + bgp_config_write_redistribute (vty, bgp, afi, safi, &write); + + for (ALL_LIST_ELEMENTS (bgp->group, node, nnode, group)) + { + if (group->conf->afc[afi][safi]) + { + bgp_config_write_family_header (vty, afi, safi, &write); + bgp_config_write_peer (vty, bgp, group->conf, afi, safi); + } + } + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + if (peer->afc[afi][safi]) + { + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) + { + bgp_config_write_family_header (vty, afi, safi, &write); + bgp_config_write_peer (vty, bgp, peer, afi, safi); + } + } + } + + 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); + + return write; +} + +int +bgp_config_write (struct vty *vty) +{ + int write = 0; + struct bgp *bgp; + struct peer_group *group; + struct peer *peer; + struct listnode *node, *nnode; + struct listnode *mnode, *mnnode; + + /* BGP Multiple instance. */ + if (bgp_option_check (BGP_OPT_MULTIPLE_INSTANCE)) + { + vty_out (vty, "bgp multiple-instance%s", VTY_NEWLINE); + write++; + } + + /* BGP Config type. */ + if (bgp_option_check (BGP_OPT_CONFIG_CISCO)) + { + vty_out (vty, "bgp config-type cisco%s", VTY_NEWLINE); + write++; + } + + /* BGP configuration. */ + for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp)) + { + if (write) + vty_out (vty, "!%s", VTY_NEWLINE); + + /* Router bgp ASN */ + vty_out (vty, "router bgp %u", bgp->as); + + if (bgp_option_check (BGP_OPT_MULTIPLE_INSTANCE)) + { + if (bgp->name) + vty_out (vty, " view %s", bgp->name); + } + vty_out (vty, "%s", VTY_NEWLINE); + + /* No Synchronization */ + if (bgp_option_check (BGP_OPT_CONFIG_CISCO)) + vty_out (vty, " no synchronization%s", VTY_NEWLINE); + + /* BGP fast-external-failover. */ + if (CHECK_FLAG (bgp->flags, BGP_FLAG_NO_FAST_EXT_FAILOVER)) + vty_out (vty, " no bgp fast-external-failover%s", VTY_NEWLINE); + + /* BGP router ID. */ + if (CHECK_FLAG (bgp->config, BGP_CONFIG_ROUTER_ID)) + vty_out (vty, " bgp router-id %s%s", inet_ntoa (bgp->router_id), + VTY_NEWLINE); + + /* BGP log-neighbor-changes. */ + 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)) + vty_out (vty, " bgp always-compare-med%s", VTY_NEWLINE); + + /* BGP default ipv4-unicast. */ + if (bgp_flag_check (bgp, BGP_FLAG_NO_DEFAULT_IPV4)) + vty_out (vty, " no bgp default ipv4-unicast%s", VTY_NEWLINE); + + /* BGP default local-preference. */ + if (bgp->default_local_pref != BGP_DEFAULT_LOCAL_PREF) + vty_out (vty, " bgp default local-preference %d%s", + bgp->default_local_pref, VTY_NEWLINE); + + /* BGP client-to-client reflection. */ + if (bgp_flag_check (bgp, BGP_FLAG_NO_CLIENT_TO_CLIENT)) + vty_out (vty, " no bgp client-to-client reflection%s", VTY_NEWLINE); + + /* BGP cluster ID. */ + if (CHECK_FLAG (bgp->config, BGP_CONFIG_CLUSTER_ID)) + vty_out (vty, " bgp cluster-id %s%s", inet_ntoa (bgp->cluster_id), + VTY_NEWLINE); + + /* Confederation identifier*/ + if (CHECK_FLAG (bgp->config, BGP_CONFIG_CONFEDERATION)) + vty_out (vty, " bgp confederation identifier %i%s", bgp->confed_id, + VTY_NEWLINE); + + /* Confederation peer */ + if (bgp->confed_peers_cnt > 0) + { + int i; + + vty_out (vty, " bgp confederation peers"); + + for (i = 0; i < bgp->confed_peers_cnt; i++) + vty_out(vty, " %u", bgp->confed_peers[i]); + + vty_out (vty, "%s", VTY_NEWLINE); + } + + /* BGP enforce-first-as. */ + if (bgp_flag_check (bgp, BGP_FLAG_ENFORCE_FIRST_AS)) + vty_out (vty, " bgp enforce-first-as%s", VTY_NEWLINE); + + /* BGP deterministic-med. */ + if (bgp_flag_check (bgp, BGP_FLAG_DETERMINISTIC_MED)) + vty_out (vty, " bgp deterministic-med%s", VTY_NEWLINE); + + /* BGP graceful-restart. */ + 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); + + /* BGP bestpath method. */ + if (bgp_flag_check (bgp, BGP_FLAG_ASPATH_IGNORE)) + 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) + || bgp_flag_check (bgp, BGP_FLAG_MED_MISSING_AS_WORST)) + { + vty_out (vty, " bgp bestpath med"); + if (bgp_flag_check (bgp, BGP_FLAG_MED_CONFED)) + vty_out (vty, " confed"); + if (bgp_flag_check (bgp, BGP_FLAG_MED_MISSING_AS_WORST)) + vty_out (vty, " missing-as-worst"); + vty_out (vty, "%s", VTY_NEWLINE); + } + + /* BGP network import check. */ + if (bgp_flag_check (bgp, BGP_FLAG_IMPORT_CHECK)) + vty_out (vty, " bgp network import-check%s", VTY_NEWLINE); + + /* BGP flag dampening. */ + if (CHECK_FLAG (bgp->af_flags[AFI_IP][SAFI_UNICAST], + BGP_CONFIG_DAMPENING)) + bgp_config_write_damp (vty); + + /* BGP static route configuration. */ + bgp_config_write_network (vty, bgp, AFI_IP, SAFI_UNICAST, &write); + + /* BGP redistribute configuration. */ + bgp_config_write_redistribute (vty, bgp, AFI_IP, SAFI_UNICAST, &write); + + /* BGP timers configuration. */ + if (bgp->default_keepalive != BGP_DEFAULT_KEEPALIVE + && bgp->default_holdtime != BGP_DEFAULT_HOLDTIME) + vty_out (vty, " timers bgp %d %d%s", bgp->default_keepalive, + bgp->default_holdtime, VTY_NEWLINE); + + /* peer-group */ + for (ALL_LIST_ELEMENTS (bgp->group, node, nnode, group)) + { + bgp_config_write_peer (vty, bgp, group->conf, AFI_IP, SAFI_UNICAST); + } + + /* Normal neighbor configuration. */ + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) + 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, AFI_IP, SAFI_UNICAST, &write); + + /* No auto-summary */ + if (bgp_option_check (BGP_OPT_CONFIG_CISCO)) + vty_out (vty, " no auto-summary%s", VTY_NEWLINE); + + /* IPv4 multicast configuration. */ + write += bgp_config_write_family (vty, bgp, AFI_IP, SAFI_MULTICAST); + + /* 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; +} + +void +bgp_master_init (void) +{ + memset (&bgp_master, 0, sizeof (struct bgp_master)); + + bm = &bgp_master; + bm->bgp = list_new (); + bm->listen_sockets = list_new (); + bm->port = BGP_PORT_DEFAULT; + bm->master = thread_master_create (); + bm->start_time = bgp_clock (); +} + + +void +bgp_init (void) +{ + + /* allocates some vital data structures used by peer commands in vty_init */ + bgp_scan_init (); + + /* Init zebra. */ + bgp_zebra_init (bm->master); + + /* BGP VTY commands installation. */ + bgp_vty_init (); + + /* BGP inits. */ + bgp_attr_init (); + bgp_debug_init (); + bgp_dump_init (); + bgp_route_init (); + bgp_route_map_init (); + bgp_address_init (); + bgp_scan_vty_init(); + bgp_mplsvpn_init (); + bgp_encap_init (); + + /* Access list initialize. */ + access_list_init (); + access_list_add_hook (peer_distribute_update); + access_list_delete_hook (peer_distribute_update); + + /* Filter list initialize. */ + bgp_filter_init (); + as_list_add_hook (peer_aslist_update); + as_list_delete_hook (peer_aslist_update); + + /* Prefix list initialize.*/ + prefix_list_init (); + prefix_list_add_hook (peer_prefix_list_update); + prefix_list_delete_hook (peer_prefix_list_update); + + /* Community list initialize. */ + bgp_clist = community_list_init (); + +#ifdef HAVE_SNMP + bgp_snmp_init (); +#endif /* HAVE_SNMP */ +} + +void +bgp_terminate (void) +{ + struct bgp *bgp; + struct peer *peer; + struct listnode *node, *nnode; + struct listnode *mnode, *mnnode; + + for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp)) + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + if (peer->status == Established) + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_PEER_UNCONFIG); + + bgp_cleanup_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; + } +} diff --git a/bgpd/bgpd.conf.sample b/bgpd/bgpd.conf.sample new file mode 100644 index 0000000..b6a8b6f --- /dev/null +++ b/bgpd/bgpd.conf.sample @@ -0,0 +1,29 @@ +! -*- bgp -*- +! +! BGPd sample configuratin file +! +! $Id: bgpd.conf.sample,v 1.1 2002/12/13 20:15:29 paul Exp $ +! +hostname bgpd +password zebra +!enable password please-set-at-here +! +!bgp mulitple-instance +! +router bgp 7675 +! bgp router-id 10.0.0.1 +! network 10.0.0.0/8 +! neighbor 10.0.0.2 remote-as 7675 +! neighbor 10.0.0.2 route-map set-nexthop out +! neighbor 10.0.0.2 ebgp-multihop +! neighbor 10.0.0.2 next-hop-self +! +! access-list all permit any +! +!route-map set-nexthop permit 10 +! match ip address all +! set ip next-hop 10.0.0.1 +! +!log file bgpd.log +! +log stdout diff --git a/bgpd/bgpd.conf.sample2 b/bgpd/bgpd.conf.sample2 new file mode 100644 index 0000000..d376ad2 --- /dev/null +++ b/bgpd/bgpd.conf.sample2 @@ -0,0 +1,77 @@ +! +! Zebra configuration saved from vty +! 2002/07/01 03:16:33 +! +hostname bgpd +password zebra +log file bgpd.log +log stdout +! +router bgp 7675 + no bgp default ipv4-unicast + neighbor 3ffe:506:1000::2 remote-as 7675 + neighbor fe80::200:c0ff:fe30:9be3 remote-as 9377 + neighbor fe80::200:c0ff:fe30:9be3 interface sit3 + neighbor fe80::210:5aff:fe6b:3cee remote-as 7675 + neighbor fe80::210:5aff:fe6b:3cee interface eth0 + neighbor fe80::290:27ff:fe51:84c7 remote-as 4691 + neighbor fe80::290:27ff:fe51:84c7 description DTI + neighbor fe80::290:27ff:fe51:84c7 interface sit7 + neighbor fe80::2a0:c9ff:fec8:82ec remote-as 7530 + neighbor fe80::2a0:c9ff:fec8:82ec description IRI + neighbor fe80::2a0:c9ff:fec8:82ec interface sit8 + neighbor fe80::2e0:18ff:fe98:2725 remote-as 2500 + neighbor fe80::2e0:18ff:fe98:2725 description WIDE + neighbor fe80::2e0:18ff:fe98:2725 interface sit5 + neighbor fe80::2e0:18ff:fea8:bf5 remote-as 65000 + neighbor fe80::2e0:18ff:fea8:bf5 interface sit6 +! + address-family ipv6 + network 3ffe:506::/33 + network 3ffe:1800:e800::/40 + aggregate-address 3ffe:506::/32 + redistribute connected + neighbor 3ffe:506:1000::2 activate + neighbor fe80::200:c0ff:fe30:9be3 activate + neighbor fe80::200:c0ff:fe30:9be3 route-map set-nexthop out + neighbor fe80::210:5aff:fe6b:3cee activate + neighbor fe80::290:27ff:fe51:84c7 activate + neighbor fe80::290:27ff:fe51:84c7 route-map set-nexthop out + neighbor fe80::2a0:c9ff:fec8:82ec activate + neighbor fe80::2a0:c9ff:fec8:82ec route-map set-nexthop out + neighbor fe80::2e0:18ff:fe98:2725 activate + neighbor fe80::2e0:18ff:fe98:2725 distribute-list nla1 out + neighbor fe80::2e0:18ff:fe98:2725 route-map set-nexthop out + neighbor fe80::2e0:18ff:fea8:bf5 activate + neighbor fe80::2e0:18ff:fea8:bf5 route-map set-nexthop out + exit-address-family +! +ipv6 access-list all permit any +ipv6 access-list nla1 deny 3ffe:506::/33 +ipv6 access-list nla1 permit 3ffe:506::/32 +ipv6 access-list nla1 deny any +ipv6 access-list ntt-nla1 permit 3ffe:1800:0:ffff::c/127 +ipv6 access-list ntt-nla1 deny 3ffe:1800:e800::/41 +ipv6 access-list ntt-nla1 permit 3ffe:1800:e800::/40 +ipv6 access-list ntt-nla1 deny any +! +ipv6 prefix-list 6bone-filter seq 5 permit 3ffe::/17 ge 24 le 24 +ipv6 prefix-list 6bone-filter seq 10 permit 3ffe:8000::/17 ge 28 le 28 +ipv6 prefix-list 6bone-filter seq 12 deny 3ffe::/16 +ipv6 prefix-list 6bone-filter seq 15 permit 2000::/3 ge 16 le 16 +ipv6 prefix-list 6bone-filter seq 20 permit 2001::/16 ge 35 le 35 +! +route-map set-nexthop permit 10 + match ipv6 address all + set ipv6 next-hop global 3ffe:506::1 + set ipv6 next-hop local fe80::cbb5:591a + set ip next-hop 203.181.89.26 + set community 7675:0 +! +route-map set-link-local permit 10 + match ipv6 address all + set ipv6 next-hop local fe80::cbb5:591a + set ipv6 next-hop global 3ffe:1800:0:ffff::d +! +line vty +! diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h new file mode 100644 index 0000000..03df2f6 --- /dev/null +++ b/bgpd/bgpd.h @@ -0,0 +1,1002 @@ +/* BGP message definition header. + Copyright (C) 1996, 97, 98, 99, 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. */ + +#ifndef _QUAGGA_BGPD_H +#define _QUAGGA_BGPD_H + +/* For union sockunion. */ +#include "sockunion.h" + +/* Typedef BGP specific types. */ +typedef u_int32_t as_t; +typedef u_int16_t as16_t; /* we may still encounter 16 Bit asnums */ +typedef u_int16_t bgp_size_t; + +/* BGP master for system wide configurations and variables. */ +struct bgp_master +{ + /* BGP instance list. */ + struct list *bgp; + + /* BGP thread master. */ + struct thread_master *master; + + /* work queues */ + struct work_queue *process_main_queue; + struct work_queue *process_rsclient_queue; + + /* Listening sockets */ + struct list *listen_sockets; + + /* BGP port number. */ + u_int16_t port; + + /* Listener address */ + char *address; + + /* BGP start time. */ + time_t start_time; + + /* Various BGP global configuration. */ + u_char options; +#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. */ +struct bgp +{ + /* AS number of this BGP instance. */ + as_t as; + + /* Name of this BGP instance. */ + char *name; + + /* Reference count to allow peer_delete to finish after bgp_delete */ + int lock; + + /* Self peer. */ + struct peer *peer_self; + + /* BGP peer. */ + struct list *peer; + + /* BGP peer group. */ + struct list *group; + + /* BGP route-server-clients. */ + struct list *rsclient; + + /* BGP configuration. */ + u_int16_t config; +#define BGP_CONFIG_ROUTER_ID (1 << 0) +#define BGP_CONFIG_CLUSTER_ID (1 << 1) +#define BGP_CONFIG_CONFEDERATION (1 << 2) + + /* BGP router identifier. */ + struct in_addr router_id; + struct in_addr router_id_static; + + /* BGP route reflector cluster ID. */ + struct in_addr cluster_id; + + /* BGP confederation information. */ + as_t confed_id; + as_t *confed_peers; + int confed_peers_cnt; + + struct thread *t_startup; + + /* BGP 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) +#define BGP_FLAG_MED_CONFED (1 << 3) +#define BGP_FLAG_NO_DEFAULT_IPV4 (1 << 4) +#define BGP_FLAG_NO_CLIENT_TO_CLIENT (1 << 5) +#define BGP_FLAG_ENFORCE_FIRST_AS (1 << 6) +#define BGP_FLAG_COMPARE_ROUTER_ID (1 << 7) +#define BGP_FLAG_ASPATH_IGNORE (1 << 8) +#define BGP_FLAG_IMPORT_CHECK (1 << 9) +#define BGP_FLAG_NO_FAST_EXT_FAILOVER (1 << 10) +#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]; +#define BGP_CONFIG_DAMPENING (1 << 0) + + /* Static route configuration. */ + struct bgp_table *route[AFI_MAX][SAFI_MAX]; + + /* Aggregate address configuration. */ + struct bgp_table *aggregate[AFI_MAX][SAFI_MAX]; + + /* BGP routing information base. */ + struct bgp_table *rib[AFI_MAX][SAFI_MAX]; + + /* BGP redistribute configuration. */ + u_char redist[AFI_MAX][ZEBRA_ROUTE_MAX]; + + /* BGP redistribute metric configuration. */ + u_char redist_metric_flag[AFI_MAX][ZEBRA_ROUTE_MAX]; + u_int32_t redist_metric[AFI_MAX][ZEBRA_ROUTE_MAX]; + + /* BGP redistribute route-map. */ + struct + { + char *name; + struct route_map *map; + } rmap[AFI_MAX][ZEBRA_ROUTE_MAX]; + + /* BGP distance configuration. */ + 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; + + /* BGP default timer. */ + u_int32_t default_holdtime; + u_int32_t default_keepalive; + + /* 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. */ +struct peer_group +{ + /* Name of the peer-group. */ + char *name; + + /* Pointer to BGP. */ + struct bgp *bgp; + + /* Peer-group client list. */ + struct list *peer; + + /* Peer-group config */ + struct peer *conf; +}; + +/* BGP Notify message format. */ +struct bgp_notify +{ + u_char code; + u_char subcode; + char *data; + bgp_size_t length; +}; + +/* Next hop self address. */ +struct bgp_nexthop +{ + struct interface *ifp; + struct in_addr v4; + struct in6_addr v6_global; + struct in6_addr v6_local; +}; + +/* BGP router distinguisher value. */ +#define BGP_RD_SIZE 8 + +struct bgp_rd +{ + u_char val[BGP_RD_SIZE]; +}; + +#define RMAP_IN 0 +#define RMAP_OUT 1 +#define RMAP_IMPORT 2 +#define RMAP_EXPORT 3 +#define RMAP_MAX 4 + +/* BGP filter structure. */ +struct bgp_filter +{ + /* Distribute-list. */ + struct + { + char *name; + struct access_list *alist; + } dlist[FILTER_MAX]; + + /* Prefix-list. */ + struct + { + char *name; + struct prefix_list *plist; + } plist[FILTER_MAX]; + + /* Filter-list. */ + struct + { + char *name; + struct as_list *aslist; + } aslist[FILTER_MAX]; + + /* Route-map. */ + struct + { + char *name; + struct route_map *map; + } map[RMAP_MAX]; + + /* Unsuppress-map. */ + struct + { + char *name; + struct route_map *map; + } 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 +{ + /* BGP structure. */ + struct bgp *bgp; + + /* reference count, primarily to allow bgp_process'ing of route_node's + * to be done after a struct peer is deleted. + * + * named 'lock' for hysterical reasons within Quagga. + */ + int lock; + + /* BGP peer group. */ + struct peer_group *group; + u_char af_group[AFI_MAX][SAFI_MAX]; + + /* Peer's remote AS number. */ + as_t as; + + /* 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; + + /* Remote router ID. */ + struct in_addr remote_id; + + /* Local router ID. */ + struct in_addr local_id; + + /* Peer specific RIB when configured as route-server-client. */ + struct bgp_table *rib[AFI_MAX][SAFI_MAX]; + + /* Packet receive and send buffer. */ + struct stream *ibuf; + 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; + + /* Peer index, used for dumping TABLE_DUMP_V2 format */ + uint16_t table_dump_index; + + /* 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 */ + char *host; /* Printable address of the peer. */ + union sockunion su; /* Sockunion address of the peer. */ + time_t uptime; /* Last Up/Down time */ + time_t readtime; /* Last read time */ + time_t resettime; /* Last reset time */ + + ifindex_t ifindex; /* ifindex of the BGP connection. */ + char *ifname; /* bind interface name. */ + char *update_if; + union sockunion *update_source; + struct zlog *log; + + union sockunion *su_local; /* Sockunion of local address. */ + union sockunion *su_remote; /* Sockunion of remote address. */ + int shared_network; /* Is this peer shared same network. */ + struct bgp_nexthop nexthop; /* Nexthop */ + + /* Peer address family configuration. */ + u_char afc[AFI_MAX][SAFI_MAX]; + u_char afc_nego[AFI_MAX][SAFI_MAX]; + u_char afc_adv[AFI_MAX][SAFI_MAX]; + u_char afc_recv[AFI_MAX][SAFI_MAX]; + + /* Capability flags (reset in bgp_stop) */ + u_int16_t cap; +#define PEER_CAP_REFRESH_ADV (1 << 0) /* refresh advertised */ +#define PEER_CAP_REFRESH_OLD_RCV (1 << 1) /* refresh old received */ +#define PEER_CAP_REFRESH_NEW_RCV (1 << 2) /* refresh rfc received */ +#define PEER_CAP_DYNAMIC_ADV (1 << 3) /* dynamic advertised */ +#define PEER_CAP_DYNAMIC_RCV (1 << 4) /* dynamic received */ +#define PEER_CAP_RESTART_ADV (1 << 5) /* restart advertised */ +#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]; +#define PEER_CAP_ORF_PREFIX_SM_ADV (1 << 0) /* send-mode advertised */ +#define PEER_CAP_ORF_PREFIX_RM_ADV (1 << 1) /* receive-mode advertised */ +#define PEER_CAP_ORF_PREFIX_SM_RCV (1 << 2) /* send-mode received */ +#define PEER_CAP_ORF_PREFIX_RM_RCV (1 << 3) /* receive-mode received */ +#define PEER_CAP_ORF_PREFIX_SM_OLD_RCV (1 << 4) /* send-mode received */ +#define PEER_CAP_ORF_PREFIX_RM_OLD_RCV (1 << 5) /* receive-mode received */ +#define PEER_CAP_RESTART_AF_RCV (1 << 6) /* graceful restart afi/safi received */ +#define PEER_CAP_RESTART_AF_PRESERVE_RCV (1 << 7) /* graceful restart afi/safi F-bit received */ + + /* Global configuration flags. */ + u_int32_t flags; +#define PEER_FLAG_PASSIVE (1 << 0) /* passive mode */ +#define PEER_FLAG_SHUTDOWN (1 << 1) /* shutdown */ +#define PEER_FLAG_DONT_CAPABILITY (1 << 2) /* dont-capability */ +#define PEER_FLAG_OVERRIDE_CAPABILITY (1 << 3) /* override-capability */ +#define PEER_FLAG_STRICT_CAP_MATCH (1 << 4) /* strict-match */ +#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]; + + /* Per AF configuration flags. */ + u_int32_t af_flags[AFI_MAX][SAFI_MAX]; +#define PEER_FLAG_SEND_COMMUNITY (1 << 0) /* send-community */ +#define PEER_FLAG_SEND_EXT_COMMUNITY (1 << 1) /* send-community ext. */ +#define PEER_FLAG_NEXTHOP_SELF (1 << 2) /* next-hop-self */ +#define PEER_FLAG_REFLECTOR_CLIENT (1 << 3) /* reflector-client */ +#define PEER_FLAG_RSERVER_CLIENT (1 << 4) /* route-server-client */ +#define PEER_FLAG_SOFT_RECONFIG (1 << 5) /* soft-reconfiguration */ +#define PEER_FLAG_AS_PATH_UNCHANGED (1 << 6) /* transparent-as */ +#define PEER_FLAG_NEXTHOP_UNCHANGED (1 << 7) /* transparent-next-hop */ +#define PEER_FLAG_MED_UNCHANGED (1 << 8) /* transparent-next-hop */ +#define PEER_FLAG_DEFAULT_ORIGINATE (1 << 9) /* default-originate */ +#define PEER_FLAG_REMOVE_PRIVATE_AS (1 << 10) /* remove-private-as */ +#define PEER_FLAG_ALLOWAS_IN (1 << 11) /* set allowas-in */ +#define PEER_FLAG_ORF_PREFIX_SM (1 << 12) /* orf capability send-mode */ +#define PEER_FLAG_ORF_PREFIX_RM (1 << 13) /* orf capability receive-mode */ +#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; + + /* default-originate route-map. */ + struct + { + char *name; + struct route_map *map; + } default_rmap[AFI_MAX][SAFI_MAX]; + + /* Peer status flags. */ + u_int16_t sflags; +#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_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 */ + + /* Peer status af flags (reset in bgp_stop) */ + u_int16_t af_sflags[AFI_MAX][SAFI_MAX]; +#define PEER_STATUS_ORF_PREFIX_SEND (1 << 0) /* prefix-list send peer */ +#define PEER_STATUS_ORF_WAIT_REFRESH (1 << 1) /* wait refresh received peer */ +#define PEER_STATUS_DEFAULT_ORIGINATE (1 << 2) /* default-originate peer */ +#define PEER_STATUS_PREFIX_THRESHOLD (1 << 3) /* exceed prefix-threshold */ +#define PEER_STATUS_PREFIX_LIMIT (1 << 4) /* exceed prefix-limit */ +#define PEER_STATUS_EOR_SEND (1 << 5) /* end-of-rib send to peer */ +#define PEER_STATUS_EOR_RECEIVED (1 << 6) /* end-of-rib received from peer */ + + /* Default attribute value for the peer. */ + u_int32_t config; +#define PEER_CONFIG_WEIGHT (1 << 0) /* Default weight. */ +#define PEER_CONFIG_TIMER (1 << 1) /* keepalive & holdtime */ +#define PEER_CONFIG_CONNECT (1 << 2) /* connect */ +#define PEER_CONFIG_ROUTEADV (1 << 3) /* route advertise */ + u_int32_t weight; + u_int32_t holdtime; + u_int32_t keepalive; + u_int32_t connect; + u_int32_t routeadv; + + /* Timer values. */ + u_int32_t v_start; + u_int32_t v_connect; + u_int32_t v_holdtime; + u_int32_t v_keepalive; + u_int32_t v_routeadv; + u_int32_t v_pmax_restart; + u_int32_t v_gr_restart; + + /* Threads. */ + struct thread *t_read; + struct thread *t_write; + struct thread *t_start; + struct thread *t_connect; + struct thread *t_holdtime; + struct thread *t_keepalive; + struct thread *t_routeadv; + struct thread *t_pmax_restart; + struct thread *t_gr_restart; + struct thread *t_gr_stale; + + /* workqueues */ + struct work_queue *clear_node_queue; + + /* Statistics field */ + u_int32_t open_in; /* Open message input count */ + u_int32_t open_out; /* Open message output count */ + u_int32_t update_in; /* Update message input count */ + u_int32_t update_out; /* Update message ouput count */ + time_t update_time; /* Update message received time. */ + u_int32_t keepalive_in; /* Keepalive input count */ + u_int32_t keepalive_out; /* Keepalive output count */ + u_int32_t notify_in; /* Notify input count */ + u_int32_t notify_out; /* Notify output count */ + u_int32_t refresh_in; /* Route Refresh input count */ + u_int32_t refresh_out; /* Route Refresh output count */ + u_int32_t dynamic_cap_in; /* Dynamic Capability input count. */ + u_int32_t dynamic_cap_out; /* Dynamic Capability output count. */ + + /* BGP state count */ + u_int32_t established; /* Established */ + u_int32_t dropped; /* Dropped */ + + /* Syncronization list and time. */ + struct bgp_synchronize *sync[AFI_MAX][SAFI_MAX]; + time_t synctime; + + /* Send prefix count. */ + unsigned long scount[AFI_MAX][SAFI_MAX]; + + /* Announcement attribute hash. */ + struct hash *hash[AFI_MAX][SAFI_MAX]; + + /* Notify data. */ + struct bgp_notify notify; + + /* Whole packet size to be read. */ + unsigned long packet_size; + + /* Filter structure. */ + struct bgp_filter filter[AFI_MAX][SAFI_MAX]; + + /* ORF Prefix-list */ + struct prefix_list *orf_plist[AFI_MAX][SAFI_MAX]; + + /* Prefix count. */ + unsigned long pcount[AFI_MAX][SAFI_MAX]; + + /* Max prefix count. */ + unsigned long pmax[AFI_MAX][SAFI_MAX]; + u_char pmax_threshold[AFI_MAX][SAFI_MAX]; + u_int16_t pmax_restart[AFI_MAX][SAFI_MAX]; +#define MAXIMUM_PREFIX_THRESHOLD_DEFAULT 75 + + /* allowas-in. */ + char allowas_in[AFI_MAX][SAFI_MAX]; + + /* peer reset cause */ + char last_reset; +#define PEER_DOWN_RID_CHANGE 1 /* bgp router-id command */ +#define PEER_DOWN_REMOTE_AS_CHANGE 2 /* neighbor remote-as command */ +#define PEER_DOWN_LOCAL_AS_CHANGE 3 /* neighbor local-as command */ +#define PEER_DOWN_CLID_CHANGE 4 /* bgp cluster-id command */ +#define PEER_DOWN_CONFED_ID_CHANGE 5 /* bgp confederation identifier command */ +#define PEER_DOWN_CONFED_PEER_CHANGE 6 /* bgp confederation peer command */ +#define PEER_DOWN_RR_CLIENT_CHANGE 7 /* neighbor route-reflector-client command */ +#define PEER_DOWN_RS_CLIENT_CHANGE 8 /* neighbor route-server-client command */ +#define PEER_DOWN_UPDATE_SOURCE_CHANGE 9 /* neighbor update-source command */ +#define PEER_DOWN_AF_ACTIVATE 10 /* neighbor activate command */ +#define PEER_DOWN_USER_SHUTDOWN 11 /* neighbor shutdown command */ +#define PEER_DOWN_USER_RESET 12 /* clear ip bgp command */ +#define PEER_DOWN_NOTIFY_RECEIVED 13 /* notification received */ +#define PEER_DOWN_NOTIFY_SEND 14 /* notification send */ +#define PEER_DOWN_CLOSE_SESSION 15 /* tcp session close */ +#define PEER_DOWN_NEIGHBOR_DELETE 16 /* neghbor delete */ +#define PEER_DOWN_RMAP_BIND 17 /* neghbor peer-group command */ +#define PEER_DOWN_RMAP_UNBIND 18 /* no neighbor peer-group command */ +#define PEER_DOWN_CAPABILITY_CHANGE 19 /* neighbor capability command */ +#define PEER_DOWN_PASSIVE_CHANGE 20 /* neighbor passive command */ +#define PEER_DOWN_MULTIHOP_CHANGE 21 /* neighbor multihop command */ +#define PEER_DOWN_NSF_CLOSE_SESSION 22 /* NSF tcp session close */ + + /* The kind of route-map Flags.*/ + u_char rmap_type; +#define PEER_RMAP_TYPE_IN (1 << 0) /* neighbor route-map in */ +#define PEER_RMAP_TYPE_OUT (1 << 1) /* neighbor route-map out */ +#define PEER_RMAP_TYPE_NETWORK (1 << 2) /* network route-map */ +#define PEER_RMAP_TYPE_REDISTRIBUTE (1 << 3) /* redistribute route-map */ +#define PEER_RMAP_TYPE_DEFAULT (1 << 4) /* default-originate route-map */ +#define PEER_RMAP_TYPE_NOSET (1 << 5) /* not allow to set commands */ +#define PEER_RMAP_TYPE_IMPORT (1 << 6) /* neighbor route-map import */ +#define PEER_RMAP_TYPE_EXPORT (1 << 7) /* neighbor route-map export */ +}; + +#define PEER_PASSWORD_MINLEN (1) +#define PEER_PASSWORD_MAXLEN (80) + +/* This structure's member directly points incoming packet data + stream. */ +struct bgp_nlri +{ + /* AFI. */ + afi_t afi; + + /* SAFI. */ + safi_t safi; + + /* Pointer to NLRI byte stream. */ + u_char *nlri; + + /* Length of whole NLRI. */ + bgp_size_t length; +}; + +/* BGP versions. */ +#define BGP_VERSION_4 4 + +/* Default BGP port number. */ +#define BGP_PORT_DEFAULT 179 + +/* BGP message header and packet size. */ +#define BGP_MARKER_SIZE 16 +#define BGP_HEADER_SIZE 19 +#define BGP_MAX_PACKET_SIZE 4096 + +/* BGP minimum message size. */ +#define BGP_MSG_OPEN_MIN_SIZE (BGP_HEADER_SIZE + 10) +#define BGP_MSG_UPDATE_MIN_SIZE (BGP_HEADER_SIZE + 4) +#define BGP_MSG_NOTIFY_MIN_SIZE (BGP_HEADER_SIZE + 2) +#define BGP_MSG_KEEPALIVE_MIN_SIZE (BGP_HEADER_SIZE + 0) +#define BGP_MSG_ROUTE_REFRESH_MIN_SIZE (BGP_HEADER_SIZE + 4) +#define BGP_MSG_CAPABILITY_MIN_SIZE (BGP_HEADER_SIZE + 3) + +/* BGP message types. */ +#define BGP_MSG_OPEN 1 +#define BGP_MSG_UPDATE 2 +#define BGP_MSG_NOTIFY 3 +#define BGP_MSG_KEEPALIVE 4 +#define BGP_MSG_ROUTE_REFRESH_NEW 5 +#define BGP_MSG_CAPABILITY 6 +#define BGP_MSG_ROUTE_REFRESH_OLD 128 + +/* BGP open optional parameter. */ +#define BGP_OPEN_OPT_AUTH 1 +#define BGP_OPEN_OPT_CAP 2 + +/* BGP4 attribute type codes. */ +#define BGP_ATTR_ORIGIN 1 +#define BGP_ATTR_AS_PATH 2 +#define BGP_ATTR_NEXT_HOP 3 +#define BGP_ATTR_MULTI_EXIT_DISC 4 +#define BGP_ATTR_LOCAL_PREF 5 +#define BGP_ATTR_ATOMIC_AGGREGATE 6 +#define BGP_ATTR_AGGREGATOR 7 +#define BGP_ATTR_COMMUNITIES 8 +#define BGP_ATTR_ORIGINATOR_ID 9 +#define BGP_ATTR_CLUSTER_LIST 10 +#define BGP_ATTR_DPA 11 +#define BGP_ATTR_ADVERTISER 12 +#define BGP_ATTR_RCID_PATH 13 +#define BGP_ATTR_MP_REACH_NLRI 14 +#define BGP_ATTR_MP_UNREACH_NLRI 15 +#define BGP_ATTR_EXT_COMMUNITIES 16 +#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 +#define BGP_ORIGIN_EGP 1 +#define BGP_ORIGIN_INCOMPLETE 2 + +/* BGP notify message codes. */ +#define BGP_NOTIFY_HEADER_ERR 1 +#define BGP_NOTIFY_OPEN_ERR 2 +#define BGP_NOTIFY_UPDATE_ERR 3 +#define BGP_NOTIFY_HOLD_ERR 4 +#define BGP_NOTIFY_FSM_ERR 5 +#define BGP_NOTIFY_CEASE 6 +#define BGP_NOTIFY_CAPABILITY_ERR 7 +#define BGP_NOTIFY_MAX 8 + +#define BGP_NOTIFY_SUBCODE_UNSPECIFIC 0 + +/* BGP_NOTIFY_HEADER_ERR sub codes. */ +#define BGP_NOTIFY_HEADER_NOT_SYNC 1 +#define BGP_NOTIFY_HEADER_BAD_MESLEN 2 +#define BGP_NOTIFY_HEADER_BAD_MESTYPE 3 +#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 +#define BGP_NOTIFY_OPEN_UNSUP_PARAM 4 +#define BGP_NOTIFY_OPEN_AUTH_FAILURE 5 +#define BGP_NOTIFY_OPEN_UNACEP_HOLDTIME 6 +#define BGP_NOTIFY_OPEN_UNSUP_CAPBL 7 +#define BGP_NOTIFY_OPEN_MAX 8 + +/* BGP_NOTIFY_UPDATE_ERR sub codes. */ +#define BGP_NOTIFY_UPDATE_MAL_ATTR 1 +#define BGP_NOTIFY_UPDATE_UNREC_ATTR 2 +#define BGP_NOTIFY_UPDATE_MISS_ATTR 3 +#define BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR 4 +#define BGP_NOTIFY_UPDATE_ATTR_LENG_ERR 5 +#define BGP_NOTIFY_UPDATE_INVAL_ORIGIN 6 +#define BGP_NOTIFY_UPDATE_AS_ROUTE_LOOP 7 +#define BGP_NOTIFY_UPDATE_INVAL_NEXT_HOP 8 +#define BGP_NOTIFY_UPDATE_OPT_ATTR_ERR 9 +#define BGP_NOTIFY_UPDATE_INVAL_NETWORK 10 +#define BGP_NOTIFY_UPDATE_MAL_AS_PATH 11 +#define BGP_NOTIFY_UPDATE_MAX 12 + +/* BGP_NOTIFY_CEASE sub codes (RFC 4486). */ +#define BGP_NOTIFY_CEASE_MAX_PREFIX 1 +#define BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN 2 +#define BGP_NOTIFY_CEASE_PEER_UNCONFIG 3 +#define BGP_NOTIFY_CEASE_ADMIN_RESET 4 +#define BGP_NOTIFY_CEASE_CONNECT_REJECT 5 +#define BGP_NOTIFY_CEASE_CONFIG_CHANGE 6 +#define BGP_NOTIFY_CEASE_COLLISION_RESOLUTION 7 +#define BGP_NOTIFY_CEASE_OUT_OF_RESOURCE 8 +#define BGP_NOTIFY_CEASE_MAX 9 + +/* BGP_NOTIFY_CAPABILITY_ERR sub codes (draft-ietf-idr-dynamic-cap-02). */ +#define BGP_NOTIFY_CAPABILITY_INVALID_ACTION 1 +#define BGP_NOTIFY_CAPABILITY_INVALID_LENGTH 2 +#define BGP_NOTIFY_CAPABILITY_MALFORMED_CODE 3 +#define BGP_NOTIFY_CAPABILITY_MAX 4 + +/* BGP finite state machine status. */ +#define Idle 1 +#define Connect 2 +#define Active 3 +#define OpenSent 4 +#define OpenConfirm 5 +#define Established 6 +#define Clearing 7 +#define Deleted 8 +#define BGP_STATUS_MAX 9 + +/* BGP finite state machine events. */ +#define BGP_Start 1 +#define BGP_Stop 2 +#define TCP_connection_open 3 +#define TCP_connection_closed 4 +#define TCP_connection_open_failed 5 +#define TCP_fatal_error 6 +#define ConnectRetry_timer_expired 7 +#define Hold_Timer_expired 8 +#define KeepAlive_timer_expired 9 +#define Receive_OPEN_message 10 +#define Receive_KEEPALIVE_message 11 +#define Receive_UPDATE_message 12 +#define Receive_NOTIFICATION_message 13 +#define Clearing_Completed 14 +#define BGP_Stop_with_error 15 +#define BGP_EVENTS_MAX 16 + +/* BGP timers default value. */ +#define BGP_INIT_START_TIMER 1 +#define BGP_DEFAULT_HOLDTIME 180 +#define BGP_DEFAULT_KEEPALIVE 60 +#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 + +/* BGP graceful restart */ +#define BGP_DEFAULT_RESTART_TIME 120 +#define BGP_DEFAULT_STALEPATH_TIME 360 + +/* RFC4364 */ +#define SAFI_MPLS_LABELED_VPN 128 + +/* Max TTL value. */ +#define TTL_MAX 255 + +/* BGP uptime string length. */ +#define BGP_UPTIME_LEN 25 + +/* Default configuration settings for bgpd. */ +#define BGP_VTY_PORT 2605 +#define BGP_DEFAULT_CONFIG "bgpd.conf" + +/* Check AS path loop when we send NLRI. */ +/* #define BGP_SEND_ASPATH_CHECK */ + +/* Flag for peer_clear_soft(). */ +enum bgp_clear_type +{ + BGP_CLEAR_SOFT_NONE, + BGP_CLEAR_SOFT_OUT, + BGP_CLEAR_SOFT_IN, + BGP_CLEAR_SOFT_BOTH, + BGP_CLEAR_SOFT_IN_ORF_PREFIX, + BGP_CLEAR_SOFT_RSCLIENT +}; + +/* Macros. */ +#define BGP_INPUT(P) ((P)->ibuf) +#define BGP_INPUT_PNT(P) (STREAM_PNT(BGP_INPUT(P))) +#define BGP_IS_VALID_STATE_FOR_NOTIF(S)\ + (((S) == OpenSent) || ((S) == OpenConfirm) || ((S) == Established)) + +/* BGP error codes. */ +#define BGP_SUCCESS 0 +#define BGP_ERR_INVALID_VALUE -1 +#define BGP_ERR_INVALID_FLAG -2 +#define BGP_ERR_INVALID_AS -3 +#define BGP_ERR_INVALID_BGP -4 +#define BGP_ERR_PEER_GROUP_MEMBER -5 +#define BGP_ERR_MULTIPLE_INSTANCE_USED -6 +#define BGP_ERR_PEER_GROUP_MEMBER_EXISTS -7 +#define BGP_ERR_PEER_BELONGS_TO_GROUP -8 +#define BGP_ERR_PEER_GROUP_AF_UNCONFIGURED -9 +#define BGP_ERR_PEER_GROUP_NO_REMOTE_AS -10 +#define BGP_ERR_PEER_GROUP_CANT_CHANGE -11 +#define BGP_ERR_PEER_GROUP_MISMATCH -12 +#define BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT -13 +#define BGP_ERR_MULTIPLE_INSTANCE_NOT_SET -14 +#define BGP_ERR_AS_MISMATCH -15 +#define BGP_ERR_PEER_INACTIVE -16 +#define BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER -17 +#define BGP_ERR_PEER_GROUP_HAS_THE_FLAG -18 +#define BGP_ERR_PEER_FLAG_CONFLICT -19 +#define BGP_ERR_PEER_GROUP_SHUTDOWN -20 +#define BGP_ERR_PEER_FILTER_CONFLICT -21 +#define BGP_ERR_NOT_INTERNAL_PEER -22 +#define BGP_ERR_REMOVE_PRIVATE_AS -23 +#define BGP_ERR_AF_UNCONFIGURED -24 +#define BGP_ERR_SOFT_RECONFIG_UNCONFIGURED -25 +#define BGP_ERR_INSTANCE_MISMATCH -26 +#define BGP_ERR_LOCAL_AS_ALLOWED_ONLY_FOR_EBGP -27 +#define BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS -28 +#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_CANNOT_HAVE_LOCAL_AS_SAME_AS_REMOTE_AS -32 +#define BGP_ERR_MAX -33 + +extern struct bgp_master *bm; + +/* Prototypes. */ +extern void bgp_terminate (void); +extern void bgp_reset (void); +extern time_t bgp_clock (void); +extern void bgp_zclient_reset (void); +extern int bgp_nexthop_set (union sockunion *, union sockunion *, + struct bgp_nexthop *, struct peer *); +extern struct bgp *bgp_get_default (void); +extern struct bgp *bgp_lookup (as_t, const char *); +extern struct bgp *bgp_lookup_by_name (const char *); +extern struct peer *peer_lookup (struct bgp *, union sockunion *); +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 *); + +/* + * 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); +extern void bgp_route_map_init (void); + +extern int bgp_option_set (int); +extern int bgp_option_unset (int); +extern int bgp_option_check (int); + +extern int bgp_get (struct bgp **, as_t *, const char *); +extern int bgp_delete (struct bgp *); + +extern int bgp_flag_set (struct bgp *, int); +extern int bgp_flag_unset (struct bgp *, int); +extern int bgp_flag_check (struct bgp *, int); + +extern void bgp_lock (struct bgp *); +extern void bgp_unlock (struct bgp *); + +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 *); + +extern int bgp_confederation_id_set (struct bgp *, as_t); +extern int bgp_confederation_id_unset (struct bgp *); +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 keepalive, u_int32_t holdtime); +extern int bgp_timers_unset (struct bgp *); + +extern int bgp_default_local_preference_set (struct bgp *, u_int32_t); +extern int bgp_default_local_preference_unset (struct bgp *); + +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 *); +extern int peer_group_unbind (struct bgp *, struct peer *, struct peer_group *, + afi_t, safi_t); + +extern int peer_flag_set (struct peer *, u_int32_t); +extern int peer_flag_unset (struct peer *, u_int32_t); + +extern int peer_af_flag_set (struct peer *, afi_t, safi_t, u_int32_t); +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_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 *, 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 *); +extern int peer_default_originate_unset (struct peer *, afi_t, safi_t); + +extern int peer_port_set (struct peer *, u_int16_t); +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 keepalive, u_int32_t holdtime); +extern int peer_timers_unset (struct peer *); + +extern int peer_timers_connect_set (struct peer *, u_int32_t); +extern int peer_timers_connect_unset (struct peer *); + +extern int peer_advertise_interval_set (struct peer *, u_int32_t); +extern int peer_advertise_interval_unset (struct peer *); + +extern int peer_interface_set (struct peer *, const char *); +extern int peer_interface_unset (struct peer *); + +extern int peer_distribute_set (struct peer *, afi_t, safi_t, int, const char *); +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, int); +extern int peer_local_as_unset (struct peer *); + +extern int peer_prefix_list_set (struct peer *, afi_t, safi_t, int, const char *); +extern int peer_prefix_list_unset (struct peer *, afi_t, safi_t, int); + +extern int peer_aslist_set (struct peer *, afi_t, safi_t, int, const char *); +extern int peer_aslist_unset (struct peer *,afi_t, safi_t, int); + +extern int peer_route_map_set (struct peer *, afi_t, safi_t, int, const char *); +extern int peer_route_map_unset (struct peer *, afi_t, safi_t, int); + +extern int peer_unsuppress_map_set (struct peer *, afi_t, safi_t, const char *); + +extern int peer_password_set (struct peer *, const char *); +extern int peer_password_unset (struct peer *); + +extern int peer_unsuppress_map_unset (struct peer *, afi_t, safi_t); + +extern int peer_maximum_prefix_set (struct peer *, afi_t, safi_t, u_int32_t, u_char, int, u_int16_t); +extern int peer_maximum_prefix_unset (struct peer *, afi_t, safi_t); + +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 void bgp_scan_finish (void); +#endif /* _QUAGGA_BGPD_H */ diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100755 index 0000000..c0b95d3 --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +# This file exists to document the proper way to initialize autotools, +# and so that those used to the presence of bootstrap.sh or autogen.sh +# will have an eaiser time. + +autoreconf -i diff --git a/buildtest.sh b/buildtest.sh new file mode 100755 index 0000000..04fc2cc --- /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 0000000..ac7a323 --- /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 new file mode 100755 index 0000000..ae292f1 --- /dev/null +++ b/configure.ac @@ -0,0 +1,1713 @@ +## +## Configure template file for Quagga. +## autoconf will generate configure script. +## +## Copyright (c) 1996, 97, 98, 99, 2000 Kunihiro Ishiguro +## Portions Copyright (c) 2003 Paul Jakma +## +AC_PREREQ(2.60) + +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]) + +dnl ----------------------------------- +dnl Get hostname and other information. +dnl ----------------------------------- +AC_CANONICAL_BUILD() +AC_CANONICAL_HOST() +AC_CANONICAL_TARGET() + +# 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. +BSD awk complains: awk: gensub doesn't support backreferences (subst "\1") ]) +fi +AC_ARG_VAR([GAWK],[GNU AWK]) + +dnl default is to match previous behavior +exampledir=${sysconfdir} +AC_ARG_ENABLE([exampledir], + AS_HELP_STRING([--enable-exampledir], + [specify alternate directory for examples]), + exampledir="$enableval",) +dnl XXX add --exampledir to autoconf standard directory list somehow +AC_SUBST(exampledir) + +dnl default is to match previous behavior +pkgsrcrcdir="" +pkgsrcdir="" +AC_ARG_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 +AC_SUBST(pkgsrcdir) +AC_SUBST(pkgsrcrcdir) + +dnl ------------ +dnl Check CFLAGS +dnl ------------ +AC_ARG_WITH(cflags, +[ --with-cflags Set CFLAGS for use in compilation.]) +if test "x$with_cflags" != "x" ; then + CFLAGS="$with_cflags" ; cflags_specified=yes ; +elif test -n "$CFLAGS" ; then + cflags_specified=yes ; +fi + +dnl -------------------- +dnl Check CC and friends +dnl -------------------- +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 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 + AC_MSG_WARN([Will not be able to make PDF versions of TeX documents]) +else + HAVE_LATEX=true +fi +AM_CONDITIONAL([HAVE_LATEX], [test "x$HAVE_LATEX" = "xtrue"]) +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])], + [COMPILER="SUNPRO" + AC_MSG_RESULT([yes])] + ) +fi + +dnl --------------------------------------------- +dnl If CLFAGS doesn\'t exist set default value +dnl AC_PROG_CC will have set minimal default +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 Sun Studio 10 / SunPro 5.7 is also supported, +dnl so lets set some sane CFLAGS for it. +dnl --------------------------------------------- + +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 + "SUNPRO") + 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([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 -------------- +AC_PROG_INSTALL +AC_PROG_LN_S +AC_PROG_MAKE_SET +AC_CHECK_TOOL(AR, ar) + +dnl --------------------------- +dnl We, perhaps unfortunately, +dnl depend on GNU Make specific +dnl constructs. +dnl Give the user a warning if +dnl not GNU Make. +dnl --------------------------- +AC_CACHE_CHECK([if ${MAKE-make} is GNU make], [quagga_cv_gnu_make], + [quagga_cv_gnu_make=no + if ${MAKE-make} --version 2>/dev/null | \ + grep '^GNU Make ' >/dev/null ; then + quagga_cv_gnu_make=yes; + fi + ] +) + +dnl ----------------- +dnl System extensions +dnl ----------------- +AC_GNU_SOURCE + + +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, + AS_HELP_STRING([--disable-vtysh], [do not build integrated vty shell for Quagga])) +AC_ARG_ENABLE(doc, + AS_HELP_STRING([--disable-doc], [do not build docs])) +AC_ARG_ENABLE(zebra, + AS_HELP_STRING([--disable-zebra], [do not build zebra daemon])) +AC_ARG_ENABLE(bgpd, + AS_HELP_STRING([--disable-bgpd], [do not build bgpd])) +AC_ARG_ENABLE(ripd, + AS_HELP_STRING([--disable-ripd], [do not build ripd])) +AC_ARG_ENABLE(ripngd, + AS_HELP_STRING([--disable-ripngd], [do not build ripngd])) +AC_ARG_ENABLE(ospfd, + AS_HELP_STRING([--disable-ospfd], [do not build ospfd])) +AC_ARG_ENABLE(ospf6d, + 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, + AS_HELP_STRING([--disable-watchquagga], [do not build watchquagga])) +AC_ARG_ENABLE(isisd, + 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, + AS_HELP_STRING([--disable-bgp-announce,], [turn off BGP route announcement])) +AC_ARG_ENABLE(snmp, + AS_HELP_STRING([--enable-snmp=ARG], [enable SNMP support (smux or agentx)])) +AC_ARG_WITH(libpam, + AS_HELP_STRING([--with-libpam], [use libpam for PAM support in vtysh])) +AC_ARG_ENABLE(tcp-zebra, + AS_HELP_STRING([--enable-tcp-zebra], [enable TCP/IP socket connection between zebra and protocol daemon])) +AC_ARG_ENABLE(ospfapi, + AS_HELP_STRING([--disable-ospfapi], [do not build OSPFAPI to access the OSPF LSA Database])) +AC_ARG_ENABLE(ospfclient, + 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, + AS_HELP_STRING([--enable-multipath=ARG], [enable multipath function, ARG must be digit])) +AC_ARG_ENABLE(user, + AS_HELP_STRING([--enable-user=USER], [user to run Quagga suite as (default quagga)])) +AC_ARG_ENABLE(group, + AS_HELP_STRING([--enable-group=GROUP], [group to run Quagga suite as (default quagga)])) +AC_ARG_ENABLE(vty_group, + AS_HELP_STRING([--enable-vty-group=ARG], [set vty sockets to have specified group as owner])) +AC_ARG_ENABLE(configfile_mask, + AS_HELP_STRING([--enable-configfile-mask=ARG], [set mask for config files])) +AC_ARG_ENABLE(logfile_mask, + AS_HELP_STRING([--enable-logfile-mask=ARG], [set mask for log files])) + +AC_ARG_ENABLE(rtadv, + AS_HELP_STRING([--disable-rtadv], [disable IPV6 router advertisement feature])) +AC_ARG_ENABLE(irdp, + AS_HELP_STRING([--enable-irdp], [enable IRDP server support in zebra])) +AC_ARG_ENABLE(isis_topology, + AS_HELP_STRING([--enable-isis-topology], [enable IS-IS topology generator])) +AC_ARG_ENABLE(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, + AS_HELP_STRING([--enable-gcc-ultra-verbose], [enable ultra verbose GCC warnings])) +AC_ARG_ENABLE(linux24_tcp_md5, + AS_HELP_STRING([--enable-linux24-tcp-md5], [enable support for old, Linux-2.4 RFC2385 patch])) +AC_ARG_ENABLE(gcc-rdynamic, + 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, + AS_HELP_STRING([--disable-time-check], [disable slow thread warning messages])) +AC_ARG_ENABLE(pcreposix, + 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 + if test x"${enable_time_check}" = x"yes" -o x"${enable_time_check}" = x ; then + AC_DEFINE(CONSUMED_TIME_CHECK,5000000,Consumed Time Check) + else + AC_DEFINE_UNQUOTED(CONSUMED_TIME_CHECK,$enable_time_check,Consumed Time Check) + fi +fi + +if test "${enable_fpm}" = "yes"; then + AC_DEFINE(HAVE_FPM,,Forwarding Plane Manager support) +fi + +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 + +# 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 "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 + AC_DEFINE(HAVE_TCP_MD5_LINUX24,,Old Linux 2.4 TCP MD5 Signature Patch) +fi + +AC_MSG_CHECKING(if zebra should be configurable to send Route Advertisements) +if test "${enable_rtadv}" != "no"; then + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_RTADV,,Enable IPv6 Routing Advertisement support) +else + AC_MSG_RESULT(no) +fi + +if test "${enable_irdp}" = "yes"; then + AC_DEFINE(HAVE_IRDP,, IRDP ) +fi + +if test "${enable_isisd}" != "no" && test "${enable_isis_topology}" = yes; then + AC_DEFINE(TOPOLOGY_GENERATE,,Enable IS-IS topology generator code) + ISIS_TOPOLOGY_INCLUDES="-I\$(srcdir)/topology" + ISIS_TOPOLOGY_DIR="topology" + ISIS_TOPOLOGY_LIB="./topology/libtopology.a" +fi + +AC_SUBST(ISIS_TOPOLOGY_INCLUDES) +AC_SUBST(ISIS_TOPOLOGY_DIR) +AC_SUBST(ISIS_TOPOLOGY_LIB) + +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 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 + AC_MSG_ERROR([--enable-vty-group requires a group as argument, not yes]) +elif test x"${enable_vty_group}" != x""; then + if test x"${enable_vty_group}" != x"no"; then + AC_DEFINE_UNQUOTED(VTY_GROUP, "${enable_vty_group}", VTY Sockets Group) + fi +fi +AC_SUBST([enable_user]) +AC_SUBST([enable_group]) +AC_SUBST([enable_vty_group]) + +enable_configfile_mask=${enable_configfile_mask:-0600} +AC_DEFINE_UNQUOTED(CONFIGFILE_MASK, ${enable_configfile_mask}, Mask for config files) + +enable_logfile_mask=${enable_logfile_mask:-0600} +AC_DEFINE_UNQUOTED(LOGFILE_MASK, ${enable_logfile_mask}, Mask for log files) + +MPATH_NUM=1 + +case "${enable_multipath}" in + 0) + MPATH_NUM=64 + ;; + [[1-9]|[1-9][0-9]|[1-9][0-9][0-9]]) + MPATH_NUM="${enable_multipath}" + ;; + "") + ;; + *) + AC_MSG_FAILURE([Please specify digit to enable multipath ARG]) + ;; +esac + +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 +dnl ------------------------------------ +AC_C_CONST +AC_C_INLINE +AC_C_RESTRICT +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_STRUCT_TM + +dnl ------------------------- +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/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], +[#ifdef SUNOS_5 +#define _XPG4_2 +#define __EXTENSIONS__ +#endif +#include +#if STDC_HEADERS +# include +# include +#else +# if HAVE_STDLIB_H +# include +# endif +#endif +#if HAVE_SYS_TYPES_H +# include +#endif +/* sys/conf.h depends on param.h on FBSD at least */ +#if HAVE_SYS_PARAM_H +# include +#endif +/* Required for MAXSIG */ +#if HAVE_SIGNAL_H +# include +#endif +#if HAVE_SYS_SOCKET_H +# include +#endif +#ifdef __APPLE__ +# define __APPLE_USE_RFC_3542 +#endif +#if HAVE_NETINET_IN_H +# include +#endif +#ifdef TIME_WITH_SYS_TIME +# include +# include +#else +# ifdef HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif /* TIME_WITH_SYS_TIME */ +])dnl + +dnl HAVE_NET_IF_H must be discovered by the time the longer AC_CHECK_HEADERS +dnl round below execution begins, otherwise it doesn't properly detect +dnl HAVE_NETINET6_IN6_VAR_H, HAVE_NET_IF_VAR_H and HAVE_STRUCT_IN6_ALIASREQ +dnl on FreeBSD (BZ#408). + +AC_CHECK_HEADERS([net/if.h], [], [], QUAGGA_INCLUDES) + +m4_define([QUAGGA_INCLUDES], +QUAGGA_INCLUDES +[#if HAVE_NET_IF_H +# include +#endif +])dnl + +dnl Same applies for HAVE_NET_IF_VAR_H, which HAVE_NETINET6_ND6_H and +dnl HAVE_NETINET_IN_VAR_H depend upon. But if_var.h depends on if.h, hence +dnl an additional round for it. + +AC_CHECK_HEADERS([net/if_var.h], [], [], QUAGGA_INCLUDES) + +m4_define([QUAGGA_INCLUDES], +QUAGGA_INCLUDES +[#if HAVE_NET_IF_VAR_H +# include +#endif +])dnl + +AC_CHECK_HEADERS([sys/un.h netinet/in_systm.h netinet/in_var.h \ + net/if_dl.h net/netopt.h net/route.h \ + inet/nd.h arpa/inet.h netinet/ip_icmp.h \ + fcntl.h stddef.h sys/ioctl.h syslog.h wchar.h wctype.h \ + sys/sysctl.h sys/sockio.h kvm.h sys/conf.h], + [], [], QUAGGA_INCLUDES) + +AC_CHECK_HEADERS([ucontext.h], [], [], +[#ifndef __USE_GNU +#define __USE_GNU +#endif /* __USE_GNU */ +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 +# include +#endif +#if HAVE_NETINET_IN_SYSTM_H +# include +#endif +#if HAVE_NETINET_IN_VAR_H +# include +#endif +#if HAVE_NET_IF_DL_H +# include +#endif +#if HAVE_NET_NETOPT_H +# include +#endif +#if HAVE_NET_ROUTE_H +# include +#endif +#if HAVE_INET_ND_H +# include +#endif +#if HAVE_ARPA_INET_H +# include +#endif +/* Required for IDRP */ +#if HAVE_NETINET_IP_ICMP_H +# include +#endif +])dnl + +dnl V6 headers are checked below, after we check for v6 + +dnl Some systems (Solaris 2.x) require libnsl (Network Services Library) +case "$host" in + [*-sunos5.[6-7]*] | [*-solaris2.[6-7]*]) + opsys=sol2-6 + AC_DEFINE(SUNOS_56, 1, SunOS 5.6 to 5.7) + AC_DEFINE(SUNOS_5, 1, SunOS 5) + AC_CHECK_LIB(xnet, main) + CURSES=-lcurses + SOLARIS="solaris" + ;; + [*-sunos5.[8-9]] \ + | [*-sunos5.1[0-9]] \ + | [*-sunos5.1[0-9].[0-9]] \ + | [*-solaris2.[8-9]] \ + | [*-solaris2.1[0-9]] \ + | [*-solaris2.1[0-9].[0-9]]) + opsys=sol8 + AC_DEFINE(SUNOS_59, 1, [SunOS 5.8 up]) + AC_DEFINE(SUNOS_5, 1, [SunOS 5]) + AC_CHECK_LIB(socket, main) + AC_CHECK_LIB(nsl, main) + AC_CHECK_LIB(umem, main) + AC_CHECK_FUNCS([printstack], + [AC_DEFINE([HAVE_PRINTSTACK],1,[Solaris printstack]) + 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) + ;; + *-openbsd*) + opsys=openbsd + AC_DEFINE(OPEN_BSD,,OpenBSD) + ;; +esac + +AC_SYS_LARGEFILE + +dnl --------------------- +dnl Integrated VTY option +dnl --------------------- +case "${enable_vtysh}" in + "no") VTYSH="";; + *) VTYSH="vtysh"; + AC_DEFINE(VTYSH,,VTY shell) +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 +dnl [TODO] on Linux, and in [TODO] on Solaris. + AC_CHECK_LIB(termcap, tputs, LIBREADLINE="$LIBREADLINE -ltermcap", + [AC_CHECK_LIB(tinfo, tputs, LIBREADLINE="$LIBREADLINE -ltinfo", + [AC_CHECK_LIB(curses, tputs, LIBREADLINE="$LIBREADLINE -lcurses", + [AC_CHECK_LIB(ncurses, tputs, + LIBREADLINE="$LIBREADLINE -lncurses")] + )] + )] + ) + 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.]) + fi + AC_CHECK_HEADER(readline/history.h) + if test $ac_cv_header_readline_history_h = no;then + AC_MSG_ERROR([readline is too old to have readline/history.h, please update to the latest readline library.]) + fi + AC_CHECK_LIB(readline, rl_completion_matches, + LIBREADLINE="$LIBREADLINE",, "$LIBREADLINE") + if test $ac_cv_lib_readline_rl_completion_matches = no; then + AC_DEFINE(rl_completion_matches,completion_matches,Old readline) + fi + ;; + "no" ) VTYSH="";; +esac +AC_SUBST(LIBREADLINE) +AM_CONDITIONAL(VTYSH, test "x$VTYSH" = "xvtysh") + +dnl ---------- +dnl PAM module +dnl +dnl Quagga detects the PAM library it is built against by checking for a +dnl functional pam_misc.h (Linux-PAM) or openpam.h (OpenPAM) header. pam_misc.h +dnl is known to #include pam_appl.h, the standard header of a PAM library, and +dnl openpam.h doesn't do that, although depends on the header too. Hence a +dnl little assistance to AC_CHECK_HEADER is necessary for the proper detection +dnl of OpenPAM. +dnl ---------- +if test "$with_libpam" = "yes"; then + AC_CHECK_HEADER([security/pam_misc.h], + [AC_DEFINE(HAVE_PAM_MISC_H,,Have pam_misc.h) + AC_DEFINE(PAM_CONV_FUNC,misc_conv,Have misc_conv) + pam_conv_func="misc_conv" + ], + [], QUAGGA_INCLUDES) + AC_CHECK_HEADER([security/openpam.h], + [AC_DEFINE(HAVE_OPENPAM_H,,Have openpam.h) + AC_DEFINE(PAM_CONV_FUNC,openpam_ttyconv,Have openpam_ttyconv) + pam_conv_func="openpam_ttyconv" + ], + [], QUAGGA_INCLUDES[#include ]) + if test -z "$ac_cv_header_security_pam_misc_h$ac_cv_header_security_openpam_h" ; then + AC_MSG_WARN([*** pam support will not be built ***]) + with_libpam="no" + fi +fi + +if test "$with_libpam" = "yes"; then +dnl took this test from proftpds configure.in and suited to our needs +dnl ------------------------------------------------------------------------- +dnl +dnl This next check looks funky due to a linker problem with some versions +dnl of the PAM library. Prior to 0.72 release, the Linux PAM shared library +dnl omitted requiring libdl linking information. PAM-0.72 or better ships +dnl with RedHat 6.2 and Debian 2.2 or better. +AC_CHECK_LIB(pam, pam_start, + [AC_CHECK_LIB(pam, $pam_conv_func, + [AC_DEFINE(USE_PAM,,Use PAM for authentication) + LIBPAM="-lpam"], + [AC_DEFINE(USE_PAM,,Use PAM for authentication) + LIBPAM="-lpam -lpam_misc"] + ) + ], + + [AC_CHECK_LIB(pam, pam_end, + [AC_CHECK_LIB(pam, $pam_conv_func, + [AC_DEFINE(USE_PAM,,Use PAM for authentication) + LIBPAM="-lpam -ldl"], + [AC_DEFINE(USE_PAM,,Use PAM for authentication) + LIBPAM="-lpam -ldl -lpam_misc"] + ) + ],AC_MSG_WARN([*** pam support will not be built ***]), + [-ldl]) + ] +) +fi +AC_SUBST(LIBPAM) + +dnl ------------------------------- +dnl Endian-ness check +dnl ------------------------------- +AC_WORDS_BIGENDIAN + +dnl ------------------------------- +dnl check the size in byte of the C +dnl ------------------------------- +dnl AC_CHECK_SIZEOF(char) +dnl AC_CHECK_SIZEOF(int) +dnl AC_CHECK_SIZEOF(short) +dnl AC_CHECK_SIZEOF(long) + +dnl ---------------------------- +dnl check existance of functions +dnl ---------------------------- +AC_FUNC_CHOWN +AC_FUNC_FNMATCH +AC_FUNC_FORK +AC_FUNC_MEMCMP +AC_FUNC_MKTIME +AC_FUNC_STRFTIME +AC_FUNC_STAT +AC_FUNC_SELECT_ARGTYPES +AC_FUNC_STRFTIME +dnl Avoid AC_FUNC_STRNLEN because it pulls in AC_SYSTEM_EXTENSIONS which +dnl can lead to strange side effects. So we just check for strnlen +dnl directly, see below. +dnl AC_FUNC_STRNLENdnl +AC_FUNC_VPRINTF + +dnl ------------------------------- +dnl bgpd needs pow() and hence libm +dnl ------------------------------- +TMPLIBS="$LIBS" +AC_CHECK_HEADER([math.h], + [AC_CHECK_LIB([m], [pow], + [LIBM="-lm" + LIBS="$LIBS $LIBM" + AC_DEFINE(HAVE_LIBM,, Have libm) + AC_CHECK_FUNCS(pow,[],[LIBM=""]) + ]) +]) +if test x"$LIBM" = x ; then + AC_MSG_WARN([Unable to find working pow function - bgpd may not link]) +fi +LIBS="$TMPLIBS" +AC_SUBST(LIBM) + +dnl --------------- +dnl other functions +dnl --------------- +AC_CHECK_FUNCS([dup2 ftruncate getcwd gethostbyname getpagesize gettimeofday \ + inet_ntoa inet_aton strnlen \ + memchr memmove memset select socket \ + strcasecmp strchr strcspn strdup strerror \ + strncasecmp strndup strrchr strspn strstr \ + strtol strtoul strlcat strlcpy \ + daemon snprintf vsnprintf \ + if_nametoindex if_indextoname getifaddrs \ + uname fcntl getgrouplist]) + + +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 + 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" +fi +AC_SUBST(RT_METHOD) +AC_SUBST(KERNEL_METHOD) +AM_CONDITIONAL([HAVE_NETLINK], [test "x$netlink" = "xyes"]) + +dnl -------------------------- +dnl Determine IS-IS I/O method +dnl -------------------------- +AC_DEFINE(ISIS_METHOD_PFPACKET, 1, [ constant value for isis method pfpacket ]) +AC_DEFINE(ISIS_METHOD_DLPI, 2, [ constant value for isis method dlpi ]) +AC_DEFINE(ISIS_METHOD_BPF, 3, [ constant value for isis method bpf ]) +AC_CHECK_HEADER(net/bpf.h) +AC_CHECK_HEADER(sys/dlpi.h) +AC_MSG_CHECKING(zebra IS-IS I/O method) +if test x"$opsys" = x"gnu-linux"; then + AC_MSG_RESULT(pfpacket) + ISIS_METHOD_MACRO="ISIS_METHOD_PFPACKET" +elif test x"$opsys" = x"sol2-6" -o x"$opsys" = x"sol8"; then + AC_MSG_RESULT(DLPI) + ISIS_METHOD_MACRO="ISIS_METHOD_DLPI" +else + if test $ac_cv_header_net_bpf_h = no; then + if test $ac_cv_header_sys_dlpi_h = no; then + AC_MSG_RESULT(none) + AC_MSG_WARN([*** IS-IS support will not be built ***]) + ISISD="" + else + AC_MSG_RESULT(DLPI) + fi + ISIS_METHOD_MACRO="ISIS_METHOD_DLPI" + else + AC_MSG_RESULT(BPF) + ISIS_METHOD_MACRO="ISIS_METHOD_BPF" + fi +fi +AC_DEFINE_UNQUOTED(ISIS_METHOD, $ISIS_METHOD_MACRO, [ selected method for isis, == one of the constants ]) + +dnl ------------------------------------ +dnl check for broken CMSG_FIRSTHDR macro +dnl ------------------------------------ +AC_MSG_CHECKING(for broken CMSG_FIRSTHDR) +AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#ifdef SUNOS_5 +#define _XPG4_2 +#define __EXTENSIONS__ +#endif +#ifdef HAVE_STDLIB_H +# include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +main() +{ + struct msghdr msg; + char buf[4]; + + msg.msg_control = buf; + msg.msg_controllen = 0; + + if (CMSG_FIRSTHDR(&msg) != NULL) + exit(0); + exit (1); +}]])],[AC_MSG_RESULT(yes - using workaround) AC_DEFINE(HAVE_BROKEN_CMSG_FIRSTHDR,,Broken CMSG_FIRSTHDR)], +[AC_MSG_RESULT(no)],[AC_MSG_RESULT(no)]) + +dnl ------------------------------ +dnl check kernel route read method +dnl ------------------------------ +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 /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 + "/dev/ip") + case "$host" in + *-freebsd*) quagga_cv_rtread_method="sysctl";; + *) quagga_cv_rtread_method="getmsg";; + esac;; + *) + quagga_cv_rtread_method="sysctl";; +esac +fi]) +RTREAD_METHOD=rtread_${quagga_cv_rtread_method}.o +AC_SUBST(RTREAD_METHOD) + +dnl ----------------------------- +dnl check interface lookup method +dnl ----------------------------- +IOCTL_METHOD=ioctl.o +AC_MSG_CHECKING(interface looking up method) +if test "$netlink" = yes; then + AC_MSG_RESULT(netlink) + IF_METHOD=if_netlink.o +elif test "$opsys" = "sol2-6";then + AC_MSG_RESULT(Solaris GIF) + IF_METHOD=if_ioctl.o +elif test "$opsys" = "sol8";then + AC_MSG_RESULT(Solaris GLIF) + IF_METHOD=if_ioctl_solaris.o + IOCTL_METHOD=ioctl_solaris.o +elif test "$opsys" = "openbsd";then + AC_MSG_RESULT(openbsd) + IF_METHOD=if_ioctl.o +elif grep NET_RT_IFLIST /usr/include/sys/socket.h >/dev/null 2>&1; then + AC_MSG_RESULT(sysctl) + IF_METHOD=if_sysctl.o + AC_DEFINE(HAVE_NET_RT_IFLIST,,NET_RT_IFLIST) +else + AC_MSG_RESULT(ioctl) + IF_METHOD=if_ioctl.o +fi +AC_SUBST(IF_METHOD) +AC_SUBST(IOCTL_METHOD) + +dnl --------------------------------------------------------------- +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) || defined(__OpenBSD__) || defined(__APPLE__) || defined(__DragonFly__) || defined(__sun) + return (0); +#else + #error No support for BSD struct ip_mreq hack detected +#endif],[AC_MSG_RESULT(yes) +AC_DEFINE(HAVE_BSD_STRUCT_IP_MREQ_HACK,,[Can pass ifindex in struct ip_mreq])], +AC_MSG_RESULT(no)) + +AC_MSG_CHECKING([for RFC3678 protocol-independed API]) +AC_TRY_COMPILE([ +#include +#include +], [struct group_req gr; int sock; setsockopt(sock, IPPROTO_IP, MCAST_JOIN_GROUP, (void*)&gr, sizeof(gr)); +], [AC_MSG_RESULT(yes) +AC_DEFINE(HAVE_RFC3678,1,[Have RFC3678 protocol-independed API])], +AC_MSG_RESULT(no)) + +dnl --------------------------------------------------------------- +dnl figure out how to check link-state +dnl --------------------------------------------------------------- +AC_CHECK_HEADER([net/if.h], + [AC_CHECK_HEADER( [net/if_media.h], + [m4_define([LINK_DETECT_INCLUDES], + QUAGGA_INCLUDES + [#include + ]) + AC_CHECK_MEMBERS( [struct ifmediareq.ifm_status], + AC_DEFINE(HAVE_BSD_LINK_DETECT,,[BSD link-detect]), + [], LINK_DETECT_INCLUDES)], + [], + 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 ------------------------ + +AC_CHECK_HEADER([netinet/tcp.h], + [m4_define([MD5_INCLUDES], + QUAGGA_INCLUDES + [#include + ]) + AC_CHECK_DECLS([TCP_MD5SIG], [], [], MD5_INCLUDES)], + [], + QUAGGA_INCLUDES) +if test $ac_cv_have_decl_TCP_MD5SIG = no; then + AC_CHECK_HEADER([linux/tcp.h], + [m4_define([MD5_INCLUDES], + QUAGGA_INCLUDES + [#include + ]) + AC_CHECK_DECLS([TCP_MD5SIG], [], [], MD5_INCLUDES)]) +fi + +dnl ----------------------------- +dnl check ipforward detect method +dnl ----------------------------- +AC_CACHE_CHECK([ipforward method], [quagga_cv_ipforward_method], +[if test x$cross_compiling = xyes; then + if test x"$opsys" = x"gnu-linux"; then + quagga_cv_ipforward_method=/proc/net/snmp + else + quagga_cv_ipforward_method=/dev/ip + fi +else + for quagga_cv_ipforward_method in /proc/net/snmp /dev/ip /dev/null; + do + test x`ls $quagga_cv_ipforward_method 2>/dev/null` = x"$quagga_cv_ipforward_method" && break + done +fi +case $quagga_cv_ipforward_method in + "/proc/net/snmp") quagga_cv_ipforward_method="proc";; + "/dev/ip") + case "$host" in + *-freebsd*) quagga_cv_ipforward_method="sysctl";; + *) quagga_cv_ipforward_method="solaris";; + esac;; + *) quagga_cv_ipforward_method="sysctl";; +esac]) +IPFORWARD=ipforward_${quagga_cv_ipforward_method}.o +AC_SUBST(IPFORWARD) + +AC_CHECK_FUNCS(getaddrinfo, [have_getaddrinfo=yes], [have_getaddrinfo=no]) + +dnl ---------- +dnl IPv6 check +dnl ---------- +AC_MSG_CHECKING(whether does this OS have IPv6 stack) +dnl --------- +dnl KAME IPv6 +dnl --------- + if grep WIDE /usr/include/netinet6/in6.h >/dev/null 2>&1; then + AC_DEFINE(KAME,1,KAME IPv6) + AC_MSG_RESULT(KAME) +dnl ------------------------------------ +dnl Solaris 9, 10 and potentially higher +dnl ------------------------------------ + elif test x"$opsys" = x"sol8"; then + AC_DEFINE(SOLARIS_IPV6, 1, Solaris IPv6) + AC_MSG_RESULT(Solaris IPv6) +dnl ---------- +dnl Linux IPv6 +dnl ---------- + elif test x"$opsys" = x"gnu-linux"; then + AC_DEFINE(LINUX_IPV6,1,Linux IPv6 stack) + AC_MSG_RESULT(Linux IPv6) + else + AC_MSG_ERROR([Failed to detect IPv6 stack]) + fi + +dnl this is unconditial, for compatibility +AC_DEFINE(HAVE_IPV6,1,IPv6) + +dnl ------------------ +dnl IPv6 header checks +dnl ------------------ +AC_CHECK_HEADERS([netinet6/in6.h netinet/in6_var.h netinet/icmp6.h \ + netinet6/in6_var.h netinet6/nd6.h], [], [], + QUAGGA_INCLUDES) + +m4_define([QUAGGA_INCLUDES],dnl +QUAGGA_INCLUDES +[#if HAVE_NETINET6_IN6_H +#include +#endif +#if HAVE_NETINET_IN6_VAR_H +#include +#endif +#if HAVE_NETINET_ICMP6_H +# include +#endif +#if HAVE_NETINET6_IN6_VAR_H +# include +#endif +#if HAVE_NETINET6_ND6_H +# include +#endif +])dnl + +dnl disable doc check +if test "${enable_doc}" = "no";then + DOC="" +else + DOC="doc" +fi + +dnl -------------------- +dnl Daemon disable check +dnl -------------------- +if test "${enable_zebra}" = "no";then + ZEBRA="" +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 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 + 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_ospfapi}" != "no";then + AC_DEFINE(SUPPORT_OSPF_API,,OSPFAPI) + + if test "${enable_ospfclient}" != "no";then + OSPFCLIENT="ospfclient" + fi +fi + +AM_CONDITIONAL(OSPFCLIENT, test "x$OSPFCLIENT" = "xospfclient") + +case "${enable_ripngd}" in + "no" ) RIPNGD="";; + * ) RIPNGD="ripngd";; +esac +AM_CONDITIONAL(RIPNGD, test "x$RIPNGD" = "xripngd") + +case "${enable_ospf6d}" in + "no" ) OSPF6D="";; + * ) OSPF6D="ospf6d";; +esac +AM_CONDITIONAL(OSPF6D, test "x$OSPF6D" = "xospf6d") + +case "${enable_isisd}" in + "no" ) ISISD="";; + * ) ISISD="isisd";; +esac +AM_CONDITIONAL(ISISD, test "x$ISISD" = "xisisd") + +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) +else + AC_DEFINE(DISABLE_BGP_ANNOUNCE,0,Disable BGP installation to zebra) +fi + +AC_SUBST(DOC) +AC_SUBST(ZEBRA) +AC_SUBST(BGPD) +AC_SUBST(RIPD) +AC_SUBST(RIPNGD) +AC_SUBST(OSPFD) +AC_SUBST(OSPF6D) +AC_SUBST(NHRPD) +AC_SUBST(WATCHQUAGGA) +AC_SUBST(ISISD) +AC_SUBST(PIMD) +AC_SUBST(SOLARIS) +AC_SUBST(VTYSH) +AC_SUBST(CURSES) +AC_SUBST(OSPFCLIENT) +AC_SUBST(OSPFAPI) +AC_CHECK_LIB(c, inet_ntop, [AC_DEFINE(HAVE_INET_NTOP,,inet_ntop)]) +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 check system has PCRE regexp +dnl --------------------------- +if test "x$enable_pcreposix" = "xyes"; then + AC_CHECK_LIB(pcreposix, pcreposix_regexec, ,[enable_pcreposix=no + AC_MSG_WARN([*** falling back to other regex library ***]) ]) +fi + +if test "x$enable_pcreposix" != "xyes"; then +dnl --------------------------- +dnl check system has GNU regexp +dnl --------------------------- +AC_MSG_CHECKING(whether system has GNU regex) +AC_CHECK_LIB(c, regexec, +[AC_DEFINE(HAVE_GNU_REGEX,,GNU regexp library) + LIB_REGEX=""], +[LIB_REGEX="regex.o"]) +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}" != ""; 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 --------------------------- +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, 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], + [], [], QUAGGA_INCLUDES) + +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) + +dnl --------------------------- +dnl IRDP/pktinfo/icmphdr checks +dnl --------------------------- +AC_CHECK_TYPES([struct in_pktinfo], + [AC_CHECK_TYPES([struct icmphdr], + [if test "${enable_irdp}" != "no"; then + AC_DEFINE(HAVE_IRDP,, IRDP) + fi], + [if test "${enable_irdp}" = "yes"; then + AC_MSG_ERROR(['IRDP requires in_pktinfo at the moment!']) + fi], [QUAGGA_INCLUDES])], + [if test "${enable_irdp}" = "yes"; then + 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 -------------------------------------- +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 +dnl -------------------------------------- +AC_CHECK_DECL(CLOCK_MONOTONIC, + [AC_CHECK_LIB(rt, clock_gettime, [LIBS="$LIBS -lrt"]) + AC_DEFINE(HAVE_CLOCK_MONOTONIC,, Have monotonic clock) +], [AC_MSG_RESULT(no)], [QUAGGA_INCLUDES]) + +dnl ------------------- +dnl capabilities checks +dnl ------------------- +if test "${enable_capabilities}" != "no"; then + AC_MSG_CHECKING(whether prctl PR_SET_KEEPCAPS is available) + AC_TRY_COMPILE([#include ],[prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);], + [AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_PR_SET_KEEPCAPS,,prctl) + quagga_ac_keepcaps="yes"], + AC_MSG_RESULT(no) + ) + if test x"${quagga_ac_keepcaps}" = x"yes"; then + AC_CHECK_HEADERS(sys/capability.h) + fi + if test x"${ac_cv_header_sys_capability_h}" = x"yes"; then + AC_CHECK_LIB(cap, cap_init, + [AC_DEFINE(HAVE_LCAPS,1,Capabilities) + LIBCAP="-lcap" + quagga_ac_lcaps="yes"] + ) + else + AC_CHECK_HEADERS(priv.h, + [AC_MSG_CHECKING(Solaris style privileges are available) + AC_TRY_COMPILE([#include ],[getpflags(PRIV_AWARE);], + [AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_SOLARIS_CAPABILITIES,1,getpflags) + quagga_ac_scaps="yes"], + AC_MSG_RESULT(no) + ) + ] + ) + fi + if test x"${quagga_ac_scaps}" = x"yes" \ + -o x"${quagga_ac_lcaps}" = x"yes"; then + AC_DEFINE(HAVE_CAPABILITIES,1,capabilities) + fi +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 --------------------------- +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 +dnl this must try and link using LIBS, in +dnl order to check no alternative allocator +dnl has been specified, which might not provide +dnl mallinfo, e.g. such as Umem on Solaris. +dnl ----------------------------------------- +AC_CHECK_HEADER([malloc.h], + [AC_MSG_CHECKING(whether mallinfo is available) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], + [[struct mallinfo ac_x; ac_x = mallinfo ();]])], + [AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_MALLINFO,,mallinfo)], + AC_MSG_RESULT(no) + ) + ], [], QUAGGA_INCLUDES) + +dnl ---------- +dnl configure date +dnl ---------- +CONFDATE=`date '+%Y%m%d'` +AC_SUBST(CONFDATE) + +dnl ------- +dnl DejaGNU +dnl ------- +if test x"$DEJAGNU" = x +then + DEJAGNU="\$(top_srcdir)/tests/global-conf.exp" +fi +RUNTESTDEFAULTFLAGS="-x --tool \$\$tool" + +AC_SUBST(DEJAGNU) +AC_SUBST(RUNTESTDEFAULTFLAGS) + +dnl ------------------------------ +dnl set paths for state directory +dnl ------------------------------ +AC_MSG_CHECKING(directory to use for state file) +if test "${prefix}" = "NONE"; then + quagga_statedir_prefix=""; +else + quagga_statedir_prefix=${prefix} +fi +if test "${localstatedir}" = '${prefix}/var'; then + for QUAGGA_STATE_DIR in ${quagga_statedir_prefix}/var/run dnl + ${quagga_statedir_prefix}/var/adm dnl + ${quagga_statedir_prefix}/etc dnl + /var/run dnl + /var/adm dnl + /etc dnl + /dev/null; + do + test -d $QUAGGA_STATE_DIR && break + done + quagga_statedir=$QUAGGA_STATE_DIR +else + quagga_statedir=${localstatedir} +fi +if test $quagga_statedir = "/dev/null"; then + AC_MSG_ERROR('STATE DIRECTORY NOT FOUND! FIX OR SPECIFY --localstatedir!') +fi +AC_MSG_RESULT(${quagga_statedir}) +AC_SUBST(quagga_statedir) + +AC_DEFINE_UNQUOTED(PATH_ZEBRA_PID, "$quagga_statedir/zebra.pid",zebra PID) +AC_DEFINE_UNQUOTED(PATH_RIPD_PID, "$quagga_statedir/ripd.pid",ripd PID) +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_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) +AC_DEFINE_UNQUOTED(RIP_VTYSH_PATH, "$quagga_statedir/ripd.vty",rip vty socket) +AC_DEFINE_UNQUOTED(RIPNG_VTYSH_PATH, "$quagga_statedir/ripngd.vty",ripng vty socket) +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(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 ------------------------------- +dnl Quagga sources should always be +dnl current wrt interfaces. Dont +dnl allow deprecated interfaces to +dnl be exposed. +dnl ------------------------------- +AC_DEFINE(QUAGGA_NO_DEPRECATED_INTERFACES, 1, Hide deprecated interfaces) + +dnl --------------------------- +dnl Check htonl works correctly +dnl --------------------------- +AC_MSG_CHECKING(for working htonl) +AC_CACHE_VAL(ac_cv_htonl_works, + [AC_LINK_IFELSE([AC_LANG_PROGRAM([QUAGGA_INCLUDES],[htonl (0);])], + [ac_cv_htonl_works=yes], [ac_cv_htonl_works=no]) + ] +) +AC_MSG_RESULT($ac_cv_htonl_works) + +AC_CONFIG_FILES([Makefile lib/Makefile qpb/Makefile zebra/Makefile ripd/Makefile + ripngd/Makefile bgpd/Makefile ospfd/Makefile watchquagga/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 + isisd/topology/Makefile + pkgsrc/bgpd.sh pkgsrc/ospf6d.sh pkgsrc/ospfd.sh + pkgsrc/ripd.sh pkgsrc/ripngd.sh pkgsrc/zebra.sh]) +AC_CONFIG_FILES([solaris/Makefile]) + +AC_CONFIG_FILES([vtysh/extract.pl],[chmod +x vtysh/extract.pl]) +## Hack, but working solution to avoid rebuilding of quagga.info. +## It's already in CVS until texinfo 4.7 is more common. +AC_OUTPUT + +echo " +Quagga configuration +-------------------- +quagga version : ${PACKAGE_VERSION} +host operating system : ${host_os} +source code location : ${srcdir} +compiler : ${CC} +compiler flags : ${CFLAGS} +make : ${MAKE-make} +linker flags : ${LDFLAGS} ${LIBS} ${LIBCAP} ${LIBREADLINE} ${LIBM} +state file directory : ${quagga_statedir} +config file directory : `eval echo \`echo ${sysconfdir}\`` +example directory : `eval echo \`echo ${exampledir}\`` +user to run as : ${enable_user} +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." + +if test x"$quagga_cv_gnu_make" = x"no"; then echo " +Warning: The ${MAKE-make} programme detected, either in your path or +via the MAKE variable, is not GNU Make. GNU make may be installed as +gmake on some systems. and is required to complete a build of Quagga +" > /dev/stderr +fi diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 0000000..459531b --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1,32 @@ +Makefile +Makefile.in +mdate-sh +draft-zebra-00.txt +quagga.info-* +zebra.html +version.texi +quagga.html +quagga.info +*.pdf +*.eps +quagga.ps +quagga.dvi +stamp-vti +.nfs* +*.aux +*.cp +*.cps +*.fn +*.fns +*.ky +*.kys +*.log +*.op +*.pg +*.toc +*.tp +*.vr +.arch-inventory +.arch-ids +*~ +*.loT diff --git a/doc/BGP-TypeCode b/doc/BGP-TypeCode new file mode 100644 index 0000000..b321807 --- /dev/null +++ b/doc/BGP-TypeCode @@ -0,0 +1,24 @@ + + BGP-4[+] UPDATE Attribute TypeCode list + + Value Attribute References +========================================================================= + 1 ORIGIN [RFC 4271] + 2 AS_PATH [RFC 4271] + 3 NEXT_HOP [RFC 4271] + 4 MULTI_EXIT_DISC [RFC 4271] + 5 LOCAL_PREF [RFC 4271] + 6 ATOMIC_AGGREGATE [RFC 4271] + 7 AGGREGATOR [RFC 4271] + 8 COMMUNITIES [RFC 1997] + 9 ORIGINATOR_ID [RFC 4456] + 10 CLUSTER_LIST [RFC 4456] + 11 DPA [draft-ietf-idr-bgp-dpa-05.txt(expired)] + 12 ADVERTISER [RFC 1863] + 13 RCID_PATH [RFC 1863] + 14 MP_REACH_NLRI [RFC 4760] + 15 MP_UNREACH_NLRI [RFC 4760] + 16 EXT_COMMUNITIES [RFC 4360] + 17 AS4_PATH [RFC 4893] + 18 AS4_AGGREGATOR [RFC 4893] +========================================================================= diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100644 index 0000000..38920c8 --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1,125 @@ +## Process this file with automake to produce Makefile.in. + +# Dia, the version i have at least, doesn't do very good EPS output +# (some of the text is scaled strangely). So this will work, but +# it is probably better to use something like gimp to convert the +# dia exported PNG files to EPS manually. +# +# Here we use 'convert' from the well known 'ImageMagick' package +# to do conversion from png to eps/pdf for figures. +# PDF form is required for quagga.pdf, using PDFTex at least. +# +# TeX implementation, which we depend on already anyway. +# +# dia -> (dia) -> png -> (convert) -> eps -> (epstopdf) -> pdf +SUFFIXES = .png .eps .dia .pdf +DIATOPNG = dia -t png -e +DIATOEPS = dia -t eps -e +PNGTOEPS = convert -antialias -contrast -despeckle +PNGTOPDF = $(PNGTOEPS) +EPSTOPDF = epstopdf + +# The figure sources +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) +figures_eps = $(figures_names_parts:%=fig%.eps) +figures_txt = $(figures_names_parts:%=fig%.txt) + +# rather twisted logic because we have to build PDFs of the EPS figures for +# PDFTex and yet build one PDF, quagga.pdf, from texi source. Which means we +# cant rely on a single automatic rule for *.pdf, eg the one automatically +# provided by automake. If you are an automake wizard, please feel free to +# compact it somehow. + +#quagga.pdf: $(info_TEXINFOS) $(quagga_TEXINFOS) +# $(TEXI2PDF) -o "$@" $< || true + +info_TEXINFOS = quagga.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 ospf_fundamentals.texi isisd.texi nhrpd.texi \ + $(figures_txt) + +.png.eps: + $(PNGTOEPS) $< "$@" + +.png.pdf: + $(PNGTOPDF) $< "$@" + +.dia.png: + $(DIATOPNG) "$@" $< + +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 + +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) \ + 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 new file mode 100644 index 0000000..3904c5f --- /dev/null +++ b/doc/appendix.texi @@ -0,0 +1,257 @@ +@node Packet Binary Dump Format +@appendix Packet Binary Dump Format + + Quagga can dump routing protocol packet into file with a binary format +(@pxref{Dump BGP packets and table}). + + It seems to be better that we share the MRT's header format for +backward compatibility with MRT's dump logs. We should also define the +binary format excluding the header, because we must support both IP +v4 and v6 addresses as socket addresses and / or routing entries. + + In the last meeting, we discussed to have a version field in the +header. But Masaki told us that we can define new `type' value rather +than having a `version' field, and it seems to be better because we +don't need to change header format. + + Here is the common header format. This is same as that of MRT. + +@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 | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +@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 +Address Family == IP (version 4) + +@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 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Source AS number | Destination AS number | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Interface Index | Address Family | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Source IP address | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Destination IP address | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Old State | New State | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +@end group +@end example + +Where State is the value defined in RFC1771. + +If `type' is PROTOCOL_BGP4MP, `subtype' is BGP4MP_STATE_CHANGE, +and Address Family == IP version 6 + +@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 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Source AS number | Destination AS number | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Interface Index | Address Family | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Source IP address | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Source IP address (Cont'd) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Source IP address (Cont'd) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Source IP address (Cont'd) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Destination IP address | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Destination IP address (Cont'd) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Destination IP address (Cont'd) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Destination IP address (Cont'd) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Old State | New State | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +@end group +@end example + +If `type' is PROTOCOL_BGP4MP, `subtype' is BGP4MP_MESSAGE, +and Address Family == IP (version 4) + +@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 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Source AS number | Destination AS number | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Interface Index | Address Family | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Source IP address | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Destination IP address | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| BGP Message Packet | +| | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +@end group +@end example + +Where BGP Message Packet is the whole contents of the +BGP4 message including header portion. + +If `type' is PROTOCOL_BGP4MP, `subtype' is BGP4MP_MESSAGE, +and Address Family == IP version 6 + +@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 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Source AS number | Destination AS number | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Interface Index | Address Family | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Source IP address | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Source IP address (Cont'd) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Source IP address (Cont'd) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Source IP address (Cont'd) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Destination IP address | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Destination IP address (Cont'd) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Destination IP address (Cont'd) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Destination IP address (Cont'd) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| BGP Message Packet | +| | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +@end group +@end example + +If `type' is PROTOCOL_BGP4MP, `subtype' is BGP4MP_ENTRY, +and Address Family == IP (version 4) + +@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 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| View # | Status | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Time Last Change | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Address Family | SAFI | Next-Hop-Len | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Next Hop Address | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Prefix Length | Address Prefix [variable] | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Attribute Length | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| BGP Attribute [variable length] | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +@end group +@end example + +If `type' is PROTOCOL_BGP4MP, `subtype' is BGP4MP_ENTRY, +and Address Family == IP version 6 + +@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 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| View # | Status | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Time Last Change | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Address Family | SAFI | Next-Hop-Len | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Next Hop Address | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Next Hop Address (Cont'd) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Next Hop Address (Cont'd) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Next Hop Address (Cont'd) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Prefix Length | Address Prefix [variable] | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Address Prefix (cont'd) [variable] | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Attribute Length | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| BGP Attribute [variable length] | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +@end group +@end example + + BGP4 Attribute must not contain MP_UNREACH_NLRI. + If BGP Attribute has MP_REACH_NLRI field, it must has + zero length NLRI, e.g., MP_REACH_NLRI has only Address + Family, SAFI and next-hop values. + +If `type' is PROTOCOL_BGP4MP and `subtype' is BGP4MP_SNAPSHOT, + +@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 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| View # | File Name [variable] | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +@end group +@end example + + The file specified in "File Name" contains all routing entries, + which are in the format of ``subtype == BGP4MP_ENTRY''. + +@example +@group +Constants: + /* type value */ + #define MSG_PROTOCOL_BGP4MP 16 + #define MSG_PROTOCOL_BGP4MP_ET 17 + /* subtype value */ + #define BGP4MP_STATE_CHANGE 0 + #define BGP4MP_MESSAGE 1 + #define BGP4MP_ENTRY 2 + #define BGP4MP_SNAPSHOT 3 +@end group +@end example diff --git a/doc/basic.texi b/doc/basic.texi new file mode 100644 index 0000000..92e8d9e --- /dev/null +++ b/doc/basic.texi @@ -0,0 +1,605 @@ +@node Basic commands +@chapter Basic commands + +There are five routing daemons in use, and there is one manager daemon. +These daemons may be located on separate machines from the manager +daemon. Each of these daemons will listen on a particular port for +incoming VTY connections. The routing daemons are: + +@itemize @bullet +@item @command{ripd}, @command{ripngd}, @command{ospfd}, @command{ospf6d}, @command{bgpd} +@item @command{zebra} +@end itemize + +The following sections discuss commands common to all the routing +daemons. + +@menu +* 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 + + + +@node Config Commands +@section Config Commands + +@cindex Configuration files for running the software +@c A -not configuration files for installing the software +@cindex Files for running configurations +@cindex Modifying the herd's behavior +@cindex Getting the herd running + + +@menu +* Basic Config Commands:: Some of the generic config commands +* Sample Config File:: An example config file +@end menu + + +In a config file, you can write the debugging options, a vty's password, +routing daemon configurations, a log file name, and so forth. This +information forms the initial command set for a routing beast as it is +starting. + +Config files are generally found in: + +@itemize @w{} +@item @file{@value{INSTALL_PREFIX_ETC}/*.conf} +@end itemize + +Each of the daemons has its own +config file. For example, zebra's default config file name is: + +@itemize @w{} +@item @file{@value{INSTALL_PREFIX_ETC}/zebra.conf} +@end itemize + +The daemon name plus @file{.conf} is the default config file name. You +can specify a config file using the @kbd{-f} or @kbd{--config-file} +options when starting the daemon. + + + +@node Basic Config Commands +@subsection Basic Config Commands + +@deffn Command {hostname @var{hostname}} {} +Set hostname of the router. +@end deffn + +@deffn Command {password @var{password}} {} +Set password for vty interface. If there is no password, a vty won't +accept connections. +@end deffn + +@deffn Command {enable password @var{password}} {} +Set enable password. +@end deffn + +@deffn Command {log trap @var{level}} {} +@deffnx Command {no log trap} {} +These commands are deprecated and are present only for historical compatibility. +The log trap command sets the current logging level for all enabled +logging destinations, and it sets the default for all future logging commands +that do not specify a level. The normal default +logging level is debugging. The @code{no} form of the command resets +the default level for future logging commands to debugging, but it does +not change the logging level of existing logging destinations. +@end deffn + + +@deffn Command {log stdout} {} +@deffnx Command {log stdout @var{level}} {} +@deffnx Command {no log stdout} {} +Enable logging output to stdout. +If the optional second argument specifying the +logging level is not present, the default logging level (typically debugging, +but can be changed using the deprecated @code{log trap} command) will be used. +The @code{no} form of the command disables logging to stdout. +The @code{level} argument must have one of these values: +emergencies, alerts, critical, errors, warnings, notifications, informational, or debugging. Note that the existing code logs its most important messages +with severity @code{errors}. +@end deffn + +@deffn Command {log file @var{filename}} {} +@deffnx Command {log file @var{filename} @var{level}} {} +@deffnx Command {no log file} {} +If you want to log into a file, please specify @code{filename} as +in this example: +@example +log file /var/log/quagga/bgpd.log informational +@end example +If the optional second argument specifying the +logging level is not present, the default logging level (typically debugging, +but can be changed using the deprecated @code{log trap} command) will be used. +The @code{no} form of the command disables logging to a file. + +Note: if you do not configure any file logging, and a daemon crashes due +to a signal or an assertion failure, it will attempt to save the crash +information in a file named /var/tmp/quagga..crashlog. +For security reasons, this will not happen if the file exists already, so +it is important to delete the file after reporting the crash information. +@end deffn + +@deffn Command {log syslog} {} +@deffnx Command {log syslog @var{level}} {} +@deffnx Command {no log syslog} {} +Enable logging output to syslog. +If the optional second argument specifying the +logging level is not present, the default logging level (typically debugging, +but can be changed using the deprecated @code{log trap} command) will be used. +The @code{no} form of the command disables logging to syslog. +@end deffn + +@deffn Command {log monitor} {} +@deffnx Command {log monitor @var{level}} {} +@deffnx Command {no log monitor} {} +Enable logging output to vty terminals that have enabled logging +using the @code{terminal monitor} command. +By default, monitor logging is enabled at the debugging level, but this +command (or the deprecated @code{log trap} command) can be used to change +the monitor logging level. +If the optional second argument specifying the +logging level is not present, the default logging level (typically debugging, +but can be changed using the deprecated @code{log trap} command) will be used. +The @code{no} form of the command disables logging to terminal monitors. +@end deffn + +@deffn Command {log facility @var{facility}} {} +@deffnx Command {no log facility} {} +This command changes the facility used in syslog messages. The default +facility is @code{daemon}. The @code{no} form of the command resets +the facility to the default @code{daemon} facility. +@end deffn + +@deffn Command {log record-priority} {} +@deffnx Command {no log record-priority} {} +To include the severity in all messages logged to a file, to stdout, or to +a terminal monitor (i.e. anything except syslog), +use the @code{log record-priority} global configuration command. +To disable this option, use the @code{no} form of the command. By default, +the severity level is not included in logged messages. Note: some +versions of syslogd (including Solaris) can be configured to include +the facility and level in the messages emitted. +@end deffn + +@deffn Command {log timestamp precision @var{<0-6>}} {} +@deffnx Command {no log timestamp precision} {} +This command sets the precision of log message timestamps to the +given number of digits after the decimal point. Currently, +the value must be in the range 0 to 6 (i.e. the maximum precision +is microseconds). +To restore the default behavior (1-second accuracy), use the +@code{no} form of the command, or set the precision explicitly to 0. + +@example +@group +log timestamp precision 3 +@end group +@end example + +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 + +@deffn Command {service advanced-vty} {} +Enable advanced mode VTY. +@end deffn + +@deffn Command {service terminal-length @var{<0-512>}} {} +Set system wide line configuration. This configuration command applies +to all VTY interfaces. +@end deffn + +@deffn Command {line vty} {} +Enter vty configuration mode. +@end deffn + +@deffn Command {banner motd default} {} +Set default motd string. +@end deffn + +@deffn Command {no banner motd} {} +No motd banner string will be printed. +@end deffn + +@deffn {Line Command} {exec-timeout @var{minute}} {} +@deffnx {Line Command} {exec-timeout @var{minute} @var{second}} {} +Set VTY connection timeout value. When only one argument is specified +it is used for timeout value in minutes. Optional second argument is +used for timeout value in seconds. Default timeout value is 10 minutes. +When timeout value is zero, it means no timeout. +@end deffn + +@deffn {Line Command} {no exec-timeout} {} +Do not perform timeout at all. This command is as same as +@command{exec-timeout 0 0}. +@end deffn + +@deffn {Line Command} {access-class @var{access-list}} {} +Restrict vty connections with an access list. +@end deffn + +@node Sample Config File +@subsection Sample Config File + + +Below is a sample configuration file for the zebra daemon. + +@example +@group +! +! Zebra configuration file +! +hostname Router +password zebra +enable password zebra +! +log stdout +! +! +@end group +@end example + +'!' and '#' are comment characters. If the first character of the word +is one of the comment characters then from the rest of the line forward +will be ignored as a comment. + +@example +password zebra!password +@end example + +If a comment character is not the first character of the word, it's a +normal character. So in the above example '!' will not be regarded as a +comment and the password is set to 'zebra!password'. + + + +@node Terminal Mode Commands +@section Terminal Mode Commands + +@deffn Command {write terminal} {} +Displays the current configuration to the vty interface. +@end deffn + +@deffn Command {write file} {} +Write current configuration to configuration file. +@end deffn + +@deffn Command {configure terminal} {} +Change to configuration mode. This command is the first step to +configuration. +@end deffn + +@deffn Command {terminal length @var{<0-512>}} {} +Set terminal display length to @var{<0-512>}. If length is 0, no +display control is performed. +@end deffn + +@deffn Command {who} {} +Show a list of currently connected vty sessions. +@end deffn + +@deffn Command {list} {} +List all available commands. +@end deffn + +@deffn Command {show version} {} +Show the current version of @value{PACKAGE_NAME} and its build host information. +@end deffn + +@deffn Command {show logging} {} +Shows the current configuration of the logging system. This includes +the status of all logging destinations. +@end deffn + +@deffn Command {logmsg @var{level} @var{message}} {} +Send a message to all logging destinations that are enabled for messages +of the given severity. +@end deffn + + + + +@node Common Invocation Options +@section Common Invocation Options +@c COMMON_OPTIONS +@c OPTIONS section of the man page + +These options apply to all @value{PACKAGE_NAME} daemons. + +@table @samp + +@item -d +@itemx --daemon +Runs in daemon mode. + +@item -f @var{file} +@itemx --config_file=@var{file} +Set configuration file name. + +@item -h +@itemx --help +Display this help and exit. + +@item -i @var{file} +@itemx --pid_file=@var{file} + +Upon startup the process identifier of the daemon is written to a file, +typically in @file{/var/run}. This file can be used by the init system +to implement commands such as @command{@dots{}/init.d/zebra status}, +@command{@dots{}/init.d/zebra restart} or @command{@dots{}/init.d/zebra +stop}. + +The file name is an run-time option rather than a configure-time option +so that multiple routing daemons can be run simultaneously. This is +useful when using @value{PACKAGE_NAME} to implement a routing looking glass. One +machine can be used to collect differing routing views from differing +points in the network. + +@item -A @var{address} +@itemx --vty_addr=@var{address} +Set the VTY local address to bind to. If set, the VTY socket will only +be bound to this address. + +@item -P @var{port} +@itemx --vty_port=@var{port} +Set the VTY TCP port number. If set to 0 then the TCP VTY sockets will not +be opened. + +@item -u @var{user} +@itemx --vty_addr=@var{user} +Set the user and group to run as. + +@item -v +@itemx --version +Print program version. + +@end table + + + +@node Virtual Terminal Interfaces +@section Virtual Terminal Interfaces + +VTY -- Virtual Terminal [aka TeletYpe] Interface is a command line +interface (CLI) for user interaction with the routing daemon. + +@menu +* VTY Overview:: Basics about VTYs +* VTY Modes:: View, Enable, and Other VTY modes +* VTY CLI Commands:: Commands for movement, edition, and management +@end menu + + + +@node VTY Overview +@subsection VTY Overview + + +VTY stands for Virtual TeletYpe interface. It means you can connect to +the daemon via the telnet protocol. + +To enable a VTY interface, you have to setup a VTY password. If there +is no VTY password, one cannot connect to the VTY interface at all. + +@example +@group +% telnet localhost 2601 +Trying 127.0.0.1... +Connected to localhost. +Escape character is '^]'. + +Hello, this is @value{PACKAGE_NAME} (version @value{VERSION}) +@value{COPYRIGHT_STR} + +User Access Verification + +Password: XXXXX +Router> ? + enable Turn on privileged commands + exit Exit current mode and down to previous mode + help Description of the interactive help system + list Print command list + show Show running system information + who Display who is on a vty +Router> enable +Password: XXXXX +Router# configure terminal +Router(config)# interface eth0 +Router(config-if)# ip address 10.0.0.1/8 +Router(config-if)# ^Z +Router# +@end group +@end example + +'?' is very useful for looking up commands. + +@node VTY Modes +@subsection VTY Modes + +There are three basic VTY modes: + +@menu +* VTY View Mode:: Mode for read-only interaction +* VTY Enable Mode:: Mode for read-write interaction +* VTY Other Modes:: Special modes (tftp, etc) +@end menu + +There are commands that may be restricted to specific VTY modes. + +@node VTY View Mode +@subsubsection VTY View Mode +@c to be written (gpoul) + + +This mode is for read-only access to the CLI. One may exit the mode by +leaving the system, or by entering @code{enable} mode. + +@node VTY Enable Mode +@subsubsection VTY Enable Mode + +@c to be written (gpoul) +This mode is for read-write access to the CLI. One may exit the mode by +leaving the system, or by escaping to view mode. + +@node VTY Other Modes +@subsubsection VTY Other Modes + + +@c to be written (gpoul) +This page is for describing other modes. + +@node VTY CLI Commands +@subsection VTY CLI Commands + +Commands that you may use at the command-line are described in the following +three subsubsections. + +@menu +* CLI Movement Commands:: Commands for moving the cursor about +* CLI Editing Commands:: Commands for changing text +* CLI Advanced Commands:: Other commands, session management and so on +@end menu + +@node CLI Movement Commands +@subsubsection CLI Movement Commands + +These commands are used for moving the CLI cursor. The @key{C} character +means press the Control Key. + +@table @kbd + +@item C-f +@itemx @key{RIGHT} +@kindex C-f +@kindex @key{RIGHT} +Move forward one character. + +@item C-b +@itemx @key{LEFT} +@kindex C-b +@kindex @key{LEFT} +Move backward one character. + +@item M-f +@kindex M-f +Move forward one word. + +@item M-b +@kindex M-b +Move backward one word. + +@item C-a +@kindex C-a +Move to the beginning of the line. + +@item C-e +@kindex C-e +Move to the end of the line. + +@end table + +@node CLI Editing Commands +@subsubsection CLI Editing Commands + +These commands are used for editing text on a line. The @key{C} +character means press the Control Key. + +@table @kbd + +@item C-h +@itemx @key{DEL} +@kindex C-h +@kindex @key{DEL} +Delete the character before point. + +@item C-d +@kindex C-d +Delete the character after point. + +@item M-d +@kindex M-d +Forward kill word. + +@item C-w +@kindex C-w +Backward kill word. + +@item C-k +@kindex C-k +Kill to the end of the line. + +@item C-u +@kindex C-u +Kill line from the beginning, erasing input. + +@item C-t +@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 +@subsubsection CLI Advanced Commands + +There are several additional CLI commands for command line completions, +insta-help, and VTY session management. + +@table @kbd + +@item C-c +@kindex C-c +Interrupt current input and moves to the next line. + +@item C-z +@kindex C-z +End current configuration session and move to top node. + + +@item C-n +@itemx @key{DOWN} +@kindex C-n +@kindex @key{DOWN} +Move down to next line in the history buffer. + +@item C-p +@itemx @key{UP} +@kindex C-p +@kindex @key{UP} +Move up to previous line in the history buffer. + +@item TAB +@kindex @key{TAB} +Use command line completion by typing @key{TAB}. + +@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 new file mode 100644 index 0000000..e680ddb --- /dev/null +++ b/doc/bgpd.8 @@ -0,0 +1,125 @@ +.TH BGPD 8 "25 November 2004" "Quagga BGPD daemon" "Version 0.97.3" +.SH NAME +bgpd \- a BGPv4, BGPv4\+, BGPv4\- routing engine for use with Quagga routing +software + +.SH SYNOPSIS +.B bgpd +[ +.B \-dhrSv +] [ +.B \-f +.I config-file +] [ +.B \-i +.I pid-file +] [ +.B \-p +.I bgp-port-number +] [ +.B \-P +.I port-number +] [ +.B \-A +.I vty-address +] [ +.B \-u +.I user +] [ +.B \-g +.I group +] +.SH DESCRIPTION +.B bgpd +is a routing component that works with the +.B Quagga +routing engine. +.SH OPTIONS +Options available for the +.B bgpd +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/bgpd.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 bgpd starts its process identifier is written to +\fB\fIpid-file\fR. The init system uses the recorded PID to stop or +restart bgpd. The likely default is \fB\fI/var/run/bgpd.pid\fR. +.TP +\fB\-p\fR, \fB\-\-bgp_port \fR\fIbgp-port-number\fR +Set the port that bgpd will listen to for bgp data. +.TP +\fB\-P\fR, \fB\-\-vty_port \fR\fIport-number\fR +Specify the port that the bgpd VTY will listen on. This defaults to +2605, as specified in \fI/etc/services\fR. +.TP +\fB\-A\fR, \fB\-\-vty_addr \fR\fIvty-address\fR +Specify the address that the bgpd 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\-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 +.TP +.BI /usr/local/sbin/bgpd +The default location of the +.B bgpd +binary. +.TP +.BI /usr/local/etc/bgpd.conf +The default location of the +.B bgpd +config file. +.TP +.BI $(PWD)/bgpd.log +If the +.B bgpd +process is config'd to output logs to a file, then you will find this +file in the directory where you started \fBbgpd\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 bgpd process may log to standard output, to a VTY, to a log +file, or through syslog to the system logs. \fBbgpd\fR supports many +debugging options, see the Info file, or the source for details. +.SH "SEE ALSO" +.BR ripd (8), +.BR ripngd (8), +.BR ospfd (8), +.BR ospf6d (8), +.BR isisd (8), +.BR nhrpd (8), +.BR zebra (8), +.BR vtysh (1) +.SH BUGS +.B bgpd +eats bugs for breakfast. If you have food for the maintainers try +.BI http://bugzilla.quagga.net +.SH AUTHORS +See +.BI http://www.zebra.org +and +.BI http://www.quagga.net +or the Info file for an accurate list of authors. + diff --git a/doc/bgpd.texi b/doc/bgpd.texi new file mode 100644 index 0000000..d5aa30c --- /dev/null +++ b/doc/bgpd.texi @@ -0,0 +1,1976 @@ +@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 + +@acronym{BGP} stands for a Border Gateway Protocol. The lastest BGP version +is 4. It is referred as BGP-4. BGP-4 is one of the Exterior Gateway +Protocols and de-fact standard of Inter Domain routing protocol. +BGP-4 is described in @cite{RFC1771, A Border Gateway Protocol +4 (BGP-4)}. + +Many extensions have been added to @cite{RFC1771}. @cite{RFC2858, +Multiprotocol Extensions for BGP-4} provides multiprotocol support to +BGP-4. + +@menu +* Starting BGP:: +* BGP router:: +* BGP MED:: +* BGP network:: +* BGP Peer:: +* BGP Peer Group:: +* BGP Address Family:: +* Autonomous System:: +* BGP Communities Attribute:: +* BGP Extended Communities Attribute:: +* Displaying BGP routes:: +* Capability Negotiation:: +* Route Reflector:: +* Route Server:: +* How to set up a 6-Bone connection:: +* Dump BGP packets and table:: +* BGP Configuration Examples:: +@end menu + +@node Starting BGP +@section Starting BGP + +Default configuration file of @command{bgpd} is @file{bgpd.conf}. +@command{bgpd} searches the current directory first then +@value{INSTALL_PREFIX_ETC}/bgpd.conf. All of bgpd's command must be +configured in @file{bgpd.conf}. + +@command{bgpd} specific invocation options are described below. Common +options may also be specified (@pxref{Common Invocation Options}). + +@table @samp +@item -p @var{PORT} +@itemx --bgp_port=@var{PORT} +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 +@section BGP router + + First of all you must configure BGP router with @command{router bgp} +command. To configure BGP router, you need AS number. AS number is an +identification of autonomous system. BGP protocol uses the AS number +for detecting whether the BGP connection is internal one or external one. + +@deffn Command {router bgp @var{asn}} {} +Enable a BGP protocol process with the specified @var{asn}. After +this statement you can input any @code{BGP Commands}. You can not +create different BGP process under different @var{asn} without +specifying @code{multiple-instance} (@pxref{Multiple instance}). +@end deffn + +@deffn Command {no router bgp @var{asn}} {} +Destroy a BGP protocol process with the specified @var{asn}. +@end deffn + +@deffn {BGP} {bgp router-id @var{A.B.C.D}} {} +This command specifies the router-ID. If @command{bgpd} connects to @command{zebra} it gets +interface and address information. In that case default router ID value +is selected as the largest IP Address of the interfaces. When +@code{router zebra} is not enabled @command{bgpd} can't get interface information +so @code{router-id} is set to 0.0.0.0. So please set router-id by hand. +@end deffn + +@menu +* BGP distance:: +* BGP decision process:: +* BGP route flap dampening:: +@end menu + +@node BGP distance +@subsection BGP distance + +@deffn {BGP} {distance bgp <1-255> <1-255> <1-255>} {} +This command change distance value of BGP. Each argument is distance +value for external routes, internal routes and local routes. +@end deffn + +@deffn {BGP} {distance <1-255> @var{A.B.C.D/M}} {} +@deffnx {BGP} {distance <1-255> @var{A.B.C.D/M} @var{word}} {} +This command set distance value to +@end deffn + +@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 +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 8. IGP cost check +Prefer the route with the lower IGP cost. + +@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 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. + +@end table + +@deffn {BGP} {bgp bestpath as-path confed} {} +This command specifies that the length of confederation path sets and +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 + +@deffn {BGP} {bgp dampening @var{<1-45>} @var{<1-20000>} @var{<1-20000>} @var{<1-255>}} {} +This command enables BGP route-flap dampening and specifies dampening parameters. + +@table @asis +@item @asis{half-life} +Half-life time for the penalty +@item @asis{reuse-threshold} +Value to start reusing a route +@item @asis{suppress-threshold} +Value to start suppressing a route +@item @asis{max-suppress} +Maximum duration to suppress a stable route +@end table + +The route-flap damping algorithm is compatible with @cite{RFC2439}. The use of this command +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 + +@menu +* BGP route:: +* Route Aggregation:: +* Redistribute to BGP:: +@end menu + +@node BGP route +@subsection BGP route + +@deffn {BGP} {network @var{A.B.C.D/M}} {} +This command adds the announcement network. +@example +@group +router bgp 1 + network 10.0.0.0/8 +@end group +@end example +This configuration example says that network 10.0.0.0/8 will be +announced to all neighbors. Some vendors' routers don't advertise +routes if they aren't present in their IGP routing tables; @code{bgpd} +doesn't care about IGP routes when announcing its routes. +@end deffn + +@deffn {BGP} {no network @var{A.B.C.D/M}} {} +@end deffn + +@node Route Aggregation +@subsection Route Aggregation + +@deffn {BGP} {aggregate-address @var{A.B.C.D/M}} {} +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 include +AS set. +@end deffn + +@deffn {BGP} {aggregate-address @var{A.B.C.D/M} summary-only} {} +This command specifies an aggregate address. Aggreated routes will +not be announce. +@end deffn + +@deffn {BGP} {no aggregate-address @var{A.B.C.D/M}} {} +@end deffn + +@node Redistribute to BGP +@subsection Redistribute to BGP + +@deffn {BGP} {redistribute kernel} {} +Redistribute kernel route to BGP process. +@end deffn + +@deffn {BGP} {redistribute static} {} +Redistribute static route to BGP process. +@end deffn + +@deffn {BGP} {redistribute connected} {} +Redistribute connected route to BGP process. +@end deffn + +@deffn {BGP} {redistribute rip} {} +Redistribute RIP route to BGP process. +@end deffn + +@deffn {BGP} {redistribute ospf} {} +Redistribute OSPF route to BGP process. +@end deffn + +@node BGP Peer +@section BGP Peer + +@menu +* Defining Peer:: +* BGP Peer commands:: +* Peer filtering:: +@end menu + +@node Defining Peer +@subsection Defining Peer + +@deffn {BGP} {neighbor @var{peer} remote-as @var{asn}} {} +Creates a new neighbor whose remote-as is @var{asn}. @var{peer} +can be an IPv4 address or an IPv6 address. +@example +@group +router bgp 1 + neighbor 10.0.0.1 remote-as 2 +@end group +@end example +In this case my router, in AS-1, is trying to peer with AS-2 at +10.0.0.1. + +This command must be the first command used when configuring a neighbor. +If the remote-as is not specified, @command{bgpd} will complain like this: +@example +can't find neighbor 10.0.0.1 +@end example +@end deffn + +@node BGP Peer commands +@subsection BGP Peer commands + +In a @code{router bgp} clause there are neighbor specific configurations +required. + +@deffn {BGP} {neighbor @var{peer} shutdown} {} +@deffnx {BGP} {no neighbor @var{peer} shutdown} {} +Shutdown the peer. We can delete the neighbor's configuration by +@code{no neighbor @var{peer} remote-as @var{as-number}} but all +configuration of the neighbor will be deleted. When you want to +preserve the configuration, but want to drop the BGP peer, use this +syntax. +@end deffn + +@deffn {BGP} {neighbor @var{peer} ebgp-multihop} {} +@deffnx {BGP} {no neighbor @var{peer} ebgp-multihop} {} +@end deffn + +@deffn {BGP} {neighbor @var{peer} description ...} {} +@deffnx {BGP} {no neighbor @var{peer} description ...} {} +Set description of the peer. +@end deffn + +@deffn {BGP} {neighbor @var{peer} version @var{version}} {} +Set up the neighbor's BGP version. @var{version} can be @var{4}, +@var{4+} or @var{4-}. BGP version @var{4} is the default value used for +BGP peering. BGP version @var{4+} means that the neighbor supports +Multiprotocol Extensions for BGP-4. BGP version @var{4-} is similar but +the neighbor speaks the old Internet-Draft revision 00's Multiprotocol +Extensions for BGP-4. Some routing software is still using this +version. +@end deffn + +@deffn {BGP} {neighbor @var{peer} interface @var{ifname}} {} +@deffnx {BGP} {no neighbor @var{peer} interface @var{ifname}} {} +When you connect to a BGP peer over an IPv6 link-local address, you +have to specify the @var{ifname} of the interface used for the +connection. To specify IPv4 session addresses, see the +@code{neighbor @var{peer} update-source} command below. + +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 [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 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{}} {} +@deffnx {BGP} {no neighbor @var{peer} update-source} {} +Specify the IPv4 source address to use for the @acronym{BGP} session to this +neighbour, may be specified as either an IPv4 address directly or +as an interface name (in which case the @command{zebra} daemon MUST be running +in order for @command{bgpd} to be able to retrieve interface state). +@example +@group +router bgp 64555 + neighbor foo update-source 192.168.0.1 + neighbor bar update-source lo0 +@end group +@end example +@end deffn + +@deffn {BGP} {neighbor @var{peer} default-originate} {} +@deffnx {BGP} {no neighbor @var{peer} default-originate} {} +@command{bgpd}'s default is to not announce the default route (0.0.0.0/0) even it +is in routing table. When you want to announce default routes to the +peer, use this command. +@end deffn + +@deffn {BGP} {neighbor @var{peer} port @var{port}} {} +@deffnx {BGP} {neighbor @var{peer} port @var{port}} {} +@end deffn + +@deffn {BGP} {neighbor @var{peer} send-community} {} +@deffnx {BGP} {neighbor @var{peer} send-community} {} +@end deffn + +@deffn {BGP} {neighbor @var{peer} weight @var{weight}} {} +@deffnx {BGP} {no neighbor @var{peer} weight @var{weight}} {} +This command specifies a default @var{weight} value for the neighbor's +routes. +@end deffn + +@deffn {BGP} {neighbor @var{peer} maximum-prefix @var{number}} {} +@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 + +@deffn {BGP} {neighbor @var{peer} distribute-list @var{name} [in|out]} {} +This command specifies a distribute-list for the peer. @var{direct} is +@samp{in} or @samp{out}. +@end deffn + +@deffn {BGP command} {neighbor @var{peer} prefix-list @var{name} [in|out]} {} +@end deffn + +@deffn {BGP command} {neighbor @var{peer} filter-list @var{name} [in|out]} {} +@end deffn + +@deffn {BGP} {neighbor @var{peer} route-map @var{name} [in|out]} {} +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 + +@deffn {BGP} {neighbor @var{word} peer-group} {} +This command defines a new peer group. +@end deffn + +@deffn {BGP} {neighbor @var{peer} peer-group @var{word}} {} +This command bind specific peer to peer group @var{word}. +@end deffn + +@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 + +The @acronym{AS,Autonomous System} number is one of the essential +element of BGP. BGP is a distance vector routing protocol, and the +AS-Path framework provides distance vector metric and loop detection to +BGP. @cite{RFC1930, Guidelines for creation, selection, and +registration of an Autonomous System (AS)} provides some background on +the concepts of an AS. + +The AS number is a two octet value, ranging in value from 1 to 65535. +The AS numbers 64512 through 65535 are defined as private AS numbers. +Private AS numbers must not to be advertised in the global Internet. + +@menu +* AS Path Regular Expression:: +* Display BGP Routes by AS Path:: +* AS Path Access List:: +* Using AS Path in Route Map:: +* Private AS Numbers:: +@end menu + +@node AS Path Regular Expression +@subsection AS Path Regular Expression + +AS path regular expression can be used for displaying BGP routes and +AS path access list. AS path regular expression is based on +@code{POSIX 1003.2} regular expressions. Following description is +just a subset of @code{POSIX} regular expression. User can use full +@code{POSIX} regular expression. Adding to that special character '_' +is added for AS path regular expression. + +@table @code +@item . +Matches any single character. +@item * +Matches 0 or more occurrences of pattern. +@item + +Matches 1 or more occurrences of pattern. +@item ? +Match 0 or 1 occurrences of pattern. +@item ^ +Matches the beginning of the line. +@item $ +Matches the end of the line. +@item _ +Character @code{_} has special meanings in AS path regular expression. +It matches to space and comma , and AS set delimiter @{ and @} and AS +confederation delimiter @code{(} and @code{)}. And it also matches to +the beginning of the line and the end of the line. So @code{_} can be +used for AS value boundaries match. @code{show ip bgp regexp _7675_} +matches to all of BGP routes which as AS number include @var{7675}. +@end table + +@node Display BGP Routes by AS Path +@subsection Display BGP Routes by AS Path + +To show BGP routes which has specific AS path information @code{show +ip bgp} command can be used. + +@deffn Command {show ip bgp regexp @var{line}} {} +This commands display BGP routes that matches AS path regular +expression @var{line}. +@end deffn + +@node AS Path Access List +@subsection AS Path Access List + +AS path access list is user defined AS path. + +@deffn {Command} {ip as-path access-list @var{word} @{permit|deny@} @var{line}} {} +This command defines a new AS path access list. +@end deffn + +@deffn {Command} {no ip as-path access-list @var{word}} {} +@deffnx {Command} {no ip as-path access-list @var{word} @{permit|deny@} @var{line}} {} +@end deffn + +@node Using AS Path in Route Map +@subsection Using AS Path in Route Map + +@deffn {Route Map} {match as-path @var{word}} {} +@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 +@subsection Private AS Numbers + +@c ----------------------------------------------------------------------- +@node BGP Communities Attribute +@section BGP Communities Attribute + +BGP communities attribute is widely used for implementing policy +routing. Network operators can manipulate BGP communities attribute +based on their network policy. BGP communities attribute is defined +in @cite{RFC1997, BGP Communities Attribute} and +@cite{RFC1998, An Application of the BGP Community Attribute +in Multi-home Routing}. It is an optional transitive attribute, +therefore local policy can travel through different autonomous system. + +Communities attribute is a set of communities values. Each +communities value is 4 octet long. The following format is used to +define communities value. + +@table @code +@item AS:VAL +This format represents 4 octet communities value. @code{AS} is high +order 2 octet in digit format. @code{VAL} is low order 2 octet in +digit format. This format is useful to define AS oriented policy +value. For example, @code{7675:80} can be used when AS 7675 wants to +pass local policy value 80 to neighboring peer. +@item internet +@code{internet} represents well-known communities value 0. +@item no-export +@code{no-export} represents well-known communities value @code{NO_EXPORT}@* +@r{(0xFFFFFF01)}. All routes carry this value must not be advertised +to outside a BGP confederation boundary. If neighboring BGP peer is +part of BGP confederation, the peer is considered as inside a BGP +confederation boundary, so the route will be announced to the peer. +@item no-advertise +@code{no-advertise} represents well-known communities value +@code{NO_ADVERTISE}@*@r{(0xFFFFFF02)}. All routes carry this value +must not be advertise to other BGP peers. +@item local-AS +@code{local-AS} represents well-known communities value +@code{NO_EXPORT_SUBCONFED} @r{(0xFFFFFF03)}. All routes carry this +value must not be advertised to external BGP peers. Even if the +neighboring router is part of confederation, it is considered as +external BGP peer, so the route will not be announced to the peer. +@end table + + When BGP communities attribute is received, duplicated communities +value in the communities attribute is ignored and each communities +values are sorted in numerical order. + +@menu +* BGP Community Lists:: +* Numbered BGP Community Lists:: +* BGP Community in Route Map:: +* Display BGP Routes by Community:: +* Using BGP Communities Attribute:: +@end menu + +@node BGP Community Lists +@subsection BGP Community Lists + + BGP community list is a user defined BGP communites attribute list. +BGP community list can be used for matching or manipulating BGP +communities attribute in updates. + +There are two types of community list. One is standard community +list and another is expanded community list. Standard community list +defines communities attribute. Expanded community list defines +communities attribute string with regular expression. Standard +community list is compiled into binary format when user define it. +Standard community list will be directly compared to BGP communities +attribute in BGP updates. Therefore the comparison is faster than +expanded community list. + +@deffn Command {ip community-list standard @var{name} @{permit|deny@} @var{community}} {} +This command defines a new standard community list. @var{community} +is communities value. The @var{community} is compiled into community +structure. We can define multiple community list under same name. In +that case match will happen user defined order. Once the +community list matches to communities attribute in BGP updates it +return permit or deny by the community list definition. When there is +no matched entry, deny will be returned. When @var{community} is +empty it matches to any routes. +@end deffn + +@deffn Command {ip community-list expanded @var{name} @{permit|deny@} @var{line}} {} +This command defines a new expanded community list. @var{line} is a +string expression of communities attribute. @var{line} can include +regular expression to match communities attribute in BGP updates. +@end deffn + +@deffn Command {no ip community-list @var{name}} {} +@deffnx Command {no ip community-list standard @var{name}} {} +@deffnx Command {no ip community-list expanded @var{name}} {} +These commands delete community lists specified by @var{name}. All of +community lists shares a single name space. So community lists can be +removed simpley specifying community lists name. +@end deffn + +@deffn {Command} {show ip community-list} {} +@deffnx {Command} {show ip community-list @var{name}} {} +This command display current community list information. When +@var{name} is specified the specified community list's information is +shown. + +@example +# show ip community-list +Named Community standard list CLIST + permit 7675:80 7675:100 no-export + deny internet +Named Community expanded list EXPAND + permit : + +# show ip community-list CLIST +Named Community standard list CLIST + permit 7675:80 7675:100 no-export + deny internet +@end example +@end deffn + +@node Numbered BGP Community Lists +@subsection Numbered BGP Community Lists + +When number is used for BGP community list name, the number has +special meanings. Community list number in the range from 1 and 99 is +standard community list. Community list number in the range from 100 +to 199 is expanded community list. These community lists are called +as numbered community lists. On the other hand normal community lists +is called as named community lists. + +@deffn Command {ip community-list <1-99> @{permit|deny@} @var{community}} {} +This command defines a new community list. <1-99> is standard +community list number. Community list name within this range defines +standard community list. When @var{community} is empty it matches to +any routes. +@end deffn + +@deffn Command {ip community-list <100-199> @{permit|deny@} @var{community}} {} +This command defines a new community list. <100-199> is expanded +community list number. Community list name within this range defines +expanded community list. +@end deffn + +@deffn Command {ip community-list @var{name} @{permit|deny@} @var{community}} {} +When community list type is not specifed, the community list type is +automatically detected. If @var{community} can be compiled into +communities attribute, the community list is defined as a standard +community list. Otherwise it is defined as an expanded community +list. This feature is left for backward compability. Use of this +feature is not recommended. +@end deffn + +@node BGP Community in Route Map +@subsection BGP Community in Route Map + +In Route Map (@pxref{Route Map}), we can match or set BGP +communities attribute. Using this feature network operator can +implement their network policy based on BGP communities attribute. + +Following commands can be used in Route Map. + +@deffn {Route Map} {match community @var{word}} {} +@deffnx {Route Map} {match community @var{word} exact-match} {} +This command perform match to BGP updates using community list +@var{word}. When the one of BGP communities value match to the one of +communities value in community list, it is match. When +@code{exact-match} keyword is spcified, match happen only when BGP +updates have completely same communities value specified in the +community list. +@end deffn + +@deffn {Route Map} {set community none} {} +@deffnx {Route Map} {set community @var{community}} {} +@deffnx {Route Map} {set community @var{community} additive} {} +This command manipulate communities value in BGP updates. When +@code{none} is specified as communities value, it removes entire +communities attribute from BGP updates. When @var{community} is not +@code{none}, specified communities value is set to BGP updates. If +BGP updates already has BGP communities value, the existing BGP +communities value is replaced with specified @var{community} value. +When @code{additive} keyword is specified, @var{community} is appended +to the existing communities value. +@end deffn + +@deffn {Route Map} {set comm-list @var{word} delete} {} +This command remove communities value from BGP communities attribute. +The @var{word} is community list name. When BGP route's communities +value matches to the community list @var{word}, the communities value +is removed. When all of communities value is removed eventually, the +BGP update's communities attribute is completely removed. +@end deffn + +@node Display BGP Routes by Community +@subsection Display BGP Routes by Community + +To show BGP routes which has specific BGP communities attribute, +@code{show ip bgp} command can be used. The @var{community} value and +community list can be used for @code{show ip bgp} command. + +@deffn Command {show ip bgp community} {} +@deffnx Command {show ip bgp community @var{community}} {} +@deffnx Command {show ip bgp community @var{community} exact-match} {} +@code{show ip bgp community} displays BGP routes which has communities +attribute. When @var{community} is specified, BGP routes that matches +@var{community} value is displayed. For this command, @code{internet} +keyword can't be used for @var{community} value. When +@code{exact-match} is specified, it display only routes that have an +exact match. +@end deffn + +@deffn Command {show ip bgp community-list @var{word}} {} +@deffnx Command {show ip bgp community-list @var{word} exact-match} {} +This commands display BGP routes that matches community list +@var{word}. When @code{exact-match} is specified, display only routes +that have an exact match. +@end deffn + +@node Using BGP Communities Attribute +@subsection Using BGP Communities Attribute + +Following configuration is the most typical usage of BGP communities +attribute. AS 7675 provides upstream Internet connection to AS 100. +When following configuration exists in AS 7675, AS 100 networks +operator can set local preference in AS 7675 network by setting BGP +communities attribute to the updates. + +@example +router bgp 7675 + neighbor 192.168.0.1 remote-as 100 + neighbor 192.168.0.1 route-map RMAP in +! +ip community-list 70 permit 7675:70 +ip community-list 70 deny +ip community-list 80 permit 7675:80 +ip community-list 80 deny +ip community-list 90 permit 7675:90 +ip community-list 90 deny +! +route-map RMAP permit 10 + match community 70 + set local-preference 70 +! +route-map RMAP permit 20 + match community 80 + set local-preference 80 +! +route-map RMAP permit 30 + match community 90 + set local-preference 90 +@end example + +Following configuration announce 10.0.0.0/8 from AS 100 to AS 7675. +The route has communities value 7675:80 so when above configuration +exists in AS 7675, announced route's local preference will be set to +value 80. + +@example +router bgp 100 + network 10.0.0.0/8 + neighbor 192.168.0.2 remote-as 7675 + neighbor 192.168.0.2 route-map RMAP out +! +ip prefix-list PLIST permit 10.0.0.0/8 +! +route-map RMAP permit 10 + match ip address prefix-list PLIST + set community 7675:80 +@end example + +Following configuration is an example of BGP route filtering using +communities attribute. This configuration only permit BGP routes +which has BGP communities value 0:80 or 0:90. Network operator can +put special internal communities value at BGP border router, then +limit the BGP routes announcement into the internal network. + +@example +router bgp 7675 + neighbor 192.168.0.1 remote-as 100 + neighbor 192.168.0.1 route-map RMAP in +! +ip community-list 1 permit 0:80 0:90 +! +route-map RMAP permit in + match community 1 +@end example + +Following exmaple filter BGP routes which has communities value 1:1. +When there is no match community-list returns deny. To avoid +filtering all of routes, we need to define permit any at last. + +@example +router bgp 7675 + neighbor 192.168.0.1 remote-as 100 + neighbor 192.168.0.1 route-map RMAP in +! +ip community-list standard FILTER deny 1:1 +ip community-list standard FILTER permit +! +route-map RMAP permit 10 + match community FILTER +@end example + +Communities value keyword @code{internet} has special meanings in +standard community lists. In below example @code{internet} act as +match any. It matches all of BGP routes even if the route does not +have communities attribute at all. So community list @code{INTERNET} +is same as above example's @code{FILTER}. + +@example +ip community-list standard INTERNET deny 1:1 +ip community-list standard INTERNET permit internet +@end example + +Following configuration is an example of communities value deletion. +With this configuration communities value 100:1 and 100:2 is removed +from BGP updates. For communities value deletion, only @code{permit} +community-list is used. @code{deny} community-list is ignored. + +@example +router bgp 7675 + neighbor 192.168.0.1 remote-as 100 + neighbor 192.168.0.1 route-map RMAP in +! +ip community-list standard DEL permit 100:1 100:2 +! +route-map RMAP permit 10 + set comm-list DEL delete +@end example + +@c ----------------------------------------------------------------------- +@node BGP Extended Communities Attribute +@section BGP Extended Communities Attribute + +BGP extended communities attribute is introduced with MPLS VPN/BGP +technology. MPLS VPN/BGP expands capability of network infrastructure +to provide VPN functionality. At the same time it requires a new +framework for policy routing. With BGP Extended Communities Attribute +we can use Route Target or Site of Origin for implementing network +policy for MPLS VPN/BGP. + +BGP Extended Communities Attribute is similar to BGP Communities +Attribute. It is an optional transitive attribute. BGP Extended +Communities Attribute can carry multiple Extended Community value. +Each Extended Community value is eight octet length. + +BGP Extended Communities Attribute provides an extended range +compared with BGP Communities Attribute. Adding to that there is a +type field in each value to provides community space structure. + +There are two format to define Extended Community value. One is AS +based format the other is IP address based format. + +@table @code +@item AS:VAL +This is a format to define AS based Extended Community value. +@code{AS} part is 2 octets Global Administrator subfield in Extended +Community value. @code{VAL} part is 4 octets Local Administrator +subfield. @code{7675:100} represents AS 7675 policy value 100. +@item IP-Address:VAL +This is a format to define IP address based Extended Community value. +@code{IP-Address} part is 4 octets Global Administrator subfield. +@code{VAL} part is 2 octets Local Administrator subfield. +@code{10.0.0.1:100} represents +@end table + +@menu +* BGP Extended Community Lists:: +* BGP Extended Communities in Route Map:: +@end menu + +@node BGP Extended Community Lists +@subsection BGP Extended Community Lists + +Expanded Community Lists is a user defined BGP Expanded Community +Lists. + +@deffn Command {ip extcommunity-list standard @var{name} @{permit|deny@} @var{extcommunity}} {} +This command defines a new standard extcommunity-list. +@var{extcommunity} is extended communities value. The +@var{extcommunity} is compiled into extended community structure. We +can define multiple extcommunity-list under same name. In that case +match will happen user defined order. Once the extcommunity-list +matches to extended communities attribute in BGP updates it return +permit or deny based upon the extcommunity-list definition. When +there is no matched entry, deny will be returned. When +@var{extcommunity} is empty it matches to any routes. +@end deffn + +@deffn Command {ip extcommunity-list expanded @var{name} @{permit|deny@} @var{line}} {} +This command defines a new expanded extcommunity-list. @var{line} is +a string expression of extended communities attribute. @var{line} can +include regular expression to match extended communities attribute in +BGP updates. +@end deffn + +@deffn Command {no ip extcommunity-list @var{name}} {} +@deffnx Command {no ip extcommunity-list standard @var{name}} {} +@deffnx Command {no ip extcommunity-list expanded @var{name}} {} +These commands delete extended community lists specified by +@var{name}. All of extended community lists shares a single name +space. So extended community lists can be removed simpley specifying +the name. +@end deffn + +@deffn {Command} {show ip extcommunity-list} {} +@deffnx {Command} {show ip extcommunity-list @var{name}} {} +This command display current extcommunity-list information. When +@var{name} is specified the community list's information is shown. + +@example +# show ip extcommunity-list +@end example +@end deffn + +@node BGP Extended Communities in Route Map +@subsection BGP Extended Communities in Route Map + +@deffn {Route Map} {match extcommunity @var{word}} {} +@end deffn + +@deffn {Route Map} {set extcommunity rt @var{extcommunity}} {} +This command set Route Target value. +@end deffn + +@deffn {Route Map} {set extcommunity soo @var{extcommunity}} {} +This command set Site of Origin value. +@end deffn + +@c ----------------------------------------------------------------------- +@node Displaying BGP routes +@section Displaying BGP Routes + +@menu +* Show IP BGP:: +* More Show IP BGP:: +@end menu + +@node Show IP BGP +@subsection Show IP BGP + +@deffn {Command} {show ip bgp} {} +@deffnx {Command} {show ip bgp @var{A.B.C.D}} {} +@deffnx {Command} {show ip bgp @var{X:X::X:X}} {} +This command displays BGP routes. When no route is specified it +display all of IPv4 BGP routes. +@end deffn + +@example +BGP table version is 0, local router ID is 10.1.1.1 +Status codes: s suppressed, d damped, h history, * valid, > best, i - internal +Origin codes: i - IGP, e - EGP, ? - incomplete + + Network Next Hop Metric LocPrf Weight Path +*> 1.1.1.1/32 0.0.0.0 0 32768 i + +Total number of prefixes 1 +@end example + +@node More Show IP BGP +@subsection More Show IP BGP + +@deffn {Command} {show ip bgp regexp @var{line}} {} +This command display BGP routes using AS path regular expression (@pxref{Display BGP Routes by AS Path}). +@end deffn + +@deffn Command {show ip bgp community @var{community}} {} +@deffnx Command {show ip bgp community @var{community} exact-match} {} +This command display BGP routes using @var{community} (@pxref{Display +BGP Routes by Community}). +@end deffn + +@deffn Command {show ip bgp community-list @var{word}} {} +@deffnx Command {show ip bgp community-list @var{word} exact-match} {} +This command display BGP routes using community list (@pxref{Display +BGP Routes by Community}). +@end deffn + +@deffn {Command} {show ip bgp summary} {} +@end deffn + +@deffn {Command} {show ip bgp neighbor [@var{peer}]} {} +@end deffn + +@deffn {Command} {clear ip bgp @var{peer}} {} +Clear peers which have addresses of X.X.X.X +@end deffn + +@deffn {Command} {clear ip bgp @var{peer} soft in} {} +Clear peer using soft reconfiguration. +@end deffn + +@deffn {Command} {show ip bgp dampened-paths} {} +Display paths suppressed due to dampening +@end deffn + +@deffn {Command} {show ip bgp flap-statistics} {} +Display flap statistics of routes +@end deffn + +@deffn {Command} {show debug} {} +@end deffn + +@deffn {Command} {debug event} {} +@end deffn + +@deffn {Command} {debug update} {} +@end deffn + +@deffn {Command} {debug keepalive} {} +@end deffn + +@deffn {Command} {no debug event} {} +@end deffn + +@deffn {Command} {no debug update} {} +@end deffn + +@deffn {Command} {no debug keepalive} {} +@end deffn + +@node Capability Negotiation +@section Capability Negotiation + +When adding IPv6 routing information exchange feature to BGP. There +were some proposals. @acronym{IETF,Internet Engineering Task Force} +@acronym{IDR, Inter Domain Routing} @acronym{WG, Working group} adopted +a proposal called Multiprotocol Extension for BGP. The specification +is described in @cite{RFC2283}. The protocol does not define new protocols. +It defines new attributes to existing BGP. When it is used exchanging +IPv6 routing information it is called BGP-4+. When it is used for +exchanging multicast routing information it is called MBGP. + +@command{bgpd} supports Multiprotocol Extension for BGP. So if remote +peer supports the protocol, @command{bgpd} can exchange IPv6 and/or +multicast routing information. + +Traditional BGP did not have the feature to detect remote peer's +capabilities, e.g. whether it can handle prefix types other than IPv4 +unicast routes. This was a big problem using Multiprotocol Extension +for BGP to operational network. @cite{RFC2842, Capabilities +Advertisement with BGP-4} adopted a feature called Capability +Negotiation. @command{bgpd} use this Capability Negotiation to detect +the remote peer's capabilities. If the peer is only configured as IPv4 +unicast neighbor, @command{bgpd} does not send these Capability +Negotiation packets (at least not unless other optional BGP features +require capability negotation). + +By default, Quagga will bring up peering with minimal common capability +for the both sides. For example, local router has unicast and +multicast capabilitie and remote router has unicast capability. In +this case, the local router will establish the connection with unicast +only capability. When there are no common capabilities, Quagga sends +Unsupported Capability error and then resets the connection. + +If you want to completely match capabilities with remote peer. Please +use @command{strict-capability-match} command. + +@deffn {BGP} {neighbor @var{peer} strict-capability-match} {} +@deffnx {BGP} {no neighbor @var{peer} strict-capability-match} {} +Strictly compares remote capabilities and local capabilities. If capabilities +are different, send Unsupported Capability error then reset connection. +@end deffn + +You may want to disable sending Capability Negotiation OPEN message +optional parameter to the peer when remote peer does not implement +Capability Negotiation. Please use @command{dont-capability-negotiate} +command to disable the feature. + +@deffn {BGP} {neighbor @var{peer} dont-capability-negotiate} {} +@deffnx {BGP} {no neighbor @var{peer} dont-capability-negotiate} {} +Suppress sending Capability Negotiation as OPEN message optional +parameter to the peer. This command only affects the peer is configured +other than IPv4 unicast configuration. +@end deffn + +When remote peer does not have capability negotiation feature, remote +peer will not send any capabilities at all. In that case, bgp +configures the peer with configured capabilities. + +You may prefer locally configured capabilities more than the negotiated +capabilities even though remote peer sends capabilities. If the peer +is configured by @command{override-capability}, @command{bgpd} ignores +received capabilities then override negotiated capabilities with +configured values. + +@deffn {BGP} {neighbor @var{peer} override-capability} {} +@deffnx {BGP} {no neighbor @var{peer} override-capability} {} +Override the result of Capability Negotiation with local configuration. +Ignore remote peer's capability value. +@end deffn + +@node Route Reflector +@section Route Reflector + +@deffn {BGP} {bgp cluster-id @var{a.b.c.d}} {} +@end deffn + +@deffn {BGP} {neighbor @var{peer} route-reflector-client} {} +@deffnx {BGP} {no neighbor @var{peer} route-reflector-client} {} +@end deffn + +@node Route Server +@section Route Server + +At an Internet Exchange point, many ISPs are connected to each other by +external BGP peering. Normally these external BGP connection are done by +@samp{full mesh} method. As with internal BGP full mesh formation, +this method has a scaling problem. + +This scaling problem is well known. Route Server is a method to resolve +the problem. Each ISP's BGP router only peers to Route Server. Route +Server serves as BGP information exchange to other BGP routers. By +applying this method, numbers of BGP connections is reduced from +O(n*(n-1)/2) to O(n). + +Unlike normal BGP router, Route Server must have several routing tables +for managing different routing policies for each BGP speaker. We call the +routing tables as different @code{view}s. @command{bgpd} can work as +normal BGP router or Route Server or both at the same time. + +@menu +* Multiple instance:: +* BGP instance and view:: +* Routing policy:: +* Viewing the view:: +@end menu + +@node Multiple instance +@subsection Multiple instance + +To enable multiple view function of @code{bgpd}, you must turn on +multiple instance feature beforehand. + +@deffn {Command} {bgp multiple-instance} {} +Enable BGP multiple instance feature. After this feature is enabled, +you can make multiple BGP instances or multiple BGP views. +@end deffn + +@deffn {Command} {no bgp multiple-instance} {} +Disable BGP multiple instance feature. You can not disable this feature +when BGP multiple instances or views exist. +@end deffn + +When you want to make configuration more Cisco like one, + +@deffn {Command} {bgp config-type cisco} {} +Cisco compatible BGP configuration output. +@end deffn + +When bgp config-type cisco is specified, + +``no synchronization'' is displayed. +``no auto-summary'' is displayed. + +``network'' and ``aggregate-address'' argument is displayed as +``A.B.C.D M.M.M.M'' + +Quagga: network 10.0.0.0/8 +Cisco: network 10.0.0.0 + +Quagga: aggregate-address 192.168.0.0/24 +Cisco: aggregate-address 192.168.0.0 255.255.255.0 + +Community attribute handling is also different. If there is no +configuration is specified community attribute and extended community +attribute are sent to neighbor. When user manually disable the +feature community attribute is not sent to the neighbor. In case of +@command{bgp config-type cisco} is specified, community attribute is not +sent to the neighbor by default. To send community attribute user has +to specify @command{neighbor A.B.C.D send-community} command. + +@example +! +router bgp 1 + neighbor 10.0.0.1 remote-as 1 + no neighbor 10.0.0.1 send-community +! +router bgp 1 + neighbor 10.0.0.1 remote-as 1 + neighbor 10.0.0.1 send-community +! +@end example + +@deffn {Command} {bgp config-type zebra} {} +Quagga style BGP configuration. This is default. +@end deffn + +@node BGP instance and view +@subsection BGP instance and view + +BGP instance is a normal BGP process. The result of route selection +goes to the kernel routing table. You can setup different AS at the +same time when BGP multiple instance feature is enabled. + +@deffn {Command} {router bgp @var{as-number}} {} +Make a new BGP instance. You can use arbitrary word for the @var{name}. +@end deffn + +@example +@group +bgp multiple-instance +! +router bgp 1 + neighbor 10.0.0.1 remote-as 2 + neighbor 10.0.0.2 remote-as 3 +! +router bgp 2 + neighbor 10.0.0.3 remote-as 4 + neighbor 10.0.0.4 remote-as 5 +@end group +@end example + +BGP view is almost same as normal BGP process. The result of +route selection does not go to the kernel routing table. BGP view is +only for exchanging BGP routing information. + +@deffn {Command} {router bgp @var{as-number} view @var{name}} {} +Make a new BGP view. You can use arbitrary word for the @var{name}. This +view's route selection result does not go to the kernel routing table. +@end deffn + +With this command, you can setup Route Server like below. + +@example +@group +bgp multiple-instance +! +router bgp 1 view 1 + neighbor 10.0.0.1 remote-as 2 + neighbor 10.0.0.2 remote-as 3 +! +router bgp 2 view 2 + neighbor 10.0.0.3 remote-as 4 + neighbor 10.0.0.4 remote-as 5 +@end group +@end example + +@node Routing policy +@subsection Routing policy + +You can set different routing policy for a peer. For example, you can +set different filter for a peer. + +@example +@group +bgp multiple-instance +! +router bgp 1 view 1 + neighbor 10.0.0.1 remote-as 2 + neighbor 10.0.0.1 distribute-list 1 in +! +router bgp 1 view 2 + neighbor 10.0.0.1 remote-as 2 + neighbor 10.0.0.1 distribute-list 2 in +@end group +@end example + +This means BGP update from a peer 10.0.0.1 goes to both BGP view 1 and view +2. When the update is inserted into view 1, distribute-list 1 is +applied. On the other hand, when the update is inserted into view 2, +distribute-list 2 is applied. + +@node Viewing the view +@subsection Viewing the view + +To display routing table of BGP view, you must specify view name. + +@deffn {Command} {show ip bgp view @var{name}} {} +Display routing table of BGP view @var{name}. +@end deffn + +@node How to set up a 6-Bone connection +@section How to set up a 6-Bone connection + + +@example +@group +zebra configuration +=================== +! +! Actually there is no need to configure zebra +! + +bgpd configuration +================== +! +! This means that routes go through zebra and into the kernel. +! +router zebra +! +! MP-BGP configuration +! +router bgp 7675 + bgp router-id 10.0.0.1 + neighbor 3ffe:1cfa:0:2:2a0:c9ff:fe9e:f56 remote-as @var{as-number} +! + address-family ipv6 + network 3ffe:506::/32 + neighbor 3ffe:1cfa:0:2:2a0:c9ff:fe9e:f56 activate + neighbor 3ffe:1cfa:0:2:2a0:c9ff:fe9e:f56 route-map set-nexthop out + neighbor 3ffe:1cfa:0:2:2c0:4fff:fe68:a231 remote-as @var{as-number} + neighbor 3ffe:1cfa:0:2:2c0:4fff:fe68:a231 route-map set-nexthop out + exit-address-family +! +ipv6 access-list all permit any +! +! Set output nexthop address. +! +route-map set-nexthop permit 10 + match ipv6 address all + set ipv6 nexthop global 3ffe:1cfa:0:2:2c0:4fff:fe68:a225 + set ipv6 nexthop local fe80::2c0:4fff:fe68:a225 +! +! logfile FILENAME is obsolete. Please use log file FILENAME + +log file bgpd.log +! +@end group +@end example + +@node Dump BGP packets and table +@section Dump BGP packets and table + +@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} [@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-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 + +Example of a session to an upstream, advertising only one prefix to it. + +@example +router bgp 64512 + bgp router-id 10.236.87.1 + network 10.236.87.0/24 + neighbor upstream peer-group + neighbor upstream remote-as 64515 + neighbor upstream capability dynamic + neighbor upstream prefix-list pl-allowed-adv out + neighbor 10.1.1.1 peer-group upstream + neighbor 10.1.1.1 description ACME ISP +! +ip prefix-list pl-allowed-adv seq 5 permit 82.195.133.0/25 +ip prefix-list pl-allowed-adv seq 10 deny any + +@end example + +A more complex example. With upstream, peer and customer sessions. +Advertising global prefixes and NO_EXPORT prefixes and providing +actions for customer routes based on community values. Extensive use of +route-maps and the 'call' feature to support selective advertising of +prefixes. This example is intended as guidance only, it has NOT been +tested and almost certainly containts silly mistakes, if not serious +flaws. + +@example +router bgp 64512 + bgp router-id 10.236.87.1 + network 10.123.456.0/24 + network 10.123.456.128/25 route-map rm-no-export + neighbor upstream capability dynamic + neighbor upstream route-map rm-upstream-out out + neighbor cust capability dynamic + neighbor cust route-map rm-cust-in in + neighbor cust route-map rm-cust-out out + neighbor cust send-community both + neighbor peer capability dynamic + neighbor peer route-map rm-peer-in in + neighbor peer route-map rm-peer-out out + neighbor peer send-community both + neighbor 10.1.1.1 remote-as 64515 + neighbor 10.1.1.1 peer-group upstream + neighbor 10.2.1.1 remote-as 64516 + neighbor 10.2.1.1 peer-group upstream + neighbor 10.3.1.1 remote-as 64517 + neighbor 10.3.1.1 peer-group cust-default + neighbor 10.3.1.1 description customer1 + neighbor 10.3.1.1 prefix-list pl-cust1-network in + neighbor 10.4.1.1 remote-as 64518 + neighbor 10.4.1.1 peer-group cust + neighbor 10.4.1.1 prefix-list pl-cust2-network in + neighbor 10.4.1.1 description customer2 + neighbor 10.5.1.1 remote-as 64519 + neighbor 10.5.1.1 peer-group peer + neighbor 10.5.1.1 prefix-list pl-peer1-network in + neighbor 10.5.1.1 description peer AS 1 + neighbor 10.6.1.1 remote-as 64520 + neighbor 10.6.1.1 peer-group peer + neighbor 10.6.1.1 prefix-list pl-peer2-network in + neighbor 10.6.1.1 description peer AS 2 +! +ip prefix-list pl-default permit 0.0.0.0/0 +! +ip prefix-list pl-upstream-peers permit 10.1.1.1/32 +ip prefix-list pl-upstream-peers permit 10.2.1.1/32 +! +ip prefix-list pl-cust1-network permit 10.3.1.0/24 +ip prefix-list pl-cust1-network permit 10.3.2.0/24 +! +ip prefix-list pl-cust2-network permit 10.4.1.0/24 +! +ip prefix-list pl-peer1-network permit 10.5.1.0/24 +ip prefix-list pl-peer1-network permit 10.5.2.0/24 +ip prefix-list pl-peer1-network permit 192.168.0.0/24 +! +ip prefix-list pl-peer2-network permit 10.6.1.0/24 +ip prefix-list pl-peer2-network permit 10.6.2.0/24 +ip prefix-list pl-peer2-network permit 192.168.1.0/24 +ip prefix-list pl-peer2-network permit 192.168.2.0/24 +ip prefix-list pl-peer2-network permit 172.16.1/24 +! +ip as-path access-list asp-own-as permit ^$ +ip as-path access-list asp-own-as permit _64512_ +! +! ################################################################# +! Match communities we provide actions for, on routes receives from +! customers. Communities values of :X, with X, have actions: +! +! 100 - blackhole the prefix +! 200 - set no_export +! 300 - advertise only to other customers +! 400 - advertise only to upstreams +! 500 - set no_export when advertising to upstreams +! 2X00 - set local_preference to X00 +! +! blackhole the prefix of the route +ip community-list standard cm-blackhole permit 64512:100 +! +! set no-export community before advertising +ip community-list standard cm-set-no-export permit 64512:200 +! +! advertise only to other customers +ip community-list standard cm-cust-only permit 64512:300 +! +! advertise only to upstreams +ip community-list standard cm-upstream-only permit 64512:400 +! +! advertise to upstreams with no-export +ip community-list standard cm-upstream-noexport permit 64512:500 +! +! set local-pref to least significant 3 digits of the community +ip community-list standard cm-prefmod-100 permit 64512:2100 +ip community-list standard cm-prefmod-200 permit 64512:2200 +ip community-list standard cm-prefmod-300 permit 64512:2300 +ip community-list standard cm-prefmod-400 permit 64512:2400 +ip community-list expanded cme-prefmod-range permit 64512:2... +! +! Informational communities +! +! 3000 - learned from upstream +! 3100 - learned from customer +! 3200 - learned from peer +! +ip community-list standard cm-learnt-upstream permit 64512:3000 +ip community-list standard cm-learnt-cust permit 64512:3100 +ip community-list standard cm-learnt-peer permit 64512:3200 +! +! ################################################################### +! Utility route-maps +! +! These utility route-maps generally should not used to permit/deny +! routes, i.e. they do not have meaning as filters, and hence probably +! should be used with 'on-match next'. These all finish with an empty +! permit entry so as not interfere with processing in the caller. +! +route-map rm-no-export permit 10 + set community additive no-export +route-map rm-no-export permit 20 +! +route-map rm-blackhole permit 10 + description blackhole, up-pref and ensure it cant escape this AS + set ip next-hop 127.0.0.1 + set local-preference 10 + set community additive no-export +route-map rm-blackhole permit 20 +! +! Set local-pref as requested +route-map rm-prefmod permit 10 + match community cm-prefmod-100 + set local-preference 100 +route-map rm-prefmod permit 20 + match community cm-prefmod-200 + set local-preference 200 +route-map rm-prefmod permit 30 + match community cm-prefmod-300 + set local-preference 300 +route-map rm-prefmod permit 40 + match community cm-prefmod-400 + set local-preference 400 +route-map rm-prefmod permit 50 +! +! Community actions to take on receipt of route. +route-map rm-community-in permit 10 + description check for blackholing, no point continuing if it matches. + match community cm-blackhole + call rm-blackhole +route-map rm-community-in permit 20 + match community cm-set-no-export + call rm-no-export + on-match next +route-map rm-community-in permit 30 + match community cme-prefmod-range + call rm-prefmod +route-map rm-community-in permit 40 +! +! ##################################################################### +! Community actions to take when advertising a route. +! These are filtering route-maps, +! +! Deny customer routes to upstream with cust-only set. +route-map rm-community-filt-to-upstream deny 10 + match community cm-learnt-cust + match community cm-cust-only +route-map rm-community-filt-to-upstream permit 20 +! +! Deny customer routes to other customers with upstream-only set. +route-map rm-community-filt-to-cust deny 10 + match community cm-learnt-cust + match community cm-upstream-only +route-map rm-community-filt-to-cust permit 20 +! +! ################################################################### +! The top-level route-maps applied to sessions. Further entries could +! be added obviously.. +! +! Customers +route-map rm-cust-in permit 10 + call rm-community-in + on-match next +route-map rm-cust-in permit 20 + set community additive 64512:3100 +route-map rm-cust-in permit 30 +! +route-map rm-cust-out permit 10 + call rm-community-filt-to-cust + on-match next +route-map rm-cust-out permit 20 +! +! Upstream transit ASes +route-map rm-upstream-out permit 10 + description filter customer prefixes which are marked cust-only + call rm-community-filt-to-upstream + on-match next +route-map rm-upstream-out permit 20 + description only customer routes are provided to upstreams/peers + match community cm-learnt-cust +! +! Peer ASes +! outbound policy is same as for upstream +route-map rm-peer-out permit 10 + call rm-upstream-out +! +route-map rm-peer-in permit 10 + set community additive 64512:3200 +@end example diff --git a/doc/defines.texi b/doc/defines.texi new file mode 100644 index 0000000..b7b529e --- /dev/null +++ b/doc/defines.texi @@ -0,0 +1,14 @@ +@c -*- texinfo -*- + +@c Set variables +@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} + +@c These may vary with installation environment. +@set INSTALL_PREFIX_ETC /etc/quagga +@set INSTALL_PREFIX_SBIN /usr/sbin +@set INSTALL_PREFIX_STATE /var/run/quagga diff --git a/doc/draft-zebra-00.ms b/doc/draft-zebra-00.ms new file mode 100644 index 0000000..2599472 --- /dev/null +++ b/doc/draft-zebra-00.ms @@ -0,0 +1,209 @@ +.pl 10.0i +.po 0 +.ll 7.2i +.lt 7.2i +.nr LL 7.2i +.nr LT 7.2i +.ds LF Ishiguro +.ds RF FORMFEED[Page %] +.ds CF +.ds LH RFC DRAFT +.ds RH March 1998 +.ds CH +.hy 0 +.ad l +Network Working Group K. Ishiguro +Request for Comments: DRAFT Digital Magic Labs, Inc. + March 1998 +.sp 2 +.ce +Zebra Protocol Draft +.sp 2 +.fi +.ne 4 +Status of this Memo +.sp +.in 3 +This draft is very eary beta version. +.sp +.in 0 +.ne 4 +Introduction +.sp +.in 3 +The zebra protocol is a communication protocol between kernel +routing table manager and routing protocol daemon. It is built over +TCP/IP protocol suite. +.sp +.in 0 +.ne 4 +Request message formats +.sp +.in 3 +zebra is TCP-based protocol. +.sp +Below is request packet format. +.sp +.in 0 +.DS +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 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Length (2) | Command (1) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +.DE +.sp +.in 3 +Length is total packet length. +.sp +Here is summary of command list. +.sp +.in 0 +.DS +1 - ZEBRA_IPV4_ROUTE_ADD +2 - ZEBRA_IPV4_ROUTE_DELETE +3 - ZEBRA_IPV6_ROUTE_ADD +4 - ZEBRA_IPV6_ROUTE_DELETE +5 - ZEBRA_GET_ONE_INTERFACE +6 - ZEBRA_GET_ALL_INTERFACE +7 - ZEBRA_GET_HOSTINFO +.DE +.sp +.in 0 +.ne 4 +IPv4 reply message formats +.sp +.in 0 +.DS +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 (1) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Gateway (4) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +.DE +.sp +.in 3 +Type field specify route's origin type. +.sp +.in 0 +.DS +1 - ZEBRA_ROUTE_RESERVE +2 - ZEBRA_ROUTE_CONNECT +3 - ZEBRA_ROUTE_STATIC +4 - ZEBRA_ROUTE_RIP +5 - ZEBRA_ROUTE_RIPNG +6 - ZEBRA_ROUTE_BGP +7 - ZEBRA_ROUTE_RADIX +.DE +.sp +.in 3 +After above message there can be variale length IPv4 prefix data. +Each IPv4 prefix is encoded as a two tuple of the form +.sp +.in 0 +.DS ++----------------------+ +|Subnet mask (1 octet) | ++----------------------+ +|IPv4 prefix (variable)| ++----------------------+ +.DE +.sp +.in 0 +.ne 4 +IPv6 reply message formats +.sp +.in 0 +.DS +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 (1) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Gateway (16) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +.DE +.sp +.in 3 +Type field specify route's origin type. +.sp +.in 0 +.DS +1 - ZEBRA_ROUTE_RESERVE +2 - ZEBRA_ROUTE_CONNECT +3 - ZEBRA_ROUTE_STATIC +4 - ZEBRA_ROUTE_RIP +5 - ZEBRA_ROUTE_RIPNG +6 - ZEBRA_ROUTE_BGP +7 - ZEBRA_ROUTE_RADIX +.DE +.sp +.in 0 +.DS ++----------------------+ +| ifindex (4 octet) | ++----------------------+ +| prefixlen (1 octet)| ++----------------------+ +|IPv6 prefix (variable)| ++----------------------+ +.DE +.sp +.in 3 +I am not sure but it seems some operation systems IPv6 +implementation may need interface index when add and delete +linklocal routes. +.sp +I have added ifindex field to specify IPv6 routes interface +index. If this index is value zero, it will ignored. +.sp +.in 0 +.ne 4 +Interface information message format. +.sp +.in 0 +.DS +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 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Interface name (20) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Index (1) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Inteface flag (4) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Inteface metric (4) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Inteface MTU (4) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Inteface Address count (4) | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +.DE +.sp +.in 3 +Address message format. +.sp +.in 0 +.ne 4 +Host inforamtion message format. +.sp +.in 0 +.DS +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 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +|IPv4 forwarding|IPv6 forwarding| ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +.DE +.sp +.in 3 +Host information contain IPv4/IPv6 forwarding information. diff --git a/doc/fig-normal-processing.dia b/doc/fig-normal-processing.dia new file mode 100644 index 0000000..c9e8e68 --- /dev/null +++ b/doc/fig-normal-processing.dia @@ -0,0 +1,1738 @@ + + + + + + + + + + + + + #A4# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Best +Path +Selection# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Local +RIB# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #From Peer A# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #From Peer B# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #From Peer C# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #From Peer D# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #To Peer A# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #To Peer B# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #To Peer C# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #To Peer D# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #D# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #C# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #B# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #A# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #A# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #B# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #C# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #D# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #X# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #“Out” Filter +for Peer X# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #“In” Filter +for Peer X# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #X# + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/fig-normal-processing.png b/doc/fig-normal-processing.png new file mode 100644 index 0000000..e4b8fdc Binary files /dev/null and b/doc/fig-normal-processing.png differ diff --git a/doc/fig-normal-processing.txt b/doc/fig-normal-processing.txt new file mode 100644 index 0000000..01f0e17 --- /dev/null +++ b/doc/fig-normal-processing.txt @@ -0,0 +1,11 @@ + + _______________________________ + / _________ _________ \ +From Peer A --->|(A)-|Best | | |-[A]|--->To Peer A +From Peer B --->|(B)-|Path |-->|Local-RIB|-[B]|--->To Peer B +From Peer C --->|(C)-|Selection| | |-[C]|--->To Peer C +From Peer D --->|(D)-|_________| |_________|-[D]|--->To Peer D + \_______________________________/ + +Key: (X) - 'In' Filter applied to Peer X's announcements + [X] - 'Out' Filter applied to announcements to Peer X diff --git a/doc/fig-rs-processing.dia b/doc/fig-rs-processing.dia new file mode 100644 index 0000000..de7d79f --- /dev/null +++ b/doc/fig-rs-processing.dia @@ -0,0 +1,4426 @@ + + + + + + + + + + + + + #A4# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Best +Path +Selection# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Main +Loc-RIB# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #From Peer A# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #From RS-Client B# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #D# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #C# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #B# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #A# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #From RS-Client D# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #From RS-Client C# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Best +Path +Selection# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Loc-RIB +For C# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Best +Path +Selection# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Loc-RIB +For D# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Best +Path +Selection# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Loc-RIB +For B# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #D# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #C# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #B# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #B# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #B# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #C# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #C# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #C# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #D# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #B# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #D# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #D# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #D# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #B# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #C# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #To RS-Client B# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #B# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #C# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #To RS-Client C# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #A# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #To Peer A# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #D# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #To RS-Client D# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #“In” Filter +for Peer X# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #X# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #X# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #“Out” Filter +for Peer X# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Export Policy +of RS-Client X# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #X# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Import Policy +of RS-Client X# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #X# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/fig-rs-processing.png b/doc/fig-rs-processing.png new file mode 100644 index 0000000..c0cac5c Binary files /dev/null and b/doc/fig-rs-processing.png differ diff --git a/doc/fig-rs-processing.txt b/doc/fig-rs-processing.txt new file mode 100644 index 0000000..eafe146 --- /dev/null +++ b/doc/fig-rs-processing.txt @@ -0,0 +1,47 @@ +From Peer A + | From RS-Client B + | | From RS-Client C + | | | From RS-Client D + | | | | + | | | | Main / Normal RIB + | | | | ________________________________ + | | | | / _________ _________ \ + | | | +--->|(D)-|Best | | Main | | + | | +--|--->|(C)-|Path |-->|Local-RIB|->[A]|--->To Peer A + | +--|--|--->|(B)-|Selection| | | | + +--|--|--|--->|(A)-|_________| |_________| | + | | | | \________________________________/ + | | | | + | | | | ________________________________ + | | | | / _________ _________ \ + | | | +--->*D*->|{B}-|Best | |RS-Client| | + | | +--|--->*C*->|{B}-|Path |-->|Local-RIB|->[B]|--->To RS-Client B + | | | | | |Selection| | for B | | + +--|--|--|-------->|{B}-|_________| |_________| | + | | | | \________________________________/ + | | | | + | | | | ________________________________ + | | | | / _________ _________ \ + | | | +--->*D*->|{C}-|Best | |RS-Client| | + | | | | | |Path |-->|Local-RIB|->[C]|--->To RS-Client C + | +--|--|--->*B*->|{C}-|Selection| | for C | | + +--|--|--|-------->|{C}-|_________| |_________| | + | | | \________________________________/ + | | | + | | | ________________________________ + | | | / _________ _________ \ + | | | | |Best | |RS-Client| | + | | +------>*C*->|{D}-|Path |-->|Local-RIB|->[D]|--->To RS-Client D + | +--------->*B*->|{D}-|Selection| | for D | | + +----------------->|{D}-|_________| |_________| | + \________________________________/ + + +Key: (X) - 'In' Filter applied to Peer X's announcements before + considering announcement for the normal main Local-RIB + [X] - 'Out' Filter applied to announcements to Peer X + *X* - 'Export' Filter of RS-Client X, to apply X's policies + before its routes may be considered for other RS-Clients + RIBs. + {X} - 'Import' Filter of RS-Client X, to apply X's policies + on routes before allowing them into X's RIB. diff --git a/doc/fig_topologies_full.txt b/doc/fig_topologies_full.txt new file mode 100644 index 0000000..cc8025a --- /dev/null +++ b/doc/fig_topologies_full.txt @@ -0,0 +1,6 @@ +(RF1)--(RF2) + | \ / | + | \/ | + | /\ | + | / \ | +(RF3)--(RF4) diff --git a/doc/fig_topologies_rs.txt b/doc/fig_topologies_rs.txt new file mode 100644 index 0000000..0bd1730 --- /dev/null +++ b/doc/fig_topologies_rs.txt @@ -0,0 +1,5 @@ +(RF1) (RF2) + \ / + [RS] + / \ +(RF3) (RF4) diff --git a/doc/filter.texi b/doc/filter.texi new file mode 100644 index 0000000..5a9a15e --- /dev/null +++ b/doc/filter.texi @@ -0,0 +1,182 @@ +@node Filtering +@comment node-name, next, previous, up +@chapter Filtering + +Quagga provides many very flexible filtering features. Filtering is used +for both input and output of the routing information. Once filtering is +defined, it can be applied in any direction. + +@menu +* IP Access List:: +* IP Prefix List:: +@end menu + +@node IP Access List +@comment node-name, next, previous, up +@section IP Access List + +@deffn {Command} {access-list @var{name} permit @var{ipv4-network}} {} +@deffnx {Command} {access-list @var{name} deny @var{ipv4-network}} {} +@end deffn + +Basic filtering is done by @code{access-list} as shown in the +following example. + +@example +access-list filter deny 10.0.0.0/9 +access-list filter permit 10.0.0.0/8 +@end example + +@node IP Prefix List +@comment node-name, next, previous, up +@section IP Prefix List + +@command{ip prefix-list} provides the most powerful prefix based +filtering mechanism. In addition to @command{access-list} functionality, +@command{ip prefix-list} has prefix length range specification and +sequential number specification. You can add or delete prefix based +filters to arbitrary points of prefix-list using sequential number specification. + +If no ip prefix-list is specified, it acts as permit. If @command{ip prefix-list} +is defined, and no match is found, default deny is applied. + +@c @deffn {Command} {ip prefix-list @var{name} [seq @var{number}] permit|deny [le @var{prefixlen}] [ge @var{prefixlen}]} {} +@deffn {Command} {ip prefix-list @var{name} (permit|deny) @var{prefix} [le @var{len}] [ge @var{len}]} {} +@deffnx {Command} {ip prefix-list @var{name} seq @var{number} (permit|deny) @var{prefix} [le @var{len}] [ge @var{len}]} {} + +You can create @command{ip prefix-list} using above commands. + +@table @asis + +@item @asis{seq} +seq @var{number} can be set either automatically or manually. In the +case that sequential numbers are set manually, the user may pick any +number less than 4294967295. In the case that sequential number are set +automatically, the sequential number will increase by a unit of five (5) +per list. If a list with no specified sequential number is created +after a list with a specified sequential number, the list will +automatically pick the next multiple of five (5) as the list number. +For example, if a list with number 2 already exists and a new list with +no specified number is created, the next list will be numbered 5. If +lists 2 and 7 already exist and a new list with no specified number is +created, the new list will be numbered 10. + +@item @asis{le} +@command{le} command specifies prefix length. The prefix list will be +applied if the prefix length is less than or equal to the le prefix length. + +@item @asis{ge} +@command{ge} command specifies prefix length. The prefix list will be +applied if the prefix length is greater than or equal to the ge prefix length. + +@end table + +@end deffn + +Less than or equal to prefix numbers and greater than or equal to +prefix numbers can be used together. The order of the le and ge +commands does not matter. + +If a prefix list with a different sequential number but with the exact +same rules as a previous list is created, an error will result. +However, in the case that the sequential number and the rules are +exactly similar, no error will result. + +If a list with the same sequential number as a previous list is created, +the new list will overwrite the old list. + +Matching of IP Prefix is performed from the smaller sequential number to the +larger. The matching will stop once any rule has been applied. + +In the case of no le or ge command, the prefix length must match exactly the +length specified in the prefix list. + +@deffn {Command} {no ip prefix-list @var{name}} {} +@end deffn + +@menu +* ip prefix-list description:: +* ip prefix-list sequential number control:: +* Showing ip prefix-list:: +* Clear counter of ip prefix-list:: +@end menu + +@node ip prefix-list description +@subsection ip prefix-list description + +@deffn {Command} {ip prefix-list @var{name} description @var{desc}} {} +Descriptions may be added to prefix lists. This command adds a +description to the prefix list. +@end deffn + +@deffn {Command} {no ip prefix-list @var{name} description [@var{desc}]} {} +Deletes the description from a prefix list. It is possible to use the +command without the full description. +@end deffn + +@node ip prefix-list sequential number control +@subsection ip prefix-list sequential number control + +@deffn {Command} {ip prefix-list sequence-number} {} +With this command, the IP prefix list sequential number is displayed. +This is the default behavior. +@end deffn + +@deffn {Command} {no ip prefix-list sequence-number} {} +With this command, the IP prefix list sequential number is not +displayed. +@end deffn + +@node Showing ip prefix-list +@subsection Showing ip prefix-list + +@deffn {Command} {show ip prefix-list} {} +Display all IP prefix lists. +@end deffn + +@deffn {Command} {show ip prefix-list @var{name}} {} +Show IP prefix list can be used with a prefix list name. +@end deffn + +@deffn {Command} {show ip prefix-list @var{name} seq @var{num}} {} +Show IP prefix list can be used with a prefix list name and sequential +number. +@end deffn + +@deffn {Command} {show ip prefix-list @var{name} @var{a.b.c.d/m}} {} +If the command longer is used, all prefix lists with prefix lengths equal to +or longer than the specified length will be displayed. +If the command first match is used, the first prefix length match will be +displayed. +@end deffn + +@deffn {Command} {show ip prefix-list @var{name} @var{a.b.c.d/m} longer} {} +@end deffn + +@deffn {Command} {show ip prefix-list @var{name} @var{a.b.c.d/m} first-match} {} +@end deffn + +@deffn {Command} {show ip prefix-list summary} {} +@end deffn +@deffn {Command} {show ip prefix-list summary @var{name}} {} +@end deffn + +@deffn {Command} {show ip prefix-list detail} {} +@end deffn +@deffn {Command} {show ip prefix-list detail @var{name}} {} +@end deffn + +@node Clear counter of ip prefix-list +@subsection Clear counter of ip prefix-list + +@deffn {Command} {clear ip prefix-list} {} +Clears the counters of all IP prefix lists. Clear IP Prefix List can be +used with a specified name and prefix. +@end deffn + +@deffn {Command} {clear ip prefix-list @var{name}} {} +@end deffn + +@deffn {Command} {clear ip prefix-list @var{name} @var{a.b.c.d/m}} {} +@end deffn + diff --git a/doc/install.texi b/doc/install.texi new file mode 100644 index 0000000..4ad282a --- /dev/null +++ b/doc/install.texi @@ -0,0 +1,295 @@ +@node Installation +@chapter Installation + +@cindex How to install Quagga +@cindex Installation +@cindex Installing Quagga +@cindex Building the system +@cindex Making Quagga + +There are three steps for installing the software: configuration, +compilation, and installation. + +@menu +* Configure the Software:: +* Build the Software:: +* Install the Software:: +@end menu + +The easiest way to get Quagga running is to issue the following +commands: + +@example +% configure +% make +% make install +@end example + +@node Configure the Software +@section Configure the Software + +@menu +* The Configure script and its options:: +* Least-Privilege support:: +* Linux notes:: +@end menu + +@node The Configure script and its options +@subsection The Configure script and its options + +@cindex Configuration options +@cindex Options for configuring +@cindex Build options +@cindex Distribution configuration +@cindex Options to @code{./configure} + +Quagga has an excellent configure script which automatically detects most +host configurations. There are several additional configure options you can +use to turn off IPv6 support, to disable the compilation of specific +daemons, and to enable SNMP support. + +@table @option +@item --disable-ipv6 +Turn off IPv6 related features and daemons. Quagga configure script +automatically detects IPv6 stack. But sometimes you might want to +disable IPv6 support of Quagga. +@item --disable-zebra +Do not build zebra daemon. +@item --disable-ripd +Do not build ripd. +@item --disable-ripngd +Do not build ripngd. +@item --disable-ospfd +Do not build ospfd. +@item --disable-ospf6d +Do not build ospf6d. +@item --disable-bgpd +Do not build bgpd. +@item --disable-bgp-announce +Make @command{bgpd} which does not make bgp announcements at all. This +feature is good for using @command{bgpd} as a BGP announcement listener. +@item --enable-netlink +Force to enable @sc{gnu}/Linux netlink interface. Quagga configure +script detects netlink interface by checking a header file. When the header +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 --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 --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 --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 +script. By default, the executables are placed in @file{/usr/local/sbin} +and the configuration files in @file{/usr/local/etc}. The @file{/usr/local/} +installation prefix and other directories may be changed using the following +options to the configuration script. + +@table @option +@item --prefix=@var{prefix} +Install architecture-independent files in @var{prefix} [/usr/local]. +@item --sysconfdir=@var{dir} +Look for configuration files in @var{dir} [@var{prefix}/etc]. Note +that sample configuration files will be installed here. +@item --localstatedir=@var{dir} +Configure zebra to use @var{dir} for local state files, such +as pid files and unix sockets. +@end table + +@example +% ./configure --disable-ipv6 +@end example + +This command will configure zebra and the routing daemons. + +@node Least-Privilege support +@subsection Least-Privilege support + +@cindex Quagga Least-Privileges +@cindex Quagga Privileges + +Additionally, you may configure zebra to drop its elevated privileges +shortly after startup and switch to another user. The configure script will +automatically try to configure this support. There are three configure +options to control the behaviour of Quagga daemons. + +@table @option +@item --enable-user=@var{user} +Switch to user @var{ARG} shortly after startup, and run as user @var{ARG} +in normal operation. +@item --enable-group=@var{group} +Switch real and effective group to @var{group} shortly after +startup. +@item --enable-vty-group=@var{group} +Create Unix Vty sockets (for use with vtysh) with group owndership set to +@var{group}. This allows one to create a seperate group which is +restricted to accessing only the Vty sockets, hence allowing one to +delegate this group to individual users, or to run vtysh setgid to +this group. +@end table + +The default user and group which will be configured is 'quagga' if no user +or group is specified. Note that this user or group requires write access to +the local state directory (see --localstatedir) and requires at least read +access, and write access if you wish to allow daemons to write out their +configuration, to the configuration directory (see --sysconfdir). + +On systems which have the 'libcap' capabilities manipulation library +(currently only linux), the quagga system will retain only minimal +capabilities required, further it will only raise these capabilities for +brief periods. On systems without libcap, quagga will run as the user +specified and only raise its uid back to uid 0 for brief periods. + +@node Linux notes +@subsection Linux Notes + +@cindex Configuring Quagga +@cindex Building on Linux boxes +@cindex Linux configurations + +There are several options available only to @sc{gnu}/Linux systems: +@footnote{@sc{gnu}/Linux has very flexible kernel configuration features}. If +you use @sc{gnu}/Linux, make sure that the current kernel configuration is +what you want. Quagga will run with any kernel configuration but some +recommendations do exist. + +@table @var + +@item CONFIG_NETLINK +Kernel/User netlink socket. This is a brand new feature which enables an +advanced interface between the Linux kernel and zebra (@pxref{Kernel Interface}). + +@item CONFIG_RTNETLINK +Routing messages. +This makes it possible to receive netlink routing messages. If you +specify this option, @command{zebra} can detect routing information +updates directly from the kernel (@pxref{Kernel Interface}). + +@item CONFIG_IP_MULTICAST +IP: multicasting. +This option should be specified when you use @command{ripd} (@pxref{RIP}) or +@command{ospfd} (@pxref{OSPFv2}) because these protocols use multicast. + +@end table + +IPv6 support has been added in @sc{gnu}/Linux kernel version 2.2. If you +try to use the Quagga IPv6 feature on a @sc{gnu}/Linux kernel, please +make sure the following libraries have been installed. Please note that +these libraries will not be needed when you uses @sc{gnu} C library 2.1 +or upper. + +@table @code + +@item inet6-apps +The @code{inet6-apps} package includes basic IPv6 related libraries such +as @code{inet_ntop} and @code{inet_pton}. Some basic IPv6 programs such +as @command{ping}, @command{ftp}, and @command{inetd} are also +included. The @code{inet-apps} can be found at +@uref{ftp://ftp.inner.net/pub/ipv6/}. + +@item net-tools +The @code{net-tools} package provides an IPv6 enabled interface and +routing utility. It contains @command{ifconfig}, @command{route}, +@command{netstat}, and other tools. @code{net-tools} may be found at +@uref{http://www.tazenda.demon.co.uk/phil/net-tools/}. + +@end table +@c A - end of footnote + +@node Build the Software +@section Build the Software + +After configuring the software, you will need to compile it for your +system. Simply issue the command @command{make} in the root of the source +directory and the software will be compiled. If you have *any* problems +at this stage, be certain to send a bug report @xref{Bug Reports}. + +@example +% ./configure +. +. +. +./configure output +. +. +. +% make +@end example +@c A - End of node, Building the Software + + +@node Install the Software +@comment node-name, next, previous, up +@section Install the Software + +Installing the software to your system consists of copying the compiled +programs and supporting files to a standard location. After the +installation process has completed, these files have been copied +from your work directory to @file{/usr/local/bin}, and @file{/usr/local/etc}. + +To install the Quagga suite, issue the following command at your shell +prompt: @command{make install}. + +@example +% +% make install +% +@end example + +Quagga daemons have their own terminal interface or VTY. After +installation, you have to setup each beast's port number to connect to +them. Please add the following entries to @file{/etc/services}. + +@example +zebrasrv 2600/tcp # zebra service +zebra 2601/tcp # zebra vty +ripd 2602/tcp # RIPd vty +ripngd 2603/tcp # RIPngd vty +ospfd 2604/tcp # OSPFd vty +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 +added to @file{/etc/services} so there is no need to add it. If you +specify a port number when starting the daemon, these entries may not be +needed. + +You may need to make changes to the config files in +@file{@value{INSTALL_PREFIX_ETC}/*.conf}. @xref{Config Commands}. diff --git a/doc/ipv6.texi b/doc/ipv6.texi new file mode 100644 index 0000000..5be2bab --- /dev/null +++ b/doc/ipv6.texi @@ -0,0 +1,183 @@ +@node IPv6 Support +@chapter IPv6 Support + +Quagga fully supports IPv6 routing. As described so far, Quagga supports +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 +messages to the all nodes that exist on the network. + +@menu +* Router Advertisement:: +@end menu + +@node Router Advertisement +@section Router Advertisement + +@deffn {Interface Command} {no ipv6 nd suppress-ra} {} +Send router advertisment messages. +@end deffn + +@deffn {Interface Command} {ipv6 nd suppress-ra} {} +Don't send router advertisment messages. +@end deffn + +@deffn {Interface Command} {ipv6 nd prefix @var{ipv6prefix} [@var{valid-lifetime}] [@var{preferred-lifetime}] [off-link] [no-autoconfig] [router-address]} {} +Configuring the IPv6 prefix to include in router advertisements. Several prefix +specific optional parameters and flags may follow: +@itemize @bullet +@item +@var{valid-lifetime} - the length of time in seconds during what the prefix is +valid for the purpose of on-link determination. Value @var{infinite} represents +infinity (i.e. a value of all one bits (@code{0xffffffff})). + +Range: @code{<0-4294967295>} Default: @code{2592000} + +@item +@var{preferred-lifetime} - the length of time in seconds during what addresses +generated from the prefix remain preferred. Value @var{infinite} represents +infinity. + +Range: @code{<0-4294967295>} Default: @code{604800} + +@item +@var{off-link} - indicates that advertisement makes no statement about on-link or +off-link properties of the prefix. + +Default: not set, i.e. this prefix can be used for on-link determination. + +@item +@var{no-autoconfig} - indicates to hosts on the local link that the specified prefix +cannot be used for IPv6 autoconfiguration. + +Default: not set, i.e. prefix can be used for autoconfiguration. + +@item +@var{router-address} - indicates to hosts on the local link that the specified +prefix +contains a complete IP address by setting R flag. + +Default: not set, i.e. hosts do not assume a complete IP address is placed. +@end itemize +@end deffn + +@deffn {Interface Command} {ipv6 nd ra-interval <1-1800>} {} +@deffnx {Interface Command} {no ipv6 nd ra-interval [<1-1800>]} {} +The maximum time allowed between sending unsolicited multicast router +advertisements from the interface, in seconds. + +Default: @code{600} +@end deffn + +@deffn {Interface Command} {ipv6 nd ra-interval msec <70-1800000>} {} +@deffnx {Interface Command} {no ipv6 nd ra-interval [msec <70-1800000>]} {} +The maximum time allowed between sending unsolicited multicast router +advertisements from the interface, in milliseconds. + +Default: @code{600000} +@end deffn + +@deffn {Interface Command} {ipv6 nd ra-lifetime <0-9000>} {} +@deffnx {Interface Command} {no ipv6 nd ra-lifetime [<0-9000>]} {} +The value to be placed in the Router Lifetime field of router advertisements +sent from the interface, in seconds. Indicates the usefulness of the router +as a default router on this interface. Setting the value to zero indicates +that the router should not be considered a default router on this interface. +Must be either zero or between value specified with @var{ipv6 nd ra-interval} +(or default) and 9000 seconds. + +Default: @code{1800} +@end deffn + +@deffn {Interface Command} {ipv6 nd reachable-time <1-3600000>} {} +@deffnx {Interface Command} {no ipv6 nd reachable-time [<1-3600000>]} {} +The value to be placed in the Reachable Time field in the Router Advertisement +messages sent by the router, in milliseconds. The configured time enables the +router to detect unavailable neighbors. The value zero means unspecified (by +this router). + +Default: @code{0} +@end deffn + +@deffn {Interface Command} {ipv6 nd managed-config-flag} {} +@deffnx {Interface Command} {no ipv6 nd managed-config-flag} {} +Set/unset flag in IPv6 router advertisements which indicates to hosts that they +should use managed (stateful) protocol for addresses autoconfiguration in +addition to any addresses autoconfigured using stateless address +autoconfiguration. + +Default: not set +@end deffn + +@deffn {Interface Command} {ipv6 nd other-config-flag} {} +@deffnx {Interface Command} {no ipv6 nd other-config-flag} {} +Set/unset flag in IPv6 router advertisements which indicates to hosts that +they should use administered (stateful) protocol to obtain autoconfiguration +information other than addresses. + +Default: not set +@end deffn + +@deffn {Interface Command} {ipv6 nd home-agent-config-flag} {} +@deffnx {Interface Command} {no ipv6 nd home-agent-config-flag} {} +Set/unset flag in IPv6 router advertisements which indicates to hosts that +the router acts as a Home Agent and includes a Home Agent Option. + +Default: not set +@end deffn + +@deffn {Interface Command} {ipv6 nd home-agent-preference <0-65535>} {} +@deffnx {Interface Command} {no ipv6 nd home-agent-preference [<0-65535>]} {} +The value to be placed in Home Agent Option, when Home Agent config flag is set, +which indicates to hosts Home Agent preference. The default value of 0 stands +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>]} {} +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. + +Default: 0 +@end deffn + +@deffn {Interface Command} {ipv6 nd adv-interval-option} {} +@deffnx {Interface Command} {no ipv6 nd adv-interval-option} {} +Include an Advertisement Interval option which indicates to hosts the maximum time, +in milliseconds, between successive unsolicited Router Advertisements. + +Default: not set +@end deffn + +@deffn {Interface Command} {ipv6 nd router-preference (high|medium|low)} {} +@deffnx {Interface Command} {no ipv6 nd router-preference [(high|medium|low)]} {} +Set default router preference in IPv6 router advertisements per RFC4191. + +Default: medium +@end deffn + +@deffn {Interface Command} {ipv6 nd mtu <1-65535>} {} +@deffnx {Interface Command} {no ipv6 nd mtu [<1-65535>]} {} +Include an MTU (type 5) option in each RA packet to assist the attached hosts +in proper interface configuration. The announced value is not verified to be +consistent with router interface MTU. + +Default: don't advertise any MTU option +@end deffn + +@example +@group +interface eth0 + no ipv6 nd suppress-ra + ipv6 nd prefix 2001:0DB8:5009::/64 +@end group +@end example + +For more information see @cite{RFC2462 (IPv6 Stateless Address Autoconfiguration)} +, @cite{RFC4861 (Neighbor Discovery for IP Version 6 (IPv6))} +, @cite{RFC6275 (Mobility Support in IPv6)} +and @cite{RFC4191 (Default Router Preferences and More-Specific Routes)}. diff --git a/doc/isisd.8 b/doc/isisd.8 new file mode 100644 index 0000000..84e6cf5 --- /dev/null +++ b/doc/isisd.8 @@ -0,0 +1,111 @@ +.TH IS-IS 8 "25 November 2004" "Quagga IS-IS daemon" "Version 0.97.3" +.SH NAME +isisd \- an IS-IS routing engine for use with Quagga routing software. +.SH SYNOPSIS +.B isisd +[ +.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 isisd +is a routing component that works with the +.B Quagga +routing engine. +.SH OPTIONS +Options available for the +.B isisd +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/isisd.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 isisd starts its process identifier is written to +\fB\fIpid-file\fR. The init system uses the recorded PID to stop or +restart isisd. The likely default is \fB\fI/var/run/isisd.pid\fR. +.TP +\fB\-P\fR, \fB\-\-vty_port \fR\fIport-number\fR +Specify the port that the isisd 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 isisd 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/isisd +The default location of the +.B isisd +binary. +.TP +.BI /usr/local/etc/isisd.conf +The default location of the +.B isisd +config file. +.TP +.BI $(PWD)/isisd.log +If the +.B isisd +process is config'd to output logs to a file, then you will find this +file in the directory where you started \fBisisd\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 isisd process may log to standard output, to a VTY, to a log +file, or through syslog to the system logs. \fBisisd\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) +.SH BUGS +\fBisisd\fR is ALPHA quality at the moment and hasn't any way ready for +production use. + +.B isisd +eats bugs for breakfast. If you have food for the maintainers try +.BI http://bugzilla.quagga.net +.SH AUTHORS +See +.BI http://isisd.sourceforge.net +or the Info file for an accurate list of authors. + diff --git a/doc/isisd.texi b/doc/isisd.texi new file mode 100644 index 0000000..bbc2896 --- /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/kernel.texi b/doc/kernel.texi new file mode 100644 index 0000000..67fbb5e --- /dev/null +++ b/doc/kernel.texi @@ -0,0 +1,47 @@ +@node Kernel Interface +@chapter Kernel Interface + +There are several different methods for reading kernel routing table +information, updating kernel routing tables, and for looking up +interfaces. + +@table @samp + +@item ioctl +The @samp{ioctl} method is a very traditional way for reading or writing +kernel information. @samp{ioctl} can be used for looking up interfaces +and for modifying interface addresses, flags, mtu settings and other +types of information. Also, @samp{ioctl} can insert and delete kernel +routing table entries. It will soon be available on almost any platform +which zebra supports, but it is a little bit ugly thus far, so if a +better method is supported by the kernel, zebra will use that. + +@item sysctl +@samp{sysctl} can lookup kernel information using MIB (Management +Information Base) syntax. Normally, it only provides a way of getting +information from the kernel. So one would usually want to change kernel +information using another method such as @samp{ioctl}. + +@item proc filesystem +@samp{proc filesystem} provides an easy way of getting kernel +information. + +@item routing socket + +@item netlink +On recent Linux kernels (2.0.x and 2.2.x), there is a kernel/user +communication support called @code{netlink}. It makes asynchronous +communication between kernel and Quagga possible, similar to a routing +socket on BSD systems. + +Before you use this feature, be sure to select (in kernel configuration) +the kernel/netlink support option 'Kernel/User network link driver' and +'Routing messages'. + +Today, the /dev/route special device file is obsolete. Netlink +communication is done by reading/writing over netlink socket. + +After the kernel configuration, please reconfigure and rebuild Quagga. +You can use netlink as a dynamic routing update channel between Quagga +and the kernel. +@end table diff --git a/doc/main.texi b/doc/main.texi new file mode 100644 index 0000000..6d42c04 --- /dev/null +++ b/doc/main.texi @@ -0,0 +1,498 @@ +@node Zebra +@chapter Zebra + +@c SYNOPSIS +@command{zebra} is an IP routing manager. It provides kernel routing +table updates, interface lookups, and redistribution of routes between +different routing protocols. + +@menu +* 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 + + +@node Invoking zebra +@section Invoking zebra + +Besides the common invocation options (@pxref{Common Invocation Options}), the +@command{zebra} specific invocation options are listed below. + +@table @samp +@item -b +@itemx --batch +Runs in batch mode. @command{zebra} parses configuration file and terminates +immediately. + +@item -k +@itemx --keep_kernel +When zebra starts up, don't delete old self inserted routes. + +@item -r +@itemx --retain +When program terminates, retain routes added by zebra. + +@end table + +@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 + +@deffn {Interface Command} {shutdown} {} +@deffnx {Interface Command} {no shutdown} {} +Up or down the current interface. +@end deffn + +@deffn {Interface Command} {ip address @var{address/prefix}} {} +@deffnx {Interface Command} {ipv6 address @var{address/prefix}} {} +@deffnx {Interface Command} {no ip address @var{address/prefix}} {} +@deffnx {Interface Command} {no ipv6 address @var{address/prefix}} {} +Set the IPv4 or IPv6 address/prefix for the interface. +@end deffn + +@deffn {Interface Command} {ip address @var{address/prefix} secondary} {} +@deffnx {Interface Command} {no ip address @var{address/prefix} secondary} {} +Set the secondary flag for this address. This causes ospfd to not treat the +address as a distinct subnet. +@end deffn + +@deffn {Interface Command} {description @var{description} ...} {} +Set description for the interface. +@end deffn + +@deffn {Interface Command} {multicast} {} +@deffnx {Interface Command} {no multicast} {} +Enable or disables multicast flag for the interface. +@end deffn + +@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 +configuration. +@end deffn + +@deffn {Interface Command} {link-detect} {} +@deffnx {Interface Command} {no link-detect} {} +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 + +Static routing is a very fundamental feature of routing technology. It +defines static prefix and gateway. + +@deffn Command {ip route @var{network} @var{gateway}} {} +@var{network} is destination prefix with format of A.B.C.D/M. +@var{gateway} is gateway for the prefix. When @var{gateway} is +A.B.C.D format. It is taken as a IPv4 address gateway. Otherwise it +is treated as an interface name. If the interface name is @var{null0} then +zebra installs a blackhole route. + +@example +ip route 10.0.0.0/8 10.0.0.2 +ip route 10.0.0.0/8 ppp0 +ip route 10.0.0.0/8 null0 +@end example + +First example defines 10.0.0.0/8 static route with gateway 10.0.0.2. +Second one defines the same prefix but with gateway to interface ppp0. The +third install a blackhole route. +@end deffn + +@deffn Command {ip route @var{network} @var{netmask} @var{gateway}} {} +This is alternate version of above command. When @var{network} is +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.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. +@end deffn + +@deffn Command {ip route @var{network} @var{gateway} @var{distance}} {} +Installs the route with the specified distance. +@end deffn + +Multiple nexthop static route + +@example +ip route 10.0.0.1/32 10.0.0.2 +ip route 10.0.0.1/32 10.0.0.3 +ip route 10.0.0.1/32 eth0 +@end example + +If there is no route to 10.0.0.2 and 10.0.0.3, and interface eth0 +is reachable, then the last route is installed into the kernel. + +If zebra has been compiled with multipath support, and both 10.0.0.2 and +10.0.0.3 are reachable, zebra will install a multipath route via both +nexthops, if the platform supports this. + +@example +zebra> show ip route +S> 10.0.0.1/32 [1/0] via 10.0.0.2 inactive + via 10.0.0.3 inactive + * is directly connected, eth0 +@end example + +@example +ip route 10.0.0.0/8 10.0.0.2 +ip route 10.0.0.0/8 10.0.0.3 +ip route 10.0.0.0/8 null0 255 +@end example + +This will install a multihop route via the specified next-hops if they are +reachable, as well as a high-metric blackhole route, which can be useful to +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 +Routing entry for 10.0.0.0/8 + Known via "static", distance 1, metric 0 + 10.0.0.2 inactive + 10.0.0.3 inactive + +Routing entry for 10.0.0.0/8 + Known via "static", distance 255, metric 0 + directly connected, Null0 +@end example + +@deffn Command {ipv6 route @var{network} @var{gateway}} {} +@deffnx Command {ipv6 route @var{network} @var{gateway} @var{distance}} {} +These behave similarly to their ipv4 counterparts. +@end deffn + + +@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, +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 +routes received from other quagga components. The +@command{permit}/@command{deny} facilities provided by these commands +can be used to filter which routes zebra will install in the kernel. + +@deffn Command {ip protocol @var{protocol} route-map @var{routemap}} {} +Apply a route-map filter to routes for the specified protocol. @var{protocol} +can be @b{any} or one of +@b{system}, +@b{kernel}, +@b{connected}, +@b{static}, +@b{rip}, +@b{ripng}, +@b{ospf}, +@b{ospf6}, +@b{isis}, +@b{bgp}, +@b{hsls}. +@end deffn + +@deffn {Route Map} {set src @var{address}} +Within a route-map, set the preferred source address for matching routes +when installing in the kernel. +@end deffn + +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 + match ip address prefix-list ANY + set src 10.0.0.1 + +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 + +@deffn Command {show ip route} {} +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, + B - BGP * - FIB route. + +K* 0.0.0.0/0 203.181.89.241 +S 0.0.0.0/0 203.181.89.1 +C* 127.0.0.0/8 lo +C* 203.181.89.240/28 eth0 +@end group +@end example +@end deffn + +@deffn Command {show ipv6 route} {} +@end deffn + +@deffn Command {show interface} {} +@end deffn + +@deffn Command {show ip prefix-list [@var{name}]} {} +@end deffn + +@deffn Command {show route-map [@var{name}]} {} +@end deffn + +@deffn Command {show ip protocol} {} +@end deffn + +@deffn Command {show ipforward} {} +Display whether the host's IP forwarding function is enabled or not. +Almost any UNIX kernel can be configured with IP forwarding disabled. +If so, the box can't work as a router. +@end deffn + +@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/.gitignore b/doc/mpls/.gitignore new file mode 100644 index 0000000..b0a4a46 --- /dev/null +++ b/doc/mpls/.gitignore @@ -0,0 +1,5 @@ +.arch-ids +.arch-inventory +*~ +*.loT + diff --git a/doc/mpls/ChangeLog.opaque.txt b/doc/mpls/ChangeLog.opaque.txt new file mode 100644 index 0000000..afcfaa3 --- /dev/null +++ b/doc/mpls/ChangeLog.opaque.txt @@ -0,0 +1,192 @@ +----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- +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 + +1. Bug fixes + + 1.1 Though a new member "oi" has added to "struct ospf_lsa" to control + flooding scope of type-9 Opaque-LSAs, the value was always NULL + because no one set it. + + 1.2 In the function "show_ip_ospf_database_summary()" and "show_lsa_ + detail_adv_router()", VTY output for type-11 Opaque-LSAs did not + work properly. + + 1.3 URL for the opaque-type assignment reference has changed. + + 1.4 In the file "ospf_mpls_te.c", printf formats have changed to + avoid compiler warning messages; "%lu" -> "%u", "%lx" -> "%x". + Note that this hack depends on OS, compiler and their versions. + + 1.5 One of attached documentation "opaque_lsa.txt" has changed to + reflect the latest coding. + +2. Feature enhancements + + 2.1 Knowing that it is an ugly hack, an "officially unallocated" + opaque-type value 0 has newly introduced as a "wildcard", + which matches to all opaque-type. + This value must not be flooded to the network, of course. + + 2.2 The Opaque-core module makes use of newly introduced hooks to + dispatch every LSDB change (LSA installation and deletion) to + preregistered opaque users. + Therefore, by providing appropriate callback functions as new + parameters of "ospf_register_opaque_functab()", an opaque user + can refer to every LSA instance to be installed into, or to be + deleted from, the LSDB. + +----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- +Changes 2001.10.31 + +1. Bug fixes + + 1.1 Since each LSA has their own lifetime, they will remain in a + routing domain (being stored in LSDB of each router), until their + age naturally reach to MaxAge or explicitly being flushed by the + originated router. Therefore, if a router restarted with a short + downtime, it is possible that previously flooded self-originated + LSAs might received if the NSM status is not less than Exchange. + + There were some problems in the way of handling self-originated + Opaque-LSAs if they are contained in a received LSUpd message, + but not installed to the local LSDB yet. + Regardless of some conditions to start originating Opaque-LSAs + (there should be at least one opaque-capable full-state neighbor), + the function "ospf_flood()" will be called to flood and install + this brand-new looking LSA. + As the result, when the NSM of an opaque-capable neighbor gets + full, internal state inconsistency happens; a user of Opaque-LSA + such as MPLS-TE can refer to self-originated LSAs in the local + LSDB, but cannot modify their contents... + + Above problems have fixed with a policy "flush it from the whole + routing domain and keep silent until the flushing completed". + By using this sweeping technique, we can be free from confusion + caused by self-originated LSAs received via network. + + 1.2 The function "ospf_opaque_type_name()" contained massive ifdefs + corresponding to each "opaque-type". + These unnecessary ifdefs are removed completely. + + 1.3 In the function "ospf_delete_opaque_functab()", there was an + improper loop control that causes illegal memory access. + Original coding was "next = nextnode (node)". + + 1.4 The function "ospf_mpls_te_ism_change()" could not handle the + case when the ISM changes from Waiting to DR/BDR/Other. + So, there was a case that even if one of an ISM become + operational and MPLS-TE module has started, the corresponding + Opaque-LSA cannot be originated. + + 1.5 The function "ospf_opaque_lsa_reoriginate_schedule()" did not + allow to be called multiple times, simply because handling + module for the given "lsa-type & opaque-type" already exists. + But this assumption seems to be wrong. + Change the policy to allow this function to be called multiple + times and let the caller to decide what should do when the + corresponding callback function "(* functab->lsa_originator)()" + is called. + +2. Feature enhancements + + 2.1 The global bitmap "opaque" has introduced instead of former flag + "OpaqueCapable", to store complex conditions to handle Opaque-LSAs. + + 2.2 The MPLS-TE module now referes to "draft-katz-yeung-ospf-traffic + -06.txt", no significant changes with 05 version, though. + +----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- +Changes 2001.08.03 + +1. Bug fixes + + 1.1 Even if the ospfd started with opaque capability enabled, when + the ospfd receives an unknown opaque-type (unregistered by the + function "ospf_register_opaque_functab()" beforehand), the LSA + was discarded. As the result, only the opaque-LSAs that have + commonly registered by opaque-capable ospf routers can be + flooded in a routing domain. + + This behavior has fixed so that arbitrary opaque-type LSAs can + be flooded among opaque-capable ospf routers. + If the ospfd has opaque-LSA capability but disabled at runtime, + received opaque-LSAs can be accepted and registered to LSDB as + is, but not be flooded to the network; those opaque LSAs will + remain in LSDB until explicitly flushed by incoming LSUpd + messages with MaxAge, or their age naturally reaches to MaxAge. + + 1.2 The function "ospf_register_opaque_functab()" did not check + if the entry corresponding to the given "lsa-type, opaque-type" + combination already exists or not. + This problem has fixed not to allow multiple registration. + + 1.3 Since type-11 (AS external) LSAs will be flooded beyond areas, + there is little relationship between "struct lsa" and "struct + area". More specifically, the pointer address "lsa->area" can + be NULL if the lsa-type is 11, thus an illegal memory access + will happen. This problem has fixed. + + 1.4 When self-originated opaque-LSAs are received via network and + if the corresponding opaque-type functions are not available + (they have already deleted) at that time, those LSAs were + dropped due to "unknown opaque-type" error. + After the problem 1.1 has fixed, those "self-originated" LSAs + were registered to LSDB and then flooded to the network, even + if the processing functions did not exist... + + After all, this problem has fixed so that those LSAs should + explicitly be flushed from the routing domain immediately, if + the processing functions cannot find at that time. + + 1.5 Some typo have fixed. + + --- EXAMPLE --- + static int + opaque_lsa_originate_callback (list funclist, void *lsa_type_dependent) + ^^^^^ + --- EXAMPLE --- + +2. Feature enhancements + + 2.1 According to the description of rfc2328 in section 10.8, any + change in the router's optional capabilities should trigger + the option re-negotiation procedures with neighbors. + + --- EXCERPT --- + If for some reason the router's optional + capabilities change, the Database Exchange procedure should be + restarted by reverting to neighbor state ExStart. + --- EXCERPT --- + + For the opaque-capability changes, this feature has implemented. + More specifically, if "ospf opaque-lsa" or "no ospf opaque-lsa" + VTY command is given at runtime, all self-originated LSAs will + be flushed immediately and then all neighbor status will be + forced to ExStart by generating SeqNumberMismatch events. + + 2.1 When we change opaque-capability dynamically (ON -> OFF -> ON), + there was no trigger at "OFF->ON" timing to reactivate opaque + LSA handling modules (such as MPLS-TE) that have once forcibly + stopped at "ON->OFF" timing. + Now this dynamic reactivation feature has added. + + 2.2 The MPLS-TE module now referes to "draft-katz-yeung-ospf-traffic + -05.txt", no significant changes with 04 version, though. + +----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- +Changes 2001.03.28 + + Initial release of Opaque-LSA/MPLS-TE extensions for the zebra/ospfd. diff --git a/doc/mpls/cli_summary.txt b/doc/mpls/cli_summary.txt new file mode 100644 index 0000000..c60d0ae --- /dev/null +++ b/doc/mpls/cli_summary.txt @@ -0,0 +1,90 @@ +Summary of CLI commands, expanded for Opaque-LSA/MPLS-TE. +--------------------------------------------------------- + +router> + + show ip ospf database (asbr-summary|external|max-age|network|router|self-originate|summary|opaque-link|opaque-area|opaque-external) + + show ip ospf database (asbr-summary|external|network|router|summary|opaque-link|opaque-area|opaque-external) (self-originate|) + + show ip ospf database (asbr-summary|external|network|router|summary|opaque-link|opaque-area|opaque-external) A.B.C.D + + show ip ospf database (asbr-summary|external|network|router|summary|opaque-link|opaque-area|opaque-external) A.B.C.D (self-originate|) + + show ip ospf database (asbr-summary|external|network|router|summary|opaque-link|opaque-area|opaque-external) A.B.C.D adv-router A.B.C.D + + show ip ospf database (asbr-summary|external|network|router|summary|opaque-link|opaque-area|opaque-external) adv-router A.B.C.D + + --> Add database items: opaque-link, opaque-area, opaque-external + + show mpls-te interface [INTERFACE] + + --> Show current MPLS-TE link-TLV parameters. + If [INTERFACE] is omitted, all interfaces will be displayed. + + show mpls-te router + + --> Show current MPLS-TE Router-TLV parameters. + +router> enable +router# +router# configure terminal +router(config)# interface [INTERFACE] +router(config-if)# + + mpls-te link max-bw BANDWIDTH + + --> Set MPLS-TE link-TLV parameter: Maximum Bandwidth (Bytes/sec). + In integer or floating point format (1000, or 1.0e3) + + mpls-te link max-rsv-bw BANDWIDTH + + --> Set MPLS-TE link-TLV parameter: Maximum Reservable Bandwidth (Bytes/sec). + In integer or floating point format (1000, or 1.0e3) + + mpls-te link metric <0-4294967295> + + --> Set MPLS-TE link-TLV parameter: MPLS-TE metric. + + mpls-te link rsc-clsclr BITPATTERN + + --> Set MPLS-TE link-TLV parameter: Resource Class/Color. + In 32-bit hexadecimal format, with leading "0x" (0x0 - 0xffffffff) + + mpls-te link unrsv-bw <0-7> BANDWIDTH + + --> Set MPLS-TE link-TLV parameter: Unreserved Bandwidth (Bytes/sec). + In integer or floating point format (1000, or 1.0e3) + +router(config-if)# exit +router(config)# router ospf +router(config-router)# + + mpls-te + + --> Enable MPLS-TE functionality. + Note that master-switch "ospf opaque-lsa" must also be specified. + + mpls-te on + + --> Alias of "mpls-te" command. + + mpls-te router-address A.B.C.D + + --> Set MPLS-TE Router-TLV parameter: Router Address. + + no mpls-te + + --> Disable MPLS-TE functionality. + + no ospf opaque-lsa + + --> Disable Opaque-LSAs capability. + This node behaves Opaque-incapable node. + + ospf opaque-lsa + + --> Enable Opaque-LSAs capability. + This is the master-switch to make this node Opaque-capable. + +router# exit diff --git a/doc/mpls/opaque_lsa.txt b/doc/mpls/opaque_lsa.txt new file mode 100644 index 0000000..ae89ee3 --- /dev/null +++ b/doc/mpls/opaque_lsa.txt @@ -0,0 +1,365 @@ +1. List of "opaque-type dependent" callback functions per LSA-type. + + (N = 9,10,11) + | + | struct + | list struct struct + +-> +-------+ listnode listnode + | head |-----> +------+ +------ + | tail | | next |--------------------> | next + | count | /--| prev |<---------------------| prev + +-------+ | data |----+ | + |///////| +------+ | + +-------+ | + | + struct | + ospf_opaque_tabent | + +----------------------+ <--+ + | opaque_type | + +----------------------+ + | (Callback functions) | + +----------------------+ + + +2. Self-originated Opaque-LSAs per LSA-type. + +2.1 Type-11 (AS-external) Opaque-LSAs + + struct + ospf + +---> +-------------------+ + | |///////////////////| + | +-------------------+ + | | opaque | + | +-------------------+ + | |///////////////////| + | +-------------------+ + | | opaque_lsa_self |---+ + | +-------------------+ | + | |///////////////////| | + | +-------------------+ | + | | + ......|.............................|....................................... + : | | Almost common for type-9,10,11 LSA : + : | +-----------------------+ : + : | | : + : | | struct : + : | | list struct struct : + : | +-> +-------+ listnode listnode : + : | | head |-----> +------+ +------ : + : | | tail | | next |--------------------> | next : + : | | count | /--| prev |<---------------------| prev : + : | +-------+ | data |---+ | : + : | |///////| +------+ | : + : | +-------+ | : + : | | : + : | struct | : + : | opaque_info_per_type | : + : | +-------------------+ <--------+ : + : | | opaque_type | <------------+ : + : | +-------------------+ | : + : | | status | | : + : | +-------------------+ | : + : | | t_opaque_lsa_self | | : + : | +-------------------+ | : + : +-----| owner | | struct : + : +-------------------+ | ospf_opaque_tabent : + : | functab |-------------------> +---------------- : + : +-------------------+ | | opaque_type : + : | id_list |---+ | |(Callback Funcs) : + : +-------------------+ | | | : + : | | : + : +-----------------------+ | : + : | | : + : | struct | : + : | list struct | struct : + : +-> +-------+ listnode | listnode : + : | head |-----> +------+ | +------ : + : | tail | | next |--------------------> | next : + : | count | /--| prev |<---------------------| prev : + : +-------+ | data |---+ | | : + : |///////| +------+ | | : + : +-------+ | | : + : | | : + : struct | | : + : opaque_info_per_id | | : + : +-------------------+ <--------+ | : + : | opaque_id | | : + : +-------------------+ | : + : | t_opaque_lsa_self | | : + : +-------------------+ | : + : | opqctl_type |--------------+ : + : +-------------------+ : + : | lsa |---+ : + : +-------------------+ | : + : | : + : struct | : + : ospf_lsa | : + : +-------------+ <-------+ : + : |/////////////| struct : + : +-------------+ lsa_header : + : | data |--------------> +-------- : + : +-------------+ | : + : |/////////////| : + : +-------------+ : + : +--------| area | : + : | +-------------+ : + : --- |/////////////| : + : +-------------+ : + : +-----| oi | : + : | +-------------+ : + : --- : + :..........................................................................: + +2.2 Type-10 (area-local) Opaque-LSAs + + struct + ospf + +---------+ <-----------+ + |/////////| | + +---------+ | + | + struct | + ospf_area | + +--+---> +-----------------+ | + | | | top |-----+ + | | +-----------------+ + | | |/////////////////| struct + | | +-----------------+ ospf_lsa + | | | router_lsa_self |-----------> +--------- + | | +-----------------+ | + | | | opaque_lsa_self |-----+ | + | | +-----------------+ | + | | |/////////////////| | + | | +-----------------+ | + | | | + ...|..|.............................|....................................... + : | | | Almost common for type-9,10,11 LSA : + : | | +-----------------------+ : + : | | | : + : | | | struct : + : | | | list struct struct : + : | | +-> +-------+ listnode listnode : + : | | | head |-----> +------+ +------ : + : | | | tail | | next |--------------------> | next : + : | | | count | /--| prev |<---------------------| prev : + : | | +-------+ | data |---+ | : + : | | |///////| +------+ | : + : | | +-------+ | : + : | | | : + : | | struct | : + : | | opaque_info_per_type | : + : | | +-------------------+ <--------+ : + : | | | opaque_type | <------------+ : + : | | +-------------------+ | : + : | | | status | | : + : | | +-------------------+ | : + : | | | t_opaque_lsa_self | | : + : | | +-------------------+ | : + : | +-----| owner | | struct : + : | +-------------------+ | ospf_opaque_tabent : + : | | functab |-------------------> +---------------- : + : | +-------------------+ | | opaque_type : + : | | id_list |---+ | |(Callback Funcs) : + : | +-------------------+ | | | : + : | | | : + : | +-----------------------+ | : + : | | | : + : | | struct | : + : | | list struct | struct : + : | +-> +-------+ listnode | listnode : + : | | head |-----> +------+ | +------ : + : | | tail | | next |--------------------> | next : + : | | count | /--| prev |<---------------------| prev : + : | +-------+ | data |---+ | | : + : | |///////| +------+ | | : + : | +-------+ | | : + : | | | : + : | struct | | : + : | opaque_info_per_id | | : + : | +-------------------+ <--------+ | : + : | | opaque_id | | : + : | +-------------------+ | : + : | | t_opaque_lsa_self | | : + : | +-------------------+ | : + : | | opqctl_type |--------------+ : + : | +-------------------+ : + : | | lsa |---+ : + : | +-------------------+ | : + : | | : + : | struct | : + : | ospf_lsa | : + : | +-------------+ <-------+ : + : | |/////////////| struct : + : | +-------------+ lsa_header : + : | | data |--------------> +-------- : + : | +-------------+ | : + : | |/////////////| : + : | +-------------+ : + : +--------| area | : + : +-------------+ : + : |/////////////| : + : +-------------+ : + : +-----| oi | : + : | +-------------+ : + : --- : + :..........................................................................: + +2.3 Type-9 (link-local) Opaque-LSAs + + struct + ospf_area + +------> +---------+ <---------+ + | |/////////| | + | +---------+ | + | | + | struct | + | ospf_interface | + | +-+-> +-----------------+ | + | | | |/////////////////| | + | | | +-----------------+ | + | | | | area |---+ + | | | +-----------------+ + | | | |/////////////////| struct + | | | +-----------------+ ospf_lsa + | | | |network_lsa_self |-----------> +--------- + | | | +-----------------+ | + | | | | opaque_lsa_self |-----+ | + | | | +-----------------+ | + | | | |/////////////////| | + | | | +-----------------+ | + | | | | + ...|..|.|...........................|....................................... + : | | | | Almost common for type-9,10,11 LSA : + : | | | +-----------------------+ : + : | | | | : + : | | | | struct : + : | | | | list struct struct : + : | | | +-> +-------+ listnode listnode : + : | | | | head |-----> +------+ +------ : + : | | | | tail | | next |--------------------> | next : + : | | | | count | /--| prev |<---------------------| prev : + : | | | +-------+ | data |---+ | : + : | | | |///////| +------+ | : + : | | | +-------+ | : + : | | | | : + : | | | struct | : + : | | | opaque_info_per_type | : + : | | | +-------------------+ <--------+ : + : | | | | opaque_type | <------------+ : + : | | | +-------------------+ | : + : | | | | status | | : + : | | | +-------------------+ | : + : | | | | t_opaque_lsa_self | | : + : | | | +-------------------+ | : + : | | +---| owner | | struct : + : | | +-------------------+ | ospf_opaque_tabent : + : | | | functab |-------------------> +---------------- : + : | | +-------------------+ | | opaque_type : + : | | | id_list |---+ | |(Callback Funcs) : + : | | +-------------------+ | | | : + : | | | | : + : | | +-----------------------+ | : + : | | | | : + : | | | struct | : + : | | | list struct | struct : + : | | +-> +-------+ listnode | listnode : + : | | | head |-----> +------+ | +------ : + : | | | tail | | next |--------------------> | next : + : | | | count | /--| prev |<---------------------| prev : + : | | +-------+ | data |---+ | | : + : | | |///////| +------+ | | : + : | | +-------+ | | : + : | | | | : + : | | struct | | : + : | | opaque_info_per_id | | : + : | | +-------------------+ <--------+ | : + : | | | opaque_id | | : + : | | +-------------------+ | : + : | | | t_opaque_lsa_self | | : + : | | +-------------------+ | : + : | | | opqctl_type |--------------+ : + : | | +-------------------+ : + : | | | lsa |---+ : + : | | +-------------------+ | : + : | | | : + : | | struct | : + : | | ospf_lsa | : + : | | +-------------+ <-------+ : + : | | |/////////////| struct : + : | | +-------------+ lsa_header : + : | | | data |--------------> +-------- : + : | | +-------------+ | : + : | | |/////////////| : + : | | +-------------+ : + : +--|-----| area | : + : | +-------------+ : + : | |/////////////| : + : | +-------------+ : + : +-----| oi | : + : +-------------+ : + :..........................................................................: + + +3. Internal structures for MPLS-TE parameter management. + + struct + ospf_mpls_te + +-------------+ + | status | + +-------------+ + | iflist |---+ + +-------------+ | + |(Router-TLV) | | + +-------------+ | + | + +---------------------+ + | + | struct + | list struct struct + +---> +-------+ listnode listnode + | head |-----> +------+ +------ + | tail | | next |--------------------> | next + | count | /--| prev |<---------------------| prev + +-------+ | data |---+ | + |///////| +------+ | + +-------+ | + | + +--------------------------------+ + | + | struct + | ospf_mpls_te_linkparms + +-> +----------------+ + | instance | struct + +----------------+ interface + | ifp |--------------------> +----------+ + +----------------+ +----> |//////////| + | area |----+ | +----------+ + +----------------+ | | | info |-----+ + | flags | | | +----------+ | + +----------------+ | | |//////////| | + | (Link-TLV) | | | +----------+ | + +----------------+ | | | + | (Link-SubTLVs) | | | struct | + +----------------+ | | ospf_if_info | + | | +----------+ <---+ + | | |//////////| + struct | | +----------+ + ospf_area | | | oifs |-----+ + +-> +--------------+ <----+ | +----------+ | + | |//////////////| | | + | +--------------+ | struct | + | | route_table | + | struct | +-----------+ <--+ + | ospf_interface | | route_top | - - - - -. + | +--------------+ <----+ | +-----------+ . + | |//////////////| | | . + | +--------------+ | | struct . + | | ifp |------|----------+ route_node . + | +--------------+ | +-----------+ < - - - - + | |//////////////| | |///////////| + | +--------------+ | +-----------+ + +---| area | +-----------------| info | + +--------------+ +-----------+ + |//////////////| |///////////| + +--------------+ +-----------+ diff --git a/doc/mpls/ospfd.conf b/doc/mpls/ospfd.conf new file mode 100644 index 0000000..2b15fa4 --- /dev/null +++ b/doc/mpls/ospfd.conf @@ -0,0 +1,76 @@ +! +! Zebra configuration saved from vty +! 2001/03/16 22:07:53 +! +hostname HOSTNAME +password PASSWORD +log file /var/log/ospfd.log +! +debug ospf ism +debug ospf nsm +debug ospf lsa +debug ospf zebra +debug ospf event +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 + 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 de1 + ip ospf hello-interval 60 + ip ospf dead-interval 240 + mpls-te link metric 111 + 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 0xcd +! +interface de0 + mpls-te link metric 0 + mpls-te link rsc-clsclr 0x0 +! +interface lp0 + ip ospf network point-to-point +! +interface tun0 + ip ospf network point-to-point +! +interface sl0 + ip ospf network point-to-point +! +interface ppp0 + ip ospf network point-to-point +! +interface lo0 +! +router ospf + compatible rfc1583 + network 192.168.0.0/16 area 1 + ospf opaque-lsa + mpls-te + mpls-te router-address 1.2.3.4 +! +line vty +! diff --git a/doc/next-hop-tracking.txt b/doc/next-hop-tracking.txt new file mode 100644 index 0000000..d157866 --- /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 0000000..e227c20 --- /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 0000000..71d1ce9 --- /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.8 b/doc/ospf6d.8 new file mode 100644 index 0000000..0643226 --- /dev/null +++ b/doc/ospf6d.8 @@ -0,0 +1,111 @@ +.TH OSPF6D 8 "25 November 2004" "Quagga OSPFv3 daemon" "Version 0.97.3" +.SH NAME +ospf6d \- an OSPFv3 routing engine for use with Quagga routing software. +.SH SYNOPSIS +.B ospf6d +[ +.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 ospf6d +is a routing component that works with the +.B Quagga +routing engine. +.SH OPTIONS +Options available for the +.B ospf6d +command: +.SH OPTIONS +.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/ospf6d.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 ospf6d starts its process identifier is written to +\fB\fIpid-file\fR. The init system uses the recorded PID to stop or +restart ospf6d. The likely default is \fB\fI/var/run/ospf6d.pid\fR. +.TP +\fB\-P\fR, \fB\-\-vty_port \fR\fIport-number\fR +Specify the port that the ospf6d VTY will listen on. This defaults to +2606, as specified in \fB\fI/etc/services\fR. +.TP +\fB\-A\fR, \fB\-\-vty_addr \fR\fIvty-address\fR +Specify the address that the ospf6d 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/ospf6d +The default location of the +.B ospf6d +binary. +.TP +.BI /usr/local/etc/ospf6d.conf +The default location of the +.B ospf6d +config file. +.TP +.BI $(PWD)/ospf6d.log +If the +.B ospf6d +process is config'd to output logs to a file, then you will find this +file in the directory where you started \fBospf6d\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 ospf6d process may log to standard output, to a VTY, to a log +file, or through syslog to the system logs. \fBospf6d\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 isisd (8), +.BR zebra (8), +.BR vtysh (1) +.SH BUGS +.B ospf6d +eats bugs for breakfast. If you have food for the maintainers try +.BI http://bugzilla.quagga.net +.SH AUTHORS +See +.BI http://www.zebra.org +and +.BI http://www.quagga.net +or the Info file for an accurate list of authors. + diff --git a/doc/ospf6d.texi b/doc/ospf6d.texi new file mode 100644 index 0000000..31f4db0 --- /dev/null +++ b/doc/ospf6d.texi @@ -0,0 +1,168 @@ +@node OSPFv3 +@chapter OSPFv3 + +@command{ospf6d} is a daemon support OSPF version 3 for IPv6 network. +OSPF for IPv6 is described in RFC2740. + +@menu +* OSPF6 router:: +* OSPF6 area:: +* OSPF6 interface:: +* Redistribute routes to OSPF6:: +* Showing OSPF6 information:: +* OSPF6 Configuration Examples:: +@end menu + +@node OSPF6 router +@section OSPF6 router + +@deffn {Command} {router ospf6} {} +@end deffn + +@deffn {OSPF6 Command} {router-id @var{a.b.c.d}} {} +Set router's Router-ID. +@end deffn + +@deffn {OSPF6 Command} {interface @var{ifname} area @var{area}} {} +Bind interface to specified area, and start sending OSPF packets. @var{area} can +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 + +Area support for OSPFv3 is not yet implemented. + +@node OSPF6 interface +@section OSPF6 interface + +@deffn {Interface Command} {ipv6 ospf6 cost COST} {} +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} {} +Sets interface's Hello Interval. Default 40 +@end deffn + +@deffn {Interface Command} {ipv6 ospf6 dead-interval DEADINTERVAL} {} +Sets interface's Router Dead Interval. Default value is 40. +@end deffn + +@deffn {Interface Command} {ipv6 ospf6 retransmit-interval RETRANSMITINTERVAL} {} +Sets interface's Rxmt Interval. Default value is 5. +@end deffn + +@deffn {Interface Command} {ipv6 ospf6 priority PRIORITY} {} +Sets interface's Router Priority. Default value is 1. +@end deffn + +@deffn {Interface Command} {ipv6 ospf6 transmit-delay TRANSMITDELAY} {} +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 + +@deffn {OSPF6 Command} {redistribute static} {} +@deffnx {OSPF6 Command} {redistribute connected} {} +@deffnx {OSPF6 Command} {redistribute ripng} {} +@end deffn + +@node Showing OSPF6 information +@section Showing OSPF6 information + +@deffn {Command} {show ipv6 ospf6 [INSTANCE_ID]} {} +INSTANCE_ID is an optional OSPF instance ID. To see router ID and OSPF +instance ID, simply type "show ipv6 ospf6 ". +@end deffn + +@deffn {Command} {show ipv6 ospf6 database} {} +This command shows LSA database summary. You can specify the type of LSA. +@end deffn + +@deffn {Command} {show ipv6 ospf6 interface} {} +To see OSPF interface configuration like costs. +@end deffn + +@deffn {Command} {show ipv6 ospf6 neighbor} {} +Shows state and chosen (Backup) DR of neighbor. +@end deffn + +@deffn {Command} {show ipv6 ospf6 request-list A.B.C.D} {} +Shows requestlist of neighbor. +@end deffn + +@deffn {Command} {show ipv6 route ospf6} {} +This command shows internal routing table. +@end deffn + +@node OSPF6 Configuration Examples +@section OSPF6 Configuration Examples + +Example of ospf6d configured on one interface and area: + +@example +interface eth0 + ipv6 ospf6 instance-id 0 +! +router ospf6 + router-id 212.17.55.53 + area 0.0.0.0 range 2001:770:105:2::/64 + interface eth0 area 0.0.0.0 +! +@end example diff --git a/doc/ospf_fundamentals.texi b/doc/ospf_fundamentals.texi new file mode 100644 index 0000000..82218e6 --- /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 0000000..ccfad1a --- /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.8 b/doc/ospfd.8 new file mode 100644 index 0000000..8c819cf --- /dev/null +++ b/doc/ospfd.8 @@ -0,0 +1,113 @@ +.TH OSPFD 8 "25 November 2004" "Quagga OSPFv2 daemon" "Version 0.97.3" +.SH NAME +ospfd \- an OSPFv2 routing engine for use with Quagga routing software. +.SH SYNOPSIS +.B ospfd +[ +.B \-dhlv +] [ +.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 ospfd +is a routing component that works with the +.B Quagga +routing engine. +.SH OPTIONS +Options available for the +.B ospfd +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/ospfd.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 ospfd starts its process identifier is written to +\fB\fIpid-file\fR. The init system uses the recorded PID to stop or +restart ospfd. The likely default is \fB\fI/var/run/ospfd.pid\fR. +.TP +\fB\-P\fR, \fB\-\-vty_port \fR\fIport-number\fR +Specify the port that the ospfd VTY will listen on. This defaults to +2604, as specified in \fB\fI/etc/services\fR. +.TP +\fB\-A\fR, \fB\-\-vty_addr \fR\fIvty-address\fR +Specify the address that the ospfd 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\-a\fR, \fB\-\-apiserver \fR +Enable OSPF apiserver. Default is disabled. +.TP +\fB\-v\fR, \fB\-\-version\fR +Print the version and exit. +.SH FILES +.TP +.BI /usr/local/sbin/ospfd +The default location of the +.B ospfd +binary. +.TP +.BI /usr/local/etc/ospfd.conf +The default location of the +.B ospfd +config file. +.TP +.BI $(PWD)/ospfd.log +If the +.B ospfd +process is config'd to output logs to a file, then you will find this +file in the directory where you started \fBospfd\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 ospfd process may log to standard output, to a VTY, to a log +file, or through syslog to the system logs. \fBospfd\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 ospf6d (8), +.BR isisd (8), +.BR zebra (8), +.BR vtysh (1) +.SH BUGS +.B ospfd +eats bugs for breakfast. If you have food for the maintainers try +.BI http://bugzilla.quagga.net +.SH AUTHORS +See +.BI http://www.zebra.org +and +.BI http://www.quagga.net +or the Info file for an accurate list of authors. + diff --git a/doc/ospfd.texi b/doc/ospfd.texi new file mode 100644 index 0000000..d60ecf2 --- /dev/null +++ b/doc/ospfd.texi @@ -0,0 +1,922 @@ + +@cindex OSPFv2 +@node OSPFv2 +@chapter OSPFv2 + +@acronym{OSPF,Open Shortest Path First} version 2 is a routing protocol +which is described in @cite{RFC2328, OSPF Version 2}. OSPF is an +@acronym{IGP,Interior Gateway Protocol}. Compared with @acronym{RIP}, +@acronym{OSPF} can provide scalable network support and faster +convergence times. OSPF is widely used in large networks such as +@acronym{ISP,Internet Service Provider} backbone and enterprise +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 + +There are no @command{ospfd} specific options. Common options can be +specified (@pxref{Common Invocation Options}) to @command{ospfd}. +@command{ospfd} needs to acquire interface information from +@command{zebra} in order to function. Therefore @command{zebra} must be +running before invoking @command{ospfd}. Also, if @command{zebra} is +restarted then @command{ospfd} must be too. + +Like other daemons, @command{ospfd} configuration is done in @acronym{OSPF} +specific configuration file @file{ospfd.conf}. + +@node OSPF router +@section OSPF router + +To start OSPF process you have to specify the OSPF router. As of this +writing, @command{ospfd} does not support multiple OSPF processes. + +@deffn Command {router ospf} {} +@deffnx Command {no router ospf} {} +Enable or disable the OSPF process. @command{ospfd} does not yet +support multiple OSPF processes. So you can not specify an OSPF process +number. +@end deffn + +@deffn {OSPF Command} {ospf router-id @var{a.b.c.d}} {} +@deffnx {OSPF Command} {no ospf router-id} {} +@anchor{ospf router-id}This sets the router-ID of the OSPF process. The +router-ID may be an IP address of the router, but need not be - it can +be any arbitrary 32bit number. However it MUST be unique within the +entire OSPF domain to the OSPF speaker - bad things will happen if +multiple OSPF speakers are configured with the same router-ID! If one +is not specified then @command{ospfd} will obtain a router-ID +automatically from @command{zebra}. +@end deffn + +@deffn {OSPF Command} {ospf abr-type @var{type}} {} +@deffnx {OSPF Command} {no ospf abr-type @var{type}} {} +@var{type} can be cisco|ibm|shortcut|standard. The "Cisco" and "IBM" types +are equivalent. + +The OSPF standard for ABR behaviour does not allow an ABR to consider +routes through non-backbone areas when its links to the backbone are +down, even when there are other ABRs in attached non-backbone areas +which still can reach the backbone - this restriction exists primarily +to ensure routing-loops are avoided. + +With the "Cisco" or "IBM" ABR type, the default in this release of +Quagga, this restriction is lifted, allowing an ABR to consider +summaries learnt from other ABRs through non-backbone areas, and hence +route via non-backbone areas as a last resort when, and only when, +backbone links are down. + +Note that areas with fully-adjacent virtual-links are considered to be +"transit capable" and can always be used to route backbone traffic, and +hence are unaffected by this setting (@pxref{OSPF virtual-link}). + +More information regarding the behaviour controlled by this command can +be found in @cite{RFC 3509, Alternative Implementations of OSPF Area +Border Routers}, and @cite{draft-ietf-ospf-shortcut-abr-02.txt}. + +Quote: "Though the definition of the @acronym{ABR,Area Border Router} +in the OSPF specification does not require a router with multiple +attached areas to have a backbone connection, it is actually +necessary to provide successful routing to the inter-area and +external destinations. If this requirement is not met, all traffic +destined for the areas not connected to such an ABR or out of the +OSPF domain, is dropped. This document describes alternative ABR +behaviors implemented in Cisco and IBM routers." +@end deffn + +@deffn {OSPF Command} {ospf rfc1583compatibility} {} +@deffnx {OSPF Command} {no ospf rfc1583compatibility} {} +@cite{RFC2328}, the sucessor to @cite{RFC1583}, suggests according +to section G.2 (changes) in section 16.4 a change to the path +preference algorithm that prevents possible routing loops that were +possible in the old version of OSPFv2. More specifically it demands +that inter-area paths and intra-area backbone path are now of equal preference +but still both preferred to external paths. + +This command should NOT be set normally. +@end deffn + +@deffn {OSPF Command} {log-adjacency-changes [detail]} {} +@deffnx {OSPF Command} {no log-adjacency-changes [detail]} {} +Configures ospfd to log changes in adjacency. With the optional +detail argument, all changes in adjacency status are shown. Without detail, +only changes to full or regressions are shown. +@end deffn + +@deffn {OSPF Command} {passive-interface @var{interface}} {} +@deffnx {OSPF Command} {no passive-interface @var{interface}} {} +@anchor{OSPF passive-interface} Do not speak OSPF interface on the +given interface, but do advertise the interface as a stub link in the +router-@acronym{LSA,Link State Advertisement} for this router. This +allows one to advertise addresses on such connected interfaces without +having to originate AS-External/Type-5 LSAs (which have global flooding +scope) - as would occur if connected addresses were redistributed into +OSPF (@pxref{Redistribute routes to OSPF})@. This is the only way to +advertise non-OSPF links into stub areas. +@end deffn + +@deffn {OSPF Command} {timers throttle spf @var{delay} @var{initial-holdtime} @var{max-holdtime}} {} +@deffnx {OSPF 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}. The current +holdtime can be viewed with @ref{show ip ospf}, where it is expressed as +a multiplier of the @var{initial-holdtime}. + +@example +@group +router ospf + 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. + +This command supercedes the @command{timers spf} command in previous Quagga +releases. +@end deffn + +@deffn {OSPF Command} {max-metric router-lsa [on-startup|on-shutdown] <5-86400>} {} +@deffnx {OSPF Command} {max-metric router-lsa administrative} {} +@deffnx {OSPF Command} {no max-metric router-lsa [on-startup|on-shutdown|administrative]} {} +This enables @cite{RFC3137, OSPF Stub Router Advertisement} support, +where the OSPF process describes its transit links in its router-LSA as +having infinite distance so that other routers will avoid calculating +transit paths through the router while still being able to reach +networks through the router. + +This support may be enabled administratively (and indefinitely) or +conditionally. Conditional enabling of max-metric router-lsas can be +for a period of seconds after startup and/or for a period of seconds +prior to shutdown. + +Enabling this for a period after startup allows OSPF to converge fully +first without affecting any existing routes used by other routers, +while still allowing any connected stub links and/or redistributed +routes to be reachable. Enabling this for a period of time in advance +of shutdown allows the router to gracefully excuse itself from the OSPF +domain. + +Enabling this feature administratively allows for administrative +intervention for whatever reason, for an indefinite period of time. +Note that if the configuration is written to file, this administrative +form of the stub-router command will also be written to file. If +@command{ospfd} is restarted later, the command will then take effect +until manually deconfigured. + +Configured state of this feature as well as current status, such as the +number of second remaining till on-startup or on-shutdown ends, can be +viewed with the @ref{show ip ospf} command. +@end deffn + +@deffn {OSPF Command} {auto-cost reference-bandwidth <1-4294967>} {} +@deffnx {OSPF Command} {no auto-cost reference-bandwidth} {} +@anchor{OSPF 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 + +@deffn {OSPF Command} {network @var{a.b.c.d/m} area @var{a.b.c.d}} {} +@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 +ospf routers via this interface. + +@example +@group +router ospf + network 192.168.1.0/24 area 0.0.0.0 +@end group +@end example + +Prefix length in interface must be equal or bigger (ie. smaller network) than +prefix length in network statement. For example statement above doesn't enable +ospf on interface with address 192.168.1.1/23, but it does on interface with +address 192.168.1.129/25. + +Note that the behavior when there is a peer address +defined on an interface changed after release 0.99.7. +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 +@section OSPF area + +@deffn {OSPF Command} {area @var{a.b.c.d} range @var{a.b.c.d/m}} {} +@deffnx {OSPF Command} {area <0-4294967295> range @var{a.b.c.d/m}} {} +@deffnx {OSPF Command} {no area @var{a.b.c.d} range @var{a.b.c.d/m}} {} +@deffnx {OSPF Command} {no area <0-4294967295> range @var{a.b.c.d/m}} {} +Summarize intra area paths from specified area into one Type-3 summary-LSA +announced to other areas. This command can be used only in ABR and ONLY +router-LSAs (Type-1) and network-LSAs (Type-2) (ie. LSAs with scope area) can +be summarized. Type-5 AS-external-LSAs can't be summarized - their scope is AS. +Summarizing Type-7 AS-external-LSAs isn't supported yet by Quagga. + +@example +@group +router ospf + network 192.168.1.0/24 area 0.0.0.0 + network 10.0.0.0/8 area 0.0.0.10 + area 0.0.0.10 range 10.0.0.0/8 +@end group +@end example + +With configuration above one Type-3 Summary-LSA with routing info 10.0.0.0/8 is +announced into backbone area if area 0.0.0.10 contains at least one intra-area +network (ie. described with router or network LSA) from this range. +@end deffn + +@deffn {OSPF Command} {area @var{a.b.c.d} range IPV4_PREFIX not-advertise} {} +@deffnx {OSPF Command} {no area @var{a.b.c.d} range IPV4_PREFIX not-advertise} {} +Instead of summarizing intra area paths filter them - ie. intra area paths from this +range are not advertised into other areas. +This command makes sense in ABR only. +@end deffn + +@deffn {OSPF Command} {area @var{a.b.c.d} range IPV4_PREFIX substitute IPV4_PREFIX} {} +@deffnx {OSPF Command} {no area @var{a.b.c.d} range IPV4_PREFIX substitute IPV4_PREFIX} {} +Substitute summarized prefix with another prefix. + +@example +@group +router ospf + network 192.168.1.0/24 area 0.0.0.0 + network 10.0.0.0/8 area 0.0.0.10 + area 0.0.0.10 range 10.0.0.0/8 substitute 11.0.0.0/8 +@end group +@end example + +One Type-3 summary-LSA with routing info 11.0.0.0/8 is announced into backbone area if +area 0.0.0.10 contains at least one intra-area network (ie. described with router-LSA or +network-LSA) from range 10.0.0.0/8. +This command makes sense in ABR only. +@end deffn + +@deffn {OSPF Command} {area @var{a.b.c.d} virtual-link @var{a.b.c.d}} {} +@deffnx {OSPF Command} {area <0-4294967295> virtual-link @var{a.b.c.d}} {} +@deffnx {OSPF Command} {no area @var{a.b.c.d} virtual-link @var{a.b.c.d}} {} +@deffnx {OSPF Command} {no area <0-4294967295> virtual-link @var{a.b.c.d}} {} +@anchor{OSPF virtual-link} +@end deffn + +@deffn {OSPF Command} {area @var{a.b.c.d} shortcut} {} +@deffnx {OSPF Command} {area <0-4294967295> shortcut} {} +@deffnx {OSPF Command} {no area @var{a.b.c.d} shortcut} {} +@deffnx {OSPF Command} {no area <0-4294967295> shortcut} {} +Configure the area as Shortcut capable. See @cite{RFC3509}. This requires +that the 'abr-type' be set to 'shortcut'. +@end deffn + +@deffn {OSPF Command} {area @var{a.b.c.d} stub} {} +@deffnx {OSPF Command} {area <0-4294967295> stub} {} +@deffnx {OSPF Command} {no area @var{a.b.c.d} stub} {} +@deffnx {OSPF Command} {no area <0-4294967295> stub} {} +Configure the area to be a stub area. That is, an area where no router +originates routes external to OSPF and hence an area where all external +routes are via the ABR(s). Hence, ABRs for such an area do not need +to pass AS-External LSAs (type-5s) or ASBR-Summary LSAs (type-4) into the +area. They need only pass Network-Summary (type-3) LSAs into such an area, +along with a default-route summary. +@end deffn + +@deffn {OSPF Command} {area @var{a.b.c.d} stub no-summary} {} +@deffnx {OSPF Command} {area <0-4294967295> stub no-summary} {} +@deffnx {OSPF Command} {no area @var{a.b.c.d} stub no-summary} {} +@deffnx {OSPF Command} {no area <0-4294967295> stub no-summary} {} +Prevents an @command{ospfd} ABR from injecting inter-area +summaries into the specified stub area. +@end deffn + +@deffn {OSPF Command} {area @var{a.b.c.d} default-cost <0-16777215>} {} +@deffnx {OSPF Command} {no area @var{a.b.c.d} default-cost <0-16777215>} {} +Set the cost of default-summary LSAs announced to stubby areas. +@end deffn + +@deffn {OSPF Command} {area @var{a.b.c.d} export-list NAME} {} +@deffnx {OSPF Command} {area <0-4294967295> export-list NAME} {} +@deffnx {OSPF Command} {no area @var{a.b.c.d} export-list NAME} {} +@deffnx {OSPF Command} {no area <0-4294967295> export-list NAME} {} +Filter Type-3 summary-LSAs announced to other areas originated from intra- +area paths from specified area. + +@example +@group +router ospf + network 192.168.1.0/24 area 0.0.0.0 + network 10.0.0.0/8 area 0.0.0.10 + area 0.0.0.10 export-list foo +! +access-list foo permit 10.10.0.0/16 +access-list foo deny any +@end group +@end example + +With example above any intra-area paths from area 0.0.0.10 and from range +10.10.0.0/16 (for example 10.10.1.0/24 and 10.10.2.128/30) are announced into +other areas as Type-3 summary-LSA's, but any others (for example 10.11.0.0/16 +or 10.128.30.16/30) aren't. + +This command is only relevant if the router is an ABR for the specified +area. +@end deffn + +@deffn {OSPF Command} {area @var{a.b.c.d} import-list NAME} {} +@deffnx {OSPF Command} {area <0-4294967295> import-list NAME} {} +@deffnx {OSPF Command} {no area @var{a.b.c.d} import-list NAME} {} +@deffnx {OSPF Command} {no area <0-4294967295> import-list NAME} {} +Same as export-list, but it applies to paths announced into specified area as +Type-3 summary-LSAs. +@end deffn + +@deffn {OSPF Command} {area @var{a.b.c.d} filter-list prefix NAME in} {} +@deffnx {OSPF Command} {area @var{a.b.c.d} filter-list prefix NAME out} {} +@deffnx {OSPF Command} {area <0-4294967295> filter-list prefix NAME in} {} +@deffnx {OSPF Command} {area <0-4294967295> filter-list prefix NAME out} {} +@deffnx {OSPF Command} {no area @var{a.b.c.d} filter-list prefix NAME in} {} +@deffnx {OSPF Command} {no area @var{a.b.c.d} filter-list prefix NAME out} {} +@deffnx {OSPF Command} {no area <0-4294967295> filter-list prefix NAME in} {} +@deffnx {OSPF Command} {no area <0-4294967295> filter-list prefix NAME out} {} +Filtering Type-3 summary-LSAs to/from area using prefix lists. This command +makes sense in ABR only. +@end deffn + +@deffn {OSPF Command} {area @var{a.b.c.d} authentication} {} +@deffnx {OSPF Command} {area <0-4294967295> authentication} {} +@deffnx {OSPF Command} {no area @var{a.b.c.d} authentication} {} +@deffnx {OSPF Command} {no area <0-4294967295> authentication} {} +Specify that simple password authentication should be used for the given +area. +@end deffn + +@deffn {OSPF Command} {area @var{a.b.c.d} authentication message-digest} {} +@deffnx {OSPF Command} {area <0-4294967295> authentication message-digest} {} + +@anchor{area authentication message-digest}Specify that OSPF packets +must be authenticated with MD5 HMACs within the given area. Keying +material must also be configured on a per-interface basis (@pxref{ip +ospf message-digest-key}). + +MD5 authentication may also be configured on a per-interface basis +(@pxref{ip ospf authentication message-digest}). Such per-interface +settings will override any per-area authentication setting. +@end deffn + +@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}, +all OSPF packets are authenticated. @var{AUTH_KEY} has length up to 8 chars. + +Simple text password authentication is insecure and deprecated in favour of +MD5 HMAC authentication (@pxref{ip ospf authentication message-digest}). +@end deffn + +@deffn {Interface Command} {ip ospf authentication message-digest} {} +@anchor{ip ospf authentication message-digest}Specify that MD5 HMAC +authentication must be used on this interface. MD5 keying material must +also be configured (@pxref{ip ospf message-digest-key}). Overrides any +authentication enabled on a per-area basis (@pxref{area +authentication message-digest}). + +Note that OSPF MD5 authentication requires that time never go backwards +(correct time is NOT important, only that it never goes backwards), even +across resets, if ospfd is to be able to promptly reestabish adjacencies +with its neighbours after restarts/reboots. The host should have system +time be set at boot from an external or non-volatile source (eg battery backed clock, NTP, +etc.) or else the system clock should be periodically saved to non-volative +storage and restored at boot if MD5 authentication is to be expected to work +reliably. +@end deffn + +@deffn {Interface Command} {ip ospf message-digest-key KEYID md5 KEY} {} +@deffnx {Interface Command} {no ip ospf message-digest-key} {} +@anchor{ip ospf message-digest-key}Set OSPF authentication key to a +cryptographic password. The cryptographic algorithm is MD5. + +KEYID identifies secret key used to create the message digest. This ID +is part of the protocol and must be consistent across routers on a +link. + +KEY is the actual message digest key, of up to 16 chars (larger strings +will be truncated), and is associated with the given KEYID. +@end deffn + +@deffn {Interface Command} {ip ospf cost <1-65535>} {} +@deffnx {Interface Command} {no ip ospf cost} {} +Set link cost for the specified interface. The cost value is set to router-LSA's +metric field and used for SPF calculation. +@end deffn + +@deffn {Interface Command} {ip ospf dead-interval <1-65535>} {} +@deffnx {Interface Command} {ip ospf dead-interval minimal hello-multiplier <2-20>} {} +@deffnx {Interface Command} {no ip ospf dead-interval} {} +@anchor{ip ospf dead-interval minimal} Set number of seconds for +RouterDeadInterval timer value used for Wait Timer and Inactivity +Timer. This value must be the same for all routers attached to a +common network. The default value is 40 seconds. + +If 'minimal' is specified instead, then the dead-interval is set to 1 +second and one must specify a hello-multiplier. The hello-multiplier +specifies how many Hellos to send per second, from 2 (every 500ms) to +20 (every 50ms). Thus one can have 1s convergence time for OSPF. If this form +is specified, then the hello-interval advertised in Hello packets is set to +0 and the hello-interval on received Hello packets is not checked, thus +the hello-multiplier need NOT be the same across multiple routers on a common +link. +@end deffn + +@deffn {Interface Command} {ip ospf hello-interval <1-65535>} {} +@deffnx {Interface Command} {no ip ospf hello-interval} {} +Set number of seconds for HelloInterval timer value. Setting this value, +Hello packet will be sent every timer value seconds on the specified interface. +This value must be the same for all routers attached to a common network. +The default value is 10 seconds. + +This command has no effect if @ref{ip ospf dead-interval minimal} is also +specified for the interface. +@end deffn + +@deffn {Interface Command} {ip ospf network (broadcast|non-broadcast|point-to-multipoint|point-to-point)} {} +@deffnx {Interface Command} {no ip ospf network} {} +Set explicitly network type for specifed interface. +@end deffn + +@deffn {Interface Command} {ip ospf priority <0-255>} {} +@deffnx {Interface Command} {no ip ospf priority} {} +Set RouterPriority integer value. The router with the highest priority +will be more eligible to become Designated Router. Setting the value +to 0, makes the router ineligible to become Designated Router. The +default value is 1. +@end deffn + +@deffn {Interface Command} {ip ospf retransmit-interval <1-65535>} {} +@deffnx {Interface Command} {no ip ospf retransmit interval} {} +Set number of seconds for RxmtInterval timer value. This value is used +when retransmitting Database Description and Link State Request packets. +The default value is 5 seconds. +@end deffn + +@deffn {Interface Command} {ip ospf transmit-delay} {} +@deffnx {Interface Command} {no ip ospf transmit-delay} {} +Set number of seconds for InfTransDelay value. LSAs' age should be +incremented by this value when transmitting. +The default value is 1 seconds. +@end deffn + +@node Redistribute routes to OSPF +@section Redistribute routes to OSPF + +@deffn {OSPF Command} {redistribute (kernel|connected|static|rip|bgp)} {} +@deffnx {OSPF Command} {redistribute (kernel|connected|static|rip|bgp) @var{route-map}} {} +@deffnx {OSPF Command} {redistribute (kernel|connected|static|rip|bgp) metric-type (1|2)} {} +@deffnx {OSPF Command} {redistribute (kernel|connected|static|rip|bgp) metric-type (1|2) route-map @var{word}} {} +@deffnx {OSPF Command} {redistribute (kernel|connected|static|rip|bgp) metric <0-16777214>} {} +@deffnx {OSPF Command} {redistribute (kernel|connected|static|rip|bgp) metric <0-16777214> route-map @var{word}} {} +@deffnx {OSPF Command} {redistribute (kernel|connected|static|rip|bgp) metric-type (1|2) metric <0-16777214>} {} +@deffnx {OSPF Command} {redistribute (kernel|connected|static|rip|bgp) metric-type (1|2) metric <0-16777214> route-map @var{word}} {} +@deffnx {OSPF Command} {no redistribute (kernel|connected|static|rip|bgp)} {} +@anchor{OSPF redistribute}Redistribute routes of the specified protocol +or kind into OSPF, with the metric type and metric set if specified, +filtering the routes using the given route-map if specified. +Redistributed routes may also be filtered with distribute-lists, see +@ref{ospf distribute-list}. + +Redistributed routes are distributed as into OSPF as Type-5 External +LSAs into links to areas that accept external routes, Type-7 External LSAs +for NSSA areas and are not redistributed at all into Stub areas, where +external routes are not permitted. + +Note that for connected routes, one may instead use +@dfn{passive-interface}, see @ref{OSPF passive-interface}. +@end deffn + +@deffn {OSPF Command} {default-information originate} {} +@deffnx {OSPF Command} {default-information originate metric <0-16777214>} {} +@deffnx {OSPF Command} {default-information originate metric <0-16777214> metric-type (1|2)} {} +@deffnx {OSPF Command} {default-information originate metric <0-16777214> metric-type (1|2) route-map @var{word}} {} +@deffnx {OSPF Command} {default-information originate always} {} +@deffnx {OSPF Command} {default-information originate always metric <0-16777214>} {} +@deffnx {OSPF Command} {default-information originate always metric <0-16777214> metric-type (1|2)} {} +@deffnx {OSPF Command} {default-information originate always metric <0-16777214> metric-type (1|2) route-map @var{word}} {} +@deffnx {OSPF Command} {no default-information originate} {} +Originate an AS-External (type-5) LSA describing a default route into +all external-routing capable areas, of the specified metric and metric +type. If the 'always' keyword is given then the default is always +advertised, even when there is no default present in the routing table. +@end deffn + +@deffn {OSPF Command} {distribute-list NAME out (kernel|connected|static|rip|ospf} {} +@deffnx {OSPF Command} {no distribute-list NAME out (kernel|connected|static|rip|ospf} {} +@anchor{ospf distribute-list}Apply the access-list filter, NAME, to +redistributed routes of the given type before allowing the routes to +redistributed into OSPF (@pxref{OSPF redistribute}). +@end deffn + +@deffn {OSPF Command} {default-metric <0-16777214>} {} +@deffnx {OSPF Command} {no default-metric} {} +@end deffn + +@deffn {OSPF Command} {distance <1-255>} {} +@deffnx {OSPF Command} {no distance <1-255>} {} +@end deffn + +@deffn {OSPF Command} {distance ospf (intra-area|inter-area|external) <1-255>} {} +@deffnx {OSPF Command} {no distance ospf} {} +@end deffn + +@node Showing OSPF information +@section Showing OSPF information + +@deffn {Command} {show ip ospf} {} +@anchor{show ip ospf}Show information on a variety of general OSPF and +area state and configuration information. +@end deffn + +@deffn {Command} {show ip ospf interface [INTERFACE]} {} +Show state and configuration of OSPF the specified interface, or all +interfaces if no interface is given. +@end deffn + +@deffn {Command} {show ip ospf neighbor} {} +@deffnx {Command} {show ip ospf neighbor INTERFACE} {} +@deffnx {Command} {show ip ospf neighbor detail} {} +@deffnx {Command} {show ip ospf neighbor INTERFACE detail} {} +@end deffn + +@deffn {Command} {show ip ospf database} {} +@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} {} +@end deffn + +@deffn {Command} {show ip ospf database self-originate} {} +@end deffn + +@deffn {Command} {show ip ospf route} {} +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} {} +@end deffn + +@node OSPF Configuration Examples +@section OSPF Configuration Examples +A simple example, with MD5 authentication enabled: + +@example +@group +! +interface bge0 + ip ospf authentication message-digest + ip ospf message-digest-key 1 md5 ABCDEFGHIJK +! +router ospf + network 192.168.0.0/16 area 0.0.0.1 + area 0.0.0.1 authentication message-digest +@end group +@end example + +An @acronym{ABR} router, with MD5 authentication and performing summarisation +of networks between the areas: + +@example +@group +! +password ABCDEF +log file /var/log/quagga/ospfd.log +service advanced-vty +! +interface eth0 + ip ospf authentication message-digest + ip ospf message-digest-key 1 md5 ABCDEFGHIJK +! +interface ppp0 +! +interface br0 + ip ospf authentication message-digest + ip ospf message-digest-key 2 md5 XYZ12345 +! +router ospf + ospf router-id 192.168.0.1 + redistribute connected + passive interface ppp0 + network 192.168.0.0/24 area 0.0.0.0 + network 10.0.0.0/16 area 0.0.0.0 + network 192.168.1.0/24 area 0.0.0.1 + area 0.0.0.0 authentication message-digest + area 0.0.0.0 range 10.0.0.0/16 + area 0.0.0.0 range 192.168.0.0/24 + area 0.0.0.1 authentication message-digest + area 0.0.0.1 range 10.2.0.0/16 +! +@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 new file mode 100644 index 0000000..2301c4b --- /dev/null +++ b/doc/overview.texi @@ -0,0 +1,337 @@ +@node Overview +@chapter Overview +@cindex Overview + + @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, IS-IS, BGP-4, and BGP-4+ (@pxref{Supported +RFCs}). Quagga also supports special BGP Route Reflector and Route Server +behavior. In addition to traditional IPv4 routing protocols, Quagga also +supports IPv6 routing protocols. With SNMP daemon which supports SMUX and AgentX +protocol, Quagga provides routing protocol MIBs (@pxref{SNMP Support}). + + Quagga uses an advanced software architecture to provide you with a high +quality, multi server routing engine. Quagga has an interactive user +interface for each routing protocol and supports common client commands. +Due to this design, you can add new protocol daemons to Quagga easily. You +can use Quagga library as your program's client user interface. + + Quagga is distributed under the @sc{gnu} General Public License. + +@menu +* About Quagga:: Basic information about Quagga +* System Architecture:: The Quagga system architecture +* Supported Platforms:: Supported platforms and future plans +* Supported RFCs:: Supported RFCs +* How to get Quagga:: +* Mailing List:: Mailing list information +* Bug Reports:: Mail address for bug data +@end menu + +@node About Quagga +@comment node-name, next, previous, up +@section About Quagga +@cindex About Quagga + + Today, TCP/IP networks are covering all of the world. The Internet has +been deployed in many countries, companies, and to the home. When you +connect to the Internet your packet will pass many routers which have TCP/IP +routing functionality. + + A system with Quagga installed acts as a dedicated router. With Quagga, +your machine exchanges routing information with other routers using routing +protocols. Quagga uses this information to update the kernel routing table +so that the right data goes to the right place. You can dynamically change +the configuration and you may view routing table information from the Quagga +terminal interface. + + Adding to routing protocol support, Quagga can setup interface's flags, +interface's address, static routes and so on. If you have a small network, +or a stub network, or xDSL connection, configuring the Quagga routing +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, IS-IS or BGP. + + Traditionally, UNIX based router configuration is done by +@command{ifconfig} and @command{route} commands. Status of routing +table is displayed by @command{netstat} utility. Almost of these commands +work only if the user has root privileges. Quagga has a different system +administration method. There are two user modes in Quagga. One is normal +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, 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 +@section System Architecture +@cindex System architecture +@cindex Software architecture +@cindex Software internals + + Traditional routing software is made as a one process program which +provides all of the routing protocol functionalities. Quagga takes a +different approach. It is made from a collection of several daemons that +work together to build the routing table. There may be several +protocol-specific routing daemons and zebra the kernel routing manager. + + The @command{ripd} daemon handles the RIP protocol, while +@command{ospfd} is a daemon which supports OSPF version 2. +@command{bgpd} supports the BGP-4 protocol. For changing the kernel +routing table and for redistribution of routes between different routing +protocols, there is a kernel routing table manager @command{zebra} daemon. +It is easy to add a new routing protocol daemons to the entire routing +system without affecting any other software. You need to run only the +protocol daemon associated with routing protocols in use. Thus, user may +run a specific daemon and send routing reports to a central routing console. + + There is no need for these daemons to be running on the same machine. You +can even run several same protocol daemons on the same machine. This +architecture creates new possibilities for the routing system. + +@example +@group ++----+ +----+ +-----+ +-----+ +|bgpd| |ripd| |ospfd| |zebra| ++----+ +----+ +-----+ +-----+ + | ++---------------------------|--+ +| v | +| UNIX Kernel routing table | +| | ++------------------------------+ + + Quagga System Architecture +@end group +@end example + +Multi-process architecture brings extensibility, modularity and +maintainability. At the same time it also brings many configuration files +and terminal interfaces. Each daemon has it's own configuration file and +terminal interface. When you configure a static route, it must be done in +@command{zebra} configuration file. When you configure BGP network it must +be done in @command{bgpd} configuration file. This can be a very annoying +thing. To resolve the problem, Quagga provides integrated user interface +shell called @command{vtysh}. @command{vtysh} connects to each daemon with +UNIX domain socket and then works as a proxy for user input. + +Quagga was planned to use multi-threaded mechanism when it runs with a +kernel that supports multi-threads. But at the moment, the thread library +which comes with @sc{gnu}/Linux or FreeBSD has some problems with running +reliable services such as routing software, so we don't use threads at all. +Instead we use the @command{select(2)} system call for multiplexing the +events. + +@node Supported Platforms +@comment node-name, next, previous, up +@section Supported Platforms + +@cindex Supported platforms +@cindex Quagga on other systems +@cindex Compatibility with other systems +@cindex Operating systems that support 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 +platform which is not listed below. + +The list of officially supported platforms are listed below. Note that +Quagga may run correctly on other platforms, and may run with partial +functionality on further platforms. + +@sp 1 +@itemize @bullet +@item +@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 +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 +@sc{gnu}'s GCC +@item +LLVM's clang +@item +Intel's ICC +@end itemize + +@node Supported RFCs +@comment node-name, next, previous, up +@section Supported RFCs + + Below is the list of currently supported RFC's. + +@table @asis +@item @asis{RFC1058} +@cite{Routing Information Protocol. C.L. Hedrick. Jun-01-1988.} + +@item @asis{RF2082} +@cite{RIP-2 MD5 Authentication. F. Baker, R. Atkinson. January 1997.} + +@item @asis{RFC2453} +@cite{RIP Version 2. G. Malkin. November 1998.} + +@item @asis{RFC2080} +@cite{RIPng for IPv6. G. Malkin, R. Minnear. January 1997.} + +@item @asis{RFC2328} +@cite{OSPF Version 2. J. Moy. April 1998.} + +@item @asis{RFC2370} +@cite{The OSPF Opaque LSA Option R. Coltun. July 1998.} + +@item @asis{RFC3101} +@cite{The OSPF Not-So-Stubby Area (NSSA) Option P. Murphy. January 2003.} + +@item @asis{RFC2740} +@cite{OSPF for IPv6. R. Coltun, D. Ferguson, J. Moy. December 1999.} + +@item @asis{RFC1771} +@cite{A Border Gateway Protocol 4 (BGP-4). Y. Rekhter & T. Li. March 1995.} + +@item @asis{RFC1965} +@cite{Autonomous System Confederations for BGP. P. Traina. June 1996.} + +@item @asis{RFC1997} +@cite{BGP Communities Attribute. R. Chandra, P. Traina & T. Li. August 1996.} + +@item @asis{RFC2545} +@cite{Use of BGP-4 Multiprotocol Extensions for IPv6 Inter-Domain Routing. P. Marques, F. Dupont. March 1999.} + +@item @asis{RFC2796} +@cite{BGP Route Reflection An alternative to full mesh IBGP. T. Bates & R. Chandrasekeran. June 1996.} + +@item @asis{RFC2858} +@cite{Multiprotocol Extensions for BGP-4. T. Bates, Y. Rekhter, R. Chandra, D. Katz. June 2000.} + +@item @asis{RFC2842} +@cite{Capabilities Advertisement with BGP-4. R. Chandra, J. Scudder. May 2000.} + +@item @asis{RFC3137} +@cite{OSPF Stub Router Advertisement, A. Retana, L. Nguyen, R. White, A. Zinin, D. McPherson. June 2001} +@end table + + When SNMP support is enabled, below RFC is also supported. + +@table @asis + +@item @asis{RFC1227} +@cite{SNMP MUX protocol and MIB. M.T. Rose. May-01-1991.} + +@item @asis{RFC1657} +@cite{Definitions of Managed Objects for the Fourth Version of the +Border Gateway Protocol (BGP-4) using SMIv2. S. Willis, J. Burruss, +J. Chu, Editor. July 1994.} + +@item @asis{RFC1724} +@cite{RIP Version 2 MIB Extension. G. Malkin & F. Baker. November 1994.} + +@item @asis{RFC1850} +@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 +@comment node-name, next, previous, up +@section How to get Quagga + +The official Quagga web-site is located at: + +@uref{http://www.quagga.net/} + +and contains further information, as well as links to additional +resources. + +@uref{http://www.quagga.net/,Quagga} is a fork of GNU Zebra, whose +web-site is located at: + +@uref{http://www.zebra.org/}. + +@node Mailing List +@comment node-name, next, previous, up +@section Mailing List +@cindex How to get in touch with Quagga +@cindex Mailing Quagga +@cindex Contact information +@cindex Mailing lists + +There is a mailing list for discussions about Quagga. If you have any +comments or suggestions to Quagga, please subscribe to: + +@uref{http://lists.quagga.net/mailman/listinfo/quagga-users}. + +The @uref{http://www.quagga.net/,,Quagga} site has further information on +the available mailing lists, see: + + @uref{http://www.quagga.net/lists.php} + +@node Bug Reports +@section Bug Reports + +@cindex Bug Reports +@cindex Bug hunting +@cindex Found a bug? +@cindex Reporting bugs +@cindex Reporting software errors +@cindex Errors in the software + +If you think you have found a bug, please send a bug report to: + +@uref{http://bugzilla.quagga.net} + +When you send a bug report, please be careful about the points below. + +@itemize @bullet +@item +Please note what kind of OS you are using. If you use the IPv6 stack +please note that as well. +@item +Please show us the results of @code{netstat -rn} and @code{ifconfig -a}. +Information from zebra's VTY command @code{show ip route} will also be +helpful. +@item +Please send your configuration file with the report. If you specify +arguments to the configure script please note that too. +@end itemize + + Bug reports are very important for us to improve the quality of Quagga. +Quagga is still in the development stage, but please don't hesitate to +send a bug report to @uref{http://bugzilla.quagga.net}. diff --git a/doc/pimd.8 b/doc/pimd.8 new file mode 100644 index 0000000..0dd170a --- /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/protocol.texi b/doc/protocol.texi new file mode 100644 index 0000000..602768a --- /dev/null +++ b/doc/protocol.texi @@ -0,0 +1,112 @@ +@node Zebra Protocol +@appendix Zebra Protocol +@appendixsection Overview of the Zebra Protocol + +Zebra Protocol is used by protocol daemons to communicate with the +zebra daemon. + +Each protocol daemon may request and send information to and from the +zebra daemon such as interface states, routing state, +nexthop-validation, and so on. Protocol daemons may also install routes +with zebra. The zebra daemon manages which route is installed into the +forwarding table with the kernel. + +Zebra Protocol is a streaming protocol, with a common header. Two +versions of the header are in use. Version 0 is implicitely versioned. +Version 1 has an explicit version field. Version 0 can be distinguished +from all other versions by examining the 3rd byte of the header, which +contains a marker value for all versions bar version 0. The marker byte +corresponds to the command field in version 0, and the marker value is +a reserved command in version 0. + +We do not anticipate there will be further versions of the header for +the foreseeable future, as the command field in version 1 is wide +enough to allow for future extensions to done compatibly through +seperate commands. + +Version 0 is used by all versions of GNU Zebra as of this writing, and +versions of Quagga up to and including Quagga 0.98. Version 1 will be +used as of Quagga 1.0. + +@appendixsection Zebra Protocol Definition +@appendixsubsec Zebra Protocol Header (version 0) +@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 ++-------------------------------+---------------+ +| Length (2) | Command (1) | ++-------------------------------+---------------+ +@end group +@end example + +@appendixsubsec Zebra Protocol Common Header (version 1) +@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 ++-------------------------------+---------------+-------------+ +| Length (2) | Marker (1) | Version (1) | ++-------------------------------+---------------+-------------+ +| Command (2) | ++-------------------------------+ +@end group +@end example + +@appendixsubsec Zebra Protocol Header Field Definitions +@table @samp +@item Length +Total packet length including this header. The minimum length is 3 +bytes for version 0 messages and 6 bytes for version 1 messages. + +@item Marker +Static marker with a value of 255 always. This is to allow version 0 +Zserv headers (which do not include version explicitely) to be +distinguished from versioned headers. Not present in version 0 +messages. + +@item Version +Version number of the Zserv message. Clients should not continue +processing messages past the version field for versions they do not +recognise. Not present in version 0 messages. + +@item Command +The Zebra Protocol command. +@end table + +@appendixsubsec Zebra Protocol Commands +@multitable {ZEBRA_REDISTRIBUTE_DEFAULT_DELETE_WHATEVER} {99999} +@headitem Command @tab Value +@item ZEBRA_INTERFACE_ADD +@tab 1 +@item ZEBRA_INTERFACE_DELETE +@tab 2 +@item ZEBRA_INTERFACE_ADDRESS_ADD +@tab 3 +@item ZEBRA_INTERFACE_ADDRESS_DELETE +@tab 4 +@item ZEBRA_INTERFACE_UP +@tab 5 +@item ZEBRA_INTERFACE_DOWN +@tab 6 +@item ZEBRA_IPV4_ROUTE_ADD +@tab 7 +@item ZEBRA_IPV4_ROUTE_DELETE +@tab 8 +@item ZEBRA_IPV6_ROUTE_ADD +@tab 9 +@item ZEBRA_IPV6_ROUTE_DELETE +@tab 10 +@item ZEBRA_REDISTRIBUTE_ADD +@tab 11 +@item ZEBRA_REDISTRIBUTE_DELETE +@tab 12 +@item ZEBRA_REDISTRIBUTE_DEFAULT_ADD +@tab 13 +@item ZEBRA_REDISTRIBUTE_DEFAULT_DELETE +@tab 14 +@item ZEBRA_IPV4_NEXTHOP_LOOKUP +@tab 15 +@item ZEBRA_IPV6_NEXTHOP_LOOKUP +@tab 16 +@end multitable diff --git a/doc/quagga.texi b/doc/quagga.texi new file mode 100644 index 0000000..13e988e --- /dev/null +++ b/doc/quagga.texi @@ -0,0 +1,144 @@ +\input texinfo @c -*- texinfo -*- + +@c %**start of header +@setfilename quagga.info +@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 +@include version.texi + +@copying +@value{COPYRIGHT_STR} +@quotation + +Permission is granted to make and distribute verbatim copies of this +manual provided the copyright notice and this permission notice are +preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions, +except that this permission notice may be stated in a translation +approved by Kunihiro Ishiguro. +@end quotation +@end copying + +@c Info entry +@dircategory Routing Software: +@direntry +* @value{PACKAGE_NAME}: (quagga). The Quagga Software Routing Suite +@end direntry + +@c @smallbook + +@ifinfo +This file documents the Quagga Software Routing Suite which manages common +TCP/IP routing protocols. + +This is Edition @value{EDITION}, last updated @value{UPDATED} of +@cite{The Quagga Manual}, for @uref{http://www.quagga.net/,,@value{PACKAGE_NAME}} +Version @value{VERSION}. + +@insertcopying +@end ifinfo + +@titlepage +@title @uref{http://www.quagga.net,,Quagga} +@subtitle A routing software package for TCP/IP networks +@subtitle @uref{http://www.quagga.net,,@value{PACKAGE_NAME}} @value{VERSION} +@subtitle @value{UPDATED-MONTH} +@author @value{AUTHORS} + +@page +@vskip 0pt plus 1filll + +@insertcopying +@end titlepage +@page + +@ifnottex +@node Top +@top Quagga + +@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 Quagga @value{VERSION}. @uref{http://www.quagga.net,,Quagga} is a fork of +@uref{http://www.zebra.org,,GNU Zebra}. + +@insertcopying +@end ifnottex + +@menu +* Overview:: +* Installation:: +* Basic commands:: +* Zebra:: +* RIP:: +* RIPng:: +* OSPFv2:: +* OSPFv3:: +* ISIS:: +* NHRP:: +* BGP:: +* Configuring Quagga as a Route Server:: +* VTY shell:: +* Filtering:: +* Route Map:: +* IPv6 Support:: +* Kernel Interface:: +* SNMP Support:: +* Zebra Protocol:: +* Packet Binary Dump Format:: +* Command Index:: +* VTY Key Index:: +* Index:: +@end menu +@contents + +@include overview.texi +@include install.texi +@include basic.texi +@include main.texi +@include ripd.texi +@include ripngd.texi +@include ospfd.texi +@include ospf6d.texi +@include isisd.texi +@include nhrpd.texi +@include bgpd.texi +@include routeserver.texi +@include vtysh.texi +@include filter.texi +@include routemap.texi +@include ipv6.texi +@include kernel.texi +@include snmp.texi +@include protocol.texi +@include appendix.texi + +@node Command Index +@unnumbered Command Index + +@printindex fn + +@node VTY Key Index +@unnumbered VTY Key Index + +@printindex ky + +@node Index +@unnumbered Index + +@printindex cp +@bye diff --git a/doc/ripd.8 b/doc/ripd.8 new file mode 100644 index 0000000..8fa9bf2 --- /dev/null +++ b/doc/ripd.8 @@ -0,0 +1,113 @@ +.TH RIPD 8 "25 November 2004" "Quagga RIP daemon" "Version 0.97.3" +.SH NAME +ripd \- a RIP routing engine for use with Quagga routing software. +.SH SYNOPSIS +.B ripd +[ +.B \-dhrv +] [ +.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 ripd +is a routing component that works with the +.B Quagga +routing engine. +.SH OPTIONS +Options available for the +.B ripd +command: +.SH OPTIONS +.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/ripd.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 ripd starts its process identifier is written to +\fB\fIpid-file\fR. The init system uses the recorded PID to stop or +restart ripd. The likely default is \fB\fI/var/run/ripd.pid\fR. +.TP +\fB\-P\fR, \fB\-\-vty_port \fR\fIport-number\fR +Specify the port that the ripd VTY will listen on. This defaults to +2602, as specified in \fB\fI/etc/services\fR. +.TP +\fB\-A\fR, \fB\-\-vty_addr \fR\fIvty-address\fR +Specify the address that the ripd 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\-r\fR, \fB\-\-retain\fR +When the program terminates, retain routes added by \fBripd\fR. +.TP +\fB\-v\fR, \fB\-\-version\fR +Print the version and exit. +.SH FILES +.TP +.BI /usr/local/sbin/ripd +The default location of the +.B ripd +binary. +.TP +.BI /usr/local/etc/ripd.conf +The default location of the +.B ripd +config file. +.TP +.BI $(PWD)/ripd.log +If the +.B ripd +process is config'd to output logs to a file, then you will find this +file in the directory where you started \fBripd\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 ripd process may log to standard output, to a VTY, to a log +file, or through syslog to the system logs. \fBripd\fR supports many +debugging options, see the Info file, or the source for details. +.SH "SEE ALSO" +.BR bgpd (8), +.BR ripngd (8), +.BR ospfd (8), +.BR ospf6d (8), +.BR isisd (8), +.BR zebra (8), +.BR vtysh (1) +.SH BUGS +.B ripd +eats bugs for breakfast. If you have food for the maintainers try +.BI http://bugzilla.quagga.net +.SH AUTHORS +See +.BI http://www.zebra.org +and +.BI http://www.quagga.net +or the Info file for an accurate list of authors. diff --git a/doc/ripd.texi b/doc/ripd.texi new file mode 100644 index 0000000..78d63ee --- /dev/null +++ b/doc/ripd.texi @@ -0,0 +1,623 @@ +@c -*-texinfo-*- +@c This is part of the Quagga Manual. +@c @value{COPYRIGHT_STR} +@c See file quagga.texi for copying conditions. +@node RIP +@chapter RIP + +RIP -- Routing Information Protocol is widely deployed interior gateway +protocol. RIP was developed in the 1970s at Xerox Labs as part of the +XNS routing protocol. RIP is a @dfn{distance-vector} protocol and is +based on the @dfn{Bellman-Ford} algorithms. As a distance-vector +protocol, RIP router send updates to its neighbors periodically, thus +allowing the convergence to a known topology. In each update, the +distance to any given network will be broadcasted to its neighboring +router. + +@command{ripd} supports RIP version 2 as described in RFC2453 and RIP +version 1 as described in RFC1058. + +@menu +* Starting and Stopping ripd:: +* RIP Configuration:: +* RIP Version Control:: +* How to Announce RIP route:: +* Filtering RIP Routes:: +* RIP Metric Manipulation:: +* RIP distance:: +* RIP route-map:: +* RIP Authentication:: +* RIP Timers:: +* Show RIP Information:: +* RIP Debug Commands:: +@end menu + +@node Starting and Stopping ripd +@section Starting and Stopping ripd + +The default configuration file name of @command{ripd}'s is +@file{ripd.conf}. When invocation @command{ripd} searches directory +@value{INSTALL_PREFIX_ETC}. If @file{ripd.conf} is not there next +search current directory. + +RIP uses UDP port 520 to send and receive RIP packets. So the user must have +the capability to bind the port, generally this means that the user must +have superuser privileges. RIP protocol requires interface information +maintained by @command{zebra} daemon. So running @command{zebra} +is mandatory to run @command{ripd}. Thus minimum sequence for running +RIP is like below: + +@example +@group +# zebra -d +# ripd -d +@end group +@end example + +Please note that @command{zebra} must be invoked before @command{ripd}. + +To stop @command{ripd}. Please use @command{kill `cat +/var/run/ripd.pid`}. Certain signals have special meaningss to @command{ripd}. + +@table @samp +@item SIGHUP +Reload configuration file @file{ripd.conf}. All configurations are +reseted. All routes learned so far are cleared and removed from routing +table. +@item SIGUSR1 +Rotate @command{ripd} logfile. +@item SIGINT +@itemx SIGTERM +@command{ripd} sweeps all installed RIP routes then terminates properly. +@end table + +@command{ripd} invocation options. Common options that can be specified +(@pxref{Common Invocation Options}). + +@table @samp +@item -r +@itemx --retain +When the program terminates, retain routes added by @command{ripd}. +@end table + +@menu +* RIP netmask:: +@end menu + +@node RIP netmask +@subsection RIP netmask + +The netmask features of @command{ripd} support both version 1 and version 2 of +RIP. Version 1 of RIP originally contained no netmask information. In +RIP version 1, network classes were originally used to determine the +size of the netmask. Class A networks use 8 bits of mask, Class B +networks use 16 bits of masks, while Class C networks use 24 bits of +mask. Today, the most widely used method of a network mask is assigned +to the packet on the basis of the interface that received the packet. +Version 2 of RIP supports a variable length subnet mask (VLSM). By +extending the subnet mask, the mask can be divided and reused. Each +subnet can be used for different purposes such as large to middle size +LANs and WAN links. Quagga @command{ripd} does not support the non-sequential +netmasks that are included in RIP Version 2. + +In a case of similar information with the same prefix and metric, the +old information will be suppressed. Ripd does not currently support +equal cost multipath routing. + + +@node RIP Configuration +@section RIP Configuration + +@deffn Command {router rip} {} +The @code{router rip} command is necessary to enable RIP. To disable +RIP, use the @code{no router rip} command. RIP must be enabled before +carrying out any of the RIP commands. +@end deffn + +@deffn Command {no router rip} {} +Disable RIP. +@end deffn + +@deffn {RIP Command} {network @var{network}} {} +@deffnx {RIP Command} {no network @var{network}} {} +Set the RIP enable interface by @var{network}. The interfaces which +have addresses matching with @var{network} are enabled. + +This group of commands either enables or disables RIP interfaces between +certain numbers of a specified network address. For example, if the +network for 10.0.0.0/24 is RIP enabled, this would result in all the +addresses from 10.0.0.0 to 10.0.0.255 being enabled for RIP. The @code{no +network} command will disable RIP for the specified network. +@end deffn + +@deffn {RIP Command} {network @var{ifname}} {} +@deffnx {RIP Command} {no network @var{ifname}} {} +Set a RIP enabled interface by @var{ifname}. Both the sending and +receiving of RIP packets will be enabled on the port specified in the +@code{network ifname} command. The @code{no network ifname} command will disable +RIP on the specified interface. +@end deffn + +@deffn {RIP Command} {neighbor @var{a.b.c.d}} {} +@deffnx {RIP Command} {no neighbor @var{a.b.c.d}} {} +Specify RIP neighbor. When a neighbor doesn't understand multicast, +this command is used to specify neighbors. In some cases, not all +routers will be able to understand multicasting, where packets are sent +to a network or a group of addresses. In a situation where a neighbor +cannot process multicast packets, it is necessary to establish a direct +link between routers. The neighbor command allows the network +administrator to specify a router as a RIP neighbor. The @code{no +neighbor a.b.c.d} command will disable the RIP neighbor. +@end deffn + +Below is very simple RIP configuration. Interface @code{eth0} and +interface which address match to @code{10.0.0.0/8} are RIP enabled. + +@example +@group +! +router rip + network 10.0.0.0/8 + network eth0 +! +@end group +@end example + +Passive interface + +@deffn {RIP command} {passive-interface (@var{IFNAME}|default)} {} +@deffnx {RIP command} {no passive-interface @var{IFNAME}} {} +This command sets the specified interface to passive mode. On passive mode +interface, all receiving packets are processed as normal and ripd does +not send either multicast or unicast RIP packets except to RIP neighbors +specified with @code{neighbor} command. The interface may be specified +as @var{default} to make ripd default to passive on all interfaces. + +The default is to be passive on all interfaces. +@end deffn + +RIP split-horizon + +@deffn {Interface command} {ip split-horizon} {} +@deffnx {Interface command} {no ip split-horizon} {} +Control split-horizon on the interface. Default is @code{ip +split-horizon}. If you don't perform split-horizon on the interface, +please specify @code{no ip split-horizon}. +@end deffn + +@node RIP Version Control +@section RIP Version Control + +RIP can be configured to send either Version 1 or Version 2 packets. +The default is to send RIPv2 while accepting both RIPv1 and RIPv2 (and +replying with packets of the appropriate version for REQUESTS / +triggered updates). The version to receive and send can be specified +globally, and further overriden on a per-interface basis if needs be +for send and receive seperately (see below). + +It is important to note that RIPv1 can not be authenticated. Further, +if RIPv1 is enabled then RIP will reply to REQUEST packets, sending the +state of its RIP routing table to any remote routers that ask on +demand. For a more detailed discussion on the security implications of +RIPv1 see @ref{RIP Authentication}. + +@deffn {RIP Command} {version @var{version}} {} +Set RIP version to accept for reads and send. @var{version} +can be either `1'' or `2''. + +Disabling RIPv1 by specifying version 2 is STRONGLY encouraged, +@xref{RIP Authentication}. This may become the default in a future +release. + +Default: Send Version 2, and accept either version. +@end deffn + +@deffn {RIP Command} {no version} {} +Reset the global version setting back to the default. +@end deffn + +@deffn {Interface command} {ip rip send version @var{version}} {} +@var{version} can be `1', `2' or `1 2'. + +This interface command overrides the global rip version setting, and +selects which version of RIP to send packets with, for this interface +specifically. Choice of RIP Version 1, RIP Version 2, or both versions. +In the latter case, where `1 2' is specified, packets will be both +broadcast and multicast. + +Default: Send packets according to the global version (version 2) +@end deffn + +@deffn {Interface command} {ip rip receive version @var{version}} {} +@var{version} can be `1', `2' or `1 2'. + +This interface command overrides the global rip version setting, and +selects which versions of RIP packets will be accepted on this +interface. Choice of RIP Version 1, RIP Version 2, or both. + +Default: Accept packets according to the global setting (both 1 and 2). +@end deffn + +@node How to Announce RIP route +@section How to Announce RIP route + +@deffn {RIP command} {redistribute kernel} {} +@deffnx {RIP command} {redistribute kernel metric <0-16>} {} +@deffnx {RIP command} {redistribute kernel route-map @var{route-map}} {} +@deffnx {RIP command} {no redistribute kernel} {} +@code{redistribute kernel} redistributes routing information from +kernel route entries into the RIP tables. @code{no redistribute kernel} +disables the routes. +@end deffn + +@deffn {RIP command} {redistribute static} {} +@deffnx {RIP command} {redistribute static metric <0-16>} {} +@deffnx {RIP command} {redistribute static route-map @var{route-map}} {} +@deffnx {RIP command} {no redistribute static} {} +@code{redistribute static} redistributes routing information from +static route entries into the RIP tables. @code{no redistribute static} +disables the routes. +@end deffn + +@deffn {RIP command} {redistribute connected} {} +@deffnx {RIP command} {redistribute connected metric <0-16>} {} +@deffnx {RIP command} {redistribute connected route-map @var{route-map}} {} +@deffnx {RIP command} {no redistribute connected} {} +Redistribute connected routes into the RIP tables. @code{no +redistribute connected} disables the connected routes in the RIP tables. +This command redistribute connected of the interface which RIP disabled. +The connected route on RIP enabled interface is announced by default. +@end deffn + +@deffn {RIP command} {redistribute ospf} {} +@deffnx {RIP command} {redistribute ospf metric <0-16>} {} +@deffnx {RIP command} {redistribute ospf route-map @var{route-map}} {} +@deffnx {RIP command} {no redistribute ospf} {} +@code{redistribute ospf} redistributes routing information from +ospf route entries into the RIP tables. @code{no redistribute ospf} +disables the routes. +@end deffn + +@deffn {RIP command} {redistribute bgp} {} +@deffnx {RIP command} {redistribute bgp metric <0-16>} {} +@deffnx {RIP command} {redistribute bgp route-map @var{route-map}} {} +@deffnx {RIP command} {no redistribute bgp} {} +@code{redistribute bgp} redistributes routing information from +bgp route entries into the RIP tables. @code{no redistribute bgp} +disables the routes. +@end deffn + +If you want to specify RIP only static routes: + +@deffn {RIP command} {default-information originate} {} +@end deffn + +@deffn {RIP command} {route @var{a.b.c.d/m}} {} +@deffnx {RIP command} {no route @var{a.b.c.d/m}} {} +This command is specific to Quagga. The @code{route} command makes a static +route only inside RIP. This command should be used only by advanced +users who are particularly knowledgeable about the RIP protocol. In +most cases, we recommend creating a static route in Quagga and +redistributing it in RIP using @code{redistribute static}. +@end deffn + +@node Filtering RIP Routes +@section Filtering RIP Routes + +RIP routes can be filtered by a distribute-list. + +@deffn Command {distribute-list @var{access_list} @var{direct} @var{ifname}} {} +You can apply access lists to the interface with a @code{distribute-list} +command. @var{access_list} is the access list name. @var{direct} is +@samp{in} or @samp{out}. If @var{direct} is @samp{in} the access list +is applied to input packets. + +The @code{distribute-list} command can be used to filter the RIP path. +@code{distribute-list} can apply access-lists to a chosen interface. +First, one should specify the access-list. Next, the name of the +access-list is used in the distribute-list command. For example, in the +following configuration @samp{eth0} will permit only the paths that +match the route 10.0.0.0/8 + +@example +@group +! +router rip + distribute-list private in eth0 +! +access-list private permit 10 10.0.0.0/8 +access-list private deny any +! +@end group +@end example +@end deffn + +@code{distribute-list} can be applied to both incoming and outgoing data. + +@deffn Command {distribute-list prefix @var{prefix_list} (in|out) @var{ifname}} {} +You can apply prefix lists to the interface with a +@code{distribute-list} command. @var{prefix_list} is the prefix list +name. Next is the direction of @samp{in} or @samp{out}. If +@var{direct} is @samp{in} the access list is applied to input packets. +@end deffn + +@node RIP Metric Manipulation +@section RIP Metric Manipulation + +RIP metric is a value for distance for the network. Usually +@command{ripd} increment the metric when the network information is +received. Redistributed routes' metric is set to 1. + +@deffn {RIP command} {default-metric <1-16>} {} +@deffnx {RIP command} {no default-metric <1-16>} {} +This command modifies the default metric value for redistributed routes. The +default value is 1. This command does not affect connected route +even if it is redistributed by @command{redistribute connected}. To modify +connected route's metric value, please use @command{redistribute +connected metric} or @command{route-map}. @command{offset-list} also +affects connected routes. +@end deffn + +@deffn {RIP command} {offset-list @var{access-list} (in|out)} {} +@deffnx {RIP command} {offset-list @var{access-list} (in|out) @var{ifname}} {} +@end deffn + +@node RIP distance +@section RIP distance + +Distance value is used in zebra daemon. Default RIP distance is 120. + +@deffn {RIP command} {distance <1-255>} {} +@deffnx {RIP command} {no distance <1-255>} {} +Set default RIP distance to specified value. +@end deffn + +@deffn {RIP command} {distance <1-255> @var{A.B.C.D/M}} {} +@deffnx {RIP command} {no distance <1-255> @var{A.B.C.D/M}} {} +Set default RIP distance to specified value when the route's source IP +address matches the specified prefix. +@end deffn + +@deffn {RIP command} {distance <1-255> @var{A.B.C.D/M} @var{access-list}} {} +@deffnx {RIP command} {no distance <1-255> @var{A.B.C.D/M} @var{access-list}} {} +Set default RIP distance to specified value when the route's source IP +address matches the specified prefix and the specified access-list. +@end deffn + +@node RIP route-map +@section RIP route-map + +Usage of @command{ripd}'s route-map support. + +Optional argument route-map MAP_NAME can be added to each @code{redistribute} +statement. + +@example +redistribute static [route-map MAP_NAME] +redistribute connected [route-map MAP_NAME] +..... +@end example + +Cisco applies route-map _before_ routes will exported to rip route table. +In current Quagga's test implementation, @command{ripd} applies route-map +after routes are listed in the route table and before routes will be +announced to an interface (something like output filter). I think it is not +so clear, but it is draft and it may be changed at future. + +Route-map statement (@pxref{Route Map}) is needed to use route-map +functionality. + +@deffn {Route Map} {match interface @var{word}} {} +This command match to incoming interface. Notation of this match is +different from Cisco. Cisco uses a list of interfaces - NAME1 NAME2 +... NAMEN. Ripd allows only one name (maybe will change in the +future). Next - Cisco means interface which includes next-hop of +routes (it is somewhat similar to "ip next-hop" statement). Ripd +means interface where this route will be sent. This difference is +because "next-hop" of same routes which sends to different interfaces +must be different. Maybe it'd be better to made new matches - say +"match interface-out NAME" or something like that. +@end deffn + +@deffn {Route Map} {match ip address @var{word}} {} +@deffnx {Route Map} {match ip address prefix-list @var{word}} {} +Match if route destination is permitted by access-list. +@end deffn + +@deffn {Route Map} {match ip next-hop @var{word}} {} +@deffnx {Route Map} {match ip next-hop prefix-list @var{word}} {} +Match if route next-hop (meaning next-hop listed in the rip route-table +as displayed by "show ip rip") is permitted by access-list. +@end deffn + +@deffn {Route Map} {match metric <0-4294967295>} {} +This command match to the metric value of RIP updates. For other +protocol compatibility metric range is shown as <0-4294967295>. But +for RIP protocol only the value range <0-16> make sense. +@end deffn + +@deffn {Route Map} {set ip next-hop A.B.C.D} {} +This command set next hop value in RIPv2 protocol. This command does +not affect RIPv1 because there is no next hop field in the packet. +@end deffn + +@deffn {Route Map} {set metric <0-4294967295>} {} +Set a metric for matched route when sending announcement. The metric +value range is very large for compatibility with other protocols. For +RIP, valid metric values are from 1 to 16. +@end deffn + +@node RIP Authentication +@section RIP Authentication + +RIPv2 allows packets to be authenticated via either an insecure plain +text password, included with the packet, or via a more secure MD5 based +@acronym{HMAC, keyed-Hashing for Message AuthentiCation}, +RIPv1 can not be authenticated at all, thus when authentication is +configured @code{ripd} will discard routing updates received via RIPv1 +packets. + +However, unless RIPv1 reception is disabled entirely, +@xref{RIP Version Control}, RIPv1 REQUEST packets which are received, +which query the router for routing information, will still be honoured +by @code{ripd}, and @code{ripd} WILL reply to such packets. This allows +@code{ripd} to honour such REQUESTs (which sometimes is used by old +equipment and very simple devices to bootstrap their default route), +while still providing security for route updates which are received. + +In short: Enabling authentication prevents routes being updated by +unauthenticated remote routers, but still can allow routes (I.e. the +entire RIP routing table) to be queried remotely, potentially by anyone +on the internet, via RIPv1. + +To prevent such unauthenticated querying of routes disable RIPv1, +@xref{RIP Version Control}. + +@deffn {Interface command} {ip rip authentication mode md5} {} +@deffnx {Interface command} {no ip rip authentication mode md5} {} +Set the interface with RIPv2 MD5 authentication. +@end deffn + +@deffn {Interface command} {ip rip authentication mode text} {} +@deffnx {Interface command} {no ip rip authentication mode text} {} +Set the interface with RIPv2 simple password authentication. +@end deffn + +@deffn {Interface command} {ip rip authentication string @var{string}} {} +@deffnx {Interface command} {no ip rip authentication string @var{string}} {} +RIP version 2 has simple text authentication. This command sets +authentication string. The string must be shorter than 16 characters. +@end deffn + +@deffn {Interface command} {ip rip authentication key-chain @var{key-chain}} {} +@deffnx {Interface command} {no ip rip authentication key-chain @var{key-chain}} {} +Specifiy Keyed MD5 chain. +@end deffn + +@example +! +key chain test + key 1 + key-string test +! +interface eth1 + ip rip authentication mode md5 + ip rip authentication key-chain test +! +@end example + +@node RIP Timers +@section RIP Timers + +@deffn {RIP command} {timers basic @var{update} @var{timeout} @var{garbage}} {} + +RIP protocol has several timers. User can configure those timers' values +by @code{timers basic} command. + +The default settings for the timers are as follows: + +@itemize @bullet +@item +The update timer is 30 seconds. Every update timer seconds, the RIP +process is awakened to send an unsolicited Response message containing +the complete routing table to all neighboring RIP routers. + +@item +The timeout timer is 180 seconds. Upon expiration of the timeout, the +route is no longer valid; however, it is retained in the routing table +for a short time so that neighbors can be notified that the route has +been dropped. + +@item +The garbage collect timer is 120 seconds. Upon expiration of the +garbage-collection timer, the route is finally removed from the routing +table. + +@end itemize + +The @code{timers basic} command allows the the default values of the timers +listed above to be changed. +@end deffn + +@deffn {RIP command} {no timers basic} {} +The @code{no timers basic} command will reset the timers to the default +settings listed above. +@end deffn + +@node Show RIP Information +@section Show RIP Information + +To display RIP routes. + +@deffn Command {show ip rip} {} +Show RIP routes. +@end deffn + +The command displays all RIP routes. For routes that are received +through RIP, this command will display the time the packet was sent and +the tag information. This command will also display this information +for routes redistributed into RIP. + +@c Exmaple here. + +@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 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 + Outgoing update filter list for all interface is not set + Incoming update filter list for all interface is not set + Default redistribution metric is 1 + Redistributing: kernel connected + Default version control: send version 2, receive version 2 + Interface Send Recv + Routing for Networks: + eth0 + eth1 + 1.1.1.1 + 203.181.89.241 + Routing Information Sources: + Gateway BadPackets BadRoutes Distance Last Update +@end group +@end example + +@node RIP Debug Commands +@section RIP Debug Commands + +Debug for RIP protocol. + +@deffn Command {debug rip events} {} +Debug rip events. +@end deffn + +@code{debug rip} will show RIP events. Sending and receiving +packets, timers, and changes in interfaces are events shown with @command{ripd}. + +@deffn Command {debug rip packet} {} +Debug rip packet. +@end deffn + +@code{debug rip packet} will display detailed information about the RIP +packets. The origin and port number of the packet as well as a packet +dump is shown. + +@deffn Command {debug rip zebra} {} +Debug rip between zebra communication. +@end deffn + +This command will show the communication between @command{ripd} and +@command{zebra}. The main information will include addition and deletion of +paths to the kernel and the sending and receiving of interface information. + +@deffn Command {show debugging rip} {} +Display @command{ripd}'s debugging option. +@end deffn + +@code{show debugging rip} will show all information currently set for ripd +debug. diff --git a/doc/ripngd.8 b/doc/ripngd.8 new file mode 100644 index 0000000..6e63dc2 --- /dev/null +++ b/doc/ripngd.8 @@ -0,0 +1,114 @@ +.TH RIPNGD 8 "25 November 2004" "Quagga RIPNG daemon" "Version 0.97.3" +.SH NAME +ripngd \- a RIPNG routing engine for use with Quagga routing software. +.SH SYNOPSIS +.B ripngd +[ +.B \-dhlrv +] [ +.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 ripngd +is a routing component that works with the +.B Quagga +routing engine. +.SH OPTIONS +Options available for the +.B ripngd +command: +.SH OPTIONS +.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/ripngd.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 ripngd starts its process identifier is written to +\fB\fIpid-file\fR. The init system uses the recorded PID to stop or +restart ripngd. The likely default is \fB\fI/var/run/ripngd.pid\fR. +.TP +\fB\-P\fR, \fB\-\-vty_port \fR\fIport-number\fR +Specify the port that the ripngd VTY will listen on. This defaults to +2603, as specified in \fB\fI/etc/services\fR. +.TP +\fB\-A\fR, \fB\-\-vty_addr \fR\fIvty-address\fR +Specify the address that the ripngd 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\-r\fR, \fB\-\-retain\fR +When the program terminates, retain routes added by \fBripd\fR. +.TP +\fB\-v\fR, \fB\-\-version\fR +Print the version and exit. +.SH FILES +.TP +.BI /usr/local/sbin/ripngd +The default location of the +.B ripngd +binary. +.TP +.BI /usr/local/etc/ripngd.conf +The default location of the +.B ripngd +config file. +.TP +.BI $(PWD)/ripngd.log +If the +.B ripngd +process is config'd to output logs to a file, then you will find this +file in the directory where you started \fBripngd\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 ripngd process may log to standard output, to a VTY, to a log +file, or through syslog to the system logs. \fBripngd\fR supports many +debugging options, see the Info file, or the source for details. +.SH "SEE ALSO" +.BR bgpd (8), +.BR ripd (8), +.BR ospfd (8), +.BR ospf6d (8), +.BR isisd (8), +.BR zebra (8), +.BR vtysh (1) +.SH BUGS +.B ripngd +eats bugs for breakfast. If you have food for the maintainers try +.BI http://bugzilla.quagga.net +.SH AUTHORS +See +.BI http://www.zebra.org +and +.BI http://www.quagga.net +or the Info file for an accurate list of authors. + diff --git a/doc/ripngd.texi b/doc/ripngd.texi new file mode 100644 index 0000000..0e58de6 --- /dev/null +++ b/doc/ripngd.texi @@ -0,0 +1,84 @@ +@c -*-texinfo-*- +@c This is part of the Quagga Manual. +@c @value{COPYRIGHT_STR} +@c See file quagga.texi for copying conditions. +@node RIPng +@chapter RIPng + +@command{ripngd} supports the RIPng protocol as described in RFC2080. It's an +IPv6 reincarnation of the RIP protocol. + +@menu +* Invoking ripngd:: +* ripngd Configuration:: +* ripngd Terminal Mode Commands:: +* ripngd Filtering Commands:: +@end menu + +@node Invoking ripngd +@section Invoking ripngd + +There are no @code{ripngd} specific invocation options. Common options +can be specified (@pxref{Common Invocation Options}). + +@node ripngd Configuration +@section ripngd Configuration + +Currently ripngd supports the following commands: + +@deffn Command {router ripng} {} +Enable RIPng. +@end deffn + +@deffn {RIPng Command} {flush_timer @var{time}} {} +Set flush timer. +@end deffn + +@deffn {RIPng Command} {network @var{network}} {} +Set RIPng enabled interface by @var{network} +@end deffn + +@deffn {RIPng Command} {network @var{ifname}} {} +Set RIPng enabled interface by @var{ifname} +@end deffn + +@deffn {RIPng Command} {route @var{network}} {} +Set RIPng static routing announcement of @var{network}. +@end deffn + +@deffn Command {router zebra} {} +This command is the default and does not appear in the configuration. +With this statement, RIPng routes go to the @command{zebra} daemon. +@end deffn + +@node ripngd Terminal Mode Commands +@section ripngd Terminal Mode Commands + +@deffn Command {show ip ripng} {} +@end deffn + +@deffn Command {show debugging ripng} {} +@end deffn + +@deffn Command {debug ripng events} {} +@end deffn + +@deffn Command {debug ripng packet} {} +@end deffn + +@deffn Command {debug ripng zebra} {} +@end deffn + +@node ripngd Filtering Commands +@section ripngd Filtering Commands + +@deffn Command {distribute-list @var{access_list} (in|out) @var{ifname}} {} +You can apply an access-list to the interface using the +@code{distribute-list} command. @var{access_list} is an access-list +name. @var{direct} is @samp{in} or @samp{out}. If @var{direct} is +@samp{in}, the access-list is applied only to incoming packets. + +@example +distribute-list local-only out sit1 +@end example +@end deffn diff --git a/doc/routemap.texi b/doc/routemap.texi new file mode 100644 index 0000000..b3ef7ca --- /dev/null +++ b/doc/routemap.texi @@ -0,0 +1,236 @@ +@node Route Map +@chapter Route Map + +Route maps provide a means to both filter and/or apply actions to +route, hence allowing policy to be applied to routes. + +@menu +* Route Map Command:: +* Route Map Match Command:: +* Route Map Set Command:: +* Route Map Call Command:: +* Route Map Exit Action Command:: +* Route Map Examples:: +@end menu + +Route-maps are an ordered list of route-map entries. Each entry may +specify up to four distincts sets of clauses: + +@table @samp +@item Matching Policy + +This specifies the policy implied if the @samp{Matching Conditions} are +met or not met, and which actions of the route-map are to be taken, if +any. The two possibilities are: + +@itemize @minus +@item +@samp{permit}: If the entry matches, then carry out the @samp{Set +Actions}. Then finish processing the route-map, permitting the route, +unless an @samp{Exit Action} indicates otherwise. + +@item +@samp{deny}: If the entry matches, then finish processing the route-map and +deny the route (return @samp{deny}). +@end itemize + +The @samp{Matching Policy} is specified as part of the command which +defines the ordered entry in the route-map. See below. + +@item Matching Conditions + +A route-map entry may, optionally, specify one or more conditions which +must be matched if the entry is to be considered further, as governed +by the Match Policy. If a route-map entry does not explicitely specify +any matching conditions, then it always matches. + +@item Set Actions + +A route-map entry may, optionally, specify one or more @samp{Set +Actions} to set or modify attributes of the route. + +@item Call Action + +Call to another route-map, after any @samp{Set Actions} have been +carried out. If the route-map called returns @samp{deny} then +processing of the route-map finishes and the route is denied, +regardless of the @samp{Matching Policy} or the @samp{Exit Policy}. If +the called route-map returns @samp{permit}, then @samp{Matching Policy} +and @samp{Exit Policy} govern further behaviour, as normal. + +@item Exit Policy + +An entry may, optionally, specify an alternative @samp{Exit Policy} to +take if the entry matched, rather than the normal policy of exiting the +route-map and permitting the route. The two possibilities are: + +@itemize @minus +@item +@samp{next}: Continue on with processing of the route-map entries. + +@item +@samp{goto N}: Jump ahead to the first route-map entry whose order in +the route-map is >= N. Jumping to a previous entry is not permitted. +@end itemize +@end table + +The default action of a route-map, if no entries match, is to deny. +I.e. a route-map essentially has as its last entry an empty @samp{deny} +entry, which matches all routes. To change this behaviour, one must +specify an empty @samp{permit} entry as the last entry in the route-map. + +To summarise the above: + +@multitable {permit} {action} {No Match} +@headitem @tab Match @tab No Match +@item @emph{Permit} @tab action @tab cont +@item @emph{Deny} @tab deny @tab cont +@end multitable + +@table @samp + +@item action +@itemize @minus +@item +Apply @emph{set} statements + +@item +If @emph{call} is present, call given route-map. If that returns a @samp{deny}, finish +processing and return @samp{deny}. + +@item +If @samp{Exit Policy} is @emph{next}, goto next route-map entry + +@item +If @samp{Exit Policy} is @emph{goto}, goto first entry whose order in the list +is >= the given order. + +@item +Finish processing the route-map and permit the route. +@end itemize + +@item deny +@itemize @minus +@item +The route is denied by the route-map (return @samp{deny}). +@end itemize + +@item cont +@itemize @minus +@item +goto next route-map entry +@end itemize +@end table + +@node Route Map Command +@section Route Map Command + +@deffn {Command} {route-map @var{route-map-name} (permit|deny) @var{order}} {} + +Configure the @var{order}'th entry in @var{route-map-name} with +@samp{Match Policy} of either @emph{permit} or @emph{deny}. + +@end deffn + +@node Route Map Match Command +@section Route Map Match Command + +@deffn {Route-map Command} {match ip address @var{access_list}} {} +Matches the specified @var{access_list} +@end deffn + +@deffn {Route-map Command} {match ip next-hop @var{ipv4_addr}} {} +Matches the specified @var{ipv4_addr}. +@end deffn + +@deffn {Route-map Command} {match aspath @var{as_path}} {} +Matches the specified @var{as_path}. +@end deffn + +@deffn {Route-map Command} {match metric @var{metric}} {} +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 + +@node Route Map Set Command +@section Route Map Set Command + +@deffn {Route-map Command} {set ip next-hop @var{ipv4_address}} {} +Set the BGP nexthop address. +@end deffn + +@deffn {Route-map Command} {set local-preference @var{local_pref}} {} +Set the BGP local preference. +@end deffn + +@deffn {Route-map Command} {set weight @var{weight}} {} +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 + +@deffn {Route-map Command} {set as-path prepend @var{as_path}} {} +Set the BGP AS path to prepend. +@end deffn + +@deffn {Route-map Command} {set community @var{community}} {} +Set the BGP community attribute. +@end deffn + +@deffn {Route-map Command} {set ipv6 next-hop global @var{ipv6_address}} {} +Set the BGP-4+ global IPv6 nexthop address. +@end deffn + +@deffn {Route-map Command} {set ipv6 next-hop local @var{ipv6_address}} {} +Set the BGP-4+ link local IPv6 nexthop address. +@end deffn + +@node Route Map Call Command +@section Route Map Call Command + +@deffn {Route-map Command} {call @var{name}} {} +Call route-map @var{name}. If it returns deny, deny the route and +finish processing the route-map. +@end deffn + +@node Route Map Exit Action Command +@section Route Map Exit Action Command + +@deffn {Route-map Command} {on-match next} {} +@deffnx {Route-map Command} {continue} {} +Proceed on to the next entry in the route-map. +@end deffn + +@deffn {Route-map Command} {on-match goto @var{N}} {} +@deffnx {Route-map Command} {continue @var{N}} {} +Proceed processing the route-map at the first entry whose order is >= N +@end deffn + +@node Route Map Examples +@section Route Map Examples + +A simple example of a route-map: + +@example +@group +route-map test permit 10 + match ip address 10 + set local-preference 200 +@end group +@end example + +This means that if a route matches ip access-list number 10 it's +local-preference value is set to 200. + +See @ref{BGP Configuration Examples} for examples of more sophisticated +useage of route-maps, including of the @samp{call} action. diff --git a/doc/routeserver.texi b/doc/routeserver.texi new file mode 100644 index 0000000..cfe9041 --- /dev/null +++ b/doc/routeserver.texi @@ -0,0 +1,550 @@ +@c -*-texinfo-*- +@c @value{COPYRIGHT_STR} +@c See file quagga.texi for copying conditions. +@c +@c This file is a modified version of Jose Luis Rubio's TeX sources +@c of his RS-Manual document + +@node Configuring Quagga as a Route Server +@chapter Configuring Quagga as a Route Server + +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 +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 +model. And finally we will show a full example of Quagga configured as Route +Server. + +@menu +* Description of the Route Server model:: +* Commands for configuring a Route Server:: +* Example of Route Server Configuration:: +@end menu + +@node Description of the Route Server model +@section Description of the Route Server model + +First we are going to describe the normal processing that BGP announcements +suffer inside a standard BGP speaker, as shown in @ref{fig:normal-processing}, +it consists of three steps: + +@itemize @bullet +@item +When an announcement is received from some peer, the `In' filters +configured for that peer are applied to the announcement. These filters can +reject the announcement, accept it unmodified, or accept it with some of its +attributes modified. + +@item +The announcements that pass the `In' filters go into the +Best Path Selection process, where they are compared to other +announcements referred to the same destination that have been +received from different peers (in case such other +announcements exist). For each different destination, the announcement +which is selected as the best is inserted into the BGP speaker's Loc-RIB. + +@item +The routes which are inserted in the Loc-RIB are +considered for announcement to all the peers (except the one +from which the route came). This is done by passing the routes +in the Loc-RIB through the `Out' filters corresponding to each +peer. These filters can reject the route, +accept it unmodified, or accept it with some of its attributes +modified. Those routes which are accepted by the `Out' filters +of a peer are announced to that peer. +@end itemize + +@float Figure,fig:normal-processing +@center @image{fig-normal-processing,400pt,,Normal announcement processing} +@caption{Announcement processing inside a ``normal'' BGP speaker} +@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 +can no longer distinguish from/to which peer each announce comes/goes. +@anchor{filter-delegation}This means that the routers connected to the route +server are not able to apply by themselves the same input/output filters +as in the full mesh scenario, so they have to delegate those functions to +the route server. + +Even more, the ``best path'' selection must be also performed inside +the route server on behalf of its clients. The reason is that if, after +applying the filters of the announcer and the (potential) receiver, the +route server decides to send to some client two or more different +announcements referred to the same destination, the client will only +retain the last one, considering it as an implicit withdrawal of the +previous announcements for the same destination. This is the expected +behavior of a BGP speaker as defined in @cite{RFC1771}, and even though +there are some proposals of mechanisms that permit multiple paths for +the same destination to be sent through a single BGP peering, none are +currently supported by most existing BGP implementations. + +As a consequence a route server must maintain additional information and +perform additional tasks for a RS-client that those necessary for common BGP +peerings. Essentially a route server must: + +@anchor{Route Server tasks} +@itemize @bullet +@item +Maintain a separated Routing Information Base (Loc-RIB) +for each peer configured as RS-client, containing the routes +selected as a result of the ``Best Path Selection'' process +that is performed on behalf of that RS-client. + +@item +Whenever it receives an announcement from a RS-client, +it must consider it for the Loc-RIBs of the other RS-clients. + +@anchor{Route-server path filter process} +@itemize @bullet +@item +This means that for each of them the route server must pass the +announcement through the appropriate `Out' filter of the +announcer. + +@item +Then through the appropriate `In' filter of +the potential receiver. + +@item +Only if the announcement is accepted by both filters it will be passed +to the ``Best Path Selection'' process. + +@item +Finally, it might go into the Loc-RIB of the receiver. +@end itemize +@end itemize + +When we talk about the ``appropriate'' filter, both the announcer and the +receiver of the route must be taken into account. Suppose that the route +server receives an announcement from client A, and the route server is +considering it for the Loc-RIB of client B. The filters that should be +applied are the same that would be used in the full mesh scenario, i.e., +first the `Out' filter of router A for announcements going to router B, and +then the `In' filter of router B for announcements coming from router A. + +We call ``Export Policy'' of a RS-client to the set of `Out' filters that +the client would use if there was no route server. The same applies for the +``Import Policy'' of a RS-client and the set of `In' filters of the client +if there was no route server. + +It is also common to demand from a route server that it does not +modify some BGP attributes (next-hop, as-path and MED) that are usually +modified by standard BGP speakers before announcing a route. + +The announcement processing model implemented by Quagga is shown in +@ref{fig:rs-processing}. The figure shows a mixture of RS-clients (B, C and D) +with normal BGP peers (A). There are some details that worth additional +comments: + +@itemize @bullet +@item +Announcements coming from a normal BGP peer are also +considered for the Loc-RIBs of all the RS-clients. But +logically they do not pass through any export policy. + +@item +Those peers that are configured as RS-clients do not +receive any announce from the `Main' Loc-RIB. + +@item +Apart from import and export policies, +`In' and `Out' filters can also be set for RS-clients. `In' +filters might be useful when the route server has also normal +BGP peers. On the other hand, `Out' filters for RS-clients are +probably unnecessary, but we decided not to remove them as +they do not hurt anybody (they can always be left empty). +@end itemize + +@float Figure,fig:rs-processing +@center @image{fig-rs-processing,430pt,,Route Server Processing Model} +@caption{Announcement processing model implemented by the Route Server} +@end float + +@node Commands for configuring a Route Server +@section Commands for configuring a Route Server + +Now we will describe the commands that have been added to quagga +in order to support the route server features. + +@deffn {Route-Server} {neighbor @var{peer-group} route-server-client} {} +@deffnx {Route-Server} {neighbor @var{A.B.C.D} route-server-client} {} +@deffnx {Route-Server} {neighbor @var{X:X::X:X} route-server-client} {} +This command configures the peer given by @var{peer}, @var{A.B.C.D} or +@var{X:X::X:X} as an RS-client. + +Actually this command is not new, it already existed in standard Quagga. It +enables the transparent mode for the specified peer. This means that some +BGP attributes (as-path, next-hop and MED) of the routes announced to that +peer are not modified. + +With the route server patch, this command, apart from setting the +transparent mode, creates a new Loc-RIB dedicated to the specified peer +(those named `Loc-RIB for X' in @ref{fig:rs-processing}.). Starting from +that moment, every announcement received by the route server will be also +considered for the new Loc-RIB. +@end deffn + +@deffn {Route-Server} {neigbor @{A.B.C.D|X.X::X.X|peer-group@} route-map WORD @{import|export@}} {} +This set of commands can be used to specify the route-map that +represents the Import or Export policy of a peer which is +configured as a RS-client (with the previous command). +@end deffn + +@deffn {Route-Server} {match peer @{A.B.C.D|X:X::X:X@}} {} +This is a new @emph{match} statement for use in route-maps, enabling them to +describe import/export policies. As we said before, an import/export policy +represents a set of input/output filters of the RS-client. This statement +makes possible that a single route-map represents the full set of filters +that a BGP speaker would use for its different peers in a non-RS scenario. + +The @emph{match peer} statement has different semantics whether it is used +inside an import or an export route-map. In the first case the statement +matches if the address of the peer who sends the announce is the same that +the address specified by @{A.B.C.D|X:X::X:X@}. For export route-maps it +matches when @{A.B.C.D|X:X::X:X@} is the address of the RS-Client into whose +Loc-RIB the announce is going to be inserted (how the same export policy is +applied before different Loc-RIBs is shown in @ref{fig:rs-processing}.). +@end deffn + +@deffn {Route-map Command} {call @var{WORD}} {} +This command (also used inside a route-map) jumps into a different +route-map, whose name is specified by @var{WORD}. When the called +route-map finishes, depending on its result the original route-map +continues or not. Apart from being useful for making import/export +route-maps easier to write, this command can also be used inside +any normal (in or out) route-map. +@end deffn + +@node Example of Route Server Configuration +@section Example of Route Server Configuration + +Finally we are going to show how to configure a Quagga daemon to act as a +Route Server. For this purpose we are going to present a scenario without +route server, and then we will show how to use the configurations of the BGP +routers to generate the configuration of the route server. + +All the configuration files shown in this section have been taken +from scenarios which were tested using the VNUML tool +@uref{http://www.dit.upm.es/vnuml,VNUML}. + +@menu +* Configuration of the BGP routers without Route Server:: +* Configuration of the BGP routers with Route Server:: +* Configuration of the Route Server itself:: +* Further considerations about Import and Export route-maps:: +@end menu + +@node Configuration of the BGP routers without Route Server +@subsection Configuration of the BGP routers without Route Server + +We will suppose that our initial scenario is an exchange point with three +BGP capable routers, named RA, RB and RC. Each of the BGP speakers generates +some routes (with the @var{network} command), and establishes BGP peerings +against the other two routers. These peerings have In and Out route-maps +configured, named like ``PEER-X-IN'' or ``PEER-X-OUT''. For example the +configuration file for router RA could be the following: + +@exampleindent 0 +@example +#Configuration for router 'RA' +! +hostname RA +password **** +! +router bgp 65001 + no bgp default ipv4-unicast + neighbor 2001:0DB8::B remote-as 65002 + neighbor 2001:0DB8::C remote-as 65003 +! + address-family ipv6 + network 2001:0DB8:AAAA:1::/64 + network 2001:0DB8:AAAA:2::/64 + network 2001:0DB8:0000:1::/64 + network 2001:0DB8:0000:2::/64 + + neighbor 2001:0DB8::B activate + neighbor 2001:0DB8::B soft-reconfiguration inbound + neighbor 2001:0DB8::B route-map PEER-B-IN in + neighbor 2001:0DB8::B route-map PEER-B-OUT out + + neighbor 2001:0DB8::C activate + neighbor 2001:0DB8::C soft-reconfiguration inbound + neighbor 2001:0DB8::C route-map PEER-C-IN in + neighbor 2001:0DB8::C route-map PEER-C-OUT out + exit-address-family +! +ipv6 prefix-list COMMON-PREFIXES seq 5 permit 2001:0DB8:0000::/48 ge 64 le 64 +ipv6 prefix-list COMMON-PREFIXES seq 10 deny any +! +ipv6 prefix-list PEER-A-PREFIXES seq 5 permit 2001:0DB8:AAAA::/48 ge 64 le 64 +ipv6 prefix-list PEER-A-PREFIXES seq 10 deny any +! +ipv6 prefix-list PEER-B-PREFIXES seq 5 permit 2001:0DB8:BBBB::/48 ge 64 le 64 +ipv6 prefix-list PEER-B-PREFIXES seq 10 deny any +! +ipv6 prefix-list PEER-C-PREFIXES seq 5 permit 2001:0DB8:CCCC::/48 ge 64 le 64 +ipv6 prefix-list PEER-C-PREFIXES seq 10 deny any +! +route-map PEER-B-IN permit 10 + match ipv6 address prefix-list COMMON-PREFIXES + set metric 100 +route-map PEER-B-IN permit 20 + match ipv6 address prefix-list PEER-B-PREFIXES + set community 65001:11111 +! +route-map PEER-C-IN permit 10 + match ipv6 address prefix-list COMMON-PREFIXES + set metric 200 +route-map PEER-C-IN permit 20 + match ipv6 address prefix-list PEER-C-PREFIXES + set community 65001:22222 +! +route-map PEER-B-OUT permit 10 + match ipv6 address prefix-list PEER-A-PREFIXES +! +route-map PEER-C-OUT permit 10 + match ipv6 address prefix-list PEER-A-PREFIXES +! +line vty +! +@end example + +@node Configuration of the BGP routers with Route Server +@subsection Configuration of the BGP routers with Route Server + +To convert the initial scenario into one with route server, first we must +modify the configuration of routers RA, RB and RC. Now they must not peer +between them, but only with the route server. For example, RA's +configuration would turn into: + +@example +# Configuration for router 'RA' +! +hostname RA +password **** +! +router bgp 65001 + no bgp default ipv4-unicast + neighbor 2001:0DB8::FFFF remote-as 65000 +! + address-family ipv6 + network 2001:0DB8:AAAA:1::/64 + network 2001:0DB8:AAAA:2::/64 + network 2001:0DB8:0000:1::/64 + network 2001:0DB8:0000:2::/64 + + neighbor 2001:0DB8::FFFF activate + neighbor 2001:0DB8::FFFF soft-reconfiguration inbound + exit-address-family +! +line vty +! +@end example + +Which is logically much simpler than its initial configuration, as it now +maintains only one BGP peering and all the filters (route-maps) have +disappeared. + +@node Configuration of the Route Server itself +@subsection Configuration of the Route Server itself + +As we said when we described the functions of a route server +(@pxref{Description of the Route Server model}), it is in charge of all the +route filtering. To achieve that, the In and Out filters from the RA, RB and +RC configurations must be converted into Import and Export policies in the +route server. + +This is a fragment of the route server configuration (we only show +the policies for client RA): + +@example +# Configuration for Route Server ('RS') +! +hostname RS +password ix +! +bgp multiple-instance +! +router bgp 65000 view RS + no bgp default ipv4-unicast + neighbor 2001:0DB8::A remote-as 65001 + neighbor 2001:0DB8::B remote-as 65002 + neighbor 2001:0DB8::C remote-as 65003 +! + address-family ipv6 + neighbor 2001:0DB8::A activate + neighbor 2001:0DB8::A route-server-client + neighbor 2001:0DB8::A route-map RSCLIENT-A-IMPORT import + neighbor 2001:0DB8::A route-map RSCLIENT-A-EXPORT export + neighbor 2001:0DB8::A soft-reconfiguration inbound + + neighbor 2001:0DB8::B activate + neighbor 2001:0DB8::B route-server-client + neighbor 2001:0DB8::B route-map RSCLIENT-B-IMPORT import + neighbor 2001:0DB8::B route-map RSCLIENT-B-EXPORT export + neighbor 2001:0DB8::B soft-reconfiguration inbound + + neighbor 2001:0DB8::C activate + neighbor 2001:0DB8::C route-server-client + neighbor 2001:0DB8::C route-map RSCLIENT-C-IMPORT import + neighbor 2001:0DB8::C route-map RSCLIENT-C-EXPORT export + neighbor 2001:0DB8::C soft-reconfiguration inbound + exit-address-family +! +ipv6 prefix-list COMMON-PREFIXES seq 5 permit 2001:0DB8:0000::/48 ge 64 le 64 +ipv6 prefix-list COMMON-PREFIXES seq 10 deny any +! +ipv6 prefix-list PEER-A-PREFIXES seq 5 permit 2001:0DB8:AAAA::/48 ge 64 le 64 +ipv6 prefix-list PEER-A-PREFIXES seq 10 deny any +! +ipv6 prefix-list PEER-B-PREFIXES seq 5 permit 2001:0DB8:BBBB::/48 ge 64 le 64 +ipv6 prefix-list PEER-B-PREFIXES seq 10 deny any +! +ipv6 prefix-list PEER-C-PREFIXES seq 5 permit 2001:0DB8:CCCC::/48 ge 64 le 64 +ipv6 prefix-list PEER-C-PREFIXES seq 10 deny any +! +route-map RSCLIENT-A-IMPORT permit 10 + match peer 2001:0DB8::B + call A-IMPORT-FROM-B +route-map RSCLIENT-A-IMPORT permit 20 + match peer 2001:0DB8::C + call A-IMPORT-FROM-C +! +route-map A-IMPORT-FROM-B permit 10 + match ipv6 address prefix-list COMMON-PREFIXES + set metric 100 +route-map A-IMPORT-FROM-B permit 20 + match ipv6 address prefix-list PEER-B-PREFIXES + set community 65001:11111 +! +route-map A-IMPORT-FROM-C permit 10 + match ipv6 address prefix-list COMMON-PREFIXES + set metric 200 +route-map A-IMPORT-FROM-C permit 20 + match ipv6 address prefix-list PEER-C-PREFIXES + set community 65001:22222 +! +route-map RSCLIENT-A-EXPORT permit 10 + match peer 2001:0DB8::B + match ipv6 address prefix-list PEER-A-PREFIXES +route-map RSCLIENT-A-EXPORT permit 20 + match peer 2001:0DB8::C + match ipv6 address prefix-list PEER-A-PREFIXES +! +... +... +... +@end example + +If you compare the initial configuration of RA with the route server +configuration above, you can see how easy it is to generate the Import and +Export policies for RA from the In and Out route-maps of RA's original +configuration. + +When there was no route server, RA maintained two peerings, one with RB and +another with RC. Each of this peerings had an In route-map configured. To +build the Import route-map for client RA in the route server, simply add +route-map entries following this scheme: + +@example +route-map permit 10 + match peer + call +route-map permit 20 + match peer + call +@end example + +This is exactly the process that has been followed to generate the route-map +RSCLIENT-A-IMPORT. The route-maps that are called inside it (A-IMPORT-FROM-B +and A-IMPORT-FROM-C) are exactly the same than the In route-maps from the +original configuration of RA (PEER-B-IN and PEER-C-IN), only the name is +different. + +The same could have been done to create the Export policy for RA (route-map +RSCLIENT-A-EXPORT), but in this case the original Out route-maps where so +simple that we decided not to use the @var{call WORD} commands, and we +integrated all in a single route-map (RSCLIENT-A-EXPORT). + +The Import and Export policies for RB and RC are not shown, but +the process would be identical. + +@node Further considerations about Import and Export route-maps +@subsection Further considerations about Import and Export route-maps + +The current version of the route server patch only allows to specify a +route-map for import and export policies, while in a standard BGP speaker +apart from route-maps there are other tools for performing input and output +filtering (access-lists, community-lists, ...). But this does not represent +any limitation, as all kinds of filters can be included in import/export +route-maps. For example suppose that in the non-route-server scenario peer +RA had the following filters configured for input from peer B: + +@example + neighbor 2001:0DB8::B prefix-list LIST-1 in + neighbor 2001:0DB8::B filter-list LIST-2 in + neighbor 2001:0DB8::B route-map PEER-B-IN in + ... + ... +route-map PEER-B-IN permit 10 + match ipv6 address prefix-list COMMON-PREFIXES + set local-preference 100 +route-map PEER-B-IN permit 20 + match ipv6 address prefix-list PEER-B-PREFIXES + set community 65001:11111 +@end example + +It is posible to write a single route-map which is equivalent to +the three filters (the community-list, the prefix-list and the +route-map). That route-map can then be used inside the Import +policy in the route server. Lets see how to do it: + +@example + neighbor 2001:0DB8::A route-map RSCLIENT-A-IMPORT import + ... +! +... +route-map RSCLIENT-A-IMPORT permit 10 + match peer 2001:0DB8::B + call A-IMPORT-FROM-B +... +... +! +route-map A-IMPORT-FROM-B permit 1 + match ipv6 address prefix-list LIST-1 + match as-path LIST-2 + on-match goto 10 +route-map A-IMPORT-FROM-B deny 2 +route-map A-IMPORT-FROM-B permit 10 + match ipv6 address prefix-list COMMON-PREFIXES + set local-preference 100 +route-map A-IMPORT-FROM-B permit 20 + match ipv6 address prefix-list PEER-B-PREFIXES + set community 65001:11111 +! +... +... +@end example + +The route-map A-IMPORT-FROM-B is equivalent to the three filters +(LIST-1, LIST-2 and PEER-B-IN). The first entry of route-map +A-IMPORT-FROM-B (sequence number 1) matches if and only if both +the prefix-list LIST-1 and the filter-list LIST-2 match. If that +happens, due to the ``on-match goto 10'' statement the next +route-map entry to be processed will be number 10, and as of that +point route-map A-IMPORT-FROM-B is identical to PEER-B-IN. If +the first entry does not match, `on-match goto 10'' will be +ignored and the next processed entry will be number 2, which will +deny the route. + +Thus, the result is the same that with the three original filters, +i.e., if either LIST-1 or LIST-2 rejects the route, it does not +reach the route-map PEER-B-IN. In case both LIST-1 and LIST-2 +accept the route, it passes to PEER-B-IN, which can reject, accept +or modify the route. diff --git a/doc/snmp.texi b/doc/snmp.texi new file mode 100644 index 0000000..0918a46 --- /dev/null +++ b/doc/snmp.texi @@ -0,0 +1,185 @@ +@node SNMP Support +@chapter SNMP Support + +@acronym{SNMP,Simple Network Managing Protocol} is a widely implemented +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}) 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:: +@end menu + +@node Getting and installing an SNMP agent +@section Getting and installing an SNMP agent + +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=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=smux} option. + +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 +used for the intercommunication of the daemons. + +In the following example the ospfd daemon will be connected to the +snmpd daemon using the password "quagga_ospfd". For testing it is +recommending to take exactly the below snmpd.conf as wrong access +restrictions can be hard to debug. + +@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 + # + # the following line is relevant for Quagga + # + smuxpeer .1.3.6.1.4.1.3317.1.2.5 quagga_ospfd + +/etc/quagga/ospf: + ! ... the rest of ospfd.conf has been omitted for clarity ... + ! + smux peer .1.3.6.1.4.1.3317.1.2.5 quagga_ospfd + ! +@end example + +After restarting snmpd and quagga, a successful connection can be verified in +the syslog and by querying the SNMP daemon: + +@example +snmpd[12300]: [smux_accept] accepted fd 12 from 127.0.0.1:36255 +snmpd[12300]: accepted smux peer: \ + oid GNOME-PRODUCT-ZEBRA-MIB::ospfd, quagga-0.96.5 + +# 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 + +Be warned that the current version (5.1.1) of the Net-SNMP daemon writes a line +for every SNMP connect to the syslog which can lead to enormous log file sizes. +If that is a problem you should consider to patch snmpd and comment out the +troublesome @code{snmp_log()} line in the function +@code{netsnmp_agent_check_packet()} in @code{agent/snmp_agent.c}. + +@node MIB and command reference +@section MIB and command reference + +The following OID numbers are used for the interprocess communication of snmpd and +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 +bgpd .1.3.6.1.4.1.3317.1.2.2 .gnome.gnomeProducts.zebra.bgpd +ripd .1.3.6.1.4.1.3317.1.2.3 .gnome.gnomeProducts.zebra.ripd +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 + +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 +bgpd .1.3.6.1.2.1.15 .iso.org.dot.internet.mgmt.mib-2.bgp +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 using SMUX: +@deffn {Command} {smux peer @var{oid}} {} +@deffnx {Command} {no smux peer @var{oid}} {} +@end deffn + +@deffn {Command} {smux peer @var{oid} @var{password}} {} +@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 new file mode 100644 index 0000000..6c67288 --- /dev/null +++ b/doc/snmptrap.texi @@ -0,0 +1,211 @@ +@c Documentation on configuring Quagga and snmpd for SNMP traps +@c contributed by Jeroen Simonetti, jsimonetti@denit.net + +@node Handling SNMP Traps +@section Handling SNMP Traps + +To handle snmp traps make sure your snmp setup of quagga works +correctly as described in the quagga documentation in @xref{SNMP Support}. + +The BGP4 mib will send traps on peer up/down events. These should be +visible in your snmp logs with a message similar to: + +@samp{snmpd[13733]: Got trap from peer on fd 14} + +To react on these traps they should be handled by a trapsink. Configure +your trapsink by adding the following lines to @file{/etc/snmpd/snmpd.conf}: + +@example + # send traps to the snmptrapd on localhost + trapsink localhost +@end example + +This will send all traps to an snmptrapd running on localhost. You can +of course also use a dedicated management station to catch traps. +Configure the snmptrapd daemon by adding the following line to +@file{/etc/snmpd/snmptrapd.conf}: + +@c Documentation contributed by Jeroen Simonetti, jsimonetti@denit.net + +@example + traphandle .1.3.6.1.4.1.3317.1.2.2 /etc/snmp/snmptrap_handle.sh +@end example + +This will use the bash script @file{/etc/snmp/snmptrap_handle.sh} to handle +the BGP4 traps. To add traps for other protocol daemons, lookup their +appropriate OID from their mib. (For additional information about which +traps are supported by your mib, lookup the mib on +@uref{http://www.oidview.com/mibs/detail.html}). + +Make sure snmptrapd is started. + +The snmptrap_handle.sh script I personally use for handling BGP4 traps +is below. You can of course do all sorts of things when handling traps, +like sound a siren, have your display flash, etc., be creative ;). + +@verbatim + #!/bin/bash + + # routers name + ROUTER=`hostname -s` + + #email address use to sent out notification + EMAILADDR="john@doe.com" + #email address used (allongside above) where warnings should be sent + EMAILADDR_WARN="sms-john@doe.com" + + # type of notification + TYPE="Notice" + + # local snmp community for getting AS belonging to peer + COMMUNITY="" + + # if a peer address is in $WARN_PEERS a warning should be sent + WARN_PEERS="192.0.2.1" + + + # get stdin + INPUT=`cat -` + + # 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'` + 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` + + # if peer address is in $WARN_PEER, the email should also + # be sent to $EMAILADDR_WARN + for ip in $WARN_PEERS; do + if [ "x$ip" == "x$peer" ]; then + EMAILADDR="$EMAILADDR,$EMAILADDR_WARN" + TYPE="WARNING" + break + fi + done + + + # convert peer state + case "$peerstate" in + 1) peerstate="Idle" ;; + 2) peerstate="Connect" ;; + 3) peerstate="Active" ;; + 4) peerstate="Opensent" ;; + 5) peerstate="Openconfirm" ;; + 6) peerstate="Established" ;; + *) peerstate="Unknown" ;; + esac + + # get textual messages for errors + case "$errorcode" in + 00) + error="No error" + suberror="" + ;; + 01) + error="Message Header Error" + case "$suberrorcode" in + 01) suberror="Connection Not Synchronized" ;; + 02) suberror="Bad Message Length" ;; + 03) suberror="Bad Message Type" ;; + *) suberror="Unknown" ;; + esac + ;; + 02) + error="OPEN Message Error" + case "$suberrorcode" in + 01) suberror="Unsupported Version Number" ;; + 02) suberror="Bad Peer AS" ;; + 03) suberror="Bad BGP Identifier" ;; + 04) suberror="Unsupported Optional Parameter" ;; + 05) suberror="Authentication Failure" ;; + 06) suberror="Unacceptable Hold Time" ;; + *) suberror="Unknown" ;; + esac + ;; + 03) + error="UPDATE Message Error" + case "$suberrorcode" in + 01) suberror="Malformed Attribute List" ;; + 02) suberror="Unrecognized Well-known Attribute" ;; + 03) suberror="Missing Well-known Attribute" ;; + 04) suberror="Attribute Flags Error" ;; + 05) suberror="Attribute Length Error" ;; + 06) suberror="Invalid ORIGIN Attribute" ;; + 07) suberror="AS Routing Loop" ;; + 08) suberror="Invalid NEXT_HOP Attribute" ;; + 09) suberror="Optional Attribute Error" ;; + 10) suberror="Invalid Network Field" ;; + 11) suberror="Malformed AS_PATH" ;; + *) suberror="Unknown" ;; + esac + ;; + 04) + error="Hold Timer Expired" + suberror="" + ;; + 05) + error="Finite State Machine Error" + suberror="" + ;; + 06) + error="Cease" + case "$suberrorcode" in + 01) suberror="Maximum Number of Prefixes Reached" ;; + 02) suberror="Administratively Shutdown" ;; + 03) suberror="Peer Unconfigured" ;; + 04) suberror="Administratively Reset" ;; + 05) suberror="Connection Rejected" ;; + 06) suberror="Other Configuration Change" ;; + 07) suberror="Connection collision resolution" ;; + 08) suberror="Out of Resource" ;; + 09) suberror="MAX" ;; + *) suberror="Unknown" ;; + esac + ;; + *) + error="Unknown" + suberror="" + ;; + esac + + # create textual message from errorcodes + if [ "x$suberror" == "x" ]; then + NOTIFY="$errorcode ($error)" + else + NOTIFY="$errorcode/$suberrorcode ($error/$suberror)" + fi + + + # form a decent subject + SUBJECT="$TYPE: $ROUTER [bgp] $peer is $peerstate: $NOTIFY" + # create the email body + MAIL=`cat << EOF + BGP notification on router $ROUTER. + + Peer: $peer + AS: $remoteas + New state: $peerstate + Notification: $NOTIFY + + Info: + $asname + $asdescr + + Snmpd uptime: $uptime + EOF` + + # mail the notification + echo "$MAIL" | mail -s "$SUBJECT" $EMAILADDR +@end verbatim diff --git a/doc/texinfo.css b/doc/texinfo.css new file mode 100644 index 0000000..f5fa4f4 --- /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 0000000..ddda670 --- /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 new file mode 100644 index 0000000..a2afa9f --- /dev/null +++ b/doc/vtysh.1 @@ -0,0 +1,103 @@ +.TH VTYSH 1 "27 July 2006" "Quagga VTY shell" "Version 0.96.5" +.SH NAME +vtysh \- a integrated shell for Quagga routing software +.SH SYNOPSIS +.B vtysh +[ +.B \-b +] +.br +.B vtysh +[ +.B \-E +] [ +.B \-d +.I daemon +] +] [ +.B \-c +.I command +] +.SH DESCRIPTION +.B vtysh +is a integrated shell for +.B Quagga +routing engine. +.SH OPTIONS +Options available for the +.B vtysh +command: +.IP "\fB\-b, \-\-boot\fP" +Execute boot startup configuration. It makes sense only if integrated config +file is in use (not default in Quagga). See Info file \fBQuagga\fR for more +info. +.IP "\fB\-c, \-\-command \fIcommand\fP" +Specify command to be executed under batch mode. It behaves like -c option in +any other shell - +.I command +is executed and +.B vtysh +exits. + +It's useful for gathering info from Quagga routing software or reconfiguring +daemons from inside shell scripts, etc. +Note that multiple commands may be executed by using more than one +-c option and/or embedding linefeed characters inside the +.I command +string. +.IP "\fB\-d, \-\-daemon \fIdaemon_name\fP" +Specify which daemon to connect to. By default, +.B vtysh +attempts to connect to all Quagga daemons running on the system. With this +flag, one can specify a single daemon to connect to instead. For example, +specifying '-d ospfd' will connect only to ospfd. This can be particularly +useful inside scripts with -c where the command is targeted for a single daemon. +.IP "\fB\-e, \-\-execute \fIcommand\fP" +Alias for -c. It's here only for compatibility with Zebra routing software and +older Quagga versions. This will be removed in future. +.IP "\fB\-E, \-\-echo\fP" +When the -c option is being used, this flag will cause the standard +.B vtysh +prompt and command to be echoed prior to displaying the results. +This is particularly useful to separate the results +when executing multiple commands. +.IP "\fB\-h, \-\-help\fP" +Display a usage message on standard output and exit. +.SH ENVIRONMENT VARIABLES +.IP "\fBVTYSH_PAGER\fR" +This should be the name of the pager to use. Default is \fBmore\fR. +.SH FILES +.TP +.BI /usr/local/etc/vtysh.conf +The default location of the +.B vtysh +config file. +.TP +.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. +.SH "SEE ALSO" +.BR bgpd (8), +.BR ripd (8), +.BR ripngd (8), +.BR ospfd (8), +.BR ospf6d (8), +.BR isisd (8), +.BR zebra (8) +.SH BUGS +.B vtysh +eats bugs for breakfast. If you have food for the maintainers try +.BI http://bugzilla.quagga.net +.SH AUTHORS +See +.BI http://www.zebra.org +and +.BI http://www.quagga.net +or the Info file for an accurate list of authors. + diff --git a/doc/vtysh.texi b/doc/vtysh.texi new file mode 100644 index 0000000..66562a9 --- /dev/null +++ b/doc/vtysh.texi @@ -0,0 +1,61 @@ +@node VTY shell +@chapter VTY shell + +@command{vtysh} is integrated shell of Quagga software. + +To use vtysh please specify ---enable-vtysh to configure script. To use +PAM for authentication use ---with-libpam option to configure script. + +vtysh only searches @value{INSTALL_PREFIX_ETC} path for vtysh.conf which +is the vtysh configuration file. Vtysh does not search current +directory for configuration file because the file includes user +authentication settings. + +Currently, vtysh.conf has only two commands. + +@menu +* VTY shell username:: +* VTY shell integrated configuration:: +@end menu + +@node VTY shell username +@section VTY shell username + +@deffn {Command} {username @var{username} nopassword} {} + +With this set, user foo does not need password authentication for user vtysh. +With PAM vtysh uses PAM authentication mechanism. + +If vtysh is compiled without PAM authentication, every user can use vtysh +without authentication. vtysh requires read/write permission +to the various daemons vty sockets, this can be accomplished through use +of unix groups and the --enable-vty-group configure option. + +@end deffn + +@node VTY shell integrated configuration +@section VTY shell integrated configuration + +@deffn {Command} {service integrated-vtysh-config} {} +Write out integrated Quagga.conf file when 'write file' is issued. + +This command controls the behaviour of vtysh when it is told to write out +the configuration. Per default, vtysh will instruct each daemon to write +out their own config files when @command{write file} is issued. However, if +@command{service integrated-vtysh-config} is set, when @command{write file} +is issued, vtysh will instruct the daemons will write out a Quagga.conf with +all daemons' commands integrated into it. + +Vtysh per default behaves as if @command{write-conf daemon} is set. Note +that both may be set at same time if one wishes to have both Quagga.conf and +daemon specific files written out. Further, note that the daemons are +hard-coded to first look for the integrated Quagga.conf file before looking +for their own file. + +We recommend you do not mix the use of the two types of files. Further, it +is better not to use the integrated Quagga.conf file, as any syntax error in +it can lead to /all/ of your daemons being unable to start up. Per daemon +files are more robust as impact of errors in configuration are limited to +the daemon in whose file the error is made. + +@end deffn diff --git a/doc/watchquagga.8 b/doc/watchquagga.8 new file mode 100644 index 0000000..ca99164 --- /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 new file mode 100644 index 0000000..6f70389 --- /dev/null +++ b/doc/zebra.8 @@ -0,0 +1,134 @@ +.TH ZEBRA 8 "25 November 2004" "Zebra daemon" "Version 0.97.3" +.SH NAME +zebra \- a routing manager for use with associated Quagga components. +.SH SYNOPSIS +.B zebra +[ +.B \-bdhklrv +] [ +.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 zebra +is a routing manager that implements the +.B zebra +route engine. +.B zebra +supports RIPv1, RIPv2, RIPng, OSPF, OSPF6, IS-IS, BGP4+, and BGP4-. +.SH OPTIONS +Options available for the +.B zebra +command: +.TP +\fB\-b\fR, \fB\-\-batch\fR +Runs in batch mode, \fBzebra\fR parses its config and exits. +.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/zebra.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 zebra starts its process identifier is written to +\fB\fIpid-file\fR. The init system uses the recorded PID to stop or +restart zebra. The likely default is \fB\fI/var/run/zebra.pid\fR. +.TP +\fB\-k\fR, \fB\-\-keep_kernel\fR +On startup, don't delete self inserted routes. +.TP +\fB\-P\fR, \fB\-\-vty_port \fR\fIport-number\fR +Specify the port that the zebra VTY will listen on. This defaults to +2601, as specified in \fB\fI/etc/services\fR. +.TP +\fB\-A\fR, \fB\-\-vty_addr \fR\fIvty-address\fR +Specify the address that the zebra 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\-r\fR, \fB\-\-retain\fR +When the program terminates, retain routes added by \fBzebra\fR. +.TP +\fB\-s\fR, \fB\-\-nl-bufsize \fR\fInetlink-buffer-size\fR +Set netlink receive buffer size. There are cases where zebra daemon can't +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 +< 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. + +Note that this affects Linux only. +.TP +\fB\-v\fR, \fB\-\-version\fR +Print the version and exit. +.SH FILES +.TP +.BI /usr/local/sbin/zebra +The default location of the +.B zebra +binary. +.TP +.BI /usr/local/etc/zebra.conf +The default location of the +.B zebra +config file. +.TP +.BI $(PWD)/zebra.log +If the +.B zebra +process is config'd to output logs to a file, then you will find this +file in the directory where you started \fBzebra\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 zebra process may log to standard output, to a VTY, to a log +file, or through syslog to the system logs. \fBzebra\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 isisd (8), +.BR nhrpd (8), +.BR vtysh (1) +.SH BUGS +.B zebra +eats bugs for breakfast. If you have food for the maintainers try +.BI http://bugzilla.quagga.net +.SH AUTHORS +See +.BI http://www.zebra.org +and +.BI http://www.quagga.net +or the Info file for an accurate list of authors. + diff --git a/fpm/.gitignore b/fpm/.gitignore new file mode 100644 index 0000000..b133c52 --- /dev/null +++ b/fpm/.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/fpm/Makefile.am b/fpm/Makefile.am new file mode 100644 index 0000000..83ab31c --- /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 0000000..8528599 --- /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 0000000..318e80a --- /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 0000000..ba18627 --- /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 0000000..8f74ac0 --- /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 0000000..b703808 --- /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 0000000..984104b --- /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/infra/buildbot/master/master.cfg b/infra/buildbot/master/master.cfg new file mode 100644 index 0000000..00e56dd --- /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 0000000..34a9340 --- /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 0000000..dcb136f --- /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 0000000..d4177a7 --- /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 0000000..6514232 --- /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 0000000..1683c0c --- /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 0000000..bfa6752 --- /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 0000000..d152b2d --- /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 0000000..90ec4bb --- /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/init/.gitignore b/init/.gitignore new file mode 100644 index 0000000..30b4bc9 --- /dev/null +++ b/init/.gitignore @@ -0,0 +1,6 @@ +Makefile +Makefile.in +.nfs* +*~ +*.loT + diff --git a/isisd/.gitignore b/isisd/.gitignore new file mode 100644 index 0000000..5e8028c --- /dev/null +++ b/isisd/.gitignore @@ -0,0 +1,15 @@ +Makefile +Makefile.in +*.o +isisd +.deps +isisd.conf +.nfs* +*.lo +*.la +*.libs +.arch-inventory +.arch-ids +*~ +*.loT +*.a diff --git a/isisd/AUTHORS b/isisd/AUTHORS new file mode 100644 index 0000000..80b3a28 --- /dev/null +++ b/isisd/AUTHORS @@ -0,0 +1,5 @@ +Sampo Saaristo +Ofer Wald +Hannes Gredler +Subbaiah Venkata +Olivier Dugeon diff --git a/isisd/Makefile.am b/isisd/Makefile.am new file mode 100644 index 0000000..bfe2e94 --- /dev/null +++ b/isisd/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 \ + @ISIS_TOPOLOGY_INCLUDES@ +DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" +INSTALL_SDATA=@INSTALL@ -m 600 +LIBS = @LIBS@ + +AM_CFLAGS = $(WERROR) + +noinst_LIBRARIES = libisis.a +sbin_PROGRAMS = isisd +SUBDIRS = topology + +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_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_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 = \ + isis_main.c $(libisis_a_SOURCES) \ + isis_bpf.c isis_dlpi.c isis_pfpacket.c + +isisd_LDADD = @ISIS_TOPOLOGY_LIB@ ../lib/libzebra.la @LIBCAP@ + +examplesdir = $(exampledir) +dist_examples_DATA = isisd.conf.sample diff --git a/isisd/README b/isisd/README new file mode 100644 index 0000000..4f13ff6 --- /dev/null +++ b/isisd/README @@ -0,0 +1,3 @@ +Constraints + + o Maximum number of interfaces 255 diff --git a/isisd/dict.c b/isisd/dict.c new file mode 100644 index 0000000..bbcb421 --- /dev/null +++ b/isisd/dict.c @@ -0,0 +1,1485 @@ +/* + * Dictionary Abstract Data Type + * Copyright (C) 1997 Kaz Kylheku + * + * Free Software License: + * + * All rights are reserved by the author, with the following exceptions: + * Permission is granted to freely reproduce and distribute this software, + * possibly in exchange for a fee, provided that this copyright notice appears + * intact. Permission is also granted to adapt this software to produce + * derivative works, as long as the modified versions carry this copyright + * notice and additional notices stating that the work has been modified. + * This source code may be translated into executable form and incorporated + * into proprietary software; there is no requirement for such software to + * contain a copyright notice related to this source. + */ + +#include "zebra.h" +#include "zassert.h" +#include "memory.h" +#include "dict.h" + +/* + * These macros provide short convenient names for structure members, + * which are embellished with dict_ prefixes so that they are + * properly confined to the documented namespace. It's legal for a + * program which uses dict to define, for instance, a macro called ``parent''. + * Such a macro would interfere with the dnode_t struct definition. + * In general, highly portable and reusable C modules which expose their + * structures need to confine structure member names to well-defined spaces. + * The resulting identifiers aren't necessarily convenient to use, nor + * readable, in the implementation, however! + */ + +#define left dict_left +#define right dict_right +#define parent dict_parent +#define color dict_color +#define key dict_key +#define data dict_data + +#define nilnode dict_nilnode +#define nodecount dict_nodecount +#define maxcount dict_maxcount +#define compare dict_compare +#define allocnode dict_allocnode +#define freenode dict_freenode +#define context dict_context +#define dupes dict_dupes + +#define dictptr dict_dictptr + +#define dict_root(D) ((D)->nilnode.left) +#define dict_nil(D) (&(D)->nilnode) +#define DICT_DEPTH_MAX 64 + +static dnode_t *dnode_alloc(void *context); +static void dnode_free(dnode_t *node, void *context); + +/* + * Perform a ``left rotation'' adjustment on the tree. The given node P and + * its right child C are rearranged so that the P instead becomes the left + * child of C. The left subtree of C is inherited as the new right subtree + * for P. The ordering of the keys within the tree is thus preserved. + */ + +static void rotate_left(dnode_t *upper) +{ + dnode_t *lower, *lowleft, *upparent; + + lower = upper->right; + upper->right = lowleft = lower->left; + lowleft->parent = upper; + + lower->parent = upparent = upper->parent; + + /* don't need to check for root node here because root->parent is + the sentinel nil node, and root->parent->left points back to root */ + + if (upper == upparent->left) { + upparent->left = lower; + } else { + assert (upper == upparent->right); + upparent->right = lower; + } + + lower->left = upper; + upper->parent = lower; +} + +/* + * This operation is the ``mirror'' image of rotate_left. It is + * the same procedure, but with left and right interchanged. + */ + +static void rotate_right(dnode_t *upper) +{ + dnode_t *lower, *lowright, *upparent; + + lower = upper->left; + upper->left = lowright = lower->right; + lowright->parent = upper; + + lower->parent = upparent = upper->parent; + + if (upper == upparent->right) { + upparent->right = lower; + } else { + assert (upper == upparent->left); + upparent->left = lower; + } + + lower->right = upper; + upper->parent = lower; +} + +/* + * Do a postorder traversal of the tree rooted at the specified + * node and free everything under it. Used by dict_free(). + */ + +static void free_nodes(dict_t *dict, dnode_t *node, dnode_t *nil) +{ + if (node == nil) + return; + free_nodes(dict, node->left, nil); + free_nodes(dict, node->right, nil); + dict->freenode(node, dict->context); +} + +/* + * This procedure performs a verification that the given subtree is a binary + * search tree. It performs an inorder traversal of the tree using the + * dict_next() successor function, verifying that the key of each node is + * strictly lower than that of its successor, if duplicates are not allowed, + * or lower or equal if duplicates are allowed. This function is used for + * debugging purposes. + */ + +static int verify_bintree(dict_t *dict) +{ + dnode_t *first, *next; + + first = dict_first(dict); + + if (dict->dupes) { + while (first && (next = dict_next(dict, first))) { + if (dict->compare(first->key, next->key) > 0) + return 0; + first = next; + } + } else { + while (first && (next = dict_next(dict, first))) { + if (dict->compare(first->key, next->key) >= 0) + return 0; + first = next; + } + } + return 1; +} + + +/* + * This function recursively verifies that the given binary subtree satisfies + * three of the red black properties. It checks that every red node has only + * black children. It makes sure that each node is either red or black. And it + * checks that every path has the same count of black nodes from root to leaf. + * It returns the blackheight of the given subtree; this allows blackheights to + * be computed recursively and compared for left and right siblings for + * mismatches. It does not check for every nil node being black, because there + * is only one sentinel nil node. The return value of this function is the + * black height of the subtree rooted at the node ``root'', or zero if the + * subtree is not red-black. + */ + +static unsigned int verify_redblack(dnode_t *nil, dnode_t *root) +{ + unsigned height_left, height_right; + + if (root != nil) { + height_left = verify_redblack(nil, root->left); + height_right = verify_redblack(nil, root->right); + if (height_left == 0 || height_right == 0) + return 0; + if (height_left != height_right) + return 0; + if (root->color == dnode_red) { + if (root->left->color != dnode_black) + return 0; + if (root->right->color != dnode_black) + return 0; + return height_left; + } + if (root->color != dnode_black) + return 0; + return height_left + 1; + } + return 1; +} + +/* + * Compute the actual count of nodes by traversing the tree and + * return it. This could be compared against the stored count to + * detect a mismatch. + */ + +static dictcount_t verify_node_count(dnode_t *nil, dnode_t *root) +{ + if (root == nil) + return 0; + else + return 1 + verify_node_count(nil, root->left) + + verify_node_count(nil, root->right); +} + +/* + * Verify that the tree contains the given node. This is done by + * traversing all of the nodes and comparing their pointers to the + * given pointer. Returns 1 if the node is found, otherwise + * returns zero. It is intended for debugging purposes. + */ + +static int verify_dict_has_node(dnode_t *nil, dnode_t *root, dnode_t *node) +{ + if (root != nil) { + return root == node + || verify_dict_has_node(nil, root->left, node) + || verify_dict_has_node(nil, root->right, node); + } + return 0; +} + + +/* + * Dynamically allocate and initialize a dictionary object. + */ + +dict_t *dict_create(dictcount_t maxcount, dict_comp_t comp) +{ + dict_t *new = XCALLOC(MTYPE_ISIS_DICT, sizeof(dict_t)); + + if (new) { + new->compare = comp; + new->allocnode = dnode_alloc; + new->freenode = dnode_free; + new->context = NULL; + new->nodecount = 0; + new->maxcount = maxcount; + new->nilnode.left = &new->nilnode; + new->nilnode.right = &new->nilnode; + new->nilnode.parent = &new->nilnode; + new->nilnode.color = dnode_black; + new->dupes = 0; + } + return new; +} + +/* + * Select a different set of node allocator routines. + */ + +void dict_set_allocator(dict_t *dict, dnode_alloc_t al, + dnode_free_t fr, void *context) +{ + assert (dict_count(dict) == 0); + assert ((al == NULL && fr == NULL) || (al != NULL && fr != NULL)); + + dict->allocnode = al ? al : dnode_alloc; + dict->freenode = fr ? fr : dnode_free; + dict->context = context; +} + +/* + * Free a dynamically allocated dictionary object. Removing the nodes + * from the tree before deleting it is required. + */ + +void dict_destroy(dict_t *dict) +{ + assert (dict_isempty(dict)); + XFREE(MTYPE_ISIS_DICT, dict); +} + +/* + * Free all the nodes in the dictionary by using the dictionary's + * installed free routine. The dictionary is emptied. + */ + +void dict_free_nodes(dict_t *dict) +{ + dnode_t *nil = dict_nil(dict), *root = dict_root(dict); + free_nodes(dict, root, nil); + dict->nodecount = 0; + dict->nilnode.left = &dict->nilnode; + dict->nilnode.right = &dict->nilnode; +} + +/* + * Obsolescent function, equivalent to dict_free_nodes + */ + +void dict_free(dict_t *dict) +{ + dict_free_nodes(dict); +} + +/* + * Initialize a user-supplied dictionary object. + */ + +dict_t *dict_init(dict_t *dict, dictcount_t maxcount, dict_comp_t comp) +{ + dict->compare = comp; + dict->allocnode = dnode_alloc; + dict->freenode = dnode_free; + dict->context = NULL; + dict->nodecount = 0; + dict->maxcount = maxcount; + dict->nilnode.left = &dict->nilnode; + dict->nilnode.right = &dict->nilnode; + dict->nilnode.parent = &dict->nilnode; + dict->nilnode.color = dnode_black; + dict->dupes = 0; + return dict; +} + +/* + * Initialize a dictionary in the likeness of another dictionary + */ + +void dict_init_like(dict_t *dict, const dict_t *template) +{ + dict->compare = template->compare; + dict->allocnode = template->allocnode; + dict->freenode = template->freenode; + dict->context = template->context; + dict->nodecount = 0; + dict->maxcount = template->maxcount; + dict->nilnode.left = &dict->nilnode; + dict->nilnode.right = &dict->nilnode; + dict->nilnode.parent = &dict->nilnode; + dict->nilnode.color = dnode_black; + dict->dupes = template->dupes; + + assert (dict_similar(dict, template)); +} + +/* + * Remove all nodes from the dictionary (without freeing them in any way). + */ + +static void dict_clear(dict_t *dict) +{ + dict->nodecount = 0; + dict->nilnode.left = &dict->nilnode; + dict->nilnode.right = &dict->nilnode; + dict->nilnode.parent = &dict->nilnode; + assert (dict->nilnode.color == dnode_black); +} + + +/* + * Verify the integrity of the dictionary structure. This is provided for + * debugging purposes, and should be placed in assert statements. Just because + * this function succeeds doesn't mean that the tree is not corrupt. Certain + * corruptions in the tree may simply cause undefined behavior. + */ + +int dict_verify(dict_t *dict) +{ + dnode_t *nil = dict_nil(dict), *root = dict_root(dict); + + /* check that the sentinel node and root node are black */ + if (root->color != dnode_black) + return 0; + if (nil->color != dnode_black) + return 0; + if (nil->right != nil) + return 0; + /* nil->left is the root node; check that its parent pointer is nil */ + if (nil->left->parent != nil) + return 0; + /* perform a weak test that the tree is a binary search tree */ + if (!verify_bintree(dict)) + return 0; + /* verify that the tree is a red-black tree */ + if (!verify_redblack(nil, root)) + return 0; + if (verify_node_count(nil, root) != dict_count(dict)) + return 0; + return 1; +} + +/* + * Determine whether two dictionaries are similar: have the same comparison and + * allocator functions, and same status as to whether duplicates are allowed. + */ + +int dict_similar(const dict_t *left, const dict_t *right) +{ + if (left->compare != right->compare) + return 0; + + if (left->allocnode != right->allocnode) + return 0; + + if (left->freenode != right->freenode) + return 0; + + if (left->context != right->context) + return 0; + + if (left->dupes != right->dupes) + return 0; + + return 1; +} + +/* + * Locate a node in the dictionary having the given key. + * If the node is not found, a null a pointer is returned (rather than + * a pointer that dictionary's nil sentinel node), otherwise a pointer to the + * located node is returned. + */ + +dnode_t *dict_lookup(dict_t *dict, const void *key) +{ + dnode_t *root = dict_root(dict); + dnode_t *nil = dict_nil(dict); + dnode_t *saved; + int result; + + /* simple binary search adapted for trees that contain duplicate keys */ + + while (root != nil) { + result = dict->compare(key, root->key); + if (result < 0) + root = root->left; + else if (result > 0) + root = root->right; + else { + if (!dict->dupes) { /* no duplicates, return match */ + return root; + } else { /* could be dupes, find leftmost one */ + do { + saved = root; + root = root->left; + while (root != nil && dict->compare(key, root->key)) + root = root->right; + } while (root != nil); + return saved; + } + } + } + + return NULL; +} + +/* + * Look for the node corresponding to the lowest key that is equal to or + * greater than the given key. If there is no such node, return null. + */ + +dnode_t *dict_lower_bound(dict_t *dict, const void *key) +{ + dnode_t *root = dict_root(dict); + dnode_t *nil = dict_nil(dict); + dnode_t *tentative = 0; + + while (root != nil) { + int result = dict->compare(key, root->key); + + if (result > 0) { + root = root->right; + } else if (result < 0) { + tentative = root; + root = root->left; + } else { + if (!dict->dupes) { + return root; + } else { + tentative = root; + root = root->left; + } + } + } + + return tentative; +} + +/* + * Look for the node corresponding to the greatest key that is equal to or + * lower than the given key. If there is no such node, return null. + */ + +dnode_t *dict_upper_bound(dict_t *dict, const void *key) +{ + dnode_t *root = dict_root(dict); + dnode_t *nil = dict_nil(dict); + dnode_t *tentative = 0; + + while (root != nil) { + int result = dict->compare(key, root->key); + + if (result < 0) { + root = root->left; + } else if (result > 0) { + tentative = root; + root = root->right; + } else { + if (!dict->dupes) { + return root; + } else { + tentative = root; + root = root->right; + } + } + } + + return tentative; +} + +/* + * Insert a node into the dictionary. The node should have been + * initialized with a data field. All other fields are ignored. + * The behavior is undefined if the user attempts to insert into + * a dictionary that is already full (for which the dict_isfull() + * function returns true). + */ + +void dict_insert(dict_t *dict, dnode_t *node, const void *key) +{ + dnode_t *where = dict_root(dict), *nil = dict_nil(dict); + dnode_t *parent = nil, *uncle, *grandpa; + int result = -1; + + node->key = key; + + assert (!dict_isfull(dict)); + assert (!dict_contains(dict, node)); + assert (!dnode_is_in_a_dict(node)); + + /* basic binary tree insert */ + + while (where != nil) { + parent = where; + result = dict->compare(key, where->key); + /* trap attempts at duplicate key insertion unless it's explicitly allowed */ + assert (dict->dupes || result != 0); + if (result < 0) + where = where->left; + else + where = where->right; + } + + assert (where == nil); + + if (result < 0) + parent->left = node; + else + parent->right = node; + + node->parent = parent; + node->left = nil; + node->right = nil; + + dict->nodecount++; + + /* red black adjustments */ + + node->color = dnode_red; + + while (parent->color == dnode_red) { + grandpa = parent->parent; + if (parent == grandpa->left) { + uncle = grandpa->right; + if (uncle->color == dnode_red) { /* red parent, red uncle */ + parent->color = dnode_black; + uncle->color = dnode_black; + grandpa->color = dnode_red; + node = grandpa; + parent = grandpa->parent; + } else { /* red parent, black uncle */ + if (node == parent->right) { + rotate_left(parent); + parent = node; + assert (grandpa == parent->parent); + /* rotation between parent and child preserves grandpa */ + } + parent->color = dnode_black; + grandpa->color = dnode_red; + rotate_right(grandpa); + break; + } + } else { /* symmetric cases: parent == parent->parent->right */ + uncle = grandpa->left; + if (uncle->color == dnode_red) { + parent->color = dnode_black; + uncle->color = dnode_black; + grandpa->color = dnode_red; + node = grandpa; + parent = grandpa->parent; + } else { + if (node == parent->left) { + rotate_right(parent); + parent = node; + assert (grandpa == parent->parent); + } + parent->color = dnode_black; + grandpa->color = dnode_red; + rotate_left(grandpa); + break; + } + } + } + + dict_root(dict)->color = dnode_black; + + assert (dict_verify(dict)); +} + +/* + * Delete the given node from the dictionary. If the given node does not belong + * to the given dictionary, undefined behavior results. A pointer to the + * deleted node is returned. + */ + +dnode_t *dict_delete(dict_t *dict, dnode_t *delete) +{ + dnode_t *nil = dict_nil(dict), *child, *delparent = delete->parent; + + /* basic deletion */ + + assert (!dict_isempty(dict)); + assert (dict_contains(dict, delete)); + + /* + * If the node being deleted has two children, then we replace it with its + * successor (i.e. the leftmost node in the right subtree.) By doing this, + * we avoid the traditional algorithm under which the successor's key and + * value *only* move to the deleted node and the successor is spliced out + * from the tree. We cannot use this approach because the user may hold + * pointers to the successor, or nodes may be inextricably tied to some + * other structures by way of embedding, etc. So we must splice out the + * node we are given, not some other node, and must not move contents from + * one node to another behind the user's back. + */ + + if (delete->left != nil && delete->right != nil) { + dnode_t *next = dict_next(dict, delete); + dnode_t *nextparent = next->parent; + dnode_color_t nextcolor = next->color; + + assert (next != nil); + assert (next->parent != nil); + assert (next->left == nil); + + /* + * First, splice out the successor from the tree completely, by + * moving up its right child into its place. + */ + + child = next->right; + child->parent = nextparent; + + if (nextparent->left == next) { + nextparent->left = child; + } else { + assert (nextparent->right == next); + nextparent->right = child; + } + + /* + * Now that the successor has been extricated from the tree, install it + * in place of the node that we want deleted. + */ + + next->parent = delparent; + next->left = delete->left; + next->right = delete->right; + next->left->parent = next; + next->right->parent = next; + next->color = delete->color; + delete->color = nextcolor; + + if (delparent->left == delete) { + delparent->left = next; + } else { + assert (delparent->right == delete); + delparent->right = next; + } + + } else { + assert (delete != nil); + assert (delete->left == nil || delete->right == nil); + + child = (delete->left != nil) ? delete->left : delete->right; + + child->parent = delparent = delete->parent; + + if (delete == delparent->left) { + delparent->left = child; + } else { + assert (delete == delparent->right); + delparent->right = child; + } + } + + delete->parent = NULL; + delete->right = NULL; + delete->left = NULL; + + dict->nodecount--; + + assert (verify_bintree(dict)); + + /* red-black adjustments */ + + if (delete->color == dnode_black) { + dnode_t *parent, *sister; + + dict_root(dict)->color = dnode_red; + + while (child->color == dnode_black) { + parent = child->parent; + if (child == parent->left) { + sister = parent->right; + assert (sister != nil); + if (sister->color == dnode_red) { + sister->color = dnode_black; + parent->color = dnode_red; + rotate_left(parent); + sister = parent->right; + assert (sister != nil); + } + if (sister->left->color == dnode_black + && sister->right->color == dnode_black) { + sister->color = dnode_red; + child = parent; + } else { + if (sister->right->color == dnode_black) { + assert (sister->left->color == dnode_red); + sister->left->color = dnode_black; + sister->color = dnode_red; + rotate_right(sister); + sister = parent->right; + assert (sister != nil); + } + sister->color = parent->color; + sister->right->color = dnode_black; + parent->color = dnode_black; + rotate_left(parent); + break; + } + } else { /* symmetric case: child == child->parent->right */ + assert (child == parent->right); + sister = parent->left; + assert (sister != nil); + if (sister->color == dnode_red) { + sister->color = dnode_black; + parent->color = dnode_red; + rotate_right(parent); + sister = parent->left; + assert (sister != nil); + } + if (sister->right->color == dnode_black + && sister->left->color == dnode_black) { + sister->color = dnode_red; + child = parent; + } else { + if (sister->left->color == dnode_black) { + assert (sister->right->color == dnode_red); + sister->right->color = dnode_black; + sister->color = dnode_red; + rotate_left(sister); + sister = parent->left; + assert (sister != nil); + } + sister->color = parent->color; + sister->left->color = dnode_black; + parent->color = dnode_black; + rotate_right(parent); + break; + } + } + } + + child->color = dnode_black; + dict_root(dict)->color = dnode_black; + } + + assert (dict_verify(dict)); + + return delete; +} + +/* + * Allocate a node using the dictionary's allocator routine, give it + * the data item. + */ + +int dict_alloc_insert(dict_t *dict, const void *key, void *data) +{ + dnode_t *node = dict->allocnode (dict->context); + + if (node) { + dnode_init(node, data); + dict_insert(dict, node, key); + return 1; + } + return 0; +} + +void dict_delete_free(dict_t *dict, dnode_t *node) +{ + dict_delete(dict, node); + dict->freenode(node, dict->context); +} + +/* + * Return the node with the lowest (leftmost) key. If the dictionary is empty + * (that is, dict_isempty(dict) returns 1) a null pointer is returned. + */ + +dnode_t *dict_first(dict_t *dict) +{ + dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *left; + + if (root != nil) + while ((left = root->left) != nil) + root = left; + + return (root == nil) ? NULL : root; +} + +/* + * Return the node with the highest (rightmost) key. If the dictionary is empty + * (that is, dict_isempty(dict) returns 1) a null pointer is returned. + */ + +dnode_t *dict_last(dict_t *dict) +{ + dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *right; + + if (root != nil) + while ((right = root->right) != nil) + root = right; + + return (root == nil) ? NULL : root; +} + +/* + * Return the given node's successor node---the node which has the + * next key in the the left to right ordering. If the node has + * no successor, a null pointer is returned rather than a pointer to + * the nil node. + */ + +dnode_t *dict_next(dict_t *dict, dnode_t *curr) +{ + dnode_t *nil = dict_nil(dict), *parent, *left; + + if (curr->right != nil) { + curr = curr->right; + while ((left = curr->left) != nil) + curr = left; + return curr; + } + + parent = curr->parent; + + while (parent != nil && curr == parent->right) { + curr = parent; + parent = curr->parent; + } + + return (parent == nil) ? NULL : parent; +} + +/* + * Return the given node's predecessor, in the key order. + * The nil sentinel node is returned if there is no predecessor. + */ + +dnode_t *dict_prev(dict_t *dict, dnode_t *curr) +{ + dnode_t *nil = dict_nil(dict), *parent, *right; + + if (curr->left != nil) { + curr = curr->left; + while ((right = curr->right) != nil) + curr = right; + return curr; + } + + parent = curr->parent; + + while (parent != nil && curr == parent->left) { + curr = parent; + parent = curr->parent; + } + + return (parent == nil) ? NULL : parent; +} + +void dict_allow_dupes(dict_t *dict) +{ + dict->dupes = 1; +} + +#undef dict_count +#undef dict_isempty +#undef dict_isfull +#undef dnode_get +#undef dnode_put +#undef dnode_getkey + +dictcount_t dict_count(dict_t *dict) +{ + return dict->nodecount; +} + +int dict_isempty(dict_t *dict) +{ + return dict->nodecount == 0; +} + +int dict_isfull(dict_t *dict) +{ + return dict->nodecount == dict->maxcount; +} + +int dict_contains(dict_t *dict, dnode_t *node) +{ + return verify_dict_has_node(dict_nil(dict), dict_root(dict), node); +} + +static dnode_t *dnode_alloc(void *context) +{ + return XCALLOC(MTYPE_ISIS_DICT_NODE, sizeof(dnode_t)); +} + +static void dnode_free(dnode_t *node, void *context) +{ + XFREE(MTYPE_ISIS_DICT_NODE, node); +} + +dnode_t *dnode_create(void *data) +{ + dnode_t *new = XCALLOC(MTYPE_ISIS_DICT_NODE, sizeof(dnode_t)); + if (new) { + new->data = data; + new->parent = NULL; + new->left = NULL; + new->right = NULL; + } + return new; +} + +dnode_t *dnode_init(dnode_t *dnode, void *data) +{ + dnode->data = data; + dnode->parent = NULL; + dnode->left = NULL; + dnode->right = NULL; + return dnode; +} + +void dnode_destroy(dnode_t *dnode) +{ + assert (!dnode_is_in_a_dict(dnode)); + XFREE(MTYPE_ISIS_DICT_NODE, dnode); +} + +void *dnode_get(dnode_t *dnode) +{ + return dnode->data; +} + +const void *dnode_getkey(dnode_t *dnode) +{ + return dnode->key; +} + +void dnode_put(dnode_t *dnode, void *data) +{ + dnode->data = data; +} + +int dnode_is_in_a_dict(dnode_t *dnode) +{ + return (dnode->parent && dnode->left && dnode->right); +} + +void dict_process(dict_t *dict, void *context, dnode_process_t function) +{ + dnode_t *node = dict_first(dict), *next; + + while (node != NULL) { + /* check for callback function deleting */ + /* the next node from under us */ + assert (dict_contains(dict, node)); + next = dict_next(dict, node); + function(dict, node, context); + node = next; + } +} + +static void load_begin_internal(dict_load_t *load, dict_t *dict) +{ + load->dictptr = dict; + load->nilnode.left = &load->nilnode; + load->nilnode.right = &load->nilnode; +} + +void dict_load_begin(dict_load_t *load, dict_t *dict) +{ + assert (dict_isempty(dict)); + load_begin_internal(load, dict); +} + +void dict_load_next(dict_load_t *load, dnode_t *newnode, const void *key) +{ + dict_t *dict = load->dictptr; + dnode_t *nil = &load->nilnode; + + assert (!dnode_is_in_a_dict(newnode)); + assert (dict->nodecount < dict->maxcount); + + #ifndef NDEBUG + if (dict->nodecount > 0) { + if (dict->dupes) + assert (dict->compare(nil->left->key, key) <= 0); + else + assert (dict->compare(nil->left->key, key) < 0); + } + #endif + + newnode->key = key; + nil->right->left = newnode; + nil->right = newnode; + newnode->left = nil; + dict->nodecount++; +} + +void dict_load_end(dict_load_t *load) +{ + dict_t *dict = load->dictptr; + dnode_t *tree[DICT_DEPTH_MAX] = { 0 }; + dnode_t *curr, *dictnil = dict_nil(dict), *loadnil = &load->nilnode, *next; + dnode_t *complete = 0; + dictcount_t fullcount = DICTCOUNT_T_MAX, nodecount = dict->nodecount; + dictcount_t botrowcount; + unsigned baselevel = 0, level = 0, i; + + assert (dnode_red == 0 && dnode_black == 1); + + while (fullcount >= nodecount && fullcount) + fullcount >>= 1; + + botrowcount = nodecount - fullcount; + + for (curr = loadnil->left; curr != loadnil; curr = next) { + next = curr->left; + + if (complete == NULL && botrowcount-- == 0) { + assert (baselevel == 0); + assert (level == 0); + baselevel = level = 1; + complete = tree[0]; + + if (complete != 0) { + tree[0] = 0; + complete->right = dictnil; + while (tree[level] != 0) { + tree[level]->right = complete; + complete->parent = tree[level]; + complete = tree[level]; + tree[level++] = 0; + } + } + } + + if (complete == NULL) { + curr->left = dictnil; + curr->right = dictnil; + curr->color = level % 2; + complete = curr; + + assert (level == baselevel); + while (tree[level] != 0) { + tree[level]->right = complete; + complete->parent = tree[level]; + complete = tree[level]; + tree[level++] = 0; + } + } else { + curr->left = complete; + curr->color = (level + 1) % 2; + complete->parent = curr; + tree[level] = curr; + complete = 0; + level = baselevel; + } + } + + if (complete == NULL) + complete = dictnil; + + for (i = 0; i < DICT_DEPTH_MAX; i++) { + if (tree[i] != 0) { + tree[i]->right = complete; + complete->parent = tree[i]; + complete = tree[i]; + } + } + + dictnil->color = dnode_black; + dictnil->right = dictnil; + complete->parent = dictnil; + complete->color = dnode_black; + dict_root(dict) = complete; + + assert (dict_verify(dict)); +} + +void dict_merge(dict_t *dest, dict_t *source) +{ + dict_load_t load; + dnode_t *leftnode = dict_first(dest), *rightnode = dict_first(source); + + assert (dict_similar(dest, source)); + + if (source == dest) + return; + + dest->nodecount = 0; + load_begin_internal(&load, dest); + + for (;;) { + if (leftnode != NULL && rightnode != NULL) { + if (dest->compare(leftnode->key, rightnode->key) < 0) + goto copyleft; + else + goto copyright; + } else if (leftnode != NULL) { + goto copyleft; + } else if (rightnode != NULL) { + goto copyright; + } else { + assert (leftnode == NULL && rightnode == NULL); + break; + } + + copyleft: + { + dnode_t *next = dict_next(dest, leftnode); + #ifndef NDEBUG + leftnode->left = NULL; /* suppress assertion in dict_load_next */ + #endif + dict_load_next(&load, leftnode, leftnode->key); + leftnode = next; + continue; + } + + copyright: + { + dnode_t *next = dict_next(source, rightnode); + #ifndef NDEBUG + rightnode->left = NULL; + #endif + dict_load_next(&load, rightnode, rightnode->key); + rightnode = next; + continue; + } + } + + dict_clear(source); + dict_load_end(&load); +} + +#ifdef KAZLIB_TEST_MAIN + +#include +#include +#include +#include + +typedef char input_t[256]; + +static int tokenize(char *string, ...) +{ + char **tokptr; + va_list arglist; + int tokcount = 0; + + va_start(arglist, string); + tokptr = va_arg(arglist, char **); + while (tokptr) { + while (*string && isspace((unsigned char) *string)) + string++; + if (!*string) + break; + *tokptr = string; + while (*string && !isspace((unsigned char) *string)) + string++; + tokptr = va_arg(arglist, char **); + tokcount++; + if (!*string) + break; + *string++ = 0; + } + va_end(arglist); + + return tokcount; +} + +static int comparef(const void *key1, const void *key2) +{ + return strcmp(key1, key2); +} + +static char *dupstring(char *str) +{ + int sz = strlen(str) + 1; + char *new = XCALLOC(MTYPE_ISIS_TMP, sz); + if (new) + memcpy(new, str, sz); + return new; +} + +static dnode_t *new_node(void *c) +{ + static dnode_t few[5]; + static int count; + + if (count < 5) + return few + count++; + + return NULL; +} + +static void del_node(dnode_t *n, void *c) +{ +} + +static int prompt = 0; + +static void construct(dict_t *d) +{ + input_t in; + int done = 0; + dict_load_t dl; + dnode_t *dn; + char *tok1, *tok2, *val; + const char *key; + char *help = + "p turn prompt on\n" + "q finish construction\n" + "a add new entry\n"; + + if (!dict_isempty(d)) + puts("warning: dictionary not empty!"); + + dict_load_begin(&dl, d); + + while (!done) { + if (prompt) + putchar('>'); + fflush(stdout); + + if (!fgets(in, sizeof(input_t), stdin)) + break; + + switch (in[0]) { + case '?': + puts(help); + break; + case 'p': + prompt = 1; + break; + case 'q': + done = 1; + break; + case 'a': + if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) { + puts("what?"); + break; + } + key = dupstring(tok1); + val = dupstring(tok2); + dn = dnode_create(val); + + if (!key || !val || !dn) { + puts("out of memory"); + free((void *) key); + free(val); + if (dn) + dnode_destroy(dn); + } + + dict_load_next(&dl, dn, key); + break; + default: + putchar('?'); + putchar('\n'); + break; + } + } + + dict_load_end(&dl); +} + +int main(void) +{ + input_t in; + dict_t darray[10]; + dict_t *d = &darray[0]; + dnode_t *dn; + int i; + char *tok1, *tok2, *val; + const char *key; + + char *help = + "a add value to dictionary\n" + "d delete value from dictionary\n" + "l lookup value in dictionary\n" + "( lookup lower bound\n" + ") lookup upper bound\n" + "# switch to alternate dictionary (0-9)\n" + "j merge two dictionaries\n" + "f free the whole dictionary\n" + "k allow duplicate keys\n" + "c show number of entries\n" + "t dump whole dictionary in sort order\n" + "m make dictionary out of sorted items\n" + "p turn prompt on\n" + "s switch to non-functioning allocator\n" + "q quit"; + + for (i = 0; i < 10; i++) + dict_init(&darray[i], DICTCOUNT_T_MAX, comparef); + + for (;;) { + if (prompt) + putchar('>'); + fflush(stdout); + + if (!fgets(in, sizeof(input_t), stdin)) + break; + + switch(in[0]) { + case '?': + puts(help); + break; + case 'a': + if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) { + puts("what?"); + break; + } + key = dupstring(tok1); + val = dupstring(tok2); + + if (!key || !val) { + puts("out of memory"); + free((void *) key); + free(val); + } + + if (!dict_alloc_insert(d, key, val)) { + puts("dict_alloc_insert failed"); + free((void *) key); + free(val); + break; + } + break; + case 'd': + if (tokenize(in+1, &tok1, (char **) 0) != 1) { + puts("what?"); + break; + } + dn = dict_lookup(d, tok1); + if (!dn) { + puts("dict_lookup failed"); + break; + } + val = dnode_get(dn); + key = dnode_getkey(dn); + dict_delete_free(d, dn); + + free(val); + free((void *) key); + break; + case 'f': + dict_free(d); + break; + case 'l': + case '(': + case ')': + if (tokenize(in+1, &tok1, (char **) 0) != 1) { + puts("what?"); + break; + } + dn = 0; + switch (in[0]) { + case 'l': + dn = dict_lookup(d, tok1); + break; + case '(': + dn = dict_lower_bound(d, tok1); + break; + case ')': + dn = dict_upper_bound(d, tok1); + break; + } + if (!dn) { + puts("lookup failed"); + break; + } + val = dnode_get(dn); + puts(val); + break; + case 'm': + construct(d); + break; + case 'k': + dict_allow_dupes(d); + break; + case 'c': + printf("%lu\n", (unsigned long) dict_count(d)); + break; + case 't': + for (dn = dict_first(d); dn; dn = dict_next(d, dn)) { + printf("%s\t%s\n", (char *) dnode_getkey(dn), + (char *) dnode_get(dn)); + } + break; + case 'q': + exit(0); + break; + case '\0': + break; + case 'p': + prompt = 1; + break; + case 's': + dict_set_allocator(d, new_node, del_node, NULL); + break; + case '#': + if (tokenize(in+1, &tok1, (char **) 0) != 1) { + puts("what?"); + break; + } else { + int dictnum = atoi(tok1); + if (dictnum < 0 || dictnum > 9) { + puts("invalid number"); + break; + } + d = &darray[dictnum]; + } + break; + case 'j': + if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) { + puts("what?"); + break; + } else { + int dict1 = atoi(tok1), dict2 = atoi(tok2); + if (dict1 < 0 || dict1 > 9 || dict2 < 0 || dict2 > 9) { + puts("invalid number"); + break; + } + dict_merge(&darray[dict1], &darray[dict2]); + } + break; + default: + putchar('?'); + putchar('\n'); + break; + } + } + + return 0; +} + +#endif diff --git a/isisd/dict.h b/isisd/dict.h new file mode 100644 index 0000000..93edb7d --- /dev/null +++ b/isisd/dict.h @@ -0,0 +1,123 @@ +/* + * Dictionary Abstract Data Type + * Copyright (C) 1997 Kaz Kylheku + * + * Free Software License: + * + * All rights are reserved by the author, with the following exceptions: + * Permission is granted to freely reproduce and distribute this software, + * possibly in exchange for a fee, provided that this copyright notice appears + * intact. Permission is also granted to adapt this software to produce + * derivative works, as long as the modified versions carry this copyright + * notice and additional notices stating that the work has been modified. + * This source code may be translated into executable form and incorporated + * into proprietary software; there is no requirement for such software to + * contain a copyright notice related to this source. + * + * $Id: dict.h,v 1.3 2005/09/25 12:04:25 hasso Exp $ + * $Name: $ + */ + +#ifndef DICT_H +#define DICT_H + +#include + +/* + * Blurb for inclusion into C++ translation units + */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned long dictcount_t; +#define DICTCOUNT_T_MAX ULONG_MAX + +/* + * The dictionary is implemented as a red-black tree + */ + +typedef enum { dnode_red, dnode_black } dnode_color_t; + +typedef struct dnode_t { + 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; +} dnode_t; + +typedef int (*dict_comp_t)(const void *, const void *); +typedef dnode_t *(*dnode_alloc_t)(void *); +typedef void (*dnode_free_t)(dnode_t *, void *); + +typedef struct dict_t { + dnode_t dict_nilnode; + dictcount_t dict_nodecount; + dictcount_t dict_maxcount; + dict_comp_t dict_compare; + dnode_alloc_t dict_allocnode; + dnode_free_t dict_freenode; + void *dict_context; + int dict_dupes; +} dict_t; + +typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *); + +typedef struct dict_load_t { + dict_t *dict_dictptr; + dnode_t dict_nilnode; +} dict_load_t; + +extern dict_t *dict_create(dictcount_t, dict_comp_t); +extern void dict_set_allocator(dict_t *, dnode_alloc_t, dnode_free_t, void *); +extern void dict_destroy(dict_t *); +extern void dict_free_nodes(dict_t *); +extern void dict_free(dict_t *); +extern dict_t *dict_init(dict_t *, dictcount_t, dict_comp_t); +extern void dict_init_like(dict_t *, const dict_t *); +extern int dict_verify(dict_t *); +extern int dict_similar(const dict_t *, const dict_t *); +extern dnode_t *dict_lookup(dict_t *, const void *); +extern dnode_t *dict_lower_bound(dict_t *, const void *); +extern dnode_t *dict_upper_bound(dict_t *, const void *); +extern void dict_insert(dict_t *, dnode_t *, const void *); +extern dnode_t *dict_delete(dict_t *, dnode_t *); +extern int dict_alloc_insert(dict_t *, const void *, void *); +extern void dict_delete_free(dict_t *, dnode_t *); +extern dnode_t *dict_first(dict_t *); +extern dnode_t *dict_last(dict_t *); +extern dnode_t *dict_next(dict_t *, dnode_t *); +extern dnode_t *dict_prev(dict_t *, dnode_t *); +extern dictcount_t dict_count(dict_t *); +extern int dict_isempty(dict_t *); +extern int dict_isfull(dict_t *); +extern int dict_contains(dict_t *, dnode_t *); +extern void dict_allow_dupes(dict_t *); +extern int dnode_is_in_a_dict(dnode_t *); +extern dnode_t *dnode_create(void *); +extern dnode_t *dnode_init(dnode_t *, void *); +extern void dnode_destroy(dnode_t *); +extern void *dnode_get(dnode_t *); +extern const void *dnode_getkey(dnode_t *); +extern void dnode_put(dnode_t *, void *); +extern void dict_process(dict_t *, void *, dnode_process_t); +extern void dict_load_begin(dict_load_t *, dict_t *); +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 *); + +#define dict_isfull(D) ((D)->dict_nodecount == (D)->dict_maxcount) +#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)) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/isisd/include-netbsd/.gitignore b/isisd/include-netbsd/.gitignore new file mode 100644 index 0000000..dd5bf7c --- /dev/null +++ b/isisd/include-netbsd/.gitignore @@ -0,0 +1,6 @@ +.arch-inventory +.arch-ids + +*~ +*.loT + diff --git a/isisd/include-netbsd/clnp.h b/isisd/include-netbsd/clnp.h new file mode 100644 index 0000000..6bc3d25 --- /dev/null +++ b/isisd/include-netbsd/clnp.h @@ -0,0 +1,547 @@ +/* $NetBSD: clnp.h,v 1.13 2001/08/20 12:00:54 wiz Exp $ */ + +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)clnp.h 8.2 (Berkeley) 4/16/94 + */ + +/*********************************************************** + Copyright IBM Corporation 1987 + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of IBM not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +IBM BE LIABLE FOR ANY SPECIAL, 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. + +******************************************************************/ + +/* + * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison + */ + +/* should be config option but cpp breaks with too many #defines */ +#define DECBIT + +/* + * Return true if the mbuf is a cluster mbuf + */ +#define IS_CLUSTER(m) ((m)->m_flags & M_EXT) + +/* + * Move the halfword into the two characters + */ +#define HTOC(msb, lsb, hword)\ + (msb) = (u_char)((hword) >> 8);\ + (lsb) = (u_char)((hword) & 0xff) +/* + * Move the two charcters into the halfword + */ +#define CTOH(msb, lsb, hword)\ + (hword) = ((msb) << 8) | (lsb) + +/* + * Return true if the checksum has been set - ie. the checksum is + * not zero + */ +#define CKSUM_REQUIRED(clnp)\ + (((clnp)->cnf_cksum_msb != 0) || ((clnp)->cnf_cksum_lsb != 0)) + +/* + * Fixed part of clnp header + */ +struct clnp_fixed { + u_char cnf_proto_id; /* network layer protocol identifier */ + u_char cnf_hdr_len; /* length indicator (octets) */ + u_char cnf_vers; /* version/protocol identifier + * extension */ + u_char cnf_ttl;/* lifetime (500 milliseconds) */ + u_char cnf_type; /* type code */ + /* Includes err_ok, more_segs, and seg_ok */ + u_char cnf_seglen_msb; /* pdu segment length (octets) high + * byte */ + u_char cnf_seglen_lsb; /* pdu segment length (octets) low + * byte */ + u_char cnf_cksum_msb; /* checksum high byte */ + u_char cnf_cksum_lsb; /* checksum low byte */ +} __attribute__((packed)); +#define CNF_TYPE 0x1f +#define CNF_ERR_OK 0x20 +#define CNF_MORE_SEGS 0x40 +#define CNF_SEG_OK 0x80 + +#define CLNP_CKSUM_OFF 0x07 /* offset of checksum */ + +#define clnl_fixed clnp_fixed + +/* + * Segmentation part of clnp header + */ +struct clnp_segment { + u_short cng_id; /* data unit identifier */ + u_short cng_off;/* segment offset */ + u_short cng_tot_len; /* total length */ +}; + +/* + * Clnp fragment reassembly structures: + * + * All packets undergoing reassembly are linked together in + * clnp_fragl structures. Each clnp_fragl structure contains a + * pointer to the original clnp packet header, as well as a + * list of packet fragments. Each packet fragment + * is headed by a clnp_frag structure. This structure contains the + * offset of the first and last byte of the fragment, as well as + * a pointer to the data (an mbuf chain) of the fragment. + */ + +/* + * NOTE: + * The clnp_frag structure is stored in an mbuf immedately + * preceding the fragment data. Since there are words in + * this struct, it must be word aligned. + * + * NOTE: + * All the fragment code assumes that the entire clnp header is + * contained in the first mbuf. + */ +struct clnp_frag { + u_int cfr_first; /* offset of first byte of this frag */ + u_int cfr_last; /* offset of last byte of this frag */ + u_int cfr_bytes; /* bytes to shave to get to data */ + struct mbuf *cfr_data; /* ptr to data for this frag */ + struct clnp_frag *cfr_next; /* next fragment in list */ +}; + +struct clnp_fragl { + struct iso_addr cfl_src;/* source of the pkt */ + struct iso_addr cfl_dst;/* destination of the pkt */ + u_short cfl_id; /* id of the pkt */ + u_char cfl_ttl;/* current ttl of pkt */ + u_short cfl_last; /* offset of last byte of packet */ + struct mbuf *cfl_orighdr; /* ptr to original header */ + struct clnp_frag *cfl_frags; /* linked list of fragments for pkt */ + struct clnp_fragl *cfl_next; /* next pkt being reassembled */ +}; + +/* + * The following structure is used to index into an options section + * of a clnp datagram. These values can be used without worry that + * offset or length fields are invalid or too big, etc. That is, + * the consistancy of the options will be guaranteed before this + * structure is filled in. Any pointer (field ending in p) is + * actually the offset from the beginning of the mbuf the option + * is contained in. A value of NULL for any pointer + * means that the option is not present. The length any option + * does not include the option code or option length fields. + */ +struct clnp_optidx { + u_short cni_securep; /* ptr to start of security option */ + char cni_secure_len; /* length of entire security option */ + + u_short cni_srcrt_s; /* offset of start of src rt option */ + u_short cni_srcrt_len; /* length of entire src rt option */ + + u_short cni_recrtp; /* ptr to beginning of recrt option */ + char cni_recrt_len; /* length of entire recrt option */ + + char cni_priorp; /* ptr to priority option */ + + u_short cni_qos_formatp; /* ptr to format of qos + * option */ + char cni_qos_len; /* length of entire qos option */ + + u_char cni_er_reason; /* reason from ER pdu option */ + + /* ESIS options */ + + u_short cni_esct; /* value from ISH ESCT option */ + + u_short cni_netmaskp; /* ptr to beginning of netmask option */ + char cni_netmask_len; /* length of entire netmask + * option */ + + u_short cni_snpamaskp; /* ptr to start of snpamask option */ + char cni_snpamask_len; /* length of entire snpamask + * option */ + +}; + +#define ER_INVALREAS 0xff /* code for invalid ER pdu discard reason */ + +/* given an mbuf and addr of option, return offset from data of mbuf */ +#define CLNP_OPTTOOFF(m, opt) ((u_short) (opt - mtod(m, caddr_t))) + +/* given an mbuf and offset of option, return address of option */ +#define CLNP_OFFTOOPT(m, off) ((caddr_t) (mtod(m, caddr_t) + off)) + +/* return true iff src route is valid */ +#define CLNPSRCRT_VALID(oidx) ((oidx) && (oidx->cni_srcrt_s)) + +/* return the offset field of the src rt */ +#define CLNPSRCRT_OFF(oidx, options)\ + (*((u_char *)(CLNP_OFFTOOPT(options, oidx->cni_srcrt_s) + 1))) + +/* return the type field of the src rt */ +#define CLNPSRCRT_TYPE(oidx, options)\ + ((u_char)(*(CLNP_OFFTOOPT(options, oidx->cni_srcrt_s)))) + +/* return the length of the current address */ +#define CLNPSRCRT_CLEN(oidx, options)\ + ((u_char)(*(CLNP_OFFTOOPT(options, oidx->cni_srcrt_s) + CLNPSRCRT_OFF(oidx, options) - 1))) + +/* return the address of the current address */ +#define CLNPSRCRT_CADDR(oidx, options)\ + ((caddr_t)(CLNP_OFFTOOPT(options, oidx->cni_srcrt_s) + CLNPSRCRT_OFF(oidx, options))) + +/* + * return true if the src route has run out of routes this is true if the + * offset of next route is greater than the end of the rt + */ +#define CLNPSRCRT_TERM(oidx, options)\ + (CLNPSRCRT_OFF(oidx, options) > oidx->cni_srcrt_len) + +/* + * Options a user can set/get + */ +#define CLNPOPT_FLAGS 0x01 /* flags: seg permitted, no er xmit, etc */ +#define CLNPOPT_OPTS 0x02 /* datagram options */ + +/* + * Values for particular datagram options + */ +#define CLNPOVAL_PAD 0xcc /* padding */ +#define CLNPOVAL_SECURE 0xc5 /* security */ +#define CLNPOVAL_SRCRT 0xc8 /* source routing */ +#define CLNPOVAL_RECRT 0xcb /* record route */ +#define CLNPOVAL_QOS 0xc3 /* quality of service */ +#define CLNPOVAL_PRIOR 0xcd /* priority */ +#define CLNPOVAL_ERREAS 0xc1 /* ER PDU ONLY: reason for discard */ + +#define CLNPOVAL_SRCSPEC 0x40 /* source address specific */ +#define CLNPOVAL_DSTSPEC 0x80 /* destination address specific */ +#define CLNPOVAL_GLOBAL 0xc0 /* globally unique */ + +/* Globally Unique QOS */ +#define CLNPOVAL_SEQUENCING 0x10 /* sequencing preferred */ +#define CLNPOVAL_CONGESTED 0x08 /* congestion experienced */ +#define CLNPOVAL_LOWDELAY 0x04 /* low transit delay */ + +#define CLNPOVAL_PARTRT 0x00 /* partial source routing */ +#define CLNPOVAL_COMPRT 0x01 /* complete source routing */ + +/* + * Clnp flags used in a control block flags field. + * NOTE: these must be out of the range of bits defined in ../net/raw_cb.h + */ +#define CLNP_NO_SEG 0x010 /* segmentation not permitted */ +#define CLNP_NO_ER 0x020 /* do not generate ERs */ +#define CLNP_SEND_RAW 0x080 /* send pkt as RAW DT not TP DT */ +#define CLNP_NO_CKSUM 0x100 /* don't use clnp checksum */ +#define CLNP_ECHO 0x200 /* send echo request */ +#define CLNP_NOCACHE 0x400 /* don't store cache information */ +#define CLNP_ECHOR 0x800 /* send echo reply */ + +/* valid clnp flags */ +#define CLNP_VFLAGS \ + (CLNP_SEND_RAW|CLNP_NO_SEG|CLNP_NO_ER|CLNP_NO_CKSUM|\ + CLNP_ECHO|CLNP_NOCACHE|CLNP_ECHOR) + +/* + * Constants used by clnp + */ +#define CLNP_HDR_MIN (sizeof (struct clnp_fixed)) +#define CLNP_HDR_MAX (254) +#define CLNP_TTL_UNITS 2 /* 500 milliseconds */ +#define CLNP_TTL 15*CLNP_TTL_UNITS /* time to live (seconds) */ +#define ISO8473_V1 0x01 + +/* + * Clnp packet types + * In order to test raw clnp and tp/clnp simultaneously, a third type of + * packet has been defined: CLNP_RAW. This is done so that the input + * routine can switch to the correct input routine (rclnp_input or + * tpclnp_input) based on the type field. If clnp had a higher level + * protocol field, this would not be necessary. + */ +#define CLNP_DT 0x1C /* normal data */ +#define CLNP_ER 0x01 /* error report */ +#define CLNP_RAW 0x1D /* debug only */ +#define CLNP_EC 0x1E /* echo packet */ +#define CLNP_ECR 0x1F /* echo reply */ + +/* + * ER pdu error codes + */ +#define GEN_NOREAS 0x00 /* reason not specified */ +#define GEN_PROTOERR 0x01 /* protocol procedure error */ +#define GEN_BADCSUM 0x02 /* incorrect checksum */ +#define GEN_CONGEST 0x03 /* pdu discarded due to congestion */ +#define GEN_HDRSYNTAX 0x04 /* header syntax error */ +#define GEN_SEGNEEDED 0x05 /* need segmentation but not allowed */ +#define GEN_INCOMPLETE 0x06 /* incomplete pdu received */ +#define GEN_DUPOPT 0x07 /* duplicate option */ + +/* address errors */ +#define ADDR_DESTUNREACH 0x80 /* destination address unreachable */ +#define ADDR_DESTUNKNOWN 0x81 /* destination address unknown */ + +/* source routing */ +#define SRCRT_UNSPECERR 0x90 /* unspecified src rt error */ +#define SRCRT_SYNTAX 0x91 /* syntax error in src rt field */ +#define SRCRT_UNKNOWNADDR 0x92 /* unknown addr in src rt field */ +#define SRCRT_BADPATH 0x93 /* path not acceptable */ + +/* lifetime */ +#define TTL_EXPTRANSIT 0xa0 /* lifetime expired during transit */ +#define TTL_EXPREASS 0xa1 /* lifetime expired during reassembly */ + +/* pdu discarded */ +#define DISC_UNSUPPOPT 0xb0 /* unsupported option not specified? */ +#define DISC_UNSUPPVERS 0xb1 /* unsupported protocol version */ +#define DISC_UNSUPPSECURE 0xb2 /* unsupported security option */ +#define DISC_UNSUPPSRCRT 0xb3 /* unsupported src rt option */ +#define DISC_UNSUPPRECRT 0xb4 /* unsupported rec rt option */ + +/* reassembly */ +#define REASS_INTERFERE 0xc0 /* reassembly interference */ +#define CLNP_ERRORS 22 + + +#ifdef CLNP_ER_CODES +u_char clnp_er_codes[CLNP_ERRORS] = { + GEN_NOREAS, GEN_PROTOERR, GEN_BADCSUM, GEN_CONGEST, + GEN_HDRSYNTAX, GEN_SEGNEEDED, GEN_INCOMPLETE, GEN_DUPOPT, + ADDR_DESTUNREACH, ADDR_DESTUNKNOWN, + SRCRT_UNSPECERR, SRCRT_SYNTAX, SRCRT_UNKNOWNADDR, SRCRT_BADPATH, + TTL_EXPTRANSIT, TTL_EXPREASS, + DISC_UNSUPPOPT, DISC_UNSUPPVERS, DISC_UNSUPPSECURE, + DISC_UNSUPPSRCRT, DISC_UNSUPPRECRT, REASS_INTERFERE +}; +#endif + +#ifdef TROLL + +#define TR_DUPEND 0x01 /* duplicate end of fragment */ +#define TR_DUPPKT 0x02 /* duplicate entire packet */ +#define TR_DROPPKT 0x04 /* drop packet on output */ +#define TR_TRIM 0x08 /* trim bytes from packet */ +#define TR_CHANGE 0x10 /* change bytes in packet */ +#define TR_MTU 0x20 /* delta to change device mtu */ +#define TR_CHUCK 0x40 /* drop packet in rclnp_input */ +#define TR_BLAST 0x80 /* force rclnp_output to blast many + * packet */ +#define TR_RAWLOOP 0x100 /* make if_loop call clnpintr + * directly */ +struct troll { + int tr_ops; /* operations to perform */ + float tr_dup_size; /* % to duplicate */ + float tr_dup_freq; /* frequency to duplicate packets */ + float tr_drop_freq; /* frequence to drop packets */ + int tr_mtu_adj; /* delta to adjust if mtu */ + int tr_blast_cnt; /* # of pkts to blast out */ +}; + +#define SN_OUTPUT(clcp, m)\ + troll_output(clcp->clc_ifp, m, clcp->clc_firsthop, clcp->clc_rt) + +#define SN_MTU(ifp, rt) (((rt && rt->rt_rmx.rmx_mtu) ?\ + rt->rt_rmx.rmx_mtu : clnp_badmtu(ifp, rt, __LINE__, __FILE__))\ + - trollctl.tr_mtu_adj) + +#ifdef _KERNEL +extern float troll_random; +#endif + +#else /* NO TROLL */ + +#define SN_OUTPUT(clcp, m)\ + (*clcp->clc_ifp->if_output)(clcp->clc_ifp, m, clcp->clc_firsthop, \ + clcp->clc_rt) + +#define SN_MTU(ifp, rt) (((rt && rt->rt_rmx.rmx_mtu) ?\ + rt->rt_rmx.rmx_mtu : clnp_badmtu(ifp, rt, __LINE__, __FILE__))) + +#endif /* TROLL */ + +/* + * Macro to remove an address from a clnp header + */ +#define CLNP_EXTRACT_ADDR(isoa, hoff, hend)\ + {\ + isoa.isoa_len = (u_char)*hoff;\ + if ((((++hoff) + isoa.isoa_len) > hend) ||\ + (isoa.isoa_len > 20) || (isoa.isoa_len == 0)) {\ + hoff = (caddr_t)0;\ + } else {\ + (void) bcopy(hoff, (caddr_t)isoa.isoa_genaddr, \ + isoa.isoa_len);\ + hoff += isoa.isoa_len;\ + }\ + } + +/* + * Macro to insert an address into a clnp header + */ +#define CLNP_INSERT_ADDR(hoff, isoa)\ + *hoff++ = (isoa).isoa_len;\ + (void) bcopy((caddr_t)((isoa).isoa_genaddr), hoff, (isoa).isoa_len);\ + hoff += (isoa).isoa_len; + +/* + * Clnp hdr cache. Whenever a clnp packet is sent, a copy of the + * header is made and kept in this cache. In addition to a copy of + * the cached clnp hdr, the cache contains + * information necessary to determine whether the new packet + * to send requires a new header to be built. + */ +struct clnp_cache { + /* these fields are used to check the validity of the cache */ + struct iso_addr clc_dst;/* destination of packet */ + struct mbuf *clc_options; /* ptr to options mbuf */ + int clc_flags; /* flags passed to clnp_output */ + + /* these fields are state that clnp_output requires to finish the pkt */ + int clc_segoff; /* offset of seg part of header */ + struct rtentry *clc_rt; /* ptr to rtentry (points into the route + * structure) */ + struct sockaddr *clc_firsthop; /* first hop of packet */ + struct ifnet *clc_ifp;/* ptr to interface structure */ + struct iso_ifaddr + *clc_ifa;/* ptr to interface address */ + struct mbuf *clc_hdr;/* cached pkt hdr (finally)! */ +}; + +#ifdef _KERNEL +struct iso_addr; +struct sockaddr_iso; +struct mbuf; +struct clnp_segment; +struct sockaddr; +struct rt_entry; +struct clnp_fragl; +struct clnp_optidx; +struct isopcb; +struct snpa_hdr; +struct iso_ifaddr; +struct route_iso; + +/* clnp_debug.c */ +char *clnp_hexp __P((char *, int, char *)); +char *clnp_iso_addrp __P((struct iso_addr *)); +char *clnp_saddr_isop __P((struct sockaddr_iso *)); + +/* clnp_er.c */ +void clnp_er_input __P((struct mbuf *, struct iso_addr *, u_int)); +void clnp_discard __P((struct mbuf *, u_int)); +void clnp_emit_er __P((struct mbuf *, u_int)); +int clnp_er_index __P((u_int)); + +int clnp_fragment __P((struct ifnet *, struct mbuf *, struct sockaddr *, + int, int, int, struct rtentry *)); +struct mbuf *clnp_reass __P((struct mbuf *, struct iso_addr *, + struct iso_addr *, struct clnp_segment *)); +int clnp_newpkt __P((struct mbuf *, struct iso_addr *, struct iso_addr *, + struct clnp_segment *)); +void clnp_insert_frag __P((struct clnp_fragl *, struct mbuf *, + struct clnp_segment *)); +struct mbuf *clnp_comp_pdu __P((struct clnp_fragl *)); +#ifdef TROLL +float troll_random __P((void)); +int troll_output __P((struct ifnet *, struct mbuf *, struct sockaddr *, + struct rtentry *)); +#endif + +/* clnp_input.c */ +void clnp_init __P((void)); +void clnlintr __P((void)); +void clnp_input __P((struct mbuf *, ...)); + +/* clnp_options.c */ +void clnp_update_srcrt __P((struct mbuf *, struct clnp_optidx *)); +void clnp_dooptions __P((struct mbuf *, struct clnp_optidx *, struct ifnet *, + struct iso_addr *)); +int clnp_set_opts __P((struct mbuf **, struct mbuf **)); +int clnp_opt_sanity __P((struct mbuf *, caddr_t, int, struct clnp_optidx *)); + +/* clnp_output.c */ +int clnp_output __P((struct mbuf *, ...)); +void clnp_ctloutput __P((void)); + +/* clnp_raw.c */ +void rclnp_input __P((struct mbuf *, ...)); +int rclnp_output __P((struct mbuf *, ...)); +int rclnp_ctloutput __P((int, struct socket *, int, int, struct mbuf **)); +int clnp_usrreq __P((struct socket *, int, struct mbuf *, struct mbuf *, + struct mbuf *, struct proc *)); + +/* clnp_subr.c */ +struct mbuf *clnp_data_ck __P((struct mbuf *, int)); +caddr_t clnp_extract_addr __P((caddr_t, int, struct iso_addr *, + struct iso_addr *)); +int clnp_ours __P((struct iso_addr *)); +void clnp_forward __P((struct mbuf *, int, struct iso_addr *, + struct clnp_optidx *, int, struct snpa_hdr *)); +caddr_t clnp_insert_addr __P((caddr_t, struct iso_addr *, struct iso_addr *)); +int clnp_route __P((struct iso_addr *, struct route_iso *, int, + struct sockaddr **, struct iso_ifaddr **)); +int clnp_srcroute __P((struct mbuf *, struct clnp_optidx *, struct route_iso *, + struct sockaddr **, struct iso_ifaddr **, + struct iso_addr *)); +int clnp_echoreply __P((struct mbuf *, int, struct sockaddr_iso *, + struct sockaddr_iso *, struct clnp_optidx *)); +int clnp_badmtu __P((struct ifnet *, struct rtentry *, int, char *)); +void clnp_ypocb __P((caddr_t, caddr_t, u_int)); + +/* clnp_timer.c */ +struct clnp_fragl *clnp_freefrags __P((struct clnp_fragl *)); +void clnp_slowtimo __P((void)); +void clnp_drain __P((void)); + +#ifdef TROLL +struct troll trollctl; +#endif /* TROLL */ + +#endif /* _KERNEL */ diff --git a/isisd/include-netbsd/esis.h b/isisd/include-netbsd/esis.h new file mode 100644 index 0000000..ded864e --- /dev/null +++ b/isisd/include-netbsd/esis.h @@ -0,0 +1,146 @@ +/* $NetBSD: esis.h,v 1.11 1997/11/03 15:01:19 is Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)esis.h 8.1 (Berkeley) 6/10/93 + */ + +/*********************************************************** + Copyright IBM Corporation 1987 + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of IBM not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +IBM BE LIABLE FOR ANY SPECIAL, 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. + +******************************************************************/ + +/* + * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison + */ + +#include + +#define SNPAC_AGE 60 /* seconds */ +#define ESIS_CONFIG 60 /* seconds */ +#define ESIS_HT (ESIS_CONFIG * 2) + +/* + * Fixed part of an ESIS header + */ +struct esis_fixed { + u_char esis_proto_id; /* network layer protocol identifier */ + u_char esis_hdr_len; /* length indicator (octets) */ + u_char esis_vers; /* version/protocol identifier + * extension */ + u_char esis_res1; /* reserved */ + u_char esis_type; /* type code */ + /* technically, type should be &='d 0x1f */ +#define ESIS_ESH 0x02 /* End System Hello */ +#define ESIS_ISH 0x04 /* Intermediate System Hello */ +#define ESIS_RD 0x06 /* Redirect */ + u_char esis_ht_msb; /* holding time (seconds) high byte */ + u_char esis_ht_lsb; /* holding time (seconds) low byte */ + u_char esis_cksum_msb; /* checksum high byte */ + u_char esis_cksum_lsb; /* checksum low byte */ +} __attribute__((packed)); +/* + * Values for ESIS datagram options + */ +#define ESISOVAL_NETMASK 0xe1 /* address mask option, RD PDU only */ +#define ESISOVAL_SNPAMASK 0xe2 /* snpa mask option, RD PDU only */ +#define ESISOVAL_ESCT 0xc6 /* end system conf. timer, ISH PDU + * only */ + + +#define ESIS_CKSUM_OFF 0x07 +#define ESIS_CKSUM_REQUIRED(pdu)\ + ((pdu->esis_cksum_msb != 0) || (pdu->esis_cksum_lsb != 0)) + +#define ESIS_VERSION 1 + +struct esis_stat { + u_short es_nomem; /* insufficient memory to send hello */ + u_short es_badcsum; /* incorrect checksum */ + u_short es_badvers; /* incorrect version number */ + u_short es_badtype; /* unknown pdu type field */ + u_short es_toosmall; /* packet too small */ + u_short es_eshsent; /* ESH sent */ + u_short es_eshrcvd; /* ESH rcvd */ + u_short es_ishsent; /* ISH sent */ + u_short es_ishrcvd; /* ISH rcvd */ + u_short es_rdsent; /* RD sent */ + u_short es_rdrcvd; /* RD rcvd */ +}; + +#ifdef _KERNEL +struct esis_stat esis_stat; +struct socket; +struct mbuf; +struct snpa_hdr; +struct clnp_optidx; +struct iso_addr; +struct rtentry; +struct sockaddr_dl; + +void esis_init __P((void)); +int esis_usrreq __P((struct socket *, int, struct mbuf *, struct mbuf *, + struct mbuf *, struct proc *)); +void esis_input __P((struct mbuf *, ...)); +void esis_rdoutput __P((struct snpa_hdr *, struct mbuf *, struct clnp_optidx *, + struct iso_addr *, struct rtentry *)); +int esis_insert_addr __P((caddr_t *, int *, struct iso_addr *, struct mbuf *, + int)); +void esis_eshinput __P((struct mbuf *, struct snpa_hdr *)); +void esis_ishinput __P((struct mbuf *, struct snpa_hdr *)); +void esis_rdinput __P((struct mbuf *, struct snpa_hdr *)); +void esis_config __P((void *)); +void esis_shoutput __P((struct ifnet *, int, int, caddr_t, int, + struct iso_addr *)); +void isis_input __P((struct mbuf *, ...)); +int isis_output __P((struct mbuf *, ...)); +void *esis_ctlinput __P((int, struct sockaddr *, void *)); +#endif /* _KERNEL */ diff --git a/isisd/include-netbsd/iso.h b/isisd/include-netbsd/iso.h new file mode 100644 index 0000000..42b9bc8 --- /dev/null +++ b/isisd/include-netbsd/iso.h @@ -0,0 +1,214 @@ +/* $NetBSD: iso.h,v 1.13 2000/07/28 12:13:34 kleink Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)iso.h 8.1 (Berkeley) 6/10/93 + */ + +/*********************************************************** + Copyright IBM Corporation 1987 + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of IBM not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +IBM BE LIABLE FOR ANY SPECIAL, 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. + +******************************************************************/ + +/* + * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison + */ + +#ifndef _NETISO_ISO_H_ +#define _NETISO_ISO_H_ + +#if 0 +#include +#endif + +#if 0 +#ifndef sa_family_t +typedef __sa_family_t sa_family_t; +#define sa_family_t __sa_family_t +#endif +#endif +/* + * Return true if this is a multicast address + * This assumes that the bit transmission is lsb first. This + * assumption is valid for 802.3 but not 802.5. There is a + * kludge to get around this for 802.5 -- see if_lan.c + * where subnetwork header is setup. + */ +#define IS_MULTICAST(snpa)\ + ((snpa)[0] & 0x01) + +/* + * Protocols + */ +#define ISOPROTO_TCP 6 /* IETF experiment */ +#define ISOPROTO_UDP 17 /* IETF experiment */ +#define ISOPROTO_TP0 25 /* connection oriented transport protocol */ +#define ISOPROTO_TP1 26 /* not implemented */ +#define ISOPROTO_TP2 27 /* not implemented */ +#define ISOPROTO_TP3 28 /* not implemented */ +#define ISOPROTO_TP4 29 /* connection oriented transport protocol */ +#define ISOPROTO_TP ISOPROTO_TP4 /* tp-4 with negotiation */ +#define ISOPROTO_CLTP 30 /* connectionless transport (not yet impl.) */ +#define ISOPROTO_CLNP 31 /* connectionless internetworking protocol */ +#define ISOPROTO_X25 32 /* cons */ +#define ISOPROTO_INACT_NL 33 /* inactive network layer! */ +#define ISOPROTO_ESIS 34 /* ES-IS protocol */ +#define ISOPROTO_INTRAISIS 35 /* IS-IS protocol */ +#define ISOPROTO_IDRP 36 /* Interdomain Routing Protocol */ + +#define ISOPROTO_RAW 255 /* raw clnp */ +#define ISOPROTO_MAX 256 + +#define ISO_PORT_RESERVED 1024 +#define ISO_PORT_USERRESERVED 5000 +/* + * Port/socket numbers: standard network functions + * NOT PRESENTLY USED + */ +#define ISO_PORT_MAINT 501 +#define ISO_PORT_ECHO 507 +#define ISO_PORT_DISCARD 509 +#define ISO_PORT_SYSTAT 511 +#define ISO_PORT_NETSTAT 515 +/* + * Port/socket numbers: non-standard application functions + */ +#define ISO_PORT_LOGIN 513 +/* + * Port/socket numbers: public use + */ +#define ISO_PORT_PUBLIC 1024 /* high bit set --> public */ + +/* + * Network layer protocol identifiers + */ +#define ISO8473_CLNP 0x81 +#define ISO9542_ESIS 0x82 +#define ISO9542X25_ESIS 0x8a +#define ISO10589_ISIS 0x83 +#define ISO8878A_CONS 0x84 +#define ISO10747_IDRP 0x85 + + +#ifndef IN_CLASSA_NET +#include +#endif /* IN_CLASSA_NET */ + + + +/* + * The following looks like a sockaddr to facilitate using tree lookup + * routines + */ +struct iso_addr { + u_char isoa_len; /* length (in bytes) */ + char isoa_genaddr[20]; /* general opaque address */ +}; + +struct sockaddr_iso { + u_char siso_len; /* length */ + sa_family_t siso_family; /* family */ + u_char siso_plen; /* presentation selector length */ + u_char siso_slen; /* session selector length */ + u_char siso_tlen; /* transport selector length */ + struct iso_addr siso_addr; /* network address */ + u_char siso_pad[6]; /* space for gosip v2 sels */ + /* makes struct 32 bytes long */ +}; +#define siso_nlen siso_addr.isoa_len +#define siso_data siso_addr.isoa_genaddr + +#define TSEL(s) ((caddr_t)((s)->siso_data + (s)->siso_nlen)) + +#define SAME_ISOADDR(a, b) \ + (bcmp((a)->siso_data, (b)->siso_data, (unsigned)(a)->siso_nlen)==0) +#define SAME_ISOIFADDR(a, b) (bcmp((a)->siso_data, (b)->siso_data, \ + (unsigned)((b)->siso_nlen - (b)->siso_tlen)) == 0) +/* + * The following are specific values for siso->siso_data[0], + * otherwise known as the AFI: + */ +#define AFI_37 0x37 /* bcd of "37" */ +#define AFI_OSINET 0x47 /* bcd of "47" */ +#define AFI_RFC986 0x47 /* bcd of "47" */ +#define AFI_SNA 0x00 /* SubNetwork Address; invalid really... */ + +#ifdef _KERNEL + +extern struct domain isodomain; +extern struct protosw isosw[]; + +#define satosiso(sa) ((struct sockaddr_iso *)(sa)) +#define sisotosa(siso) ((struct sockaddr *)(siso)) + +#else +/* user utilities definitions from the iso library */ + +#ifndef HAVE_SYS_CDEFS_H +#define __P(x) x +#define __BEGIN_DECLS +#define __END_DECLS +#else +#include +#endif + +__BEGIN_DECLS +struct iso_addr *iso_addr __P((const char *)); +char *iso_ntoa __P((const struct iso_addr *)); + +/* THESE DON'T EXIST YET */ +struct hostent *iso_gethostbyname __P((const char *)); +struct hostent *iso_gethostbyaddr __P((const char *, int, int)); +__END_DECLS + +#endif /* _KERNEL */ + +#endif /* _NETISO_ISO_H_ */ diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c new file mode 100644 index 0000000..8afabed --- /dev/null +++ b/isisd/isis_adjacency.c @@ -0,0 +1,534 @@ +/* + * IS-IS Rout(e)ing protocol - isis_adjacency.c + * handling of IS-IS adjacencies + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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 "log.h" +#include "memory.h" +#include "hash.h" +#include "vty.h" +#include "linklist.h" +#include "thread.h" +#include "if.h" +#include "stream.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/isisd.h" +#include "isisd/isis_circuit.h" +#include "isisd/isis_adjacency.h" +#include "isisd/isis_misc.h" +#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 (const u_char *id) +{ + struct isis_adjacency *adj; + + adj = XCALLOC (MTYPE_ISIS_ADJACENCY, sizeof (struct isis_adjacency)); + memcpy (adj->sysid, id, ISIS_SYS_ID_LEN); + + return adj; +} + +struct isis_adjacency * +isis_new_adj (const u_char * id, const u_char * snpa, int level, + struct isis_circuit *circuit) +{ + struct isis_adjacency *adj; + int i; + + adj = adj_alloc (id); /* P2P kludge */ + + if (adj == NULL) + { + zlog_err ("Out of memory!"); + return NULL; + } + + if (snpa) { + memcpy (adj->snpa, snpa, ETH_ALEN); + } else { + memset (adj->snpa, ' ', ETH_ALEN); + } + + adj->circuit = circuit; + adj->level = level; + adj->flaps = 0; + adj->last_flap = time (NULL); + if (circuit->circ_type == CIRCUIT_T_BROADCAST) + { + listnode_add (circuit->u.bc.adjdb[level - 1], adj); + adj->dischanges[level - 1] = 0; + for (i = 0; i < DIS_RECORDS; i++) /* clear N DIS state change records */ + { + adj->dis_record[(i * ISIS_LEVELS) + level - 1].dis + = ISIS_UNKNOWN_DIS; + adj->dis_record[(i * ISIS_LEVELS) + level - 1].last_dis_change + = time (NULL); + } + } + + return adj; +} + +struct isis_adjacency * +isis_adj_lookup (const u_char * sysid, struct list *adjdb) +{ + struct isis_adjacency *adj; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO (adjdb, node, adj)) + if (memcmp (adj->sysid, sysid, ISIS_SYS_ID_LEN) == 0) + return adj; + + return NULL; +} + +struct isis_adjacency * +isis_adj_lookup_snpa (const u_char * ssnpa, struct list *adjdb) +{ + struct listnode *node; + struct isis_adjacency *adj; + + for (ALL_LIST_ELEMENTS_RO (adjdb, node, adj)) + if (memcmp (adj->snpa, ssnpa, ETH_ALEN) == 0) + return adj; + + return NULL; +} + +void +isis_delete_adj (void *arg) +{ + struct isis_adjacency *adj = arg; + + if (!adj) + return; + + 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 new_state, + const char *reason) +{ + int old_state; + int level; + struct isis_circuit *circuit; + + old_state = adj->adj_state; + adj->adj_state = new_state; + + circuit = adj->circuit; + + if (isis->debugs & DEBUG_ADJ_PACKETS) + { + zlog_debug ("ISIS-Adj (%s): Adjacency state change %d->%d: %s", + circuit->area->area_tag, + old_state, new_state, reason ? reason : "unspecified"); + } + + if (circuit->area->log_adj_changes) + { + const char *adj_name; + struct isis_dynhn *dyn; + + 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"); + } + + 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 (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; +} + + +void +isis_adj_print (struct isis_adjacency *adj) +{ + struct isis_dynhn *dyn; + struct listnode *node; + struct in_addr *ipv4_addr; +#ifdef HAVE_IPV6 + struct in6_addr *ipv6_addr; + u_char ip6[INET6_ADDRSTRLEN]; +#endif /* HAVE_IPV6 */ + + if (!adj) + return; + dyn = dynhn_find_by_id (adj->sysid); + if (dyn) + zlog_debug ("%s", dyn->name.name); + + zlog_debug ("SystemId %20s SNPA %s, level %d\nHolding Time %d", + 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 Address(es):"); + + for (ALL_LIST_ELEMENTS_RO (adj->ipv4_addrs, node, ipv4_addr)) + zlog_debug ("%s", inet_ntoa (*ipv4_addr)); + } + +#ifdef HAVE_IPV6 + if (adj->ipv6_addrs && listcount (adj->ipv6_addrs) > 0) + { + 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); + zlog_debug ("%s", ip6); + } + } +#endif /* HAVE_IPV6 */ + zlog_debug ("Speaks: %s", nlpid2string (&adj->nlpids)); + + return; +} + +int +isis_adj_expire (struct thread *thread) +{ + struct isis_adjacency *adj; + + /* + * Get the adjacency + */ + adj = THREAD_ARG (thread); + assert (adj); + adj->t_expire = NULL; + + /* trigger the adj expire event */ + isis_adj_state_change (adj, ISIS_ADJ_DOWN, "holding time expired"); + + return 0; +} + +/* + * show isis neighbor [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]; +#endif /* HAVE_IPV6 */ + struct in_addr *ip_addr; + time_t now; + struct isis_dynhn *dyn; + int level; + struct listnode *node; + + dyn = dynhn_find_by_id (adj->sysid); + if (dyn) + vty_out (vty, " %-20s", dyn->name.name); + else + vty_out (vty, " %-20s", sysid_print (adj->sysid)); + + if (detail == ISIS_UI_LEVEL_BRIEF) + { + if (adj->circuit) + vty_out (vty, "%-12s", adj->circuit->interface->name); + else + vty_out (vty, "NULL circuit!"); + vty_out (vty, "%-3u", adj->level); /* level */ + vty_out (vty, "%-13s", adj_state2string (adj->adj_state)); + now = time (NULL); + if (adj->last_upd) + 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)); + vty_out (vty, "%s", VTY_NEWLINE); + } + + if (detail == ISIS_UI_LEVEL_DETAIL) + { + level = adj->level; + vty_out (vty, "%s", VTY_NEWLINE); + if (adj->circuit) + vty_out (vty, " Interface: %s", adj->circuit->interface->name); + else + 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); + if (adj->last_upd) + vty_out (vty, ", Expires in %s", + time2string (adj->last_upd + adj->hold_time - now)); + else + vty_out (vty, ", Expires in %s", time2string (adj->hold_time)); + 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", 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", 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 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 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); + vty_out (vty, " %s%s", ip6, VTY_NEWLINE); + } + } +#endif /* HAVE_IPV6 */ + vty_out (vty, "%s", VTY_NEWLINE); + } + return; +} + +void +isis_adj_build_neigh_list (struct list *adjdb, struct list *list) +{ + struct isis_adjacency *adj; + struct listnode *node; + + if (!list) + { + zlog_warn ("isis_adj_build_neigh_list(): NULL list"); + return; + } + + for (ALL_LIST_ELEMENTS_RO (adjdb, node, adj)) + { + if (!adj) + { + zlog_warn ("isis_adj_build_neigh_list(): NULL adj"); + return; + } + + if ((adj->adj_state == ISIS_ADJ_UP || + adj->adj_state == ISIS_ADJ_INITIALIZING)) + listnode_add (list, adj->snpa); + } + return; +} + +void +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"); + return; + } + + for (ALL_LIST_ELEMENTS_RO (adjdb, node, adj)) + { + if (!adj) + { + zlog_warn ("isis_adj_build_up_list(): NULL adj"); + return; + } + + if (adj->adj_state == ISIS_ADJ_UP) + listnode_add (list, adj); + } + + return; +} diff --git a/isisd/isis_adjacency.h b/isisd/isis_adjacency.h new file mode 100644 index 0000000..99d0c49 --- /dev/null +++ b/isisd/isis_adjacency.h @@ -0,0 +1,118 @@ +/* + * IS-IS Rout(e)ing protocol - isis_adjacency.h + * IS-IS adjacency handling + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * + * 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. + */ + +#ifndef _ZEBRA_ISIS_ADJACENCY_H +#define _ZEBRA_ISIS_ADJACENCY_H + +enum isis_adj_usage +{ + ISIS_ADJ_NONE, + ISIS_ADJ_LEVEL1, + ISIS_ADJ_LEVEL2, + ISIS_ADJ_LEVEL1AND2 +}; + +enum isis_system_type +{ + ISIS_SYSTYPE_UNKNOWN, + ISIS_SYSTYPE_ES, + ISIS_SYSTYPE_IS, + ISIS_SYSTYPE_L1_IS, + ISIS_SYSTYPE_L2_IS +}; + +enum isis_adj_state +{ + ISIS_ADJ_UNKNOWN, + ISIS_ADJ_INITIALIZING, + ISIS_ADJ_UP, + ISIS_ADJ_DOWN +}; + +/* + * we use the following codes to give an indication _why_ + * a specific adjacency is up or down + */ +enum isis_adj_updown_reason +{ + ISIS_ADJ_REASON_SEENSELF, + ISIS_ADJ_REASON_AREA_MISMATCH, + ISIS_ADJ_REASON_HOLDTIMER_EXPIRED, + ISIS_ADJ_REASON_AUTH_FAILED, + ISIS_ADJ_REASON_CHECKSUM_FAILED +}; + +#define DIS_RECORDS 8 /* keep the last 8 DIS state changes on record */ + +struct isis_dis_record +{ + int dis; /* is our neighbor the DIS ? */ + time_t last_dis_change; /* timestamp for last dis change */ +}; + +struct isis_adjacency +{ + u_char snpa[ETH_ALEN]; /* NeighbourSNPAAddress */ + u_char sysid[ISIS_SYS_ID_LEN]; /* neighbourSystemIdentifier */ + u_char lanid[ISIS_SYS_ID_LEN + 1]; /* LAN id on bcast circuits */ + int dischanges[ISIS_LEVELS]; /* how many DIS changes ? */ + /* an array of N levels for M records */ + struct isis_dis_record dis_record[DIS_RECORDS * ISIS_LEVELS]; + enum isis_adj_state adj_state; /* adjacencyState */ + enum isis_adj_usage adj_usage; /* adjacencyUsage */ + 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 */ + int level; /* level (1 or 2) */ + enum isis_system_type sys_type; /* neighbourSystemType */ + u_int16_t hold_time; /* entryRemainingTime */ + u_int32_t last_upd; + u_int32_t last_flap; /* last time the adj flapped */ + int flaps; /* number of adjacency flaps */ + struct thread *t_expire; /* expire after hold_time */ + struct isis_circuit *circuit; /* back pointer */ +}; + +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 (const u_char * id, const u_char * snpa, int level, + struct isis_circuit *circuit); +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, 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); + +#endif /* ISIS_ADJACENCY_H */ diff --git a/isisd/isis_bpf.c b/isisd/isis_bpf.c new file mode 100644 index 0000000..889b4c3 --- /dev/null +++ b/isisd/isis_bpf.c @@ -0,0 +1,361 @@ +/* + * IS-IS Rout(e)ing protocol - isis_bpf.c + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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 +#if ISIS_METHOD == ISIS_METHOD_BPF +#include +#include +#include +#include +#include + +#include "log.h" +#include "network.h" +#include "stream.h" +#include "if.h" + +#include "isisd/dict.h" +#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/isisd.h" +#include "isisd/isis_constants.h" +#include "isisd/isis_circuit.h" +#include "isisd/isis_network.h" + +#include "privs.h" + +extern struct zebra_privs_t isisd_privs; + +struct bpf_insn llcfilter[] = { + BPF_STMT (BPF_LD + BPF_B + BPF_ABS, ETHER_HDR_LEN), /* check first byte */ + BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ISO_SAP, 0, 5), + BPF_STMT (BPF_LD + BPF_B + BPF_ABS, ETHER_HDR_LEN + 1), + BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ISO_SAP, 0, 3), /* check second byte */ + BPF_STMT (BPF_LD + BPF_B + BPF_ABS, ETHER_HDR_LEN + 2), + BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 0x03, 0, 1), /* check third byte */ + BPF_STMT (BPF_RET + BPF_K, (u_int) - 1), + BPF_STMT (BPF_RET + BPF_K, 0) +}; +u_int readblen = 0; +u_char *readbuff = NULL; + +/* + * Table 9 - Architectural constants for use with ISO 8802 subnetworks + * ISO 10589 - 8.4.8 + */ + +u_char ALL_L1_ISS[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x14 }; +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 sock_buff[8192]; + +static int +open_bpf_dev (struct isis_circuit *circuit) +{ + int i = 0, fd; + char bpfdev[128]; + struct ifreq ifr; + u_int blen, immediate, seesent; + struct timeval timeout; + struct bpf_program bpf_prog; + + do + { + (void) snprintf (bpfdev, sizeof (bpfdev), "/dev/bpf%d", i++); + fd = open (bpfdev, O_RDWR); + } + while (fd < 0 && errno == EBUSY); + + if (fd < 0) + { + zlog_warn ("open_bpf_dev(): failed to create bpf socket: %s", + safe_strerror (errno)); + return ISIS_WARNING; + } + + zlog_debug ("Opened BPF device %s", bpfdev); + + memcpy (ifr.ifr_name, circuit->interface->name, sizeof (ifr.ifr_name)); + if (ioctl (fd, BIOCSETIF, (caddr_t) & ifr) < 0) + { + zlog_warn ("open_bpf_dev(): failed to bind to interface: %s", + safe_strerror (errno)); + return ISIS_WARNING; + } + + if (ioctl (fd, BIOCGBLEN, (caddr_t) & blen) < 0) + { + zlog_warn ("failed to get BPF buffer len"); + blen = circuit->interface->mtu; + } + + readblen = blen; + + if (readbuff == NULL) + readbuff = malloc (blen); + + zlog_debug ("BPF buffer len = %u", blen); + + /* BPF(4): reads return immediately upon packet reception. + * Otherwise, a read will block until either the kernel + * buffer becomes full or a timeout occurs. + */ + immediate = 1; + if (ioctl (fd, BIOCIMMEDIATE, (caddr_t) & immediate) < 0) + { + zlog_warn ("failed to set BPF dev to immediate mode"); + } + +#ifdef BIOCSSEESENT + /* + * We want to see only incoming packets + */ + seesent = 0; + if (ioctl (fd, BIOCSSEESENT, (caddr_t) & seesent) < 0) + { + zlog_warn ("failed to set BPF dev to incoming only mode"); + } +#endif + + /* + * ...but all of them + */ + if (ioctl (fd, BIOCPROMISC) < 0) + { + zlog_warn ("failed to set BPF dev to promiscuous mode"); + } + + /* + * If the buffer length is smaller than our mtu, lets try to increase it + */ + if (blen < circuit->interface->mtu) + { + if (ioctl (fd, BIOCSBLEN, &circuit->interface->mtu) < 0) + { + zlog_warn ("failed to set BPF buffer len (%u to %u)", blen, + circuit->interface->mtu); + } + } + + /* + * Set a timeout parameter - hope this helps select() + */ + timeout.tv_sec = 600; + timeout.tv_usec = 0; + if (ioctl (fd, BIOCSRTIMEOUT, (caddr_t) & timeout) < 0) + { + zlog_warn ("failed to set BPF device timeout"); + } + + /* + * And set the filter + */ + memset (&bpf_prog, 0, sizeof (struct bpf_program)); + bpf_prog.bf_len = 8; + bpf_prog.bf_insns = &(llcfilter[0]); + if (ioctl (fd, BIOCSETF, (caddr_t) & bpf_prog) < 0) + { + zlog_warn ("open_bpf_dev(): failed to install filter: %s", + safe_strerror (errno)); + return ISIS_WARNING; + } + + assert (fd > 0); + + circuit->fd = fd; + + return ISIS_OK; +} + +/* + * Create the socket and set the tx/rx funcs + */ +int +isis_sock_init (struct isis_circuit *circuit) +{ + int retval = ISIS_OK; + + if (isisd_privs.change (ZPRIVS_RAISE)) + zlog_err ("%s: could not raise privs, %s", __func__, safe_strerror (errno)); + + retval = open_bpf_dev (circuit); + + if (retval != ISIS_OK) + { + zlog_warn ("%s: could not initialize the socket", __func__); + goto end; + } + + if (circuit->circ_type == CIRCUIT_T_BROADCAST) + { + circuit->tx = isis_send_pdu_bcast; + circuit->rx = isis_recv_pdu_bcast; + } + else if (circuit->circ_type == CIRCUIT_T_P2P) + { + circuit->tx = isis_send_pdu_p2p; + circuit->rx = isis_recv_pdu_p2p; + } + else + { + zlog_warn ("isis_sock_init(): unknown circuit type"); + retval = ISIS_WARNING; + goto end; + } + +end: + if (isisd_privs.change (ZPRIVS_LOWER)) + zlog_err ("%s: could not lower privs, %s", __func__, safe_strerror (errno)); + + return retval; +} + +int +isis_recv_pdu_bcast (struct isis_circuit *circuit, u_char * ssnpa) +{ + int bytesread = 0, bytestoread, offset, one = 1; + struct bpf_hdr *bpf_hdr; + + assert (circuit->fd > 0); + + if (ioctl (circuit->fd, FIONREAD, (caddr_t) & bytestoread) < 0) + { + zlog_warn ("ioctl() FIONREAD failed: %s", safe_strerror (errno)); + } + + if (bytestoread) + { + bytesread = read (circuit->fd, readbuff, readblen); + } + if (bytesread < 0) + { + zlog_warn ("isis_recv_pdu_bcast(): read() failed: %s", + safe_strerror (errno)); + return ISIS_WARNING; + } + + if (bytesread == 0) + return ISIS_WARNING; + + bpf_hdr = (struct bpf_hdr *) readbuff; + + assert (bpf_hdr->bh_caplen == bpf_hdr->bh_datalen); + + offset = bpf_hdr->bh_hdrlen + LLC_LEN + ETHER_HDR_LEN; + + /* then we lose the BPF, LLC and ethernet headers */ + stream_write (circuit->rcv_stream, readbuff + offset, + bpf_hdr->bh_caplen - LLC_LEN - ETHER_HDR_LEN); + stream_set_getp (circuit->rcv_stream, 0); + + memcpy (ssnpa, readbuff + bpf_hdr->bh_hdrlen + ETHER_ADDR_LEN, + ETHER_ADDR_LEN); + + if (ioctl (circuit->fd, BIOCFLUSH, &one) < 0) + zlog_warn ("Flushing failed: %s", safe_strerror (errno)); + + return ISIS_OK; +} + +int +isis_recv_pdu_p2p (struct isis_circuit *circuit, u_char * ssnpa) +{ + int bytesread; + + bytesread = stream_read (circuit->rcv_stream, circuit->fd, + circuit->interface->mtu); + + if (bytesread < 0) + { + zlog_warn ("isis_recv_pdu_p2p(): read () failed: %s", safe_strerror (errno)); + return ISIS_WARNING; + } + + return ISIS_OK; +} + +int +isis_send_pdu_bcast (struct isis_circuit *circuit, int level) +{ + struct ether_header *eth; + 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); + + /* + * First the eth header + */ + eth = (struct ether_header *) sock_buff; + if (level == 1) + memcpy (eth->ether_dhost, ALL_L1_ISS, ETHER_ADDR_LEN); + else + memcpy (eth->ether_dhost, ALL_L2_ISS, ETHER_ADDR_LEN); + memcpy (eth->ether_shost, circuit->u.bc.snpa, ETHER_ADDR_LEN); + eth->ether_type = htons (stream_get_endp (circuit->snd_stream) + LLC_LEN); + + /* + * Then the LLC + */ + sock_buff[ETHER_HDR_LEN] = ISO_SAP; + sock_buff[ETHER_HDR_LEN + 1] = ISO_SAP; + sock_buff[ETHER_HDR_LEN + 2] = 0x03; + + /* then we copy the data */ + memcpy (sock_buff + (LLC_LEN + ETHER_HDR_LEN), circuit->snd_stream->data, + stream_get_endp (circuit->snd_stream)); + + /* now we can send this */ + 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; +} + +int +isis_send_pdu_p2p (struct isis_circuit *circuit, int level) +{ + return ISIS_OK; +} + +#endif /* ISIS_METHOD == ISIS_METHOD_BPF */ diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c new file mode 100644 index 0000000..1d1a59e --- /dev/null +++ b/isisd/isis_circuit.c @@ -0,0 +1,1423 @@ +/* + * IS-IS Rout(e)ing protocol - isis_circuit.h + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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 +#ifdef GNU_LINUX +#include +#else +#include +#endif + +#ifndef ETHER_ADDR_LEN +#define ETHER_ADDR_LEN ETHERADDRL +#endif + +#include "log.h" +#include "memory.h" +#include "if.h" +#include "linklist.h" +#include "command.h" +#include "thread.h" +#include "vty.h" +#include "hash.h" +#include "prefix.h" +#include "stream.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" +#include "isisd/isis_pdu.h" +#include "isisd/isis_network.h" +#include "isisd/isis_misc.h" +#include "isisd/isis_constants.h" +#include "isisd/isis_adjacency.h" +#include "isisd/isis_dr.h" +#include "isisd/isisd.h" +#include "isisd/isis_csm.h" +#include "isisd/isis_events.h" +#include "isisd/isis_te.h" + +/* + * Prototypes. + */ +int isis_interface_config_write(struct vty *); +int isis_if_new_hook(struct interface *); +int isis_if_delete_hook(struct interface *); + +struct isis_circuit * +isis_circuit_new () +{ + struct isis_circuit *circuit; + int i; + + circuit = XCALLOC (MTYPE_ISIS_CIRCUIT, sizeof (struct isis_circuit)); + 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) +{ + assert (area); + circuit->area = area; + + /* + * 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. + */ + if (area->is_type != IS_LEVEL_1_AND_2) + circuit->is_type = area->is_type; + + /* + * Add the circuit into area + */ + listnode_add (area->circuit_list, circuit); + + circuit->idx = flags_get_index (&area->flags); + + return; +} + +void +isis_circuit_deconfigure (struct isis_circuit *circuit, struct isis_area *area) +{ + /* 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; +} + +struct isis_circuit * +circuit_lookup_by_ifp (struct interface *ifp, struct list *list) +{ + struct isis_circuit *circuit = NULL; + struct listnode *node; + + if (!list) + return NULL; + + for (ALL_LIST_ELEMENTS_RO (list, node, circuit)) + if (circuit->interface == ifp) + { + assert (ifp->info == circuit); + return circuit; + } + + return NULL; +} + +struct isis_circuit * +circuit_scan_by_ifp (struct interface *ifp) +{ + struct isis_area *area; + struct listnode *node; + struct isis_circuit *circuit; + + if (ifp->info) + return (struct isis_circuit *)ifp->info; + + if (isis->area_list) + { + 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_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 */ + + 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, circuit->is_type, 0); + +#ifdef EXTREME_DEBUG + prefix2str (connected->address, buf, BUFSIZ); + zlog_debug ("Added IP address %s to circuit %d", buf, + circuit->circuit_id); +#endif /* EXTREME_DEBUG */ + } +#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; + + if (IN6_IS_ADDR_LINKLOCAL (&ipv6->prefix)) + listnode_add (circuit->ipv6_link, ipv6); + else + listnode_add (circuit->ipv6_non_link, ipv6); + if (circuit->area) + lsp_regenerate_schedule (circuit->area, circuit->is_type, 0); + +#ifdef EXTREME_DEBUG + prefix2str (connected->address, buf, BUFSIZ); + zlog_debug ("Added IPv6 address %s to circuit %d", buf, + circuit->circuit_id); +#endif /* EXTREME_DEBUG */ + } +#endif /* HAVE_IPV6 */ + return; +} + +void +isis_circuit_del_addr (struct isis_circuit *circuit, + struct connected *connected) +{ + struct prefix_ipv4 *ipv4, *ip = NULL; + struct listnode *node; + u_char buf[BUFSIZ]; +#ifdef HAVE_IPV6 + struct prefix_ipv6 *ipv6, *ip6 = NULL; + int found = 0; +#endif /* HAVE_IPV6 */ + + memset (&buf, 0, BUFSIZ); + if (connected->address->family == AF_INET) + { + ipv4 = prefix_ipv4_new (); + ipv4->prefixlen = connected->address->prefixlen; + 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)) + break; + + if (ip) + { + listnode_delete (circuit->ip_addrs, ip); + 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 ("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) + { + ipv6 = prefix_ipv6_new (); + ipv6->prefixlen = connected->address->prefixlen; + ipv6->prefix = connected->address->u.prefix6; + + if (IN6_IS_ADDR_LINKLOCAL (&ipv6->prefix)) + { + for (ALL_LIST_ELEMENTS_RO (circuit->ipv6_link, node, ip6)) + { + if (prefix_same ((struct prefix *) ip6, (struct prefix *) ipv6)) + break; + } + if (ip6) + { + listnode_delete (circuit->ipv6_link, ip6); + found = 1; + } + } + else + { + for (ALL_LIST_ELEMENTS_RO (circuit->ipv6_non_link, node, ip6)) + { + if (prefix_same ((struct prefix *) ip6, (struct prefix *) ipv6)) + break; + } + if (ip6) + { + listnode_delete (circuit->ipv6_non_link, ip6); + found = 1; + } + } + + 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 ("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, 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->circuit_id = isis_circuit_id_gen (ifp); + + isis_circuit_if_bind (circuit, ifp); + /* isis_circuit_update_addrs (circuit, ifp); */ + + if (if_is_broadcast (ifp)) + { + if (circuit->circ_type_config == CIRCUIT_T_P2P) + circuit->circ_type = CIRCUIT_T_P2P; + else + 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"); + 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); + + return; +} + +void +isis_circuit_if_del (struct isis_circuit *circuit, struct interface *ifp) +{ + struct listnode *node, *nnode; + struct connected *conn; + + assert (circuit->interface == ifp); + + /* destroy addresses */ + for (ALL_LIST_ELEMENTS (ifp->connected, node, nnode, conn)) + isis_circuit_del_addr (circuit, conn); + + if (circuit->ip_addrs) + { + assert (listcount(circuit->ip_addrs) == 0); + list_delete (circuit->ip_addrs); + circuit->ip_addrs = NULL; + } + +#ifdef HAVE_IPV6 + if (circuit->ipv6_link) + { + assert (listcount(circuit->ipv6_link) == 0); + list_delete (circuit->ipv6_link); + circuit->ipv6_link = NULL; + } + + if (circuit->ipv6_non_link) + { + 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_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) +{ + assert (circuit != NULL); + assert (ifp != NULL); + assert (circuit->interface == ifp); + assert (ifp->info == circuit); + circuit->interface = NULL; + ifp->info = NULL; +} + +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) + { + /* + * 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 + */ + + /* initilizing the hello sending threads + * for a broadcast IF + */ + + /* 8.4.1 a) commence sending of IIH PDUs */ + + 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->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->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->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)); + + /* unified init for circuits; ignore warnings below this level */ + retv = isis_sock_init (circuit); + if (retv != ISIS_OK) + { + isis_circuit_down (circuit); + return retv; + } + + /* 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); +#else + THREAD_TIMER_ON (master, circuit->t_read, isis_receive, circuit, + circuit->fd); +#endif + + circuit->lsp_queue = list_new (); + circuit->lsp_queue_last_cleared = time (NULL); + + return ISIS_OK; +} + +void +isis_circuit_down (struct isis_circuit *circuit) +{ + 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); + } + + /* 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 */ + 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; +} + +void +circuit_update_nlpids (struct isis_circuit *circuit) +{ + circuit->nlpids.count = 0; + + if (circuit->ip_router) + { + circuit->nlpids.nlpids[0] = NLPID_IP; + circuit->nlpids.count++; + } +#ifdef HAVE_IPV6 + if (circuit->ipv6_router) + { + circuit->nlpids.nlpids[circuit->nlpids.count] = NLPID_IPV6; + circuit->nlpids.count++; + } +#endif /* HAVE_IPV6 */ + return; +} + +void +isis_circuit_print_vty (struct isis_circuit *circuit, struct vty *vty, + char detail) +{ + if (detail == ISIS_UI_LEVEL_BRIEF) + { + 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); + } + + if (detail == ISIS_UI_LEVEL_DETAIL) + { + struct listnode *node; + struct prefix *ip_addr; + u_char buf[BUFSIZ]; + + 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); + } + return; +} + +int +isis_interface_config_write (struct vty *vty) +{ + int write = 0; + struct listnode *node, *node2; + struct interface *ifp; + struct isis_area *area; + struct isis_circuit *circuit; + 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)) + { + 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 */ + + /* 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); + } + + return write; +} + +struct isis_circuit * +isis_circuit_create (struct isis_area *area, struct interface *ifp) +{ + 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; +} + +void +isis_circuit_af_set (struct isis_circuit *circuit, bool ip_router, bool ipv6_router) +{ + struct isis_area *area = circuit->area; + bool change = circuit->ip_router != ip_router || circuit->ipv6_router != ipv6_router; + bool was_enabled = !!circuit->area; + + 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; + + if (!change) + return; + + circuit_update_nlpids (circuit); + + 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); +} + +int +isis_circuit_passive_set (struct isis_circuit *circuit, bool passive) +{ + if (circuit->is_passive == passive) + return 0; + + if (if_is_loopback (circuit->interface) && !passive) + return -1; + + if (circuit->state != C_STATE_UP) + { + circuit->is_passive = passive; + } + else + { + 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); + } + + return 0; +} + +int +isis_circuit_metric_set (struct isis_circuit *circuit, int level, int metric) +{ + 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; +} + +int +isis_circuit_passwd_unset (struct isis_circuit *circuit) +{ + memset(&circuit->passwd, 0, sizeof(circuit->passwd)); + return 0; +} + +static int +isis_circuit_passwd_set (struct isis_circuit *circuit, u_char passwd_type, const char *passwd) +{ + int len; + + if (!passwd) + return -1; + + len = strlen(passwd); + if (len > 254) + return -1; + + circuit->passwd.len = len; + strncpy((char *)circuit->passwd.passwd, passwd, 255); + circuit->passwd.type = passwd_type; + return 0; +} + +int +isis_circuit_passwd_cleartext_set (struct isis_circuit *circuit, const char *passwd) +{ + return isis_circuit_passwd_set (circuit, ISIS_PASSWD_TYPE_CLEARTXT, passwd); +} + +int +isis_circuit_passwd_hmac_md5_set (struct isis_circuit *circuit, const char *passwd) +{ + return isis_circuit_passwd_set (circuit, ISIS_PASSWD_TYPE_HMAC_MD5, passwd); +} +struct cmd_node interface_node = { + INTERFACE_NODE, + "%s(config-if)# ", + 1, +}; + +int +isis_circuit_circ_type_set(struct isis_circuit *circuit, int circ_type) +{ + /* 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; + } + + if (circuit->circ_type == circ_type) + return 0; + + if (circuit->state != C_STATE_UP) + { + circuit->circ_type = circ_type; + circuit->circ_type_config = circ_type; + } + else + { + struct isis_area *area = circuit->area; + if (circ_type == CIRCUIT_T_BROADCAST + && !if_is_broadcast(circuit->interface)) + return -1; + + 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); + } + return 0; +} + +int +isis_if_new_hook (struct interface *ifp) +{ + return 0; +} + +int +isis_if_delete_hook (struct interface *ifp) +{ + 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; +} + +void +isis_circuit_init () +{ + /* Initialize Zebra interface data structure */ + 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); + + isis_vty_init (); +} diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h new file mode 100644 index 0000000..9ada1e2 --- /dev/null +++ b/isisd/isis_circuit.h @@ -0,0 +1,187 @@ +/* + * IS-IS Rout(e)ing protocol - isis_circuit.h + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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. + */ + +#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 +{ + struct password *next; + int len; + u_char *pass; +}; + +struct metric +{ + u_char metric_default; + u_char metric_error; + u_char metric_expense; + u_char metric_delay; +}; + +struct isis_bcast_info +{ + u_char snpa[ETH_ALEN]; /* SNPA of this circuit */ + char run_dr_elect[2]; /* Should we run dr election ? */ + struct thread *t_run_dr[2]; /* DR election thread */ + struct thread *t_send_lan_hello[2]; /* send LAN IIHs in this thread */ + struct list *adjdb[2]; /* adjacency dbs */ + struct list *lan_neighs[2]; /* list of lx neigh snpa */ + char is_dr[2]; /* Are we level x DR ? */ + 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 */ +}; + +struct isis_p2p_info +{ + struct isis_adjacency *neighbor; + struct thread *t_send_p2p_hello; /* send P2P IIHs in this thread */ +}; + +struct isis_circuit +{ + int state; + 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 */ + int fd; /* IS-IS l1/2 socket */ + int sap_length; /* SAP length for DLPI */ + struct nlpids nlpids; + /* + * Threads + */ + struct thread *t_read; + 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_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 */ + 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 */ + u_int8_t metric[2]; + u_int32_t te_metric[2]; + 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 */ + u_int16_t upadjcount[2]; +#define ISIS_CIRCUIT_FLAPPED_AFTER_SPF 0x01 + u_char flags; + /* + * Counters as in 10589--11.2.5.9 + */ + u_int32_t adj_state_changes; /* changesInAdjacencyState */ + u_int32_t init_failures; /* intialisationFailures */ + u_int32_t ctrl_pdus_rxed; /* controlPDUsReceived */ + u_int32_t ctrl_pdus_txed; /* controlPDUsSent */ + u_int32_t desig_changes[2]; /* lanLxDesignatedIntermediateSystemChanges */ + u_int32_t rej_adjacencies; /* rejectedAdjacencies */ +}; + +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_configure (struct isis_circuit *circuit, + struct isis_area *area); +void isis_circuit_deconfigure (struct isis_circuit *circuit, + struct isis_area *area); +void isis_circuit_if_add (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 new file mode 100644 index 0000000..d158961 --- /dev/null +++ b/isisd/isis_common.h @@ -0,0 +1,71 @@ +/* + * IS-IS Rout(e)ing protocol - isis_common.h + * some common data structures + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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. + */ + +#ifndef ISIS_COMMON_H +#define ISIS_COMMON_H + +/* + * Area Address + */ +struct area_addr +{ + u_char addr_len; + u_char area_addr[20]; +}; + +struct isis_passwd +{ + u_char len; +#define ISIS_PASSWD_TYPE_UNUSED 0 +#define ISIS_PASSWD_TYPE_CLEARTXT 1 +#define ISIS_PASSWD_TYPE_HMAC_MD5 54 +#define ISIS_PASSWD_TYPE_PRIVATE 255 + u_char type; + /* Authenticate SNPs? */ +#define SNP_AUTH_SEND 0x01 +#define SNP_AUTH_RECV 0x02 + u_char snp_auth; + u_char passwd[255]; +}; + +/* + * (Dynamic) Hostname + * one struct for cache list + * one struct for LSP TLV + */ +struct hostname +{ + u_char namelen; + u_char name[255]; +}; + +/* + * Supported Protocol IDs + */ +struct nlpids +{ + u_char count; + u_char nlpids[4]; /* FIXME: enough ? */ +}; + +#endif diff --git a/isisd/isis_constants.h b/isisd/isis_constants.h new file mode 100644 index 0000000..8b21894 --- /dev/null +++ b/isisd/isis_constants.h @@ -0,0 +1,171 @@ +/* + * IS-IS Rout(e)ing protocol - isis_constants.h + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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. + */ + +#ifndef ISIS_CONSTANTS_H +#define ISIS_CONSTANTS_H + +/* + * Architectural constant values from p. 35 of ISO/IEC 10589 + */ + +#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 + +/* + * implementation specific jitter values + */ + +#define IIH_JITTER 10 /* % */ +#define MAX_AGE_JITTER 5 /* % */ +#define MAX_LSP_GEN_JITTER 5 /* % */ +#define CSNP_JITTER 10 /* % */ +#define PSNP_JITTER 10 /* % */ + +#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 + * RFC 4444 + */ +#define MAX_AGE 1200 +#define ZERO_AGE_LIFETIME 60 +#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 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 MIN_PRIORITY 0 +#define MAX_PRIORITY 127 +#define DEFAULT_PRIORITY 64 + +/* 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 + */ +#define NLPID_IP 204 +#define NLPID_IPV6 142 +#define NLPID_SNAP 128 +#define NLPID_CLNP 129 +#define NLPID_ESIS 130 + +/* + * Return values for functions + */ +#define ISIS_OK 0 +#define ISIS_WARNING 1 +#define ISIS_ERROR 2 +#define ISIS_CRITICAL 3 + +/* + * IS-IS Circuit Types + */ + +#define IS_LEVEL_1 1 +#define IS_LEVEL_2 2 +#define IS_LEVEL_1_AND_2 3 + +#define SNPA_ADDRSTRLEN 18 +#define ISIS_SYS_ID_LEN 6 +#define ISIS_NSEL_LEN 1 +#define SYSID_STRLEN 24 + +/* + * LSP bit masks + */ +#define LSPBIT_P 0x80 +#define LSPBIT_ATT 0x78 +#define LSPBIT_OL 0x04 +#define LSPBIT_IST 0x03 + +/* + * LSP bit masking macros + * taken from tcpdumps + * print-isoclns.c + */ + +#define ISIS_MASK_LSP_OL_BIT(x) ((x)&0x4) +#define ISIS_MASK_LSP_IS_L1_BIT(x) ((x)&0x1) +#define ISIS_MASK_LSP_IS_L2_BIT(x) ((x)&0x2) +#define ISIS_MASK_LSP_PARTITION_BIT(x) ((x)&0x80) +#define ISIS_MASK_LSP_ATT_BITS(x) ((x)&0x78) +#define ISIS_MASK_LSP_ATT_ERROR_BIT(x) ((x)&0x40) +#define ISIS_MASK_LSP_ATT_EXPENSE_BIT(x) ((x)&0x20) +#define ISIS_MASK_LSP_ATT_DELAY_BIT(x) ((x)&0x10) +#define ISIS_MASK_LSP_ATT_DEFAULT_BIT(x) ((x)&0x8) + +#define LLC_LEN 3 + +/* we need to be aware of the fact we are using ISO sized + * packets, using isomtu = mtu - LLC_LEN + */ +#define ISO_MTU(C) \ + ((if_is_broadcast ((C)->interface)) ? \ + (C->interface->mtu - LLC_LEN) : (C->interface->mtu)) + +#ifndef ETH_ALEN +#define ETH_ALEN 6 +#endif + +#endif /* ISIS_CONSTANTS_H */ diff --git a/isisd/isis_csm.c b/isisd/isis_csm.c new file mode 100644 index 0000000..0f642a7 --- /dev/null +++ b/isisd/isis_csm.c @@ -0,0 +1,220 @@ +/* + * IS-IS Rout(e)ing protocol - isis_csm.c + * IS-IS circuit state machine + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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 "log.h" +#include "memory.h" +#include "if.h" +#include "linklist.h" +#include "command.h" +#include "thread.h" +#include "hash.h" +#include "prefix.h" +#include "stream.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" +#include "isisd/isis_pdu.h" +#include "isisd/isis_network.h" +#include "isisd/isis_misc.h" +#include "isisd/isis_constants.h" +#include "isisd/isis_adjacency.h" +#include "isisd/isis_dr.h" +#include "isisd/isisd.h" +#include "isisd/isis_csm.h" +#include "isisd/isis_events.h" + +extern struct isis *isis; + +static const char *csm_statestr[] = { + "C_STATE_NA", + "C_STATE_INIT", + "C_STATE_CONF", + "C_STATE_UP" +}; + +#define STATE2STR(S) csm_statestr[S] + +static const char *csm_eventstr[] = { + "NO_STATE", + "ISIS_ENABLE", + "IF_UP_FROM_Z", + "ISIS_DISABLE", + "IF_DOWN_FROM_Z", +}; + +#define EVENT2STR(E) csm_eventstr[E] + +struct isis_circuit * +isis_csm_state_change (int event, struct isis_circuit *circuit, void *arg) +{ + int old_state; + + old_state = circuit ? circuit->state : C_STATE_NA; + if (isis->debugs & DEBUG_EVENTS) + zlog_debug ("CSM_EVENT: %s", EVENT2STR (event)); + + switch (old_state) + { + case C_STATE_NA: + if (circuit) + zlog_warn ("Non-null circuit while state C_STATE_NA"); + assert (circuit == NULL); + switch (event) + { + case ISIS_ENABLE: + circuit = isis_circuit_new (); + isis_circuit_configure (circuit, (struct isis_area *) arg); + circuit->state = C_STATE_CONF; + break; + case IF_UP_FROM_Z: + circuit = isis_circuit_new (); + isis_circuit_if_add (circuit, (struct interface *) arg); + listnode_add (isis->init_circ_list, circuit); + circuit->state = C_STATE_INIT; + 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); + if (isis_circuit_up (circuit) != ISIS_OK) + { + isis_circuit_deconfigure (circuit, (struct isis_area *) arg); + break; + } + circuit->state = C_STATE_UP; + 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, (struct interface *) arg); + listnode_delete (isis->init_circ_list, circuit); + isis_circuit_del (circuit); + circuit = NULL; + break; + } + break; + case C_STATE_CONF: + assert (circuit); + switch (event) + { + case ISIS_ENABLE: + zlog_warn ("circuit already enabled"); + break; + case IF_UP_FROM_Z: + 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; + isis_event_circuit_state_change (circuit, circuit->area, 1); + break; + case ISIS_DISABLE: + isis_circuit_deconfigure (circuit, (struct isis_area *) arg); + isis_circuit_del (circuit); + circuit = NULL; + break; + case IF_DOWN_FROM_Z: + zlog_warn ("circuit already disconnected"); + break; + } + break; + case C_STATE_UP: + assert (circuit); + switch (event) + { + case ISIS_ENABLE: + zlog_warn ("circuit already configured"); + break; + case IF_UP_FROM_Z: + zlog_warn ("circuit already connected"); + break; + case ISIS_DISABLE: + isis_circuit_down (circuit); + isis_circuit_deconfigure (circuit, (struct isis_area *) arg); + circuit->state = C_STATE_INIT; + 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, circuit->area, 0); + break; + } + break; + + default: + zlog_warn ("Invalid circuit state %d", old_state); + } + + if (isis->debugs & DEBUG_EVENTS) + zlog_debug ("CSM_STATE_CHANGE: %s -> %s ", STATE2STR (old_state), + circuit ? STATE2STR (circuit->state) : STATE2STR (C_STATE_NA)); + + return circuit; +} diff --git a/isisd/isis_csm.h b/isisd/isis_csm.h new file mode 100644 index 0000000..d6b13ac --- /dev/null +++ b/isisd/isis_csm.h @@ -0,0 +1,47 @@ +/* + * IS-IS Rout(e)ing protocol - isis_csm.h + * IS-IS circuit state machine + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * + * 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. + */ +#ifndef _ZEBRA_ISIS_CSM_H +#define _ZEBRA_ISIS_CSM_H + +/* + * Circuit states + */ +#define C_STATE_NA 0 +#define C_STATE_INIT 1 /* Connected to interface */ +#define C_STATE_CONF 2 /* Configured for ISIS */ +#define C_STATE_UP 3 /* CONN | CONF */ + +/* + * Circuit events + */ +#define ISIS_ENABLE 1 +#define IF_UP_FROM_Z 2 +#define ISIS_DISABLE 3 +#define IF_DOWN_FROM_Z 4 + +struct isis_circuit *isis_csm_state_change (int event, + struct isis_circuit *circuit, + void *arg); + +#endif /* _ZEBRA_ISIS_CSM_H */ diff --git a/isisd/isis_dlpi.c b/isisd/isis_dlpi.c new file mode 100644 index 0000000..7c7e090 --- /dev/null +++ b/isisd/isis_dlpi.c @@ -0,0 +1,654 @@ +/* + * IS-IS Rout(e)ing protocol - isis_dlpi.c + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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 +#if ISIS_METHOD == ISIS_METHOD_DLPI +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "network.h" +#include "stream.h" +#include "if.h" + +#include "isisd/dict.h" +#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/isisd.h" +#include "isisd/isis_network.h" + +#include "privs.h" + +extern struct zebra_privs_t isisd_privs; + +static t_uscalar_t dlpi_ctl[1024]; /* DLPI control messages */ + +/* + * Table 9 - Architectural constants for use with ISO 8802 subnetworks + * ISO 10589 - 8.4.8 + */ + +u_char ALL_L1_ISS[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x14 }; +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 u_char sock_buff[8192]; + +static u_short pf_filter[] = +{ + ENF_PUSHWORD + 0, /* Get the SSAP/DSAP values */ + ENF_PUSHLIT | ENF_CAND, /* Check them */ + ISO_SAP | (ISO_SAP << 8), + ENF_PUSHWORD + 1, /* Get the control value */ + ENF_PUSHLIT | ENF_AND, /* Isolate it */ +#ifdef _BIG_ENDIAN + 0xFF00, +#else + 0x00FF, +#endif + ENF_PUSHLIT | ENF_CAND, /* Test for expected value */ +#ifdef _BIG_ENDIAN + 0x0300 +#else + 0x0003 +#endif +}; + +/* + * We would like to use something like libdlpi here, but that's not present on + * all versions of Solaris or on any non-Solaris system, so it's nowhere near + * as portable as we'd like. Thus, we use the standards-conformant DLPI + * interfaces plus the (optional; not needed) Solaris packet filter module. + */ + +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) + { + memset (&ctlbuf, 0, sizeof (ctlbuf)); + ctlbuf.len = cbuflen; + ctlbuf.buf = (void *)cbuf; + ctlptr = &ctlbuf; + } + + if (dbuf != NULL) + { + memset (&databuf, 0, sizeof (databuf)); + databuf.len = dbuflen; + databuf.buf = (void *)dbuf; + dataptr = &databuf; + } + + /* We assume this doesn't happen often and isn't operationally significant */ + 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 +dlpirctl (int fd) +{ + struct pollfd fds[1]; + struct strbuf ctlbuf, databuf; + int flags, retv; + + do + { + /* Poll is used here in case the device doesn't speak DLPI correctly */ + memset (fds, 0, sizeof (fds)); + fds[0].fd = fd; + fds[0].events = POLLIN | POLLPRI; + if (poll (fds, 1, 1000) <= 0) + return -1; + + memset (&ctlbuf, 0, sizeof (ctlbuf)); + memset (&databuf, 0, sizeof (databuf)); + ctlbuf.maxlen = sizeof (dlpi_ctl); + ctlbuf.buf = (void *)dlpi_ctl; + databuf.maxlen = sizeof (sock_buff); + databuf.buf = (void *)sock_buff; + flags = 0; + retv = getmsg (fd, &ctlbuf, &databuf, &flags); + + if (retv < 0) + return -1; + } + while (ctlbuf.len == 0); + + if (!(retv & MORECTL)) + { + while (retv & MOREDATA) + { + flags = 0; + retv = getmsg (fd, NULL, &databuf, &flags); + } + return ctlbuf.len; + } + + while (retv & MORECTL) + { + flags = 0; + retv = getmsg (fd, &ctlbuf, &databuf, &flags); + } + return -1; +} + +static int +dlpiok (int fd, t_uscalar_t oprim) +{ + int retv; + dl_ok_ack_t *doa = (dl_ok_ack_t *)dlpi_ctl; + + retv = dlpirctl (fd); + if (retv < (ssize_t)DL_OK_ACK_SIZE || doa->dl_primitive != DL_OK_ACK || + doa->dl_correct_primitive != oprim) + { + return -1; + } + else + { + return 0; + } +} + +static int +dlpiinfo (int fd) +{ + dl_info_req_t dir; + ssize_t retv; + + memset (&dir, 0, sizeof (dir)); + dir.dl_primitive = DL_INFO_REQ; + /* Info_req uses M_PCPROTO. */ + dlpisend (fd, &dir, sizeof (dir), NULL, 0, RS_HIPRI); + retv = dlpirctl (fd); + if (retv < (ssize_t)DL_INFO_ACK_SIZE || dlpi_ctl[0] != DL_INFO_ACK) + return -1; + else + return retv; +} + +static int +dlpiopen (const char *devpath, ssize_t *acklen) +{ + int fd, flags; + + fd = open (devpath, O_RDWR | O_NONBLOCK | O_NOCTTY); + if (fd == -1) + return -1; + + /* All that we want is for the open itself to be non-blocking, not I/O. */ + flags = fcntl (fd, F_GETFL, 0); + if (flags != -1) + fcntl (fd, F_SETFL, flags & ~O_NONBLOCK); + + /* After opening, ask for information */ + if ((*acklen = dlpiinfo (fd)) == -1) + { + close (fd); + return -1; + } + + return fd; +} + +static int +dlpiattach (int fd, int unit) +{ + dl_attach_req_t dar; + + memset (&dar, 0, sizeof (dar)); + dar.dl_primitive = DL_ATTACH_REQ; + dar.dl_ppa = unit; + dlpisend (fd, &dar, sizeof (dar), NULL, 0, 0); + return dlpiok (fd, dar.dl_primitive); +} + +static int +dlpibind (int fd) +{ + dl_bind_req_t dbr; + int retv; + dl_bind_ack_t *dba = (dl_bind_ack_t *)dlpi_ctl; + + memset (&dbr, 0, sizeof (dbr)); + dbr.dl_primitive = DL_BIND_REQ; + dbr.dl_service_mode = DL_CLDLS; + dlpisend (fd, &dbr, sizeof (dbr), NULL, 0, 0); + + retv = dlpirctl (fd); + if (retv < (ssize_t)DL_BIND_ACK_SIZE || dba->dl_primitive != DL_BIND_ACK) + return -1; + else + return 0; +} + +static int +dlpimcast (int fd, const u_char *mcaddr) +{ + struct { + dl_enabmulti_req_t der; + u_char addr[ETHERADDRL]; + } dler; + + memset (&dler, 0, sizeof (dler)); + dler.der.dl_primitive = DL_ENABMULTI_REQ; + dler.der.dl_addr_length = sizeof (dler.addr); + dler.der.dl_addr_offset = dler.addr - (u_char *)&dler; + memcpy (dler.addr, mcaddr, sizeof (dler.addr)); + dlpisend (fd, &dler, sizeof (dler), NULL, 0, 0); + return dlpiok (fd, dler.der.dl_primitive); +} + +static int +dlpiaddr (int fd, u_char *addr) +{ + dl_phys_addr_req_t dpar; + dl_phys_addr_ack_t *dpaa = (dl_phys_addr_ack_t *)dlpi_ctl; + int retv; + + memset (&dpar, 0, sizeof (dpar)); + dpar.dl_primitive = DL_PHYS_ADDR_REQ; + dpar.dl_addr_type = DL_CURR_PHYS_ADDR; + dlpisend (fd, &dpar, sizeof (dpar), NULL, 0, 0); + + retv = dlpirctl (fd); + 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 > (size_t)retv) + return -1; + + bcopy((char *)dpaa + dpaa->dl_addr_offset, addr, ETHERADDRL); + return 0; +} + +static int +open_dlpi_dev (struct isis_circuit *circuit) +{ + int fd = -1, unit, retval; + char devpath[MAXPATHLEN]; + dl_info_ack_t *dia = (dl_info_ack_t *)dlpi_ctl; + ssize_t acklen; + + /* Only broadcast-type are supported at the moment */ + if (circuit->circ_type != CIRCUIT_T_BROADCAST) + { + zlog_warn ("%s: non-broadcast interface %s", __func__, + circuit->interface->name); + return ISIS_WARNING; + } + + /* Try the vanity node first, if permitted */ + if (getenv("DLPI_DEVONLY") == NULL) + { + (void) snprintf (devpath, sizeof(devpath), "/dev/net/%s", + circuit->interface->name); + fd = dlpiopen (devpath, &acklen); + } + + /* Now try as an ordinary Style 1 node */ + if (fd == -1) + { + (void) snprintf (devpath, sizeof (devpath), "/dev/%s", + circuit->interface->name); + unit = -1; + fd = dlpiopen (devpath, &acklen); + } + + /* If that fails, try again as Style 2 */ + if (fd == -1) + { + char *cp; + + cp = devpath + strlen (devpath); + while (--cp >= devpath && isdigit(*cp)) + ; + unit = strtol(cp, NULL, 0); + *cp = '\0'; + fd = dlpiopen (devpath, &acklen); + + /* If that too fails, then the device really doesn't exist */ + if (fd == -1) + { + zlog_warn ("%s: unknown interface %s", __func__, + circuit->interface->name); + return ISIS_WARNING; + } + + /* Double check the DLPI style */ + if (dia->dl_provider_style != DL_STYLE2) + { + zlog_warn ("open_dlpi_dev(): interface %s: %s is not style 2", + circuit->interface->name, devpath); + close (fd); + return ISIS_WARNING; + } + + /* If it succeeds, then we need to attach to the unit specified */ + dlpiattach (fd, unit); + + /* Reget the information, as it may be different per node */ + if ((acklen = dlpiinfo (fd)) == -1) + { + close (fd); + return ISIS_WARNING; + } + } + else + { + /* Double check the DLPI style */ + if (dia->dl_provider_style != DL_STYLE1) + { + zlog_warn ("open_dlpi_dev(): interface %s: %s is not style 1", + circuit->interface->name, devpath); + close (fd); + return ISIS_WARNING; + } + } + + /* Check that the interface we've got is the kind we expect */ + if ((dia->dl_sap_length != 2 && dia->dl_sap_length != -2) || + dia->dl_service_mode != DL_CLDLS || dia->dl_addr_length != ETHERADDRL + 2 || + dia->dl_brdcst_addr_length != ETHERADDRL) + { + zlog_warn ("%s: unsupported interface type for %s", __func__, + circuit->interface->name); + close (fd); + return ISIS_WARNING; + } + switch (dia->dl_mac_type) + { + case DL_CSMACD: + case DL_ETHER: + case DL_100VG: + case DL_100VGTPR: + case DL_ETH_CSMA: + case DL_100BT: + break; + default: + zlog_warn ("%s: unexpected mac type on %s: %lld", __func__, + circuit->interface->name, (long long)dia->dl_mac_type); + close (fd); + return ISIS_WARNING; + } + + circuit->sap_length = dia->dl_sap_length; + + /* + * The local hardware address is something that should be provided by way of + * sockaddr_dl for the interface, but isn't on Solaris. We set it here based + * on DLPI's reported address to avoid roto-tilling the world. + * (Note that isis_circuit_if_add on Solaris doesn't set the snpa.) + * + * Unfortunately, GLD is broken and doesn't provide the address after attach, + * so we need to be careful and use DL_PHYS_ADDR_REQ instead. + */ + if (dlpiaddr (fd, circuit->u.bc.snpa) == -1) + { + zlog_warn ("open_dlpi_dev(): interface %s: unable to get MAC address", + circuit->interface->name); + close (fd); + return ISIS_WARNING; + } + + /* Now bind to SAP 0. This gives us 802-type traffic. */ + if (dlpibind (fd) == -1) + { + zlog_warn ("%s: cannot bind SAP 0 on %s", __func__, + circuit->interface->name); + close (fd); + return ISIS_WARNING; + } + + /* + * Join to multicast groups according to + * 8.4.2 - Broadcast subnetwork IIH PDUs + */ + retval = 0; + retval |= dlpimcast (fd, ALL_L1_ISS); + retval |= dlpimcast (fd, ALL_ISS); + retval |= dlpimcast (fd, ALL_L2_ISS); + + if (retval != 0) + { + zlog_warn ("%s: unable to join multicast on %s", __func__, + circuit->interface->name); + close (fd); + return ISIS_WARNING; + } + + /* Push on the packet filter to avoid stray 802 packets */ + if (ioctl (fd, I_PUSH, "pfmod") == 0) + { + struct packetfilt pfil; + struct strioctl sioc; + + pfil.Pf_Priority = 0; + pfil.Pf_FilterLen = sizeof (pf_filter) / sizeof (u_short); + memcpy (pfil.Pf_Filter, pf_filter, sizeof (pf_filter)); + /* pfmod does not support transparent ioctls */ + sioc.ic_cmd = PFIOCSETF; + sioc.ic_timout = 5; + sioc.ic_len = sizeof (struct packetfilt); + sioc.ic_dp = (char *)&pfil; + if (ioctl (fd, I_STR, &sioc) == -1) + zlog_warn("%s: could not perform PF_IOCSETF on %s", + __func__, circuit->interface->name); + } + + circuit->fd = fd; + + return ISIS_OK; +} + +/* + * Create the socket and set the tx/rx funcs + */ +int +isis_sock_init (struct isis_circuit *circuit) +{ + int retval = ISIS_OK; + + if (isisd_privs.change (ZPRIVS_RAISE)) + zlog_err ("%s: could not raise privs, %s", __func__, safe_strerror (errno)); + + retval = open_dlpi_dev (circuit); + + if (retval != ISIS_OK) + { + zlog_warn ("%s: could not initialize the socket", __func__); + goto end; + } + + if (circuit->circ_type == CIRCUIT_T_BROADCAST) + { + circuit->tx = isis_send_pdu_bcast; + circuit->rx = isis_recv_pdu_bcast; + } + else + { + zlog_warn ("isis_sock_init(): unknown circuit type"); + retval = ISIS_WARNING; + goto end; + } + +end: + if (isisd_privs.change (ZPRIVS_LOWER)) + zlog_err ("%s: could not lower privs, %s", __func__, safe_strerror (errno)); + + return retval; +} + +int +isis_recv_pdu_bcast (struct isis_circuit *circuit, u_char * ssnpa) +{ + struct pollfd fds[1]; + struct strbuf ctlbuf, databuf; + int flags, retv; + dl_unitdata_ind_t *dui = (dl_unitdata_ind_t *)dlpi_ctl; + + memset (fds, 0, sizeof (fds)); + fds[0].fd = circuit->fd; + fds[0].events = POLLIN | POLLPRI; + if (poll (fds, 1, 0) <= 0) + return ISIS_WARNING; + + memset (&ctlbuf, 0, sizeof (ctlbuf)); + memset (&databuf, 0, sizeof (databuf)); + ctlbuf.maxlen = sizeof (dlpi_ctl); + ctlbuf.buf = (void *)dlpi_ctl; + databuf.maxlen = sizeof (sock_buff); + databuf.buf = (void *)sock_buff; + flags = 0; + retv = getmsg (circuit->fd, &ctlbuf, &databuf, &flags); + + if (retv < 0) + { + zlog_warn ("isis_recv_pdu_bcast: getmsg failed: %s", + safe_strerror (errno)); + return ISIS_WARNING; + } + + if (retv & (MORECTL | MOREDATA)) + { + while (retv & (MORECTL | MOREDATA)) + { + flags = 0; + retv = getmsg (circuit->fd, &ctlbuf, &databuf, &flags); + } + return ISIS_WARNING; + } + + 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 > (size_t)ctlbuf.len) + return ISIS_WARNING; + + memcpy (ssnpa, (char *)dui + dui->dl_src_addr_offset + + (circuit->sap_length > 0 ? circuit->sap_length : 0), ETHERADDRL); + + if (databuf.len < LLC_LEN || sock_buff[0] != ISO_SAP || + sock_buff[1] != ISO_SAP || sock_buff[2] != 3) + return ISIS_WARNING; + + stream_write (circuit->rcv_stream, sock_buff + LLC_LEN, + databuf.len - LLC_LEN); + stream_set_getp (circuit->rcv_stream, 0); + + return ISIS_OK; +} + +int +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); + + memset (dur, 0, sizeof (*dur)); + dur->dl_primitive = DL_UNITDATA_REQ; + dur->dl_dest_addr_length = ETHERADDRL + 2; + dur->dl_dest_addr_offset = sizeof (*dur); + + dstaddr = (char *)(dur + 1); + if (circuit->sap_length < 0) + { + dstsap = (u_short *)(dstaddr + ETHERADDRL); + } + else + { + dstsap = (u_short *)dstaddr; + dstaddr += circuit->sap_length; + } + if (level == 1) + memcpy (dstaddr, ALL_L1_ISS, ETHERADDRL); + else + memcpy (dstaddr, ALL_L2_ISS, ETHERADDRL); + /* Note: DLPI SAP values are in host byte order */ + *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)); + 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; +} + +#endif /* ISIS_METHOD == ISIS_METHOD_DLPI */ diff --git a/isisd/isis_dr.c b/isisd/isis_dr.c new file mode 100644 index 0000000..bc6ec11 --- /dev/null +++ b/isisd/isis_dr.c @@ -0,0 +1,361 @@ +/* + * IS-IS Rout(e)ing protocol - isis_dr.c + * IS-IS designated router related routines + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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 "log.h" +#include "hash.h" +#include "thread.h" +#include "linklist.h" +#include "vty.h" +#include "stream.h" +#include "if.h" + +#include "isisd/dict.h" +#include "isisd/isis_constants.h" +#include "isisd/isis_common.h" +#include "isisd/isis_misc.h" +#include "isisd/isis_flags.h" +#include "isisd/isis_circuit.h" +#include "isisd/isisd.h" +#include "isisd/isis_adjacency.h" +#include "isisd/isis_constants.h" +#include "isisd/isis_pdu.h" +#include "isisd/isis_tlv.h" +#include "isisd/isis_lsp.h" +#include "isisd/isis_dr.h" +#include "isisd/isis_events.h" + +const char * +isis_disflag2string (int disflag) +{ + + switch (disflag) + { + case ISIS_IS_NOT_DIS: + return "is not DIS"; + case ISIS_IS_DIS: + return "is DIS"; + case ISIS_WAS_DIS: + return "was DIS"; + default: + return "unknown DIS state"; + } + return NULL; /* not reached */ +} + +int +isis_run_dr_l1 (struct thread *thread) +{ + struct isis_circuit *circuit; + + circuit = THREAD_ARG (thread); + assert (circuit); + + if (circuit->u.bc.run_dr_elect[0]) + zlog_warn ("isis_run_dr(): run_dr_elect already set for l1"); + + circuit->u.bc.t_run_dr[0] = NULL; + circuit->u.bc.run_dr_elect[0] = 1; + + return ISIS_OK; +} + +int +isis_run_dr_l2 (struct thread *thread) +{ + struct isis_circuit *circuit; + + circuit = THREAD_ARG (thread); + assert (circuit); + + if (circuit->u.bc.run_dr_elect[1]) + zlog_warn ("isis_run_dr(): run_dr_elect already set for l2"); + + + circuit->u.bc.t_run_dr[1] = NULL; + circuit->u.bc.run_dr_elect[1] = 1; + + return ISIS_OK; +} + +static int +isis_check_dr_change (struct isis_adjacency *adj, int level) +{ + int i; + + if (adj->dis_record[level - 1].dis != + adj->dis_record[(1 * ISIS_LEVELS) + level - 1].dis) + /* was there a DIS state transition ? */ + { + adj->dischanges[level - 1]++; + /* ok rotate the history list through */ + for (i = DIS_RECORDS - 1; i > 0; i--) + { + adj->dis_record[(i * ISIS_LEVELS) + level - 1].dis = + adj->dis_record[((i - 1) * ISIS_LEVELS) + level - 1].dis; + adj->dis_record[(i * ISIS_LEVELS) + level - 1].last_dis_change = + adj->dis_record[((i - 1) * ISIS_LEVELS) + level - + 1].last_dis_change; + } + } + return ISIS_OK; +} + +int +isis_dr_elect (struct isis_circuit *circuit, int level) +{ + struct list *adjdb; + struct listnode *node; + struct isis_adjacency *adj, *adj_dr = NULL; + struct list *list = list_new (); + u_char own_prio; + int biggest_prio = -1; + int cmp_res, retval = ISIS_OK; + + own_prio = circuit->priority[level - 1]; + adjdb = circuit->u.bc.adjdb[level - 1]; + + if (!adjdb) + { + zlog_warn ("isis_dr_elect() adjdb == NULL"); + list_delete (list); + return ISIS_WARNING; + } + isis_adj_build_up_list (adjdb, list); + + /* + * Loop the adjacencies and find the one with the biggest priority + */ + for (ALL_LIST_ELEMENTS_RO (list, node, adj)) + { + /* clear flag for show output */ + adj->dis_record[level - 1].dis = ISIS_IS_NOT_DIS; + adj->dis_record[level - 1].last_dis_change = time (NULL); + + if (adj->prio[level - 1] > biggest_prio) + { + biggest_prio = adj->prio[level - 1]; + adj_dr = adj; + } + else if (adj->prio[level - 1] == biggest_prio) + { + /* + * Comparison of MACs breaks a tie + */ + if (adj_dr) + { + cmp_res = memcmp (adj_dr->snpa, adj->snpa, ETH_ALEN); + if (cmp_res < 0) + { + adj_dr = adj; + } + if (cmp_res == 0) + zlog_warn + ("isis_dr_elect(): multiple adjacencies with same SNPA"); + } + else + { + adj_dr = adj; + } + } + } + + if (!adj_dr) + { + /* + * Could not find the DR - means we are alone. Resign if we were DR. + */ + 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)) + { + 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); + + /* 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; + adj_dr->dis_record[level - 1].last_dis_change = time (NULL); + + /* now loop through a second time to check if there has been a DIS change + * if yes rotate the history log + */ + + for (ALL_LIST_ELEMENTS_RO (list, node, adj)) + isis_check_dr_change (adj, level); + + /* + * We are not DR - if we were -> resign + */ + if (circuit->u.bc.is_dr[level - 1]) + retval = isis_dr_resign (circuit, level); + } + list_delete (list); + return retval; +} + +int +isis_dr_resign (struct isis_circuit *circuit, int level) +{ + u_char id[ISIS_SYS_ID_LEN + 2]; + + zlog_debug ("isis_dr_resign l%d", level); + + circuit->u.bc.is_dr[level - 1] = 0; + 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_pseudo (id, circuit, level); + + if (level == 1) + { + memset (circuit->u.bc.l1_desig_is, 0, ISIS_SYS_ID_LEN + 1); + + THREAD_TIMER_OFF (circuit->t_send_csnp[0]); + + THREAD_TIMER_ON (master, circuit->u.bc.t_run_dr[0], isis_run_dr_l1, + circuit, 2 * circuit->hello_interval[0]); + + THREAD_TIMER_ON (master, circuit->t_send_psnp[0], send_l1_psnp, circuit, + isis_jitter (circuit->psnp_interval[level - 1], + PSNP_JITTER)); + } + else + { + memset (circuit->u.bc.l2_desig_is, 0, ISIS_SYS_ID_LEN + 1); + + THREAD_TIMER_OFF (circuit->t_send_csnp[1]); + + THREAD_TIMER_ON (master, circuit->u.bc.t_run_dr[1], isis_run_dr_l2, + circuit, 2 * circuit->hello_interval[1]); + + THREAD_TIMER_ON (master, circuit->t_send_psnp[1], send_l2_psnp, circuit, + isis_jitter (circuit->psnp_interval[level - 1], + PSNP_JITTER)); + } + + thread_add_event (master, isis_event_dis_status_change, circuit, 0); + + return ISIS_OK; +} + +int +isis_dr_commence (struct isis_circuit *circuit, int level) +{ + u_char old_dr[ISIS_SYS_ID_LEN + 2]; + + if (isis->debugs & DEBUG_EVENTS) + zlog_debug ("isis_dr_commence l%d", level); + + /* Lets keep a pause in DR election */ + circuit->u.bc.run_dr_elect[level - 1] = 0; + if (level == 1) + THREAD_TIMER_ON (master, circuit->u.bc.t_run_dr[0], isis_run_dr_l1, + circuit, 2 * circuit->hello_interval[0]); + else + THREAD_TIMER_ON (master, circuit->u.bc.t_run_dr[1], isis_run_dr_l2, + circuit, 2 * circuit->hello_interval[1]); + circuit->u.bc.is_dr[level - 1] = 1; + + if (level == 1) + { + memcpy (old_dr, circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1); + LSP_FRAGMENT (old_dr) = 0; + if (LSP_PSEUDO_ID (old_dr)) + { + /* there was a dr elected, purge its LSPs from the db */ + 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; + + assert (circuit->circuit_id); /* must be non-zero */ + /* if (circuit->t_send_l1_psnp) + thread_cancel (circuit->t_send_l1_psnp); */ + 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, + circuit, 2 * circuit->hello_interval[0]); + + THREAD_TIMER_ON (master, circuit->t_send_csnp[0], send_l1_csnp, circuit, + isis_jitter (circuit->csnp_interval[level - 1], + CSNP_JITTER)); + + } + else + { + memcpy (old_dr, circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1); + LSP_FRAGMENT (old_dr) = 0; + if (LSP_PSEUDO_ID (old_dr)) + { + /* there was a dr elected, purge its LSPs from the db */ + 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; + + assert (circuit->circuit_id); /* must be non-zero */ + /* if (circuit->t_send_l1_psnp) + thread_cancel (circuit->t_send_l1_psnp); */ + 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, + circuit, 2 * circuit->hello_interval[1]); + + THREAD_TIMER_ON (master, circuit->t_send_csnp[1], send_l2_csnp, circuit, + isis_jitter (circuit->csnp_interval[level - 1], + CSNP_JITTER)); + } + + thread_add_event (master, isis_event_dis_status_change, circuit, 0); + + return ISIS_OK; +} diff --git a/isisd/isis_dr.h b/isisd/isis_dr.h new file mode 100644 index 0000000..bad6836 --- /dev/null +++ b/isisd/isis_dr.h @@ -0,0 +1,42 @@ +/* + * IS-IS Rout(e)ing protocol - isis_dr.h + * IS-IS designated router related routines + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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. + */ + +#ifndef _ZEBRA_ISIS_DR_H +#define _ZEBRA_ISIS_DR_H + +int isis_run_dr_l1 (struct thread *thread); +int isis_run_dr_l2 (struct thread *thread); +int isis_dr_elect (struct isis_circuit *circuit, int level); +int isis_dr_resign (struct isis_circuit *circuit, int level); +int isis_dr_commence (struct isis_circuit *circuit, int level); +const char *isis_disflag2string (int disflag); + +enum isis_dis_state +{ + ISIS_IS_NOT_DIS, + ISIS_IS_DIS, + ISIS_WAS_DIS, + ISIS_UNKNOWN_DIS +}; + +#endif /* _ZEBRA_ISIS_DR_H */ diff --git a/isisd/isis_dynhn.c b/isisd/isis_dynhn.c new file mode 100644 index 0000000..412f098 --- /dev/null +++ b/isisd/isis_dynhn.c @@ -0,0 +1,173 @@ +/* + * IS-IS Rout(e)ing protocol - isis_dynhn.c + * Dynamic hostname cache + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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 "vty.h" +#include "linklist.h" +#include "memory.h" +#include "log.h" +#include "stream.h" +#include "command.h" +#include "if.h" +#include "thread.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_dynhn.h" +#include "isisd/isis_misc.h" +#include "isisd/isis_constants.h" + +extern struct host host; + +struct list *dyn_cache = NULL; +static int dyn_cache_cleanup (struct thread *); + +void +dyn_cache_init (void) +{ + if (dyn_cache == NULL) + dyn_cache = list_new (); + THREAD_TIMER_ON (master, isis->t_dync_clean, dyn_cache_cleanup, NULL, 120); + return; +} + +static int +dyn_cache_cleanup (struct thread *thread) +{ + struct listnode *node, *nnode; + struct isis_dynhn *dyn; + time_t now = time (NULL); + + isis->t_dync_clean = NULL; + + for (ALL_LIST_ELEMENTS (dyn_cache, node, nnode, dyn)) + { + if ((now - dyn->refresh) < MAX_LSP_LIFETIME) + continue; + + list_delete_node (dyn_cache, node); + XFREE (MTYPE_ISIS_DYNHN, dyn); + } + + THREAD_TIMER_ON (master, isis->t_dync_clean, dyn_cache_cleanup, NULL, 120); + return ISIS_OK; +} + +struct isis_dynhn * +dynhn_find_by_id (const u_char * id) +{ + struct listnode *node = NULL; + struct isis_dynhn *dyn = NULL; + + for (ALL_LIST_ELEMENTS_RO (dyn_cache, node, dyn)) + if (memcmp (dyn->id, id, ISIS_SYS_ID_LEN) == 0) + return dyn; + + 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 (const u_char * id, struct hostname *hostname, int level) +{ + struct isis_dynhn *dyn; + + dyn = dynhn_find_by_id (id); + if (dyn) + { + memcpy (&dyn->name, hostname, hostname->namelen + 1); + memcpy (dyn->id, id, ISIS_SYS_ID_LEN); + dyn->refresh = time (NULL); + return; + } + dyn = XCALLOC (MTYPE_ISIS_DYNHN, sizeof (struct isis_dynhn)); + if (!dyn) + { + zlog_warn ("isis_dynhn_insert(): out of memory!"); + return; + } + + /* we also copy the length */ + memcpy (&dyn->name, hostname, hostname->namelen + 1); + memcpy (dyn->id, id, ISIS_SYS_ID_LEN); + dyn->refresh = time (NULL); + dyn->level = level; + + listnode_add (dyn_cache, dyn); + + 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 + * 2 0000.0000.0002 bar-gw + * * 0000.0000.0004 this-gw + */ +void +dynhn_print_all (struct vty *vty) +{ + struct listnode *node; + struct isis_dynhn *dyn; + + vty_out (vty, "Level System ID Dynamic Hostname%s", VTY_NEWLINE); + for (ALL_LIST_ELEMENTS_RO (dyn_cache, node, dyn)) + { + vty_out (vty, "%-7d", dyn->level); + vty_out (vty, "%-15s%-15s%s", sysid_print (dyn->id), dyn->name.name, + VTY_NEWLINE); + } + + vty_out (vty, " * %s %s%s", sysid_print (isis->sysid), unix_hostname (), + VTY_NEWLINE); + return; +} diff --git a/isisd/isis_dynhn.h b/isisd/isis_dynhn.h new file mode 100644 index 0000000..f06a067 --- /dev/null +++ b/isisd/isis_dynhn.h @@ -0,0 +1,41 @@ +/* + * IS-IS Rout(e)ing protocol - isis_dynhn.h + * Dynamic hostname cache + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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. + */ +#ifndef _ZEBRA_ISIS_DYNHN_H +#define _ZEBRA_ISIS_DYNHN_H + +struct isis_dynhn +{ + u_char id[ISIS_SYS_ID_LEN]; + struct hostname name; + time_t refresh; + int level; +}; + +void dyn_cache_init (void); +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 new file mode 100644 index 0000000..abc4471 --- /dev/null +++ b/isisd/isis_events.c @@ -0,0 +1,278 @@ +/* + * IS-IS Rout(e)ing protocol - isis_events.h + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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 "log.h" +#include "memory.h" +#include "if.h" +#include "linklist.h" +#include "command.h" +#include "thread.h" +#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" +#include "isisd/isis_pdu.h" +#include "isisd/isis_network.h" +#include "isisd/isis_misc.h" +#include "isisd/isis_constants.h" +#include "isisd/isis_adjacency.h" +#include "isisd/isis_dr.h" +#include "isisd/isisd.h" +#include "isisd/isis_csm.h" +#include "isisd/isis_events.h" +#include "isisd/isis_spf.h" + +/* 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 + 4w4d: ISIS-Spf (tlt): L2, new LSP 0 DEAD.BEEF.0043.00-00 + 4w5d: ISIS-Spf (tlt): L1 SPF needed, periodic SPF, from 0x6091C844 + 4w5d: ISIS-Spf (tlt): L2 SPF needed, periodic SPF, from 0x6091C844 +*/ + +void +isis_event_circuit_state_change (struct isis_circuit *circuit, + struct isis_area *area, int up) +{ + area->circuit_state_changes++; + + if (isis->debugs & DEBUG_EVENTS) + zlog_debug ("ISIS-Evt (%s) circuit %s", area->area_tag, + up ? "up" : "down"); + + /* + * Regenerate LSPs this affects + */ + lsp_regenerate_schedule (area, IS_LEVEL_1 | IS_LEVEL_2, 0); + + return; +} + +static void +circuit_commence_level (struct isis_circuit *circuit, int level) +{ + if (level == 1) + { + 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[0]); + + THREAD_TIMER_ON (master, circuit->u.bc.t_send_lan_hello[0], + send_lan_l1_hello, circuit, + isis_jitter (circuit->hello_interval[0], + IIH_JITTER)); + + circuit->u.bc.lan_neighs[0] = list_new (); + } + } + else + { + 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) + { + THREAD_TIMER_ON (master, circuit->u.bc.t_run_dr[1], isis_run_dr_l2, + circuit, 2 * circuit->hello_interval[1]); + + THREAD_TIMER_ON (master, circuit->u.bc.t_send_lan_hello[1], + send_lan_l2_hello, circuit, + isis_jitter (circuit->hello_interval[1], + IIH_JITTER)); + + circuit->u.bc.lan_neighs[1] = list_new (); + } + } + + return; +} + +static void +circuit_resign_level (struct isis_circuit *circuit, int level) +{ + int idx = level - 1; + + THREAD_TIMER_OFF (circuit->t_send_csnp[idx]); + THREAD_TIMER_OFF (circuit->t_send_psnp[idx]); + + if (circuit->circ_type == CIRCUIT_T_BROADCAST) + { + 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_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->is_type), + circuit_t2string (newtype)); + + if (circuit->is_type == newtype) + return; /* No change */ + + if (!(newtype & circuit->area->is_type)) + { + zlog_err ("ISIS-Evt (%s) circuit type change - invalid level %s because" + " area is %s", circuit->area->area_tag, + circuit_t2string (newtype), + circuit_t2string (circuit->area->is_type)); + return; + } + + if (! circuit->is_passive) + { + switch (circuit->is_type) + { + case IS_LEVEL_1: + if (newtype == IS_LEVEL_2) + circuit_resign_level (circuit, 1); + circuit_commence_level (circuit, 2); + break; + case IS_LEVEL_1_AND_2: + if (newtype == IS_LEVEL_1) + circuit_resign_level (circuit, 2); + else + circuit_resign_level (circuit, 1); + break; + case IS_LEVEL_2: + if (newtype == IS_LEVEL_1) + circuit_resign_level (circuit, 2); + circuit_commence_level (circuit, 1); + break; + default: + break; + } + } + + circuit->is_type = newtype; + lsp_regenerate_schedule (circuit->area, IS_LEVEL_1 | IS_LEVEL_2, 0); + + return; +} + + /* 04/18/2002 by Gwak. */ + /************************************************************************** + * + * EVENTS for LSP generation + * + * 1) an Adajacency or Circuit Up/Down event + * 2) a chnage in Circuit metric + * 3) a change in Reachable Address metric + * 4) a change in manualAreaAddresses + * 5) a change in systemID + * 6) a change in DIS status + * 7) a chnage in the waiting status + * + * *********************************************************************** + * + * current support event + * + * 1) Adjacency Up/Down event + * 6) a change in DIS status + * + * ***********************************************************************/ + +void +isis_event_adjacency_state_change (struct isis_adjacency *adj, int newstate) +{ + /* adjacency state change event. + * - the only proto-type was supported */ + + /* invalid arguments */ + if (!adj || !adj->circuit || !adj->circuit->area) + return; + + if (isis->debugs & DEBUG_EVENTS) + zlog_debug ("ISIS-Evt (%s) Adjacency State change", + adj->circuit->area->area_tag); + + /* LSP generation again */ + lsp_regenerate_schedule (adj->circuit->area, IS_LEVEL_1 | IS_LEVEL_2, 0); + + return; +} + +/* events supporting code */ + +int +isis_event_dis_status_change (struct thread *thread) +{ + struct isis_circuit *circuit; + + circuit = THREAD_ARG (thread); + + /* invalid arguments */ + if (!circuit || !circuit->area) + return 0; + if (isis->debugs & DEBUG_EVENTS) + zlog_debug ("ISIS-Evt (%s) DIS status change", circuit->area->area_tag); + + /* LSP generation again */ + lsp_regenerate_schedule (circuit->area, IS_LEVEL_1 | IS_LEVEL_2, 0); + + return 0; +} + +void +isis_event_auth_failure (char *area_tag, const char *error_string, u_char *sysid) +{ + if (isis->debugs & DEBUG_EVENTS) + zlog_debug ("ISIS-Evt (%s) Authentication failure %s from %s", + area_tag, error_string, sysid_print (sysid)); + + return; +} diff --git a/isisd/isis_events.h b/isisd/isis_events.h new file mode 100644 index 0000000..e7cfa35 --- /dev/null +++ b/isisd/isis_events.h @@ -0,0 +1,49 @@ +/* + * IS-IS Rout(e)ing protocol - isis_events.h + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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. + */ +#ifndef _ZEBRA_ISIS_EVENTS_H +#define _ZEBRA_ISIS_EVENTS_H + +/* + * Events related to circuit + */ +void isis_event_circuit_state_change (struct isis_circuit *circuit, + struct isis_area *area, int state); +void isis_event_circuit_type_change (struct isis_circuit *circuit, + int newtype); +/* + * Events related to adjacencies + */ +void isis_event_adjacency_state_change (struct isis_adjacency *adj, + int newstate); + +int isis_event_dis_status_change (struct thread *thread); + +/* + * Error events + */ +#define AUTH_ERROR_TYPE_LSP 3 +#define AUTH_ERROR_TYPE_SNP 2 +#define AUTH_ERROR_TYPE_HELLO 1 +void isis_event_auth_failure (char *area_tag, const char *error_string, + u_char *sysid); + +#endif /* _ZEBRA_ISIS_EVENTS_H */ diff --git a/isisd/isis_flags.c b/isisd/isis_flags.c new file mode 100644 index 0000000..ec0eaa4 --- /dev/null +++ b/isisd/isis_flags.c @@ -0,0 +1,86 @@ +/* + * IS-IS Rout(e)ing protocol - isis_flags.c + * Routines for manipulation of SSN and SRM flags + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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 "log.h" +#include "linklist.h" + +#include "isisd/isis_constants.h" +#include "isisd/isis_common.h" +#include "isisd/isis_flags.h" + +void +flags_initialize (struct flags *flags) +{ + flags->maxindex = 0; + flags->free_idcs = NULL; +} + +long int +flags_get_index (struct flags *flags) +{ + struct listnode *node; + long int index; + + if (flags->free_idcs == NULL || flags->free_idcs->count == 0) + { + index = flags->maxindex++; + } + else + { + node = listhead (flags->free_idcs); + index = (long int) listgetdata (node); + listnode_delete (flags->free_idcs, (void *) index); + index--; + } + + return index; +} + +void +flags_free_index (struct flags *flags, long int index) +{ + if (index + 1 == flags->maxindex) + { + flags->maxindex--; + return; + } + + if (flags->free_idcs == NULL) + { + flags->free_idcs = list_new (); + } + + listnode_add (flags->free_idcs, (void *) (index + 1)); + + return; +} + +int +flags_any_set (u_int32_t * flags) +{ + u_int32_t zero[ISIS_MAX_CIRCUITS]; + memset (zero, 0x00, ISIS_MAX_CIRCUITS * 4); + + return bcmp (flags, zero, ISIS_MAX_CIRCUITS * 4); +} diff --git a/isisd/isis_flags.h b/isisd/isis_flags.h new file mode 100644 index 0000000..e2e42ad --- /dev/null +++ b/isisd/isis_flags.h @@ -0,0 +1,68 @@ +/* + * IS-IS Rout(e)ing protocol - isis_flags.h + * Routines for manipulation of SSN and SRM flags + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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. + */ + +#ifndef _ZEBRA_ISIS_FLAGS_H +#define _ZEBRA_ISIS_FLAGS_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 */ + +/* + * 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)); \ + } + +#define ISIS_CLEAR_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); \ + } + +#define ISIS_FLAGS_CLEAR_ALL(FLAGS) \ + { \ + memset(FLAGS,0x00,ISIS_MAX_CIRCUITS*4); \ + } + +#endif /* _ZEBRA_ISIS_FLAGS_H */ diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c new file mode 100644 index 0000000..aac8451 --- /dev/null +++ b/isisd/isis_lsp.c @@ -0,0 +1,3073 @@ +/* + * IS-IS Rout(e)ing protocol - isis_lsp.c + * LSP processing + * + * 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 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 "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 "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" +#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" + +#ifdef TOPOLOGY_GENERATE +#include "spgrid.h" +#endif + +/* 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) +{ + return memcmp (id1, id2, ISIS_SYS_ID_LEN + 2); +} + +dict_t * +lsp_db_init (void) +{ + dict_t *dict; + + dict = dict_create (DICTCOUNT_T_MAX, (dict_comp_t) lsp_id_cmp); + + return dict; +} + +struct isis_lsp * +lsp_search (u_char * id, dict_t * lspdb) +{ + dnode_t *node; + +#ifdef EXTREME_DEBUG + dnode_t *dn; + + zlog_debug ("searching db"); + for (dn = dict_first (lspdb); dn; dn = dict_next (lspdb, dn)) + { + zlog_debug ("%s\t%pX", rawlspid_print ((u_char *) dnode_getkey (dn)), + dnode_get (dn)); + } +#endif /* EXTREME DEBUG */ + + node = dict_lookup (lspdb, id); + + if (node) + return (struct isis_lsp *) dnode_get (node); + + return NULL; +} + +static void +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); + if (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); + } + + 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); +} + +void +lsp_db_destroy (dict_t * lspdb) +{ + dnode_t *dnode, *next; + struct isis_lsp *lsp; + + dnode = dict_first (lspdb); + while (dnode) + { + next = dict_next (lspdb, dnode); + lsp = dnode_get (dnode); + lsp_destroy (lsp); + dict_delete_free (lspdb, dnode); + dnode = next; + } + + dict_free (lspdb); + + return; +} + +/* + * Remove all the frags belonging to the given lsp + */ +static void +lsp_remove_frags (struct list *frags, dict_t * lspdb) +{ + dnode_t *dnode; + struct listnode *lnode, *lnnode; + struct isis_lsp *lsp; + + for (ALL_LIST_ELEMENTS (frags, lnode, lnnode, lsp)) + { + dnode = dict_lookup (lspdb, lsp->lsp_header->lsp_id); + lsp_destroy (lsp); + dnode_destroy (dict_delete (lspdb, dnode)); + } + + list_delete_all_node (frags); + + return; +} + +void +lsp_search_and_destroy (u_char * id, dict_t * lspdb) +{ + dnode_t *node; + struct isis_lsp *lsp; + + node = dict_lookup (lspdb, id); + if (node) + { + node = dict_delete (lspdb, node); + lsp = dnode_get (node); + /* + * If this is a zero lsp, remove all the frags now + */ + if (LSP_FRAGMENT (lsp->lsp_header->lsp_id) == 0) + { + if (lsp->lspu.frags) + lsp_remove_frags (lsp->lspu.frags, lspdb); + } + else + { + /* + * else just remove this frag, from the zero lsps' frag list + */ + if (lsp->lspu.zero_lsp && lsp->lspu.zero_lsp->lspu.frags) + listnode_delete (lsp->lspu.zero_lsp->lspu.frags, lsp); + } + lsp_destroy (lsp); + dnode_destroy (node); + } +} + +/* + * Compares a LSP to given values + * Params are given in net order + */ +int +lsp_compare (char *areatag, struct isis_lsp *lsp, u_int32_t seq_num, + u_int16_t checksum, u_int16_t rem_lifetime) +{ + /* no point in double ntohl on seqnum */ + if (lsp->lsp_header->seq_num == seq_num && + lsp->lsp_header->checksum == checksum && + /*comparing with 0, no need to do ntohl */ + ((lsp->lsp_header->rem_lifetime == 0 && rem_lifetime == 0) || + (lsp->lsp_header->rem_lifetime != 0 && rem_lifetime != 0))) + { + if (isis->debugs & DEBUG_SNP_PACKETS) + { + zlog_debug ("ISIS-Snp (%s): Compare LSP %s seq 0x%08x, cksum 0x%04x," + " lifetime %us", + areatag, + 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): is equal to ours seq 0x%08x," + " cksum 0x%04x, lifetime %us", + areatag, + ntohl (seq_num), ntohs (checksum), ntohs (rem_lifetime)); + } + return LSP_EQUAL; + } + + /* + * 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): 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 newer than ours seq 0x%08x, " + "cksum 0x%04x, lifetime %us", + areatag, + ntohl (lsp->lsp_header->seq_num), + ntohs (lsp->lsp_header->checksum), + ntohs (lsp->lsp_header->rem_lifetime)); + } + return LSP_NEWER; + } + if (isis->debugs & DEBUG_SNP_PACKETS) + { + zlog_debug + ("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," + " cksum 0x%04x, lifetime %us", areatag, + ntohl (lsp->lsp_header->seq_num), + ntohs (lsp->lsp_header->checksum), + ntohs (lsp->lsp_header->rem_lifetime)); + } + + 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) +{ + u_int32_t newseq; + + if (seq_num == 0 || ntohl (lsp->lsp_header->seq_num) > seq_num) + newseq = ntohl (lsp->lsp_header->seq_num) + 1; + else + newseq = seq_num + 1; + + lsp->lsp_header->seq_num = htonl (newseq); + + /* 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; +} + +/* + * Genetates checksum for LSP and its frags + */ +static void +lsp_seqnum_update (struct isis_lsp *lsp0) +{ + struct isis_lsp *lsp; + struct listnode *node; + + lsp_inc_seqnum (lsp0, 0); + + if (!lsp0->lspu.frags) + return; + + for (ALL_LIST_ELEMENTS_RO (lsp0->lspu.frags, node, lsp)) + lsp_inc_seqnum (lsp, 0); + + return; +} + +static u_int8_t +lsp_bits_generate (int level, int overload_bit, int attached_bit) +{ + 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, 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); + /* + * Get LSP data i.e. TLVs + */ + expected |= TLVFLAG_AUTH_INFO; + expected |= TLVFLAG_AREA_ADDRS; + expected |= TLVFLAG_IS_NEIGHS; + expected |= TLVFLAG_NLPID; + if (area->dynhostname) + expected |= TLVFLAG_DYN_HOSTNAME; + if (area->newmetric) + { + expected |= TLVFLAG_TE_IS_NEIGHS; + expected |= TLVFLAG_TE_IPV4_REACHABILITY; + expected |= TLVFLAG_TE_ROUTER_ID; + } + expected |= TLVFLAG_IPV4_ADDR; + expected |= TLVFLAG_IPV4_INT_REACHABILITY; + expected |= TLVFLAG_IPV4_EXT_REACHABILITY; +#ifdef HAVE_IPV6 + expected |= TLVFLAG_IPV6_ADDR; + expected |= TLVFLAG_IPV6_REACHABILITY; +#endif /* HAVE_IPV6 */ + + 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) && (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 : IS_LEVEL_1); + } + + return; +} + +void +lsp_update (struct isis_lsp *lsp, struct stream *stream, + struct isis_area *area, int level) +{ + dnode_t *dnode = NULL; + + /* 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)); + + /* rebuild the lsp data */ + lsp_update_data (lsp, stream, area, level); + + /* 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, int level) +{ + struct isis_lsp *lsp; + + lsp = XCALLOC (MTYPE_ISIS_LSP, sizeof (struct isis_lsp)); + lsp_update_data (lsp, stream, area, level); + + if (lsp0 == NULL) + { + /* + * zero lsp -> create the list for fragments + */ + lsp->lspu.frags = list_new (); + } + else + { + /* + * a fragment -> set the backpointer and add this to zero lsps frag list + */ + lsp->lspu.zero_lsp = lsp0; + listnode_add (lsp0->lspu.frags, lsp); + } + + return lsp; +} + +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; + + lsp = XCALLOC (MTYPE_ISIS_LSP, sizeof (struct isis_lsp)); + 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)); + lsp->lsp_header = (struct isis_link_state_hdr *) + (STREAM_DATA (lsp->pdu) + ISIS_FIXED_HDR_LEN); + + /* at first we fill the FIXED HEADER */ + (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 */ + /* Minimal LSP PDU size */ + lsp->lsp_header->pdu_len = htons (ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); + memcpy (lsp->lsp_header->lsp_id, lsp_id, ISIS_SYS_ID_LEN + 2); + lsp->lsp_header->checksum = checksum; /* Provided in network order */ + lsp->lsp_header->seq_num = htonl (seq_num); + lsp->lsp_header->rem_lifetime = htons (rem_lifetime); + lsp->lsp_header->lsp_bits = lsp_bits; + lsp->level = level; + lsp->age_out = ZERO_AGE_LIFETIME; + + 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 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; +} + +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 + } +} + +/* + * Build a list of LSPs with non-zero ht bounded by start and stop ids + */ +void +lsp_build_list_nonzero_ht (u_char * start_id, u_char * stop_id, + struct list *list, dict_t * lspdb) +{ + dnode_t *first, *last, *curr; + + first = dict_lower_bound (lspdb, start_id); + if (!first) + return; + + last = dict_upper_bound (lspdb, stop_id); + + curr = first; + + if (((struct isis_lsp *) (curr->dict_data))->lsp_header->rem_lifetime) + listnode_add (list, first->dict_data); + + while (curr) + { + curr = dict_next (lspdb, curr); + if (curr && + ((struct isis_lsp *) (curr->dict_data))->lsp_header->rem_lifetime) + listnode_add (list, curr->dict_data); + if (curr == last) + break; + } + + return; +} + +/* + * 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, 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); + if (!first) + return; + + last = dict_upper_bound (lspdb, 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); + count++; + } + if (count == num_lsps || curr == last) + break; + } + + return; +} + +/* + * Build a list of LSPs with SSN flag set for the given circuit + */ +void +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) + { + next = dict_next (lspdb, dnode); + lsp = dnode_get (dnode); + if (ISIS_CHECK_FLAG (lsp->SSNflags, circuit)) + { + listnode_add (list, lsp); + ++count; + } + if (count == num_lsps) + break; + dnode = next; + } + + return; +} + +static void +lsp_set_time (struct isis_lsp *lsp) +{ + assert (lsp); + + if (lsp->lsp_header->rem_lifetime == 0) + { + if (lsp->age_out > 0) + lsp->age_out--; + return; + } + + lsp->lsp_header->rem_lifetime = + htons (ntohs (lsp->lsp_header->rem_lifetime) - 1); +} + +static void +lspid_print (u_char * lsp_id, u_char * trg, char dynhost, char frag) +{ + struct isis_dynhn *dyn = NULL; + u_char id[SYSID_STRLEN]; + + if (dynhost) + dyn = dynhn_find_by_id (lsp_id); + else + 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 ()); + 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)); + else + sprintf ((char *)trg, "%s.%02x", id, LSP_PSEUDO_ID (lsp_id)); +} + +/* Convert the lsp attribute bits to attribute string */ +const char * +lsp_bits2string (u_char * lsp_bits) +{ + char *pos = lsp_bits_string; + + if (!*lsp_bits) + return " none"; + + /* we only focus on the default metric */ + pos += sprintf (pos, "%d/", + ISIS_MASK_LSP_ATT_DEFAULT_BIT (*lsp_bits) ? 1 : 0); + + pos += sprintf (pos, "%d/", + ISIS_MASK_LSP_PARTITION_BIT (*lsp_bits) ? 1 : 0); + + pos += sprintf (pos, "%d", ISIS_MASK_LSP_OL_BIT (*lsp_bits) ? 1 : 0); + + *(pos) = '\0'; + + return lsp_bits_string; +} + +/* this function prints the lsp on show isis database */ +void +lsp_print (struct isis_lsp *lsp, struct vty *vty, char dynhost) +{ + 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, "%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) + { + 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); +} + +void +lsp_print_detail (struct isis_lsp *lsp, struct vty *vty, char dynhost) +{ + struct area_addr *area_addr; + int i; + struct listnode *lnode; + struct is_neigh *is_neigh; + struct te_is_neigh *te_is_neigh; + struct ipv4_reachability *ipv4_reach; + struct in_addr *ipv4_addr; + struct te_ipv4_reachability *te_ipv4_reach; +#ifdef HAVE_IPV6 + struct ipv6_reachability *ipv6_reach; + struct in6_addr in6; + u_char buff[BUFSIZ]; +#endif + u_char LSPid[255]; + u_char hostname[255]; + u_char ipv4_reach_prefix[20]; + u_char ipv4_reach_mask[20]; + u_char ipv4_address[20]; + + lspid_print (lsp->lsp_header->lsp_id, LSPid, dynhost, 1); + lsp_print (lsp, vty, dynhost); + + /* for all area address */ + if (lsp->tlv_data.area_addrs) + for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.area_addrs, lnode, area_addr)) + { + vty_out (vty, " Area Address: %s%s", + isonet_print (area_addr->area_addr, area_addr->addr_len), + VTY_NEWLINE); + } + + /* for the nlpid tlv */ + if (lsp->tlv_data.nlpids) + { + for (i = 0; i < lsp->tlv_data.nlpids->count; i++) + { + switch (lsp->tlv_data.nlpids->nlpids[i]) + { + case NLPID_IP: + case NLPID_IPV6: + 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); + break; + } + } + } + + /* for the hostname tlv */ + if (lsp->tlv_data.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); + } + + /* 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); + } + + 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 : %-8d IS : %s%s", + is_neigh->metrics.metric_default, LSPid, VTY_NEWLINE); + } + + /* for the internal reachable tlv */ + if (lsp->tlv_data.ipv4_int_reachs) + for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv4_int_reachs, lnode, + ipv4_reach)) + { + memcpy (ipv4_reach_prefix, inet_ntoa (ipv4_reach->prefix), + sizeof (ipv4_reach_prefix)); + memcpy (ipv4_reach_mask, inet_ntoa (ipv4_reach->mask), + sizeof (ipv4_reach_mask)); + vty_out (vty, " Metric : %-8d IPv4-Internal : %s %s%s", + ipv4_reach->metrics.metric_default, ipv4_reach_prefix, + ipv4_reach_mask, VTY_NEWLINE); + } + + /* for the external reachable tlv */ + if (lsp->tlv_data.ipv4_ext_reachs) + for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv4_ext_reachs, lnode, + ipv4_reach)) + { + memcpy (ipv4_reach_prefix, inet_ntoa (ipv4_reach->prefix), + sizeof (ipv4_reach_prefix)); + memcpy (ipv4_reach_mask, inet_ntoa (ipv4_reach->mask), + sizeof (ipv4_reach_mask)); + vty_out (vty, " Metric : %-8d IPv4-External : %s %s%s", + ipv4_reach->metrics.metric_default, ipv4_reach_prefix, + ipv4_reach_mask, VTY_NEWLINE); + } + + /* IPv6 tlv */ +#ifdef HAVE_IPV6 + if (lsp->tlv_data.ipv6_reachs) + for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv6_reachs, lnode, ipv6_reach)) + { + memset (&in6, 0, sizeof (in6)); + 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 & + CTRL_INFO_DISTRIBUTION) == DISTRIBUTION_INTERNAL) + 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 : %-8d IPv6-External : %s/%d%s", + ntohl (ipv6_reach->metric), + buff, ipv6_reach->prefix_len, VTY_NEWLINE); + } +#endif + + /* TE IS neighbor tlv */ + if (lsp->tlv_data.te_is_neighs) + for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_is_neighs, lnode, te_is_neigh)) + { + lspid_print (te_is_neigh->neigh_id, LSPid, dynhost, 0); + 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 */ + if (lsp->tlv_data.te_ipv4_reachs) + for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_ipv4_reachs, lnode, + te_ipv4_reach)) + { + /* FIXME: There should be better way to output this stuff. */ + 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; +} + +/* print all the lsps info in the local lspdb */ +int +lsp_print_all (struct vty *vty, dict_t * lspdb, char detail, char dynhost) +{ + + dnode_t *node = dict_first (lspdb), *next; + int lsp_count = 0; + + if (detail == ISIS_UI_LEVEL_BRIEF) + { + while (node != NULL) + { + /* I think it is unnecessary, so I comment it out */ + /* dict_contains (lspdb, node); */ + next = dict_next (lspdb, node); + lsp_print (dnode_get (node), vty, dynhost); + node = next; + lsp_count++; + } + } + else if (detail == ISIS_UI_LEVEL_DETAIL) + { + while (node != NULL) + { + next = dict_next (lspdb, node); + lsp_print_detail (dnode_get (node), vty, dynhost); + node = next; + lsp_count++; + } + } + + return lsp_count; +} + +#define FRAG_THOLD(S,T) \ + ((STREAM_SIZE(S)*T)/100) + +/* stream*, area->lsp_frag_threshold, increment */ +#define FRAG_NEEDED(S,T,I) \ + (STREAM_SIZE(S)-STREAM_REMAIN(S)+(I) > FRAG_THOLD(S,T)) + +/* FIXME: It shouldn't be necessary to pass tlvsize here, TLVs can have + * variable length (TE TLVs, sub TLVs). */ +static void +lsp_tlv_fit (struct isis_lsp *lsp, struct list **from, struct list **to, + int tlvsize, int frag_thold, + int tlv_build_func (struct list *, struct stream *)) +{ + int count, i; + + /* can we fit all ? */ + if (!FRAG_NEEDED (lsp->pdu, frag_thold, listcount (*from) * tlvsize + 2)) + { + tlv_build_func (*from, lsp->pdu); + 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)); + count = count / tlvsize; + if (count > (int)listcount (*from)) + count = listcount (*from); + for (i = 0; i < count; i++) + { + listnode_add (*to, listgetdata (listhead (*from))); + listnode_delete (*from, listgetdata (listhead (*from))); + } + tlv_build_func (*to, lsp->pdu); + } + lsp->lsp_header->pdu_len = htons (stream_get_endp (lsp->pdu)); + 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) +{ + struct isis_lsp *lsp; + u_char frag_id[ISIS_SYS_ID_LEN + 2]; + + 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 */ + lsp_clear_data (lsp); + return lsp; + } + 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; + 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)) + { + 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); + } + } +} + +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); +} + +/* + * Builds the LSP data part. This func creates a new frag whenever + * area->lsp_frag_threshold is exceeded. + */ +static void +lsp_build (struct isis_lsp *lsp, struct isis_area *area) +{ + struct is_neigh *is_neigh; + struct te_is_neigh *te_is_neigh; + struct listnode *node, *ipnode; + int level = lsp->level; + struct isis_circuit *circuit; + struct prefix_ipv4 *ipv4; + struct ipv4_reachability *ipreach; + struct te_ipv4_reachability *te_ipreach; + struct isis_adjacency *nei; +#ifdef HAVE_IPV6 + struct prefix_ipv6 *ipv6, ip6prefix; + struct ipv6_reachability *ip6reach; +#endif /* HAVE_IPV6 */ + struct tlvs tlv_data; + struct isis_lsp *lsp0 = lsp; + 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 + */ + + /* Area addresses */ + 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 + || area->ipv6_circuits > 0 +#endif /* HAVE_IPV6 */ + ) + { + lsp->tlv_data.nlpids = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct nlpids)); + 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)); + + 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; + + 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 + { + lsp_debug("ISIS (%s): Not adding dynamic hostname (disabled)", area->area_tag); + } + + /* 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 (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 (); + lsp->tlv_data.ipv4_addrs->del = free_tlv; + } + + routerid = XMALLOC (MTYPE_ISIS_TLV, sizeof (struct in_addr)); + routerid->s_addr = isis->router_id; + listnode_add (lsp->tlv_data.ipv4_addrs, routerid); + tlv_add_in_addr (routerid, lsp->pdu, IPV4_ADDR); + + /* Exactly same data is put into TE router ID TLV, but only if new style + * 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 = 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 == IS_LEVEL_1) + { + if (tlv_data.is_neighs == NULL) + { + tlv_data.is_neighs = list_new (); + tlv_data.is_neighs->del = free_tlv; + } + is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct is_neigh)); + + memcpy (&is_neigh->neigh_id, area->topology_baseis, ISIS_SYS_ID_LEN); + is_neigh->neigh_id[ISIS_SYS_ID_LEN - 1] = (1 & 0xFF); + is_neigh->neigh_id[ISIS_SYS_ID_LEN - 2] = ((1 >> 8) & 0xFF); + is_neigh->metrics.metric_default = 0x01; + is_neigh->metrics.metric_delay = METRICS_UNSUPPORTED; + is_neigh->metrics.metric_expense = METRICS_UNSUPPORTED; + is_neigh->metrics.metric_error = METRICS_UNSUPPORTED; + listnode_add (tlv_data.is_neighs, is_neigh); + } +#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) + { + lsp_debug("ISIS (%s): Circuit is not up, ignoring.", area->area_tag); + continue; + } + + /* + * Add IPv4 internal reachability of this circuit + */ + 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) + { + tlv_data.ipv4_int_reachs = list_new (); + tlv_data.ipv4_int_reachs->del = free_tlv; + } + for (ALL_LIST_ELEMENTS_RO (circuit->ip_addrs, ipnode, ipv4)) + { + ipreach = + XMALLOC (MTYPE_ISIS_TLV, sizeof (struct ipv4_reachability)); + 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); + } + } + 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; + } + for (ALL_LIST_ELEMENTS_RO (circuit->ip_addrs, ipnode, ipv4)) + { + /* FIXME All this assumes that we have no sub TLVs. */ + te_ipreach = XCALLOC (MTYPE_ISIS_TLV, + sizeof (struct te_ipv4_reachability) + + ((ipv4->prefixlen + 7)/8) - 1); + + if (area->oldmetric) + 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 + */ + if (circuit->ipv6_router && circuit->ipv6_non_link && + circuit->ipv6_non_link->count > 0) + { + + if (tlv_data.ipv6_reachs == NULL) + { + tlv_data.ipv6_reachs = list_new (); + tlv_data.ipv6_reachs->del = free_tlv; + } + for (ALL_LIST_ELEMENTS_RO (circuit->ipv6_non_link, ipnode, ipv6)) + { + ip6reach = + XCALLOC (MTYPE_ISIS_TLV, sizeof (struct ipv6_reachability)); + + if (area->oldmetric) + ip6reach->metric = + 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); + + 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); + } + } +#endif /* HAVE_IPV6 */ + + switch (circuit->circ_type) + { + case CIRCUIT_T_BROADCAST: + if (level & circuit->is_type) + { + if (area->oldmetric) + { + if (tlv_data.is_neighs == NULL) + { + tlv_data.is_neighs = list_new (); + tlv_data.is_neighs->del = free_tlv; + } + is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct is_neigh)); + 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.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) + { + if (tlv_data.te_is_neighs == NULL) + { + tlv_data.te_is_neighs = list_new (); + tlv_data.te_is_neighs->del = free_tlv; + } + te_is_neigh = XCALLOC (MTYPE_ISIS_TLV, + sizeof (struct te_is_neigh)); + 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 = circuit->metric[level - 1]; + else + 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; + if (nei && (level & nei->circuit_t)) + { + if (area->oldmetric) + { + if (tlv_data.is_neighs == NULL) + { + tlv_data.is_neighs = list_new (); + tlv_data.is_neighs->del = free_tlv; + } + is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct is_neigh)); + memcpy (is_neigh->neigh_id, nei->sysid, ISIS_SYS_ID_LEN); + 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) + { + uint32_t metric; + + if (tlv_data.te_is_neighs == NULL) + { + tlv_data.te_is_neighs = list_new (); + tlv_data.te_is_neighs->del = free_tlv; + } + 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 = 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_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) + lsp->tlv_data.ipv4_int_reachs = list_new (); + 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_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!). */ + while (tlv_data.te_ipv4_reachs && listcount (tlv_data.te_ipv4_reachs)) + { + if (lsp->tlv_data.te_ipv4_reachs == NULL) + lsp->tlv_data.te_ipv4_reachs = list_new (); + lsp_tlv_fit (lsp, &tlv_data.te_ipv4_reachs, + &lsp->tlv_data.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); + } + +#ifdef HAVE_IPV6 + while (tlv_data.ipv6_reachs && listcount (tlv_data.ipv6_reachs)) + { + if (lsp->tlv_data.ipv6_reachs == NULL) + lsp->tlv_data.ipv6_reachs = list_new (); + lsp_tlv_fit (lsp, &tlv_data.ipv6_reachs, + &lsp->tlv_data.ipv6_reachs, + IPV6_REACH_LEN, area->lsp_frag_threshold, + tlv_add_ipv6_reachs); + if (tlv_data.ipv6_reachs && listcount (tlv_data.ipv6_reachs)) + lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, + lsp0, area, level); + } +#endif /* HAVE_IPV6 */ + + while (tlv_data.is_neighs && listcount (tlv_data.is_neighs)) + { + if (lsp->tlv_data.is_neighs == NULL) + lsp->tlv_data.is_neighs = list_new (); + lsp_tlv_fit (lsp, &tlv_data.is_neighs, + &lsp->tlv_data.is_neighs, + IS_NEIGHBOURS_LEN, area->lsp_frag_threshold, + tlv_add_is_neighs); + if (tlv_data.is_neighs && listcount (tlv_data.is_neighs)) + lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, + lsp0, area, level); + } + + while (tlv_data.te_is_neighs && listcount (tlv_data.te_is_neighs)) + { + if (lsp->tlv_data.te_is_neighs == NULL) + lsp->tlv_data.te_is_neighs = list_new (); + lsp_tlv_fit (lsp, &tlv_data.te_is_neighs, &lsp->tlv_data.te_is_neighs, + IS_NEIGHBOURS_LEN, area->lsp_frag_threshold, + tlv_add_te_is_neighs); + if (tlv_data.te_is_neighs && listcount (tlv_data.te_is_neighs)) + 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 and 7.3.9 Generation on non-pseudonode LSPs + */ +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 */ + 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); + + if (isis->debugs & DEBUG_UPDATE_PACKETS) + { + 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; +} + +/* + * Search own LSPs, update holding time and set SRM + */ +static int +lsp_regenerate (struct isis_area *area, int level) +{ + 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); + + lsp = lsp_search (lspid, lspdb); + + if (!lsp) + { + 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 (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); + + lsp->last_generated = time (NULL); + lsp_set_all_srmflags (lsp); + for (ALL_LIST_ELEMENTS_RO (lsp->lspu.frags, node, frag)) + { + 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); + } + + 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; + + if (isis->debugs & DEBUG_UPDATE_PACKETS) + { + 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); + + return ISIS_OK; +} + +/* + * Something has changed or periodic refresh -> regenerate LSP + */ +static int +lsp_l1_refresh (struct thread *thread) +{ + struct isis_area *area; + + area = THREAD_ARG (thread); + assert (area); + + area->t_lsp_refresh[0] = NULL; + area->lsp_regenerate_pending[0] = 0; + + if ((area->is_type & IS_LEVEL_1) == 0) + return ISIS_ERROR; + + sched_debug("ISIS (%s): LSP L1 refresh timer expired. Refreshing LSP...", area->area_tag); + return lsp_regenerate (area, IS_LEVEL_1); +} + +static int +lsp_l2_refresh (struct thread *thread) +{ + struct isis_area *area; + + area = THREAD_ARG (thread); + assert (area); + + area->t_lsp_refresh[1] = NULL; + area->lsp_regenerate_pending[1] = 0; + + 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, 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); + + for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; lvl++) + { + 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 < 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 + { + /* + * 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; +} + +/* + * Funcs for pseudonode LSPs + */ + +/* + * 7.3.8 and 7.3.10 Generation of level 1 and 2 pseudonode LSPs + */ +static void +lsp_build_pseudo (struct isis_lsp *lsp, struct isis_circuit *circuit, + int level) +{ + struct isis_adjacency *adj; + struct is_neigh *is_neigh; + struct te_is_neigh *te_is_neigh; + struct es_neigh *es_neigh; + struct list *adj_list; + struct listnode *node; + struct isis_area *area = circuit->area; + + 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; + /* 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 + */ + if (circuit->area->oldmetric) + { + if (lsp->tlv_data.is_neighs == NULL) + { + lsp->tlv_data.is_neighs = list_new (); + lsp->tlv_data.is_neighs->del = free_tlv; + } + is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct is_neigh)); + + 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) + { + if (lsp->tlv_data.te_is_neighs == NULL) + { + lsp->tlv_data.te_is_neighs = list_new (); + lsp->tlv_data.te_is_neighs->del = free_tlv; + } + te_is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct te_is_neigh)); + + 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 (); + isis_adj_build_up_list (circuit->u.bc.adjdb[level - 1], adj_list); + + for (ALL_LIST_ELEMENTS_RO (adj_list, node, adj)) + { + if (adj->level & level) + { + 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 == IS_LEVEL_2 && adj->sys_type == ISIS_SYSTYPE_L2_IS)) + { + /* an IS neighbour -> add it */ + if (circuit->area->oldmetric) + { + is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct is_neigh)); + + 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) + { + te_is_neigh = XCALLOC (MTYPE_ISIS_TLV, + 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 == 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 */ + if (lsp->tlv_data.es_neighs == NULL) + { + lsp->tlv_data.es_neighs = list_new (); + lsp->tlv_data.es_neighs->del = free_tlv; + } + es_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct es_neigh)); + + 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); + stream_forward_endp (lsp->pdu, ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); + + /* + * Add the authentication info if it's present + */ + 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); + + if (lsp->tlv_data.te_is_neighs && listcount (lsp->tlv_data.te_is_neighs) > 0) + tlv_add_te_is_neighs (lsp->tlv_data.te_is_neighs, lsp->pdu); + + if (lsp->tlv_data.es_neighs && listcount (lsp->tlv_data.es_neighs) > 0) + tlv_add_is_neighs (lsp->tlv_data.es_neighs, lsp->pdu); + + lsp->lsp_header->pdu_len = htons (stream_get_endp (lsp->pdu)); + + /* 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_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; + LSP_FRAGMENT (lsp_id) = 0; + + lsp = lsp_search (lsp_id, lspdb); + + if (!lsp) + { + 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); + + /* 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 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; +} + +/* + * Something has changed or periodic refresh -> regenerate pseudo LSP + */ +static int +lsp_l1_refresh_pseudo (struct thread *thread) +{ + struct isis_circuit *circuit; + u_char id[ISIS_SYS_ID_LEN + 2]; + + circuit = THREAD_ARG (thread); + + circuit->u.bc.t_refresh_pseudo_lsp[0] = NULL; + circuit->lsp_regenerate_pending[0] = 0; + + 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_pseudo (circuit, IS_LEVEL_1); +} + +static int +lsp_l2_refresh_pseudo (struct thread *thread) +{ + struct isis_circuit *circuit; + u_char id[ISIS_SYS_ID_LEN + 2]; + + circuit = THREAD_ARG (thread); + + circuit->u.bc.t_refresh_pseudo_lsp[1] = NULL; + circuit->lsp_regenerate_pending[1] = 0; + + 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 lsp_regenerate_pseudo (circuit, IS_LEVEL_2); +} + +int +lsp_regenerate_schedule_pseudo (struct isis_circuit *circuit, int level) +{ + struct isis_lsp *lsp; + u_char lsp_id[ISIS_SYS_ID_LEN + 2]; + time_t now, diff; + long timeout; + int lvl; + struct isis_area *area = circuit->area; + + if (circuit == NULL || + circuit->circ_type != CIRCUIT_T_BROADCAST || + circuit->state != C_STATE_UP) + return ISIS_OK; + + 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); + + 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; + } + + /* + * 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 ISIS_OK; +} + +/* + * Walk through LSPs for an area + * - set remaining lifetime + * - set LSPs with SRMflag set for sending + */ +int +lsp_tick (struct thread *thread) +{ + struct isis_area *area; + struct isis_circuit *circuit; + struct isis_lsp *lsp; + struct list *lsp_list; + struct listnode *lspnode, *cnode; + dnode_t *dnode, *dnode_next; + int level; + u_int16_t rem_lifetime; + + lsp_list = list_new (); + + area = THREAD_ARG (thread); + assert (area); + area->t_tick = NULL; + THREAD_TIMER_ON (master, area->t_tick, lsp_tick, area, 1); + + /* + * Build a list of LSPs with (any) SRMflag set + * and removed the ones that have aged out + */ + for (level = 0; level < ISIS_LEVELS; level++) + { + if (area->lspdb[level] && dict_count (area->lspdb[level]) > 0) + { + 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); +#endif /* TOPOLOGY_GENERATE */ + 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)) + { + 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 (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); + + return ISIS_OK; +} + +void +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; + + /* 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; +} + +/* + * Purge own LSP that is received and we don't have. + * -> Do as in 7.3.16.4 + */ +void +lsp_purge_non_exist (int level, + struct isis_link_state_hdr *lsp_hdr, + struct isis_area *area) +{ + struct isis_lsp *lsp; + + /* + * We need to create the LSP to be purged + */ + lsp = XCALLOC (MTYPE_ISIS_LSP, sizeof (struct isis_lsp)); + 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 == 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); + + /* + * 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 + */ + lsp_insert (lsp, area->lspdb[lsp->level - 1]); + + /* + * Send in to whole area + */ + 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; + u_int16_t rem_lifetime; + + lsp = THREAD_ARG (thread); + assert (lsp); + + lsp->t_lsp_top_ref = NULL; + + lsp_seqnum_update (lsp); + + lsp_set_all_srmflags (lsp); + if (isis->debugs & DEBUG_UPDATE_PACKETS) + { + zlog_debug ("ISIS-Upd (): refreshing Topology L1 %s", + rawlspid_print (lsp->lsp_header->lsp_id)); + } + /* Refresh dynamic hostname in the cache. */ + isis_dynhn_insert (lsp->lsp_header->lsp_id, lsp->tlv_data.hostname, + IS_LEVEL_1); + + 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, + lsp->area->lsp_refresh[0]); + + return ISIS_OK; +} + +void +generate_topology_lsps (struct isis_area *area) +{ + struct listnode *node; + int i, max = 0; + struct arc *arc; + u_char lspid[ISIS_SYS_ID_LEN + 2]; + struct isis_lsp *lsp; + 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; + } + + for (i = 1; i < (max + 1); i++) + { + memcpy (lspid, area->topology_baseis, ISIS_SYS_ID_LEN); + LSP_PSEUDO_ID (lspid) = 0x00; + LSP_FRAGMENT (lspid) = 0x00; + lspid[ISIS_SYS_ID_LEN - 1] = (i & 0xFF); + lspid[ISIS_SYS_ID_LEN - 2] = ((i >> 8) & 0xFF); + + 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; + + /* Creating LSP data based on topology info. */ + build_topology_lsp_data (lsp, area, i); + /* Checksum is also calculated here. */ + lsp_seqnum_update (lsp); + /* Take care of inserting dynamic hostname into cache. */ + isis_dynhn_insert (lspid, lsp->tlv_data.hostname, IS_LEVEL_1); + + refresh_time = lsp_refresh_time (lsp, rem_lifetime); + THREAD_TIMER_ON (master, lsp->t_lsp_top_ref, top_lsp_refresh, lsp, + refresh_time); + lsp_set_all_srmflags (lsp); + lsp_insert (lsp, area->lspdb[0]); + } +} + +void +remove_topology_lsps (struct isis_area *area) +{ + struct isis_lsp *lsp; + dnode_t *dnode, *dnode_next; + + dnode = dict_first (area->lspdb[0]); + while (dnode != NULL) + { + dnode_next = dict_next (area->lspdb[0], dnode); + lsp = dnode_get (dnode); + if (lsp->from_topology) + { + THREAD_TIMER_OFF (lsp->t_lsp_top_ref); + lsp_destroy (lsp); + dict_delete (area->lspdb[0], dnode); + } + dnode = dnode_next; + } +} + +void +build_topology_lsp_data (struct isis_lsp *lsp, struct isis_area *area, + int lsp_top_num) +{ + struct listnode *node; + struct arc *arc; + struct is_neigh *is_neigh; + struct te_is_neigh *te_is_neigh; + char buff[200]; + struct tlvs tlv_data; + struct isis_lsp *lsp0 = lsp; + + /* Add area addresses. FIXME: Is it needed at all? */ + 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 (lsp->tlv_data.nlpids == NULL) + lsp->tlv_data.nlpids = XMALLOC (MTYPE_ISIS_TLV, sizeof (struct nlpids)); + lsp->tlv_data.nlpids->count = 1; + lsp->tlv_data.nlpids->nlpids[0] = NLPID_IP; + + if (area->dynhostname) + { + lsp->tlv_data.hostname = XMALLOC (MTYPE_ISIS_TLV, + sizeof (struct hostname)); + memset (buff, 0x00, 200); + sprintf (buff, "%s%d", area->topology_basedynh ? area->topology_basedynh : + "feedme", lsp_top_num); + memcpy (lsp->tlv_data.hostname->name, buff, strlen (buff)); + lsp->tlv_data.hostname->namelen = strlen (buff); + } + + 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); + + memset (&tlv_data, 0, sizeof (struct tlvs)); + if (tlv_data.is_neighs == NULL) + { + tlv_data.is_neighs = list_new (); + tlv_data.is_neighs->del = free_tlv; + } + + /* Add reachability for this IS for simulated 1. */ + if (lsp_top_num == 1) + { + is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct is_neigh)); + + memcpy (&is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN); + LSP_PSEUDO_ID (is_neigh->neigh_id) = 0x00; + /* Metric MUST NOT be 0, unless it's not alias TLV. */ + is_neigh->metrics.metric_default = 0x01; + is_neigh->metrics.metric_delay = METRICS_UNSUPPORTED; + is_neigh->metrics.metric_expense = METRICS_UNSUPPORTED; + is_neigh->metrics.metric_error = METRICS_UNSUPPORTED; + listnode_add (tlv_data.is_neighs, is_neigh); + } + + /* Add IS reachabilities. */ + for (ALL_LIST_ELEMENTS_RO (area->topology, node, arc)) + { + int to_lsp = 0; + + if ((lsp_top_num != arc->from_node) && (lsp_top_num != arc->to_node)) + continue; + + if (lsp_top_num == arc->from_node) + to_lsp = arc->to_node; + else + to_lsp = arc->from_node; + + if (area->oldmetric) + { + is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct is_neigh)); + + memcpy (&is_neigh->neigh_id, area->topology_baseis, ISIS_SYS_ID_LEN); + is_neigh->neigh_id[ISIS_SYS_ID_LEN - 1] = (to_lsp & 0xFF); + is_neigh->neigh_id[ISIS_SYS_ID_LEN - 2] = ((to_lsp >> 8) & 0xFF); + is_neigh->metrics.metric_default = arc->distance; + is_neigh->metrics.metric_delay = METRICS_UNSUPPORTED; + is_neigh->metrics.metric_expense = METRICS_UNSUPPORTED; + is_neigh->metrics.metric_error = METRICS_UNSUPPORTED; + listnode_add (tlv_data.is_neighs, is_neigh); + } + + if (area->newmetric) + { + if (tlv_data.te_is_neighs == NULL) + { + tlv_data.te_is_neighs = list_new (); + tlv_data.te_is_neighs->del = free_tlv; + } + te_is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct te_is_neigh)); + memcpy (&te_is_neigh->neigh_id, area->topology_baseis, + 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); + SET_TE_METRIC(te_is_neigh, arc->distance); + listnode_add (tlv_data.te_is_neighs, te_is_neigh); + } + } + + while (tlv_data.is_neighs && listcount (tlv_data.is_neighs)) + { + if (lsp->tlv_data.is_neighs == NULL) + lsp->tlv_data.is_neighs = list_new (); + lsp_tlv_fit (lsp, &tlv_data.is_neighs, &lsp->tlv_data.is_neighs, + IS_NEIGHBOURS_LEN, area->lsp_frag_threshold, + tlv_add_is_neighs); + if (tlv_data.is_neighs && listcount (tlv_data.is_neighs)) + lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, + lsp0, area, IS_LEVEL_1); + } + + while (tlv_data.te_is_neighs && listcount (tlv_data.te_is_neighs)) + { + if (lsp->tlv_data.te_is_neighs == NULL) + lsp->tlv_data.te_is_neighs = list_new (); + lsp_tlv_fit (lsp, &tlv_data.te_is_neighs, &lsp->tlv_data.te_is_neighs, + IS_NEIGHBOURS_LEN, area->lsp_frag_threshold, + tlv_add_te_is_neighs); + if (tlv_data.te_is_neighs && listcount (tlv_data.te_is_neighs)) + lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, + lsp0, area, IS_LEVEL_1); + } + + free_tlvs (&tlv_data); + return; +} +#endif /* TOPOLOGY_GENERATE */ diff --git a/isisd/isis_lsp.h b/isisd/isis_lsp.h new file mode 100644 index 0000000..a35bfa7 --- /dev/null +++ b/isisd/isis_lsp.h @@ -0,0 +1,126 @@ +/* + * IS-IS Rout(e)ing protocol - isis_lsp.h + * LSP processing + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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. + */ + +#ifndef _ZEBRA_ISIS_LSP_H +#define _ZEBRA_ISIS_LSP_H + +/* 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 + * sake it should better be avoided */ +struct isis_lsp +{ + struct isis_fixed_hdr *isis_header; /* normally equals pdu */ + struct isis_link_state_hdr *lsp_header; /* pdu + isis_header_len */ + struct stream *pdu; /* full pdu lsp */ + union + { + 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]; + int level; /* L1 or L2? */ + int scheduled; /* scheduled for sending */ + time_t installed; + time_t last_generated; + int own_lsp; +#ifdef TOPOLOGY_GENERATE + int from_topology; + struct thread *t_lsp_top_ref; +#endif + /* used for 60 second counting when rem_lifetime is zero */ + int age_out; + struct isis_area *area; + struct tlvs tlv_data; /* Simplifies TLV access */ +}; + +dict_t *lsp_db_init (void); +void lsp_db_destroy (dict_t * lspdb); +int lsp_tick (struct thread *thread); + +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); + +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, + 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, 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, u_char num_lsps, + struct list *list, dict_t * lspdb); + +void lsp_search_and_destroy (u_char * id, dict_t * lspdb); +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 +#define LSP_NEWER 2 +#define LSP_OLDER 3 + +#define LSP_PSEUDO_ID(I) ((I)[ISIS_SYS_ID_LEN]) +#define LSP_FRAGMENT(I) ((I)[ISIS_SYS_ID_LEN + 1]) +#define OWNLSPID(I) \ + memcpy ((I), isis->sysid, ISIS_SYS_ID_LEN);\ + (I)[ISIS_SYS_ID_LEN] = 0;\ + (I)[ISIS_SYS_ID_LEN + 1] = 0 +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 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); +void build_topology_lsp_data (struct isis_lsp *lsp, + struct isis_area *area, int lsp_top_num); +#endif /* TOPOLOGY_GENERATE */ + +#endif /* ISIS_LSP */ diff --git a/isisd/isis_main.c b/isisd/isis_main.c new file mode 100644 index 0000000..bbb1fb2 --- /dev/null +++ b/isisd/isis_main.c @@ -0,0 +1,383 @@ +/* + * IS-IS Rout(e)ing protocol - isis_main.c + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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 "getopt.h" +#include "thread.h" +#include "log.h" +#include +#include "command.h" +#include "vty.h" +#include "memory.h" +#include "stream.h" +#include "if.h" +#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" +#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_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" +/* Default vty port */ +#define ISISD_VTY_PORT 2608 + +/* isisd privileges */ +zebra_capabilities_t _caps_p[] = { + ZCAP_NET_RAW, + ZCAP_BIND +}; + +struct zebra_privs_t isisd_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 = sizeof (_caps_p) / sizeof (*_caps_p), + .cap_num_i = 0 +}; + +/* isisd 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'}, + {"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'}, + {"dryrun", no_argument, NULL, 'C'}, + {"help", no_argument, NULL, 'h'}, + {0} +}; + +/* Configuration file and directory. */ +char config_default[] = SYSCONFDIR ISISD_DEFAULT_CONFIG; +char *config_file = NULL; + +/* isisd program name. */ +char *progname; + +int daemon_mode = 0; + +/* Master of threads. */ +struct thread_master *master; + +/* Process ID saved for use by init system */ +const char *pid_file = PATH_ISISD_PID; + +/* for reload */ +char _cwd[MAXPATHLEN]; +char _progpath[MAXPATHLEN]; +int _argc; +char **_argv; +char **_envp; + +/* + * Prototypes. + */ +void reload(void); +void sighup(void); +void sigint(void); +void sigterm(void); +void sigusr1(void); + + +/* Help information display. */ +static void +usage (int status) +{ + if (status != 0) + fprintf (stderr, "Try `%s --help' for more information.\n", progname); + else + { + printf ("Usage : %s [OPTION...]\n\n\ +Daemon which manages IS-IS routing\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\ +-C, --dryrun Check configuration for validity and exit\n\ +-h, --help Display this help and exit\n\ +\n\ +Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); + } + + exit (status); +} + + +void +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 +terminate (int i) +{ + exit (i); +} + +/* + * Signal handlers + */ + +void +sighup (void) +{ + zlog_debug ("SIGHUP received"); + reload (); + + return; +} + +void +sigint (void) +{ + zlog_notice ("Terminating on signal SIGINT"); + terminate (0); +} + +void +sigterm (void) +{ + zlog_notice ("Terminating on signal SIGTERM"); + terminate (0); +} + +void +sigusr1 (void) +{ + zlog_debug ("SIGUSR1 received"); + zlog_rotate (NULL); +} + +struct quagga_signal_t isisd_signals[] = +{ + { + .signal = SIGHUP, + .handler = &sighup, + }, + { + .signal = SIGUSR1, + .handler = &sigusr1, + }, + { + .signal = SIGINT, + .handler = &sigint, + }, + { + .signal = SIGTERM, + .handler = &sigterm, + }, +}; + +/* + * Main routine of isisd. Parse arguments and handle IS-IS state machine. + */ +int +main (int argc, char **argv, char **envp) +{ + char *p; + int opt, vty_port = ISISD_VTY_PORT; + struct thread thread; + char *config_file = NULL; + char *vty_addr = NULL; + int dryrun = 0; + + /* Get the programname without the preceding path. */ + progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]); + + zlog_default = openzlog (progname, ZLOG_ISIS, + LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON); + + /* for reload */ + _argc = argc; + _argv = argv; + _envp = envp; + getcwd (_cwd, sizeof (_cwd)); + if (*argv[0] == '.') + snprintf (_progpath, sizeof (_progpath), "%s/%s", _cwd, _argv[0]); + else + snprintf (_progpath, sizeof (_progpath), "%s", argv[0]); + + /* Command line argument treatment. */ + while (1) + { + opt = getopt_long (argc, argv, "df:i:z:hA:p:P:u:g:vC", 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': + zclient_serv_path_set (optarg); + break; + case 'A': + vty_addr = optarg; + break; + case 'P': + /* Deal with atoi() returning 0 on failure, and isisd not + listening on isisd port... */ + if (strcmp (optarg, "0") == 0) + { + vty_port = 0; + break; + } + vty_port = atoi (optarg); + vty_port = (vty_port ? vty_port : ISISD_VTY_PORT); + break; + case 'u': + isisd_privs.user = optarg; + break; + case 'g': + isisd_privs.group = optarg; + break; + case 'v': + printf ("ISISd version %s\n", ISISD_VERSION); + printf ("Copyright (c) 2001-2002 Sampo Saaristo," + " Ofer Wald and Hannes Gredler\n"); + print_version ("Zebra"); + exit (0); + break; + case 'C': + dryrun = 1; + break; + case 'h': + usage (0); + break; + default: + usage (1); + break; + } + } + + /* thread master */ + master = thread_master_create (); + + /* random seed from time */ + srandom (time (NULL)); + + /* + * initializations + */ + zprivs_init (&isisd_privs); + 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 (); + 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); + + /* Start execution only if not in dry-run mode */ + if (dryrun) + return(0); + + /* demonize */ + if (daemon_mode && daemon (0, 0) < 0) + { + zlog_err("IS-IS daemon failed: %s", strerror(errno)); + exit (1); + } + + /* Process ID file creation. */ + if (pid_file[0] != '\0') + pid_output (pid_file); + + /* Make isis vty socket. */ + vty_serv_sock (vty_addr, vty_port, ISIS_VTYSH_PATH); + + /* Print banner. */ + zlog_notice ("Quagga-ISISd %s starting: vty@%d", QUAGGA_VERSION, vty_port); + + /* Start finite state machine. */ + while (thread_fetch (master, &thread)) + thread_call (&thread); + + /* Not reached. */ + exit (0); +} diff --git a/isisd/isis_misc.c b/isisd/isis_misc.c new file mode 100644 index 0000000..f19b441 --- /dev/null +++ b/isisd/isis_misc.c @@ -0,0 +1,633 @@ +/* + * IS-IS Rout(e)ing protocol - isis_misc.h + * Miscellanous routines + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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 "stream.h" +#include "vty.h" +#include "hash.h" +#include "if.h" +#include "command.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/isis_csm.h" +#include "isisd/isisd.h" +#include "isisd/isis_misc.h" + +#include "isisd/isis_tlv.h" +#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; +/* len of xxxx.xxxx.xxxx + place for #0 termination */ +char sysid[15]; +/* len of xxxx.xxxx.xxxx + place for #0 termination */ +char snpa[15]; +/* len of xx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xx */ +char isonet[51]; +/* + place for #0 termination */ +/* len of xxxx.xxxx.xxxx.xx.xx + place for #0 termination */ +char lspid[21]; +/* len of xxYxxMxWxdxxhxxmxxs + place for #0 termination */ +char datestring[20]; +char nlpidstring[30]; + +/* + * This converts the isonet to its printable format + */ +const char * +isonet_print (const u_char * from, int len) +{ + int i = 0; + char *pos = isonet; + + if (!from) + return "unknown"; + + while (i < len) + { + if (i & 1) + { + sprintf (pos, "%02x", *(from + i)); + pos += 2; + } + else + { + if (i == (len - 1)) + { /* No dot at the end of address */ + sprintf (pos, "%02x", *(from + i)); + pos += 2; + } + else + { + sprintf (pos, "%02x.", *(from + i)); + pos += 3; + } + } + i++; + } + *(pos) = '\0'; + return isonet; +} + +/* + * Returns 0 on error, length of buff on ok + * extract dot from the dotted str, and insert all the number in a buff + */ +int +dotformat2buff (u_char * buff, const char * dotted) +{ + int dotlen, len = 0; + const char *pos = dotted; + u_char number[3]; + int nextdotpos = 2; + + number[2] = '\0'; + dotlen = strlen(dotted); + if (dotlen > 50) + { + /* this can't be an iso net, its too long */ + return 0; + } + + while ((pos - dotted) < dotlen && len < 20) + { + if (*pos == '.') + { + /* we expect the . at 2, and than every 5 */ + if ((pos - dotted) != nextdotpos) + { + len = 0; + break; + } + nextdotpos += 5; + pos++; + continue; + } + /* we must have at least two chars left here */ + if (dotlen - (pos - dotted) < 2) + { + len = 0; + break; + } + + if ((isxdigit ((int) *pos)) && (isxdigit ((int) *(pos + 1)))) + { + memcpy (number, pos, 2); + pos += 2; + } + else + { + len = 0; + break; + } + + *(buff + len) = (char) strtol ((char *)number, NULL, 16); + len++; + } + + return len; +} + +/* + * conversion of XXXX.XXXX.XXXX to memory + */ +int +sysid2buff (u_char * buff, const char * dotted) +{ + int len = 0; + const char *pos = dotted; + u_char number[3]; + + number[2] = '\0'; + // surely not a sysid_string if not 14 length + if (strlen (dotted) != 14) + { + return 0; + } + + while (len < ISIS_SYS_ID_LEN) + { + if (*pos == '.') + { + /* the . is not positioned correctly */ + if (((pos - dotted) != 4) && ((pos - dotted) != 9)) + { + len = 0; + break; + } + pos++; + continue; + } + if ((isxdigit ((int) *pos)) && (isxdigit ((int) *(pos + 1)))) + { + memcpy (number, pos, 2); + pos += 2; + } + else + { + len = 0; + break; + } + + *(buff + len) = (char) strtol ((char *)number, NULL, 16); + len++; + } + + return len; + +} + +/* + * converts the nlpids struct (filled by TLV #129) + * into a string + */ + +char * +nlpid2string (struct nlpids *nlpids) +{ + char *pos = nlpidstring; + int i; + + for (i = 0; i < nlpids->count; i++) + { + switch (nlpids->nlpids[i]) + { + case NLPID_IP: + pos += sprintf (pos, "IPv4"); + break; + case NLPID_IPV6: + pos += sprintf (pos, "IPv6"); + break; + case NLPID_SNAP: + pos += sprintf (pos, "SNAP"); + break; + case NLPID_CLNP: + pos += sprintf (pos, "CLNP"); + break; + case NLPID_ESIS: + pos += sprintf (pos, "ES-IS"); + break; + default: + pos += sprintf (pos, "unknown"); + break; + } + if (nlpids->count - i > 1) + pos += sprintf (pos, ", "); + + } + + *(pos) = '\0'; + + return nlpidstring; +} + +/* + * supports the given af ? + */ +int +speaks (struct nlpids *nlpids, int family) +{ + int i, speaks = 0; + + if (nlpids == (struct nlpids *) NULL) + return speaks; + for (i = 0; i < nlpids->count; i++) + { + if (family == AF_INET && nlpids->nlpids[i] == NLPID_IP) + speaks = 1; + if (family == AF_INET6 && nlpids->nlpids[i] == NLPID_IPV6) + speaks = 1; + } + + return speaks; +} + +/* + * Returns 0 on error, IS-IS Circuit Type on ok + */ +int +string2circuit_t (const char * str) +{ + + if (!str) + return 0; + + if (!strcmp (str, "level-1")) + return IS_LEVEL_1; + + if (!strcmp (str, "level-2-only") || !strcmp (str, "level-2")) + return IS_LEVEL_2; + + if (!strcmp (str, "level-1-2")) + return IS_LEVEL_1_AND_2; + + 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) +{ + switch (circuit_t) + { + case IS_LEVEL_1: + return "L1"; + case IS_LEVEL_2: + return "L2"; + case IS_LEVEL_1_AND_2: + return "L1L2"; + default: + return "??"; + } + + return NULL; /* not reached */ +} + +const char * +syst2string (int type) +{ + switch (type) + { + case ISIS_SYSTYPE_ES: + return "ES"; + case ISIS_SYSTYPE_IS: + return "IS"; + case ISIS_SYSTYPE_L1_IS: + return "1"; + case ISIS_SYSTYPE_L2_IS: + return "2"; + default: + return "??"; + } + + return NULL; /* not reached */ +} + +/* + * Print functions - we print to static vars + */ +const char * +snpa_print (const u_char * from) +{ + int i = 0; + u_char *pos = (u_char *)snpa; + + if (!from) + return "unknown"; + + while (i < ETH_ALEN - 1) + { + if (i & 1) + { + sprintf ((char *)pos, "%02x.", *(from + i)); + pos += 3; + } + else + { + sprintf ((char *)pos, "%02x", *(from + i)); + pos += 2; + + } + i++; + } + + sprintf ((char *)pos, "%02x", *(from + (ISIS_SYS_ID_LEN - 1))); + pos += 2; + *(pos) = '\0'; + + return snpa; +} + +const char * +sysid_print (const u_char * from) +{ + int i = 0; + char *pos = sysid; + + if (!from) + return "unknown"; + + while (i < ISIS_SYS_ID_LEN - 1) + { + if (i & 1) + { + sprintf (pos, "%02x.", *(from + i)); + pos += 3; + } + else + { + sprintf (pos, "%02x", *(from + i)); + pos += 2; + + } + i++; + } + + sprintf (pos, "%02x", *(from + (ISIS_SYS_ID_LEN - 1))); + pos += 2; + *(pos) = '\0'; + + return sysid; +} + +const char * +rawlspid_print (const u_char * from) +{ + char *pos = lspid; + if (!from) + return "unknown"; + memcpy (pos, sysid_print (from), 15); + pos += 14; + sprintf (pos, ".%02x", LSP_PSEUDO_ID (from)); + pos += 3; + sprintf (pos, "-%02x", LSP_FRAGMENT (from)); + pos += 3; + + *(pos) = '\0'; + + return lspid; +} + +const char * +time2string (u_int32_t time) +{ + char *pos = datestring; + u_int32_t rest; + + if (time == 0) + return "-"; + + if (time / SECS_PER_YEAR) + pos += sprintf (pos, "%uY", time / SECS_PER_YEAR); + rest = time % SECS_PER_YEAR; + if (rest / SECS_PER_MONTH) + pos += sprintf (pos, "%uM", rest / SECS_PER_MONTH); + rest = rest % SECS_PER_MONTH; + if (rest / SECS_PER_WEEK) + pos += sprintf (pos, "%uw", rest / SECS_PER_WEEK); + rest = rest % SECS_PER_WEEK; + if (rest / SECS_PER_DAY) + pos += sprintf (pos, "%ud", rest / SECS_PER_DAY); + rest = rest % SECS_PER_DAY; + if (rest / SECS_PER_HOUR) + pos += sprintf (pos, "%uh", rest / SECS_PER_HOUR); + rest = rest % SECS_PER_HOUR; + if (rest / SECS_PER_MINUTE) + pos += sprintf (pos, "%um", rest / SECS_PER_MINUTE); + rest = rest % SECS_PER_MINUTE; + if (rest) + pos += sprintf (pos, "%us", rest); + + *(pos) = 0; + + return datestring; +} + +/* + * routine to decrement a timer by a random + * number + * + * first argument is the timer and the second is + * the jitter + */ +unsigned long +isis_jitter (unsigned long timer, unsigned long jitter) +{ + int j, k; + + if (jitter >= 100) + return timer; + + if (timer == 1) + return timer; + /* + * randomizing just the percent value provides + * no good random numbers - hence the spread + * to RANDOM_SPREAD (100000), which is ok as + * most IS-IS timers are no longer than 16 bit + */ + + j = 1 + (int) ((RANDOM_SPREAD * random ()) / (RAND_MAX + 1.0)); + + k = timer - (timer * (100 - jitter)) / 100; + + timer = timer - (k * j / RANDOM_SPREAD); + + return timer; +} + +struct in_addr +newprefix2inaddr (u_char * prefix_start, u_char prefix_masklen) +{ + memset (&new_prefix, 0, sizeof (new_prefix)); + memcpy (&new_prefix, prefix_start, (prefix_masklen & 0x3F) ? + ((((prefix_masklen & 0x3F) - 1) >> 3) + 1) : 0); + return new_prefix; +} + +/* + * Returns host.name if any, otherwise + * it returns the system hostname. + */ +const char * +unix_hostname (void) +{ + static struct utsname names; + const char *hostname; + + hostname = host.name; + if (!hostname) + { + uname (&names); + hostname = names.nodename; + } + + 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 new file mode 100644 index 0000000..37eaea1 --- /dev/null +++ b/isisd/isis_misc.h @@ -0,0 +1,83 @@ +/* + * IS-IS Rout(e)ing protocol - isis_misc.h + * Miscellanous routines + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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. + */ + +#ifndef _ZEBRA_ISIS_MISC_H +#define _ZEBRA_ISIS_MISC_H + +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); +/* + * Converting input to memory stored format + * return value of 0 indicates wrong input + */ +int dotformat2buff (u_char *, const char *); +int sysid2buff (u_char *, const char *); + +/* + * Printing functions + */ +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 + */ +int speaks (struct nlpids *nlpids, int family); +unsigned long isis_jitter (unsigned long timer, unsigned long jitter); +const char *unix_hostname (void); + +/* + * macros + */ +#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 */ + +#define SECS_PER_MINUTE 60 +#define SECS_PER_HOUR 3600 +#define SECS_PER_DAY 86400 +#define SECS_PER_WEEK 604800 +#define SECS_PER_MONTH 2628000 +#define SECS_PER_YEAR 31536000 + +enum +{ + ISIS_UI_LEVEL_BRIEF, + ISIS_UI_LEVEL_DETAIL, + ISIS_UI_LEVEL_EXTENSIVE, +}; + +#endif diff --git a/isisd/isis_network.h b/isisd/isis_network.h new file mode 100644 index 0000000..e1e10df --- /dev/null +++ b/isisd/isis_network.h @@ -0,0 +1,37 @@ +/* + * IS-IS Rout(e)ing protocol - isis_network.h + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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. + */ + + +#ifndef _ZEBRA_ISIS_NETWORK_H +#define _ZEBRA_ISIS_NETWORK_H + +extern u_char ALL_L1_ISYSTEMS[]; +extern u_char ALL_L2_ISYSTEMS[]; + +int isis_sock_init (struct isis_circuit *circuit); + +int isis_recv_pdu_bcast (struct isis_circuit *circuit, u_char * ssnpa); +int isis_recv_pdu_p2p (struct isis_circuit *circuit, u_char * ssnpa); +int isis_send_pdu_bcast (struct isis_circuit *circuit, int level); +int isis_send_pdu_p2p (struct isis_circuit *circuit, int level); + +#endif /* _ZEBRA_ISIS_NETWORK_H */ diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c new file mode 100644 index 0000000..1dfb462 --- /dev/null +++ b/isisd/isis_pdu.c @@ -0,0 +1,3257 @@ +/* + * IS-IS Rout(e)ing protocol - isis_pdu.c + * PDU processing + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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 "memory.h" +#include "thread.h" +#include "linklist.h" +#include "log.h" +#include "stream.h" +#include "vty.h" +#include "hash.h" +#include "prefix.h" +#include "if.h" +#include "checksum.h" +#include "md5.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_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_tlv.h" +#include "isisd/isisd.h" +#include "isisd/isis_dynhn.h" +#include "isisd/isis_lsp.h" +#include "isisd/isis_pdu.h" +#include "isisd/iso_checksum.h" +#include "isisd/isis_csm.h" +#include "isisd/isis_events.h" +#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 */ + +#ifndef PNBBY +#define PNBBY 8 +#endif /* PNBBY */ + +/* Utility mask array. */ +static const u_char maskbit[] = { + 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff +}; + +/* + * HELPER FUNCS + */ + +/* + * Compares two sets of area addresses + */ +static int +area_match (struct list *left, struct list *right) +{ + struct area_addr *addr1, *addr2; + struct listnode *node1, *node2; + + for (ALL_LIST_ELEMENTS_RO (left, node1, addr1)) + { + for (ALL_LIST_ELEMENTS_RO (right, node2, addr2)) + { + if (addr1->addr_len == addr2->addr_len && + !memcmp (addr1->area_addr, addr2->area_addr, (int) addr1->addr_len)) + return 1; /* match */ + } + } + + return 0; /* mismatch */ +} + +/* + * Check if ip2 is in the ip1's network (function like Prefix.h:prefix_match() ) + * param ip1 the IS interface ip address structure + * param ip2 the IIH's ip address + * return 0 the IIH's IP is not in the IS's subnetwork + * 1 the IIH's IP is in the IS's subnetwork + */ +static int +ip_same_subnet (struct prefix_ipv4 *ip1, struct in_addr *ip2) +{ + u_char *addr1, *addr2; + int shift, offset, offsetloop; + int len; + + addr1 = (u_char *) & ip1->prefix.s_addr; + addr2 = (u_char *) & ip2->s_addr; + len = ip1->prefixlen; + + shift = len % PNBBY; + offsetloop = offset = len / PNBBY; + + while (offsetloop--) + if (addr1[offsetloop] != addr2[offsetloop]) + return 0; + + if (shift) + if (maskbit[shift] & (addr1[offset] ^ addr2[offset])) + return 0; + + return 1; /* match */ +} + +/* + * Compares two set of ip addresses + * param left the local interface's ip addresses + * param right the iih interface's ip address + * return 0 no match; + * 1 match; + */ +static int +ip_match (struct list *left, struct list *right) +{ + struct prefix_ipv4 *ip1; + struct in_addr *ip2; + struct listnode *node1, *node2; + + if ((left == NULL) || (right == NULL)) + return 0; + + for (ALL_LIST_ELEMENTS_RO (left, node1, ip1)) + { + for (ALL_LIST_ELEMENTS_RO (right, node2, ip2)) + { + if (ip_same_subnet (ip1, ip2)) + { + return 1; /* match */ + } + } + + } + return 0; +} + +/* + * Checks whether we should accept a PDU of given level + */ +static int +accept_level (int level, int circuit_t) +{ + int retval = ((circuit_t & level) == level); /* simple approach */ + + return retval; +} + +/* + * Verify authentication information + * Support cleartext and HMAC MD5 authentication + */ +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]; + + /* 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_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; + } + + /* + * 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; + struct nlpids *tlv_nlpids; + + if (tlvs->nlpids) + { + + tlv_nlpids = tlvs->nlpids; + if (tlv_nlpids->count > array_size (adj->nlpids.nlpids)) + return 1; + + adj->nlpids.count = tlv_nlpids->count; + + for (i = 0; i < tlv_nlpids->count; i++) + { + adj->nlpids.nlpids[i] = tlv_nlpids->nlpids[i]; + } + } + return 0; +} + +static void +tlvs_to_adj_ipv4_addrs (struct tlvs *tlvs, struct isis_adjacency *adj) +{ + struct listnode *node; + struct in_addr *ipv4_addr, *malloced; + + if (adj->ipv4_addrs) + { + adj->ipv4_addrs->del = del_addr; + list_delete (adj->ipv4_addrs); + } + adj->ipv4_addrs = list_new (); + if (tlvs->ipv4_addrs) + { + for (ALL_LIST_ELEMENTS_RO (tlvs->ipv4_addrs, node, ipv4_addr)) + { + malloced = XMALLOC (MTYPE_ISIS_TMP, sizeof (struct in_addr)); + memcpy (malloced, ipv4_addr, sizeof (struct in_addr)); + listnode_add (adj->ipv4_addrs, malloced); + } + } +} + +#ifdef HAVE_IPV6 +static void +tlvs_to_adj_ipv6_addrs (struct tlvs *tlvs, struct isis_adjacency *adj) +{ + struct listnode *node; + struct in6_addr *ipv6_addr, *malloced; + + if (adj->ipv6_addrs) + { + adj->ipv6_addrs->del = del_addr; + list_delete (adj->ipv6_addrs); + } + adj->ipv6_addrs = list_new (); + if (tlvs->ipv6_addrs) + { + for (ALL_LIST_ELEMENTS_RO (tlvs->ipv6_addrs, node, ipv6_addr)) + { + malloced = XMALLOC (MTYPE_ISIS_TMP, sizeof (struct in6_addr)); + memcpy (malloced, ipv6_addr, sizeof (struct in6_addr)); + listnode_add (adj->ipv6_addrs, malloced); + } + } + +} +#endif /* HAVE_IPV6 */ + +/* + * RECEIVE SIDE + */ + +/* + * Process P2P IIH + * ISO - 10589 + * Section 8.2.5 - Receiving point-to-point IIH PDUs + * + */ +static int +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 = 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) + { + zlog_warn ("Packet too short"); + return ISIS_WARNING; + } + + /* 8.2.5.1 PDU acceptance tests */ + + /* 8.2.5.1 a) external domain untrue */ + /* FIXME: not useful at all? */ + + /* 8.2.5.1 b) ID Length mismatch */ + /* checked at the handle_pdu */ + + /* 8.2.5.2 IIH PDU Processing */ + + /* 8.2.5.2 a) 1) Maximum Area Addresses */ + /* Already checked, and can also be ommited */ + + /* + * Get the header + */ + hdr = (struct isis_p2p_hello_hdr *) STREAM_PNT (circuit->rcv_stream); + pdu_len = ntohs (hdr->pdu_len); + + if (pdu_len < (ISIS_FIXED_HDR_LEN + ISIS_P2PHELLO_HDRLEN) || + pdu_len > ISO_MTU(circuit) || + pdu_len > stream_get_endp (circuit->rcv_stream)) + { + 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; + } + + /* + * 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); + + stream_forward_getp (circuit->rcv_stream, ISIS_P2PHELLO_HDRLEN); + + /* + * Lets get the TLVS now + */ + expected |= TLVFLAG_AREA_ADDRS; + expected |= TLVFLAG_AUTH_INFO; + expected |= TLVFLAG_NLPID; + 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), + 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->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)) + { + 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 */ + 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 + if (found & TLVFLAG_IPV6_ADDR) + tlvs_to_adj_ipv6_addrs (&tlvs, adj); +#endif /* HAVE_IPV6 */ + + /* 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); + + /* 8.2.5.2 a) a match was detected */ + if (area_match (circuit->area->area_addrs, tlvs.area_addrs)) + { + /* 8.2.5.2 a) 2) If the system is L1 - table 5 */ + if (circuit->area->is_type == IS_LEVEL_1) + { + switch (hdr->circuit_t) + { + case IS_LEVEL_1: + case IS_LEVEL_1_AND_2: + if (adj->adj_state != ISIS_ADJ_UP) + { + /* (4) adj state up */ + isis_adj_state_change (adj, ISIS_ADJ_UP, NULL); + /* (5) adj usage level 1 */ + adj->adj_usage = ISIS_ADJ_LEVEL1; + } + else if (adj->adj_usage == ISIS_ADJ_LEVEL1) + { + ; /* accept */ + } + break; + case IS_LEVEL_2: + if (adj->adj_state != ISIS_ADJ_UP) + { + /* (7) reject - wrong system type event */ + zlog_warn ("wrongSystemType"); + free_tlvs (&tlvs); + return ISIS_WARNING; /* Reject */ + } + else if (adj->adj_usage == ISIS_ADJ_LEVEL1) + { + /* (6) down - wrong system */ + isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System"); + } + break; + } + } + + /* 8.2.5.2 a) 3) If the system is L1L2 - table 6 */ + if (circuit->area->is_type == IS_LEVEL_1_AND_2) + { + switch (hdr->circuit_t) + { + case IS_LEVEL_1: + if (adj->adj_state != ISIS_ADJ_UP) + { + /* (6) adj state up */ + isis_adj_state_change (adj, ISIS_ADJ_UP, NULL); + /* (7) adj usage level 1 */ + adj->adj_usage = ISIS_ADJ_LEVEL1; + } + else if (adj->adj_usage == ISIS_ADJ_LEVEL1) + { + ; /* accept */ + } + else if ((adj->adj_usage == ISIS_ADJ_LEVEL1AND2) || + (adj->adj_usage == ISIS_ADJ_LEVEL2)) + { + /* (8) down - wrong system */ + isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System"); + } + break; + case IS_LEVEL_2: + if (adj->adj_state != ISIS_ADJ_UP) + { + /* (6) adj state up */ + isis_adj_state_change (adj, ISIS_ADJ_UP, NULL); + /* (9) adj usage level 2 */ + adj->adj_usage = ISIS_ADJ_LEVEL2; + } + else if ((adj->adj_usage == ISIS_ADJ_LEVEL1) || + (adj->adj_usage == ISIS_ADJ_LEVEL1AND2)) + { + /* (8) down - wrong system */ + isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System"); + } + else if (adj->adj_usage == ISIS_ADJ_LEVEL2) + { + ; /* Accept */ + } + break; + case IS_LEVEL_1_AND_2: + if (adj->adj_state != ISIS_ADJ_UP) + { + /* (6) adj state up */ + isis_adj_state_change (adj, ISIS_ADJ_UP, NULL); + /* (10) adj usage level 1 */ + adj->adj_usage = ISIS_ADJ_LEVEL1AND2; + } + else if ((adj->adj_usage == ISIS_ADJ_LEVEL1) || + (adj->adj_usage == ISIS_ADJ_LEVEL2)) + { + /* (8) down - wrong system */ + isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System"); + } + else if (adj->adj_usage == ISIS_ADJ_LEVEL1AND2) + { + ; /* Accept */ + } + break; + } + } + + /* 8.2.5.2 a) 4) If the system is L2 - table 7 */ + if (circuit->area->is_type == IS_LEVEL_2) + { + switch (hdr->circuit_t) + { + case IS_LEVEL_1: + if (adj->adj_state != ISIS_ADJ_UP) + { + /* (5) reject - wrong system type event */ + zlog_warn ("wrongSystemType"); + free_tlvs (&tlvs); + return ISIS_WARNING; /* Reject */ + } + else if ((adj->adj_usage == ISIS_ADJ_LEVEL1AND2) || + (adj->adj_usage == ISIS_ADJ_LEVEL2)) + { + /* (6) down - wrong system */ + isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System"); + } + break; + case IS_LEVEL_1_AND_2: + case IS_LEVEL_2: + if (adj->adj_state != ISIS_ADJ_UP) + { + /* (7) adj state up */ + isis_adj_state_change (adj, ISIS_ADJ_UP, NULL); + /* (8) adj usage level 2 */ + adj->adj_usage = ISIS_ADJ_LEVEL2; + } + else if (adj->adj_usage == ISIS_ADJ_LEVEL1AND2) + { + /* (6) down - wrong system */ + isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System"); + } + else if (adj->adj_usage == ISIS_ADJ_LEVEL2) + { + ; /* Accept */ + } + break; + } + } + } + /* 8.2.5.2 b) if no match was detected */ + else if (listcount (circuit->area->area_addrs) > 0) + { + if (circuit->area->is_type == IS_LEVEL_1) + { + /* 8.2.5.2 b) 1) is_type L1 and adj is not up */ + if (adj->adj_state != ISIS_ADJ_UP) + { + isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Area Mismatch"); + /* 8.2.5.2 b) 2)is_type L1 and adj is up */ + } + else + { + isis_adj_state_change (adj, ISIS_ADJ_DOWN, + "Down - Area Mismatch"); + } + } + /* 8.2.5.2 b 3 If the system is L2 or L1L2 - table 8 */ + else + { + switch (hdr->circuit_t) + { + case IS_LEVEL_1: + if (adj->adj_state != ISIS_ADJ_UP) + { + /* (6) reject - Area Mismatch event */ + zlog_warn ("AreaMismatch"); + free_tlvs (&tlvs); + return ISIS_WARNING; /* Reject */ + } + else if (adj->adj_usage == ISIS_ADJ_LEVEL1) + { + /* (7) down - area mismatch */ + isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Area Mismatch"); + + } + else if ((adj->adj_usage == ISIS_ADJ_LEVEL1AND2) || + (adj->adj_usage == ISIS_ADJ_LEVEL2)) + { + /* (7) down - wrong system */ + isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System"); + } + break; + case IS_LEVEL_1_AND_2: + case IS_LEVEL_2: + if (adj->adj_state != ISIS_ADJ_UP) + { + /* (8) adj state up */ + isis_adj_state_change (adj, ISIS_ADJ_UP, NULL); + /* (9) adj usage level 2 */ + adj->adj_usage = ISIS_ADJ_LEVEL2; + } + else if (adj->adj_usage == ISIS_ADJ_LEVEL1) + { + /* (7) down - wrong system */ + isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System"); + } + else if (adj->adj_usage == ISIS_ADJ_LEVEL1AND2) + { + if (hdr->circuit_t == IS_LEVEL_2) + { + /* (7) down - wrong system */ + isis_adj_state_change (adj, ISIS_ADJ_DOWN, + "Wrong System"); + } + else + { + /* (7) down - area mismatch */ + isis_adj_state_change (adj, ISIS_ADJ_DOWN, + "Area Mismatch"); + } + } + else if (adj->adj_usage == ISIS_ADJ_LEVEL2) + { + ; /* Accept */ + } + break; + } + } + } + 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 */ + + /* some of my own understanding of the ISO, why the heck does + * it not say what should I change the system_type to... + */ + switch (adj->adj_usage) + { + case ISIS_ADJ_LEVEL1: + adj->sys_type = ISIS_SYSTYPE_L1_IS; + break; + case ISIS_ADJ_LEVEL2: + adj->sys_type = ISIS_SYSTYPE_L2_IS; + break; + case ISIS_ADJ_LEVEL1AND2: + adj->sys_type = ISIS_SYSTYPE_L2_IS; + break; + case ISIS_ADJ_NONE: + adj->sys_type = ISIS_SYSTYPE_UNKNOWN; + break; + } + + + 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); + + return retval; +} + +/* + * Process IS-IS LAN Level 1/2 Hello PDU + */ +static int +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 = 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) + { + zlog_warn ("Packet too short"); + return ISIS_WARNING; + } + + if (circuit->ext_domain) + { + zlog_debug ("level %d LAN Hello received over circuit with " + "externalDomain = true", level); + return ISIS_WARNING; + } + + if (!accept_level (level, circuit->is_type)) + { + if (isis->debugs & DEBUG_ADJ_PACKETS) + { + zlog_debug ("ISIS-Adj (%s): Interface level mismatch, %s", + circuit->area->area_tag, circuit->interface->name); + } + return ISIS_WARNING; + } + +#if 0 + /* Cisco's debug message compatability */ + if (!accept_level (level, circuit->area->is_type)) + { + if (isis->debugs & DEBUG_ADJ_PACKETS) + { + zlog_debug ("ISIS-Adj (%s): is type mismatch", + circuit->area->area_tag); + } + return ISIS_WARNING; + } +#endif + /* + * Fill the header + */ + hdr.circuit_t = stream_getc (circuit->rcv_stream); + stream_get (hdr.source_id, circuit->rcv_stream, ISIS_SYS_ID_LEN); + hdr.hold_time = stream_getw (circuit->rcv_stream); + hdr.pdu_len = stream_getw (circuit->rcv_stream); + hdr.prio = stream_getc (circuit->rcv_stream); + stream_get (hdr.lan_id, circuit->rcv_stream, ISIS_SYS_ID_LEN + 1); + + 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_err ("Level %d LAN Hello with Circuit Type %d", level, + hdr.circuit_t); + return ISIS_ERROR; + } + + /* + * Then get the tlvs + */ + expected |= TLVFLAG_AUTH_INFO; + expected |= TLVFLAG_AREA_ADDRS; + expected |= TLVFLAG_LAN_NEIGHS; + expected |= TLVFLAG_NLPID; + 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, + &auth_tlv_offset); + + if (retval > ISIS_WARNING) + { + zlog_warn ("parse_tlvs() failed"); + goto out; + } + + if (!(found & TLVFLAG_AREA_ADDRS)) + { + zlog_warn ("No Area addresses TLV in Level %d LAN IS to IS hello", + level); + retval = ISIS_WARNING; + 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->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 (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) + { + zlog_debug ("ISIS-Adj (%s): Area mismatch, level %d IIH on %s", + circuit->area->area_tag, level, + circuit->interface->name); + } + retval = ISIS_OK; + goto out; + } + + /* + * it's own IIH PDU - discard silently + */ + if (!memcmp (circuit->u.bc.snpa, ssnpa, ETH_ALEN)) + { + zlog_debug ("ISIS-Adj (%s): it's own IIH PDU - discarded", + circuit->area->area_tag); + + retval = ISIS_OK; + goto out; + } + + /* + * 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 LAN IIH from %s\n", circuit->interface->name); + } +#ifndef HAVE_IPV6 + else /* !(found & TLVFLAG_IPV4_ADDR) */ + zlog_warn ("ISIS-Adj: no IPv4 in LAN IIH from %s " + "(this isisd has no IPv6)\n", circuit->interface->name); + +#else + if (found & TLVFLAG_IPV6_ADDR) + { + /* TBA: check that we have a linklocal ourselves? */ + struct listnode *node; + struct in6_addr *ip; + for (ALL_LIST_ELEMENTS_RO (tlvs.ipv6_addrs, node, ip)) + if (IN6_IS_ADDR_LINKLOCAL (ip)) + { + v6_usable = 1; + break; + } + + if (!v6_usable) + zlog_warn ("ISIS-Adj: IPv6 addresses present but no link-local " + "in LAN IIH from %s\n", circuit->interface->name); + } + + if (!(found & (TLVFLAG_IPV4_ADDR | TLVFLAG_IPV6_ADDR))) + zlog_warn ("ISIS-Adj: neither IPv4 nor IPv6 addr in LAN IIH from %s\n", + circuit->interface->name); +#endif + + if (!v6_usable && !v4_usable) + { + free_tlvs (&tlvs); + return ISIS_WARNING; + } + + + adj = isis_adj_lookup (hdr.source_id, circuit->u.bc.adjdb[level - 1]); + if ((adj == NULL) || (memcmp(adj->snpa, ssnpa, ETH_ALEN)) || + (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 == IS_LEVEL_1) + adj->sys_type = ISIS_SYSTYPE_L1_IS; + else + 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]); + } + + if(adj->dis_record[level-1].dis==ISIS_IS_DIS) + switch (level) + { + 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); + memcpy (&circuit->u.bc.l1_desig_is, hdr.lan_id, + ISIS_SYS_ID_LEN + 1); + } + break; + 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); + memcpy (&circuit->u.bc.l2_desig_is, hdr.lan_id, + ISIS_SYS_ID_LEN + 1); + } + break; + } + + adj->hold_time = hdr.hold_time; + adj->last_upd = time (NULL); + adj->prio[level - 1] = hdr.prio; + + memcpy (adj->lanid, hdr.lan_id, ISIS_SYS_ID_LEN + 1); + + tlvs_to_adj_area_addrs (&tlvs, adj); + + /* which protocol are spoken ??? */ + 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) + tlvs_to_adj_ipv4_addrs (&tlvs, adj); + +#ifdef HAVE_IPV6 + if (found & TLVFLAG_IPV6_ADDR) + tlvs_to_adj_ipv6_addrs (&tlvs, adj); +#endif /* HAVE_IPV6 */ + + adj->circuit_t = hdr.circuit_t; + + /* 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); + + /* + * If the snpa for this circuit is found from LAN Neighbours TLV + * we have two-way communication -> adjacency can be put to state "up" + */ + + if (found & TLVFLAG_LAN_NEIGHS) + { + 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"); + } + } + } + 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: + if (isis->debugs & DEBUG_ADJ_PACKETS) + { + zlog_debug ("ISIS-Adj (%s): Rcvd L%d LAN IIH from %s on %s, cirType %s, " + "cirID %u, length %zd", + circuit->area->area_tag, + level, snpa_print (ssnpa), circuit->interface->name, + circuit_t2string (circuit->is_type), + circuit->circuit_id, + stream_get_endp (circuit->rcv_stream)); + } + + free_tlvs (&tlvs); + + return retval; +} + +/* + * Process Level 1/2 Link State + * ISO - 10589 + * Section 7.3.15.1 - Action on receipt of a link state PDU + */ +static int +process_lsp (int level, struct isis_circuit *circuit, const u_char *ssnpa) +{ + struct isis_link_state_hdr *hdr; + struct isis_adjacency *adj = NULL; + struct isis_lsp *lsp, *lsp0 = NULL; + 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)); + } + + if ((stream_get_endp (circuit->rcv_stream) - + stream_get_getp (circuit->rcv_stream)) < ISIS_LSP_HDR_LEN) + { + zlog_warn ("Packet too short"); + return ISIS_WARNING; + } + + /* 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 %u, on %s", + circuit->area->area_tag, + level, + rawlspid_print (hdr->lsp_id), + ntohl (hdr->seq_num), + ntohs (hdr->checksum), + ntohs (hdr->rem_lifetime), + pdu_len, + circuit->interface->name); + } + + /* 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, + pdu_len - 12, &hdr->checksum)) + { + zlog_debug ("ISIS-Upd (%s): LSP %s invalid LSP checksum 0x%04x", + circuit->area->area_tag, + rawlspid_print (hdr->lsp_id), ntohs (hdr->checksum)); + + return ISIS_WARNING; + } + + /* 7.3.15.1 a) 1 - external domain circuit will discard lsps */ + if (circuit->ext_domain) + { + zlog_debug + ("ISIS-Upd (%s): LSP %s received at level %d over circuit with " + "externalDomain = true", circuit->area->area_tag, + rawlspid_print (hdr->lsp_id), level); + + return ISIS_WARNING; + } + + /* 7.3.15.1 a) 2,3 - manualL2OnlyMode not implemented */ + 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->is_type)); + + return ISIS_WARNING; + } + + /* 7.3.15.1 a) 4 - need to make sure IDLength matches */ + + /* 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 == IS_LEVEL_1) ? (passwd = &circuit->area->area_passwd) : + (passwd = &circuit->area->domain_passwd); + if (passwd->type) + { + 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); + return ISIS_WARNING; + } + } + /* Find the LSP in our database and compare it to this Link State header */ + lsp = lsp_search (hdr->lsp_id, circuit->area->lspdb[level - 1]); + if (lsp) + comp = lsp_compare (circuit->area->area_tag, lsp, hdr->seq_num, + hdr->checksum, hdr->rem_lifetime); + if (lsp && (lsp->own_lsp +#ifdef TOPOLOGY_GENERATE + || lsp->from_topology +#endif /* TOPOLOGY_GENERATE */ + )) + goto dontcheckadj; + + /* 7.3.15.1 a) 6 - Must check that we have an adjacency of the same level */ + /* for broadcast circuits, snpa should be compared */ + + if (circuit->circ_type == CIRCUIT_T_BROADCAST) + { + adj = isis_adj_lookup_snpa (ssnpa, circuit->u.bc.adjdb[level - 1]); + if (!adj) + { + zlog_debug ("(%s): DS ======= LSP %s, seq 0x%08x, cksum 0x%04x, " + "lifetime %us on %s", + circuit->area->area_tag, + rawlspid_print (hdr->lsp_id), + ntohl (hdr->seq_num), + ntohs (hdr->checksum), + ntohs (hdr->rem_lifetime), circuit->interface->name); + return ISIS_WARNING; /* Silently discard */ + } + } + /* for non broadcast, we just need to find same level adj */ + else + { + /* If no adj, or no sharing of level */ + if (!circuit->u.p2p.neighbor) + { + return ISIS_OK; /* Silently discard */ + } + else + { + if (((level == IS_LEVEL_1) && + (circuit->u.p2p.neighbor->adj_usage == ISIS_ADJ_LEVEL2)) || + ((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 */ + + /* 7.3.15.1 a) 8 - Passwords for level 2 - not implemented */ + + /* 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) + { + if (!lsp) + { + /* 7.3.16.4 a) 1) No LSP in db -> send an ack, but don't save */ + /* only needed on explicit update, eg - p2p */ + if (circuit->circ_type == CIRCUIT_T_P2P) + ack_lsp (hdr, circuit, level); + return retval; /* FIXME: do we need a purge? */ + } + else + { + if (memcmp (hdr->lsp_id, isis->sysid, ISIS_SYS_ID_LEN)) + { + /* LSP by some other system -> do 7.3.16.4 b) */ + /* 7.3.16.4 b) 1) */ + if (comp == LSP_NEWER) + { + lsp_update (lsp, circuit->rcv_stream, circuit->area, level); + /* ii */ + lsp_set_all_srmflags (lsp); + /* v */ + ISIS_FLAGS_CLEAR_ALL (lsp->SSNflags); /* FIXME: OTHER than c */ + + /* 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) + { + /* i */ + ISIS_CLEAR_FLAG (lsp->SRMflags, circuit); + /* ii */ + if (circuit->circ_type != CIRCUIT_T_BROADCAST) + ISIS_SET_FLAG (lsp->SSNflags, circuit); + } /* 7.3.16.4 b) 3) */ + else + { + ISIS_SET_FLAG (lsp->SRMflags, circuit); + ISIS_CLEAR_FLAG (lsp->SSNflags, circuit); + } + } + 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; + } + /* 7.3.15.1 c) - If this is our own lsp and we don't have it initiate a + * purge */ + if (memcmp (hdr->lsp_id, isis->sysid, ISIS_SYS_ID_LEN) == 0) + { + if (!lsp) + { + /* 7.3.16.4: initiate a purge */ + 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 */ + + /* In 7.3.16.1, If an Intermediate system R somewhere in the domain + * has information that the current sequence number for source S is + * "greater" than that held by S, ... */ + + if (ntohl (hdr->seq_num) > ntohl (lsp->lsp_header->seq_num)) + { + /* 7.3.16.1 */ + 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)); + } + /* If the received LSP is older or equal, + * resend the LSP which will act as ACK */ + lsp_set_all_srmflags (lsp); + } + else + { + /* 7.3.15.1 e) - This lsp originated on another system */ + + /* 7.3.15.1 e) 1) LSP newer than the one in db or no LSP in db */ + if ((!lsp || comp == LSP_NEWER)) + { + /* + * If this lsp is a frag, need to see if we have zero lsp present + */ + if (LSP_FRAGMENT (hdr->lsp_id) != 0) + { + memcpy (lspid, hdr->lsp_id, ISIS_SYS_ID_LEN + 1); + LSP_FRAGMENT (lspid) = 0; + lsp0 = lsp_search (lspid, circuit->area->lspdb[level - 1]); + if (!lsp0) + { + zlog_debug ("Got lsp frag, while zero lsp not in database"); + return ISIS_OK; + } + } + /* 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 */ + lsp_set_all_srmflags (lsp); + /* iii */ + ISIS_CLEAR_FLAG (lsp->SRMflags, circuit); + + /* iv */ + if (circuit->circ_type != CIRCUIT_T_BROADCAST) + ISIS_SET_FLAG (lsp->SSNflags, circuit); + /* FIXME: v) */ + } + /* 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, circuit->rcv_stream, circuit->area, level); + if (circuit->circ_type != CIRCUIT_T_BROADCAST) + ISIS_SET_FLAG (lsp->SSNflags, circuit); + } + /* 7.3.15.1 e) 3) LSP older than the one in db */ + else + { + ISIS_SET_FLAG (lsp->SRMflags, circuit); + ISIS_CLEAR_FLAG (lsp->SSNflags, circuit); + } + } + return retval; +} + +/* + * Process Sequence Numbers + * ISO - 10589 + * Section 7.3.15.2 - Action on receipt of a sequence numbers PDU + */ + +static int +process_snp (int snp_type, int level, struct isis_circuit *circuit, + const u_char *ssnpa) +{ + int retval = ISIS_OK; + int cmp, own_lsp; + char typechar = ' '; + 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, auth_tlv_offset = 0; + struct isis_lsp *lsp; + struct lsp_entry *entry; + struct listnode *node, *nnode; + struct listnode *node2, *nnode2; + struct tlvs tlvs; + struct list *lsp_list = NULL; + struct isis_passwd *passwd; + + if (snp_type == ISIS_SNP_CSNP_FLAG) + { + /* getting the header info */ + typechar = 'C'; + chdr = + (struct isis_complete_seqnum_hdr *) STREAM_PNT (circuit->rcv_stream); + 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 %d", pdu_len); + return ISIS_WARNING; + } + } + else + { + typechar = 'P'; + phdr = + (struct isis_partial_seqnum_hdr *) STREAM_PNT (circuit->rcv_stream); + 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 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) + { + + zlog_debug ("ISIS-Snp (%s): Rcvd L%d %cSNP on %s, " + "skipping: circuit externalDomain = true", + circuit->area->area_tag, + level, typechar, circuit->interface->name); + + return ISIS_OK; + } + + /* 7.3.15.2 a) 2,3 - manualL2OnlyMode not implemented */ + if (!accept_level (level, circuit->is_type)) + { + + zlog_debug ("ISIS-Snp (%s): Rcvd L%d %cSNP on %s, " + "skipping: circuit type %s does not match level %d", + circuit->area->area_tag, + level, + typechar, + circuit->interface->name, + 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->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); + + return ISIS_OK; + } + + /* 7.3.15.2 a) 5 - need to make sure IDLength matches - already checked */ + + /* 7.3.15.2 a) 6 - maximum area match, can be ommited since we only use 3 + * - already checked */ + + /* 7.3.15.2 a) 7 - Must check that we have an adjacency of the same level */ + /* for broadcast circuits, snpa should be compared */ + /* FIXME : Do we need to check SNPA? */ + if (circuit->circ_type == CIRCUIT_T_BROADCAST) + { + if (snp_type == ISIS_SNP_CSNP_FLAG) + { + adj = + isis_adj_lookup (chdr->source_id, circuit->u.bc.adjdb[level - 1]); + } + else + { + /* a psnp on a broadcast, how lovely of Juniper :) */ + adj = + isis_adj_lookup (phdr->source_id, circuit->u.bc.adjdb[level - 1]); + } + if (!adj) + return ISIS_OK; /* Silently discard */ + } + else + { + if (!circuit->u.p2p.neighbor) + { + 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 */ + + /* 7.3.15.2 a) 9 - Passwords for level 2 - not implemented */ + + memset (&tlvs, 0, sizeof (struct tlvs)); + + /* 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), + pdu_len - stream_get_getp (circuit->rcv_stream), + &expected, &found, &tlvs, &auth_tlv_offset); + + if (retval > ISIS_WARNING) + { + zlog_warn ("something went very wrong processing SNP"); + free_tlvs (&tlvs); + return retval; + } + + if (level == IS_LEVEL_1) + passwd = &circuit->area->area_passwd; + else + passwd = &circuit->area->domain_passwd; + + if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_RECV)) + { + if (passwd->type) + { + 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 */ + if (isis->debugs & DEBUG_SNP_PACKETS) + { + zlog_debug ("ISIS-Snp (%s): Rcvd L%d %cSNP from %s on %s", + circuit->area->area_tag, + level, + typechar, snpa_print (ssnpa), circuit->interface->name); + if (tlvs.lsp_entries) + { + for (ALL_LIST_ELEMENTS_RO (tlvs.lsp_entries, node, entry)) + { + zlog_debug ("ISIS-Snp (%s): %cSNP entry %s, seq 0x%08x," + " cksum 0x%04x, lifetime %us", + circuit->area->area_tag, + typechar, + rawlspid_print (entry->lsp_id), + ntohl (entry->seq_num), + ntohs (entry->checksum), ntohs (entry->rem_lifetime)); + } + } + } + + /* 7.3.15.2 b) Actions on LSP_ENTRIES reported */ + if (tlvs.lsp_entries) + { + for (ALL_LIST_ELEMENTS_RO (tlvs.lsp_entries, node, entry)) + { + lsp = lsp_search (entry->lsp_id, circuit->area->lspdb[level - 1]); + own_lsp = !memcmp (entry->lsp_id, isis->sysid, ISIS_SYS_ID_LEN); + if (lsp) + { + /* 7.3.15.2 b) 1) is this LSP newer */ + cmp = lsp_compare (circuit->area->area_tag, lsp, entry->seq_num, + entry->checksum, entry->rem_lifetime); + /* 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 */ + 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 + { + if (own_lsp) + { + lsp_inc_seqnum (lsp, ntohl (entry->seq_num)); + ISIS_SET_FLAG (lsp->SRMflags, circuit); + } + else + { + ISIS_SET_FLAG (lsp->SSNflags, circuit); + /* if (circuit->circ_type != CIRCUIT_T_BROADCAST) */ + ISIS_CLEAR_FLAG (lsp->SRMflags, circuit); + } + } + } + else + { + /* 7.3.15.2 b) 5) if it was not found, and all of those are not 0, + * insert it and set SSN on it */ + if (entry->rem_lifetime && entry->checksum && entry->seq_num && + memcmp (entry->lsp_id, isis->sysid, ISIS_SYS_ID_LEN)) + { + 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); + } + } + } + } + + /* 7.3.15.2 c) on CSNP set SRM for all in range which were not reported */ + if (snp_type == ISIS_SNP_CSNP_FLAG) + { + /* + * 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, + lsp_list, circuit->area->lspdb[level - 1]); + + /* Fixme: Find a better solution */ + if (tlvs.lsp_entries) + { + for (ALL_LIST_ELEMENTS (tlvs.lsp_entries, node, nnode, entry)) + { + for (ALL_LIST_ELEMENTS (lsp_list, node2, nnode2, lsp)) + { + if (lsp_id_cmp (lsp->lsp_header->lsp_id, entry->lsp_id) == 0) + { + list_delete_node (lsp_list, node2); + break; + } + } + } + } + /* 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_delete (lsp_list); + + } + + free_tlvs (&tlvs); + return retval; +} + +static int +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) + { + zlog_warn ("Packet too short ( < %d)", ISIS_CSNP_HDRLEN); + return ISIS_WARNING; + } + + return process_snp (ISIS_SNP_CSNP_FLAG, level, circuit, ssnpa); +} + +static int +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 ( < %d)", ISIS_PSNP_HDRLEN); + return ISIS_WARNING; + } + + return process_snp (ISIS_SNP_PSNP_FLAG, level, circuit, ssnpa); +} + +/* + * PDU Dispatcher + */ + +static int +isis_handle_pdu (struct isis_circuit *circuit, u_char * ssnpa) +{ + struct isis_fixed_hdr *hdr; + + int retval = ISIS_OK; + + /* + * Let's first read data from stream to the header + */ + hdr = (struct isis_fixed_hdr *) STREAM_DATA (circuit->rcv_stream); + + if ((hdr->idrp != ISO10589_ISIS) && (hdr->idrp != ISO9542_ESIS)) + { + zlog_err ("Not an IS-IS or ES-IS packet IDRP=%02x", hdr->idrp); + return ISIS_ERROR; + } + + /* now we need to know if this is an ISO 9542 packet and + * take real good care of it, waaa! + */ + if (hdr->idrp == ISO9542_ESIS) + { + 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 + */ + + if (hdr->length < ISIS_MINIMUM_FIXED_HDR_LEN) + { + zlog_err ("Fixed header length = %d", hdr->length); + return ISIS_ERROR; + } + + if (hdr->version1 != 1) + { + zlog_warn ("Unsupported ISIS version %u", hdr->version1); + return ISIS_WARNING; + } + /* either 6 or 0 */ + if ((hdr->id_len != 0) && (hdr->id_len != ISIS_SYS_ID_LEN)) + { + zlog_err + ("IDFieldLengthMismatch: ID Length field in a received PDU %u, " + "while the parameter for this IS is %u", hdr->id_len, + ISIS_SYS_ID_LEN); + return ISIS_ERROR; + } + + if (hdr->version2 != 1) + { + 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)) + { + zlog_err ("maximumAreaAddressesMismatch: maximumAreaAdresses in a " + "received PDU %u while the parameter for this IS is %u", + hdr->max_area_addrs, isis->max_area_addrs); + return ISIS_ERROR; + } + + switch (hdr->pdu_type) + { + case L1_LAN_HELLO: + retval = process_lan_hello (ISIS_LEVEL1, circuit, ssnpa); + break; + case L2_LAN_HELLO: + retval = process_lan_hello (ISIS_LEVEL2, circuit, ssnpa); + break; + case P2P_HELLO: + retval = process_p2p_hello (circuit); + break; + case L1_LINK_STATE: + retval = process_lsp (ISIS_LEVEL1, circuit, ssnpa); + break; + case L2_LINK_STATE: + retval = process_lsp (ISIS_LEVEL2, circuit, ssnpa); + break; + case L1_COMPLETE_SEQ_NUM: + retval = process_csnp (ISIS_LEVEL1, circuit, ssnpa); + break; + case L2_COMPLETE_SEQ_NUM: + retval = process_csnp (ISIS_LEVEL2, circuit, ssnpa); + break; + case L1_PARTIAL_SEQ_NUM: + retval = process_psnp (ISIS_LEVEL1, circuit, ssnpa); + break; + case L2_PARTIAL_SEQ_NUM: + retval = process_psnp (ISIS_LEVEL2, circuit, ssnpa); + break; + default: + return ISIS_ERROR; + } + + return retval; +} + +#ifdef GNU_LINUX +int +isis_receive (struct thread *thread) +{ + struct isis_circuit *circuit; + u_char ssnpa[ETH_ALEN]; + int retval; + + /* + * Get the circuit + */ + circuit = THREAD_ARG (thread); + assert (circuit); + + isis_circuit_stream(circuit, &circuit->rcv_stream); + + retval = circuit->rx (circuit, ssnpa); + circuit->t_read = NULL; + + if (retval == ISIS_OK) + retval = isis_handle_pdu (circuit, ssnpa); + + /* + * prepare for next packet. + */ + if (!circuit->is_passive) + { + THREAD_READ_ON (master, circuit->t_read, isis_receive, circuit, + circuit->fd); + } + + return retval; +} + +#else +int +isis_receive (struct thread *thread) +{ + struct isis_circuit *circuit; + u_char ssnpa[ETH_ALEN]; + int retval; + + /* + * Get the circuit + */ + circuit = THREAD_ARG (thread); + assert (circuit); + + circuit->t_read = NULL; + + isis_circuit_stream(circuit, &circuit->rcv_stream); + + retval = circuit->rx (circuit, ssnpa); + + if (retval == ISIS_OK) + retval = isis_handle_pdu (circuit, ssnpa); + + /* + * prepare for next packet. + */ + if (!circuit->is_passive) + { + circuit->t_read = thread_add_timer_msec (master, isis_receive, circuit, + listcount + (circuit->area->circuit_list) * + 100); + } + + return retval; +} + +#endif + + /* filling of the fixed isis header */ +void +fill_fixed_hdr (struct isis_fixed_hdr *hdr, u_char pdu_type) +{ + memset (hdr, 0, sizeof (struct isis_fixed_hdr)); + + hdr->idrp = ISO10589_ISIS; + + switch (pdu_type) + { + case L1_LAN_HELLO: + case L2_LAN_HELLO: + hdr->length = ISIS_LANHELLO_HDRLEN; + break; + case P2P_HELLO: + hdr->length = ISIS_P2PHELLO_HDRLEN; + break; + case L1_LINK_STATE: + case L2_LINK_STATE: + hdr->length = ISIS_LSP_HDR_LEN; + break; + case L1_COMPLETE_SEQ_NUM: + case L2_COMPLETE_SEQ_NUM: + hdr->length = ISIS_CSNP_HDRLEN; + break; + case L1_PARTIAL_SEQ_NUM: + case L2_PARTIAL_SEQ_NUM: + hdr->length = ISIS_PSNP_HDRLEN; + break; + default: + zlog_warn ("fill_fixed_hdr(): unknown pdu type %d", pdu_type); + return; + } + hdr->length += ISIS_FIXED_HDR_LEN; + hdr->pdu_type = pdu_type; + hdr->version1 = 1; + hdr->id_len = 0; /* ISIS_SYS_ID_LEN - 0==6 */ + hdr->version2 = 1; + hdr->max_area_addrs = 0; /* isis->max_area_addrs - 0==3 */ +} + +/* + * SEND SIDE + */ +static void +fill_fixed_hdr_andstream (struct isis_fixed_hdr *hdr, u_char pdu_type, + struct stream *stream) +{ + fill_fixed_hdr (hdr, pdu_type); + + stream_putc (stream, hdr->idrp); + stream_putc (stream, hdr->length); + stream_putc (stream, hdr->version1); + stream_putc (stream, hdr->id_len); + stream_putc (stream, hdr->pdu_type); + stream_putc (stream, hdr->version2); + stream_putc (stream, hdr->reserved); + stream_putc (stream, hdr->max_area_addrs); + + return; +} + +int +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; + unsigned char hmac_md5_hash[ISIS_AUTH_MD5_SIZE]; + size_t len_pointer, length, auth_tlv_offset = 0; + u_int32_t interval; + int retval; + + if (circuit->is_passive) + return ISIS_OK; + + if (circuit->interface->mtu == 0) + { + zlog_warn ("circuit has zero MTU"); + return ISIS_WARNING; + } + + isis_circuit_stream(circuit, &circuit->snd_stream); + + if (circuit->circ_type == CIRCUIT_T_BROADCAST) + if (level == IS_LEVEL_1) + fill_fixed_hdr_andstream (&fixed_hdr, L1_LAN_HELLO, + circuit->snd_stream); + else + fill_fixed_hdr_andstream (&fixed_hdr, L2_LAN_HELLO, + circuit->snd_stream); + else + fill_fixed_hdr_andstream (&fixed_hdr, P2P_HELLO, circuit->snd_stream); + + /* + * Fill LAN Level 1 or 2 Hello PDU header + */ + memset (&hello_hdr, 0, sizeof (struct isis_lan_hello_hdr)); + interval = circuit->hello_multiplier[level - 1] * + circuit->hello_interval[level - 1]; + if (interval > USHRT_MAX) + interval = USHRT_MAX; + 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); + + hello_hdr.pdu_len = 0; /* Update the PDU Length later */ + len_pointer = stream_get_endp (circuit->snd_stream) + 3 + ISIS_SYS_ID_LEN; + + /* copy the shared part of the hello to the p2p hello if needed */ + if (circuit->circ_type == CIRCUIT_T_P2P) + { + memcpy (&p2p_hello_hdr, &hello_hdr, 5 + ISIS_SYS_ID_LEN); + p2p_hello_hdr.local_id = circuit->circuit_id; + /* FIXME: need better understanding */ + stream_put (circuit->snd_stream, &p2p_hello_hdr, ISIS_P2PHELLO_HDRLEN); + } + else + { + 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 == IS_LEVEL_2) + { + memcpy (hello_hdr.lan_id, circuit->u.bc.l2_desig_is, + ISIS_SYS_ID_LEN + 1); + } + stream_put (circuit->snd_stream, &hello_hdr, ISIS_LANHELLO_HDRLEN); + } + + /* + * Then the variable length part. + */ + + /* add circuit password */ + 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; + + /* 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 (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; + /* IP interface Address TLV */ + 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 && + listcount (circuit->ipv6_link) > 0) + if (tlv_add_ipv6_addrs (circuit->ipv6_link, circuit->snd_stream)) + return ISIS_WARNING; +#endif /* HAVE_IPV6 */ + + if (circuit->pad_hellos) + if (tlv_add_padding (circuit->snd_stream)) + return ISIS_WARNING; + + length = stream_get_endp (circuit->snd_stream); + /* Update PDU length */ + stream_putw_at (circuit->snd_stream, len_pointer, (u_int16_t) length); + + /* For HMAC MD5 we need to compute the md5 hash and store it */ + if (circuit->passwd.type == ISIS_PASSWD_TYPE_HMAC_MD5) + { + 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 (STREAM_DATA (circuit->snd_stream) + auth_tlv_offset + 3, + hmac_md5_hash, ISIS_AUTH_MD5_SIZE); + } + + if (isis->debugs & DEBUG_ADJ_PACKETS) + { + if (circuit->circ_type == CIRCUIT_T_BROADCAST) + { + zlog_debug ("ISIS-Adj (%s): Sending L%d LAN IIH on %s, length %zd", + circuit->area->area_tag, level, circuit->interface->name, + length); + } + else + { + zlog_debug ("ISIS-Adj (%s): Sending P2P IIH on %s, length %zd", + circuit->area->area_tag, circuit->interface->name, + length); + } + 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-Adj (%s): Send L%d IIH on %s failed", + circuit->area->area_tag, level, circuit->interface->name); + + return retval; +} + +int +send_lan_l1_hello (struct thread *thread) +{ + struct isis_circuit *circuit; + int retval; + + circuit = THREAD_ARG (thread); + assert (circuit); + 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_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 (circuit->hello_interval[0], IIH_JITTER)); + + return retval; +} + +int +send_lan_l2_hello (struct thread *thread) +{ + struct isis_circuit *circuit; + int retval; + + circuit = THREAD_ARG (thread); + assert (circuit); + 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_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 (circuit->hello_interval[1], IIH_JITTER)); + + return retval; +} + +int +send_p2p_hello (struct thread *thread) +{ + struct isis_circuit *circuit; + + circuit = THREAD_ARG (thread); + assert (circuit); + circuit->u.p2p.t_send_p2p_hello = NULL; + + send_hello (circuit, 1); + + /* set next timer thread */ + THREAD_TIMER_ON (master, circuit->u.p2p.t_send_p2p_hello, send_p2p_hello, + circuit, isis_jitter (circuit->hello_interval[1], + IIH_JITTER)); + + return ISIS_OK; +} + +static int +build_csnp (int level, u_char * start, u_char * stop, struct list *lsps, + struct isis_circuit *circuit) +{ + struct isis_fixed_hdr fixed_hdr; + struct isis_passwd *passwd; + 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 == IS_LEVEL_1) + fill_fixed_hdr_andstream (&fixed_hdr, L1_COMPLETE_SEQ_NUM, + circuit->snd_stream); + else + fill_fixed_hdr_andstream (&fixed_hdr, L2_COMPLETE_SEQ_NUM, + circuit->snd_stream); + + /* + * Fill Level 1 or 2 Complete Sequence Numbers header + */ + + lenp = stream_get_endp (circuit->snd_stream); + stream_putw (circuit->snd_stream, 0); /* PDU length - when we know it */ + /* no need to send the source here, it is always us if we csnp */ + stream_put (circuit->snd_stream, isis->sysid, ISIS_SYS_ID_LEN); + /* with zero circuit id - ref 9.10, 9.11 */ + stream_putc (circuit->snd_stream, 0x00); + + stream_put (circuit->snd_stream, start, ISIS_SYS_ID_LEN + 2); + stream_put (circuit->snd_stream, stop, ISIS_SYS_ID_LEN + 2); + + /* + * And TLVs + */ + 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)) + { + 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); + /* 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 + */ + +int +send_csnp (struct isis_circuit *circuit, int level) +{ + 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->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); + + num_lsps = max_lsps_per_snp (ISIS_SNP_CSNP_FLAG, level, circuit); + + while (loop) + { + list = list_new (); + 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 + { + 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): 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); + } + + return retval; +} + +int +send_l1_csnp (struct thread *thread) +{ + struct isis_circuit *circuit; + int retval = ISIS_OK; + + circuit = THREAD_ARG (thread); + assert (circuit); + + 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)); + + return retval; +} + +int +send_l2_csnp (struct thread *thread) +{ + struct isis_circuit *circuit; + int retval = ISIS_OK; + + circuit = THREAD_ARG (thread); + assert (circuit); + + 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)); + + return retval; +} + +static int +build_psnp (int level, struct isis_circuit *circuit, struct list *lsps) +{ + struct isis_fixed_hdr fixed_hdr; + unsigned long lenp; + u_int16_t length; + 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; + + 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 + fill_fixed_hdr_andstream (&fixed_hdr, L2_PARTIAL_SEQ_NUM, + circuit->snd_stream); + + /* + * Fill Level 1 or 2 Partial Sequence Numbers header + */ + lenp = stream_get_endp (circuit->snd_stream); + stream_putw (circuit->snd_stream, 0); /* PDU length - when we know it */ + stream_put (circuit->snd_stream, isis->sysid, ISIS_SYS_ID_LEN); + stream_putc (circuit->snd_stream, circuit->idx); + + /* + * And TLVs + */ + + 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)) + { + 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) + { + for (ALL_LIST_ELEMENTS_RO (lsps, node, lsp)) + { + zlog_debug ("ISIS-Snp (%s): PSNP 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)); + } + } + + length = (u_int16_t) stream_get_endp (circuit->snd_stream); + /* 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; +} + +/* + * 7.3.15.4 action on expiration of partial SNP interval + * level 1 + */ +static int +send_psnp (int level, struct isis_circuit *circuit) +{ + struct isis_lsp *lsp; + struct list *list = NULL; + struct listnode *node; + u_char num_lsps; + int retval = ISIS_OK; + + if (circuit->circ_type == CIRCUIT_T_BROADCAST && + circuit->u.bc.is_dr[level - 1]) + return ISIS_OK; + + if (circuit->area->lspdb[level - 1] == NULL || + dict_count (circuit->area->lspdb[level - 1]) == 0) + return ISIS_OK; + + if (! circuit->snd_stream) + return ISIS_ERROR; + + num_lsps = max_lsps_per_snp (ISIS_SNP_PSNP_FLAG, level, circuit); + + 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; + } + + 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; + } + + /* + * 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; +} + +int +send_l1_psnp (struct thread *thread) +{ + + struct isis_circuit *circuit; + int retval = ISIS_OK; + + circuit = THREAD_ARG (thread); + assert (circuit); + + circuit->t_send_psnp[0] = NULL; + + send_psnp (1, circuit); + /* set next timer thread */ + THREAD_TIMER_ON (master, circuit->t_send_psnp[0], send_l1_psnp, circuit, + isis_jitter (circuit->psnp_interval[0], PSNP_JITTER)); + + return retval; +} + +/* + * 7.3.15.4 action on expiration of partial SNP interval + * level 2 + */ +int +send_l2_psnp (struct thread *thread) +{ + struct isis_circuit *circuit; + int retval = ISIS_OK; + + circuit = THREAD_ARG (thread); + assert (circuit); + + circuit->t_send_psnp[1] = NULL; + + send_psnp (2, circuit); + + /* set next timer thread */ + THREAD_TIMER_ON (master, circuit->t_send_psnp[1], send_l2_psnp, circuit, + isis_jitter (circuit->psnp_interval[1], PSNP_JITTER)); + + return retval; +} + +/* + * ISO 10589 - 7.3.14.3 + */ +int +send_lsp (struct thread *thread) +{ + struct isis_circuit *circuit; + struct isis_lsp *lsp; + struct listnode *node; + int clear_srm = 1; + int retval = ISIS_OK; + + circuit = THREAD_ARG (thread); + assert (circuit); + + if (!circuit->lsp_queue) + return ISIS_OK; + + node = listhead (circuit->lsp_queue); + + /* + * 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 (!node) + return ISIS_OK; + + /* + * 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. + */ + lsp = listgetdata(node); + list_delete_node (circuit->lsp_queue, node); + + /* 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); + + if (circuit->state != C_STATE_UP || circuit->is_passive == 1) + goto out; + + /* + * Do not send if levels do not match + */ + if (!(lsp->level & circuit->is_type)) + goto out; + + /* + * 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; + } + + /* 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)); + } + + clear_srm = 0; + retval = circuit->tx (circuit, lsp->level); + if (retval != ISIS_OK) + { + 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. + */ + ISIS_CLEAR_FLAG (lsp->SRMflags, circuit); + } + + return retval; +} + +int +ack_lsp (struct isis_link_state_hdr *hdr, struct isis_circuit *circuit, + int level) +{ + unsigned long lenp; + int retval; + u_int16_t length; + struct isis_fixed_hdr fixed_hdr; + + isis_circuit_stream(circuit, &circuit->snd_stream); + + // fill_llc_hdr (stream); + if (level == IS_LEVEL_1) + fill_fixed_hdr_andstream (&fixed_hdr, L1_PARTIAL_SEQ_NUM, + circuit->snd_stream); + else + fill_fixed_hdr_andstream (&fixed_hdr, L2_PARTIAL_SEQ_NUM, + circuit->snd_stream); + + + lenp = stream_get_endp (circuit->snd_stream); + stream_putw (circuit->snd_stream, 0); /* PDU length */ + stream_put (circuit->snd_stream, isis->sysid, ISIS_SYS_ID_LEN); + stream_putc (circuit->snd_stream, circuit->idx); + stream_putc (circuit->snd_stream, 9); /* code */ + stream_putc (circuit->snd_stream, 16); /* len */ + + stream_putw (circuit->snd_stream, ntohs (hdr->rem_lifetime)); + stream_put (circuit->snd_stream, hdr->lsp_id, ISIS_SYS_ID_LEN + 2); + stream_putl (circuit->snd_stream, ntohl (hdr->seq_num)); + stream_putw (circuit->snd_stream, ntohs (hdr->checksum)); + + length = (u_int16_t) stream_get_endp (circuit->snd_stream); + /* Update PDU length */ + 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 new file mode 100644 index 0000000..3eca731 --- /dev/null +++ b/isisd/isis_pdu.h @@ -0,0 +1,274 @@ +/* + * IS-IS Rout(e)ing protocol - isis_pdu.h + * PDU processing + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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. + */ + +#ifndef _ZEBRA_ISIS_PDU_H +#define _ZEBRA_ISIS_PDU_H + +#ifdef __SUNPRO_C +#pragma pack(1) +#endif + +/* + * ISO 9542 - 7.5,7.6 + * + * ES to IS Fixed Header + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | Intradomain Routeing Protocol Discriminator | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | Length Indicator | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | Version/Protocol ID extension | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | Reserved = 0 | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | 0 | 0 | 0 | PDU Type | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | Holding Time | 2 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | Checksum | 2 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + */ + +struct esis_fixed_hdr +{ + u_char idrp; + u_char length; + u_char version; + u_char id_len; + u_char pdu_type; + u_int16_t holdtime; + u_int16_t checksum; +} __attribute__ ((packed)); + +#define ESIS_FIXED_HDR_LEN 9 + +#define ESH_PDU 2 +#define ISH_PDU 4 +#define RD_PDU 5 + +/* + * IS to IS Fixed Header + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | Intradomain Routeing Protocol Discriminator | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | Length Indicator | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | Version/Protocol ID extension | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | R | R | R | PDU Type | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | Version | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | Reserved | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | Maximum Area Addresses | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + */ + +struct isis_fixed_hdr +{ + u_char idrp; + u_char length; + u_char version1; + u_char id_len; + u_char pdu_type; + u_char version2; + u_char reserved; + u_char max_area_addrs; +} __attribute__ ((packed)); + +#define ISIS_FIXED_HDR_LEN 8 + +/* + * IS-IS PDU types. + */ + +#define L1_LAN_HELLO 15 +#define L2_LAN_HELLO 16 +/* + * L1 and L2 LAN IS to IS Hello PDU header + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | Reserved | Circuit Type | 1 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * + Source ID + id_len + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | Holding Time | 2 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | PDU Length | 2 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | R | Priority | 1 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | LAN ID | id_len + 1 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + */ +struct isis_lan_hello_hdr +{ + u_char circuit_t; + u_char source_id[ISIS_SYS_ID_LEN]; + u_int16_t hold_time; + u_int16_t pdu_len; + u_char prio; + u_char lan_id[ISIS_SYS_ID_LEN + 1]; +} __attribute__ ((packed)); +#define ISIS_LANHELLO_HDRLEN 19 + +#define P2P_HELLO 17 +/* + * Point-to-point IS to IS hello PDU header + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | Reserved | Circuit Type | 1 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * + Source ID + id_len + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * + Holding Time + 2 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * + PDU Length + 2 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | Local Circuit ID | 1 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + */ +struct isis_p2p_hello_hdr +{ + u_char circuit_t; + u_char source_id[ISIS_SYS_ID_LEN]; + u_int16_t hold_time; + u_int16_t pdu_len; + u_char local_id; +} __attribute__ ((packed)); +#define ISIS_P2PHELLO_HDRLEN 12 + +#define L1_LINK_STATE 18 +#define L2_LINK_STATE 20 +/* + * L1 and L2 IS to IS link state PDU header + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * + PDU Length + 2 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * + Remaining Lifetime + 2 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | LSP ID | id_len + 2 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * + Sequence Number + 4 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * + Checksum + 2 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | P | ATT |LSPDBOL| ISTYPE | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + */ +struct isis_link_state_hdr +{ + u_int16_t pdu_len; + u_int16_t rem_lifetime; + u_char lsp_id[ISIS_SYS_ID_LEN + 2]; + u_int32_t seq_num; + u_int16_t checksum; + u_int8_t lsp_bits; +} __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 Length + 2 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * + Source ID + id_len + 1 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * + Start LSP ID + id_len + 2 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * + End LSP ID + id_len + 2 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + */ +struct isis_complete_seqnum_hdr +{ + u_int16_t pdu_len; + u_char source_id[ISIS_SYS_ID_LEN + 1]; + u_char start_lsp_id[ISIS_SYS_ID_LEN + 2]; + u_char stop_lsp_id[ISIS_SYS_ID_LEN + 2]; +}; +#define ISIS_CSNP_HDRLEN 25 + +#define L1_PARTIAL_SEQ_NUM 26 +#define L2_PARTIAL_SEQ_NUM 27 +/* + * L1 and L2 IS to IS partial sequence numbers PDU header + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * + PDU Length + 2 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * + Source ID + id_len + 1 + * +---------------------------------------------------------------+ + */ +struct isis_partial_seqnum_hdr +{ + u_int16_t pdu_len; + u_char source_id[ISIS_SYS_ID_LEN + 1]; +}; +#define ISIS_PSNP_HDRLEN 9 + +#ifdef __SUNPRO_C +#pragma pack() +#endif + +/* + * Function for receiving IS-IS PDUs + */ +int isis_receive (struct thread *thread); + +/* + * calling arguments for snp_process () + */ +#define ISIS_SNP_PSNP_FLAG 0 +#define ISIS_SNP_CSNP_FLAG 1 + +#define ISIS_AUTH_MD5_SIZE 16U + +/* + * Sending functions + */ +int send_lan_l1_hello (struct thread *thread); +int send_lan_l2_hello (struct thread *thread); +int send_p2p_hello (struct thread *thread); +int send_csnp (struct isis_circuit *circuit, int level); +int send_l1_csnp (struct thread *thread); +int send_l2_csnp (struct thread *thread); +int send_l1_psnp (struct thread *thread); +int send_l2_psnp (struct thread *thread); +int send_lsp (struct thread *thread); +int ack_lsp (struct isis_link_state_hdr *hdr, + struct isis_circuit *circuit, int level); +void fill_fixed_hdr (struct isis_fixed_hdr *hdr, u_char pdu_type); +int send_hello (struct isis_circuit *circuit, int level); + +#endif /* _ZEBRA_ISIS_PDU_H */ diff --git a/isisd/isis_pfpacket.c b/isisd/isis_pfpacket.c new file mode 100644 index 0000000..2427047 --- /dev/null +++ b/isisd/isis_pfpacket.c @@ -0,0 +1,417 @@ +/* + * IS-IS Rout(e)ing protocol - isis_pfpacket.c + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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 +#if ISIS_METHOD == ISIS_METHOD_PFPACKET +#include /* the L2 protocols */ +#include + +#include "log.h" +#include "network.h" +#include "stream.h" +#include "if.h" + +#include "isisd/dict.h" +#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/isisd.h" +#include "isisd/isis_constants.h" +#include "isisd/isis_circuit.h" +#include "isisd/isis_network.h" + +#include "privs.h" + +extern struct zebra_privs_t isisd_privs; + +/* + * Table 9 - Architectural constants for use with ISO 8802 subnetworks + * ISO 10589 - 8.4.8 + */ + +u_char ALL_L1_ISS[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x14 }; +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 uint8_t discard_buff[8192]; +static uint8_t sock_buff[8192]; + +/* + * if level is 0 we are joining p2p multicast + * FIXME: and the p2p multicast being ??? + */ +static int +isis_multicast_join (int fd, int registerto, int if_num) +{ + struct packet_mreq mreq; + + memset (&mreq, 0, sizeof (mreq)); + mreq.mr_ifindex = if_num; + if (registerto) + { + mreq.mr_type = PACKET_MR_MULTICAST; + mreq.mr_alen = ETH_ALEN; + if (registerto == 1) + memcpy (&mreq.mr_address, ALL_L1_ISS, ETH_ALEN); + else if (registerto == 2) + memcpy (&mreq.mr_address, ALL_L2_ISS, ETH_ALEN); + else if (registerto == 3) + memcpy (&mreq.mr_address, ALL_ISS, ETH_ALEN); + else + memcpy (&mreq.mr_address, ALL_ESS, ETH_ALEN); + + } + else + { + mreq.mr_type = PACKET_MR_ALLMULTI; + } +#ifdef EXTREME_DEBUG + zlog_debug ("isis_multicast_join(): fd=%d, reg_to=%d, if_num=%d, " + "address = %02x:%02x:%02x:%02x:%02x:%02x", + fd, registerto, if_num, mreq.mr_address[0], mreq.mr_address[1], + mreq.mr_address[2], mreq.mr_address[3], mreq.mr_address[4], + mreq.mr_address[5]); +#endif /* EXTREME_DEBUG */ + if (setsockopt (fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, + sizeof (struct packet_mreq))) + { + zlog_warn ("isis_multicast_join(): setsockopt(): %s", safe_strerror (errno)); + return ISIS_WARNING; + } + + return ISIS_OK; +} + +static int +open_packet_socket (struct isis_circuit *circuit) +{ + struct sockaddr_ll s_addr; + int fd, retval = ISIS_OK; + + fd = socket (PF_PACKET, SOCK_DGRAM, htons (ETH_P_ALL)); + if (fd < 0) + { + zlog_warn ("open_packet_socket(): socket() failed %s", + safe_strerror (errno)); + return ISIS_WARNING; + } + + /* + * Bind to the physical interface + */ + memset (&s_addr, 0, sizeof (struct sockaddr_ll)); + s_addr.sll_family = AF_PACKET; + s_addr.sll_protocol = htons (ETH_P_ALL); + s_addr.sll_ifindex = circuit->interface->ifindex; + + if (bind (fd, (struct sockaddr *) (&s_addr), + 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 (if_is_broadcast (circuit->interface)) + { + /* + * Join to multicast groups + * according to + * 8.4.2 - Broadcast subnetwork IIH PDUs + * FIXME: is there a case only one will fail?? + */ + /* 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); + } + + return retval; +} + +/* + * Create the socket and set the tx/rx funcs + */ +int +isis_sock_init (struct isis_circuit *circuit) +{ + int retval = ISIS_OK; + + if (isisd_privs.change (ZPRIVS_RAISE)) + zlog_err ("%s: could not raise privs, %s", __func__, safe_strerror (errno)); + + retval = open_packet_socket (circuit); + + if (retval != ISIS_OK) + { + zlog_warn ("%s: could not initialize the socket", __func__); + goto end; + } + + /* 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 (if_is_pointopoint (circuit->interface)) + { + circuit->tx = isis_send_pdu_p2p; + circuit->rx = isis_recv_pdu_p2p; + } + else + { + zlog_warn ("isis_sock_init(): unknown circuit type"); + retval = ISIS_WARNING; + goto end; + } + +end: + if (isisd_privs.change (ZPRIVS_LOWER)) + zlog_err ("%s: could not lower privs, %s", __func__, safe_strerror (errno)); + + return retval; +} + +static inline int +llc_check (u_char * llc) +{ + if (*llc != ISO_SAP || *(llc + 1) != ISO_SAP || *(llc + 2) != 3) + return 0; + + return 1; +} + +int +isis_recv_pdu_bcast (struct isis_circuit *circuit, u_char * ssnpa) +{ + int bytesread, addr_len; + struct sockaddr_ll s_addr; + u_char llc[LLC_LEN]; + + addr_len = sizeof (s_addr); + + memset (&s_addr, 0, sizeof (struct sockaddr_ll)); + + bytesread = recvfrom (circuit->fd, (void *) &llc, + LLC_LEN, MSG_PEEK, + (struct sockaddr *) &s_addr, (socklen_t *) &addr_len); + + if ((bytesread < 0) || (s_addr.sll_ifindex != (int)circuit->interface->ifindex)) + { + 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 = recvfrom (circuit->fd, discard_buff, sizeof (discard_buff), + MSG_DONTWAIT, (struct sockaddr *) &s_addr, + (socklen_t *) &addr_len); + return ISIS_WARNING; + } + /* + * Filtering by llc field, discard packets sent by this host (other circuit) + */ + if (!llc_check (llc) || s_addr.sll_pkttype == PACKET_OUTGOING) + { + /* Read the packet into 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(): recvfrom() failed"); + return ISIS_WARNING; + } + + /* on lan we have to read to the static buff first */ + 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); + + memcpy (ssnpa, &s_addr.sll_addr, s_addr.sll_halen); + + return ISIS_OK; +} + +int +isis_recv_pdu_p2p (struct isis_circuit *circuit, u_char * ssnpa) +{ + int bytesread, addr_len; + struct sockaddr_ll s_addr; + + memset (&s_addr, 0, sizeof (struct sockaddr_ll)); + addr_len = sizeof (s_addr); + + /* we can read directly to the stream */ + bytesread = stream_recvfrom (circuit->rcv_stream, circuit->fd, + circuit->interface->mtu, 0, + (struct sockaddr *) &s_addr, + (socklen_t *) &addr_len); + + if (s_addr.sll_pkttype == PACKET_OUTGOING) + { + /* Read the packet into 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(): recvfrom() failed"); + return ISIS_WARNING; + } + + /* If we don't have protocol type 0x00FE which is + * ISO over GRE we exit with pain :) + */ + if (ntohs (s_addr.sll_protocol) != 0x00FE) + { + zlog_warn ("isis_recv_pdu_p2p(): protocol mismatch(): %X", + ntohs (s_addr.sll_protocol)); + return ISIS_WARNING; + } + + memcpy (ssnpa, &s_addr.sll_addr, s_addr.sll_halen); + + return ISIS_OK; +} + +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 + */ + struct sockaddr_ll sa; + + stream_set_getp (circuit->snd_stream, 0); + memset (&sa, 0, sizeof (struct sockaddr_ll)); + sa.sll_family = AF_PACKET; + sa.sll_protocol = htons (stream_get_endp (circuit->snd_stream) + LLC_LEN); + sa.sll_ifindex = circuit->interface->ifindex; + sa.sll_halen = ETH_ALEN; + /* 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); + + /* on a broadcast circuit */ + /* first we put the LLC in */ + sock_buff[0] = 0xFE; + sock_buff[1] = 0xFE; + sock_buff[2] = 0x03; + + 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) +{ + struct sockaddr_ll sa; + ssize_t rv; + + stream_set_getp (circuit->snd_stream, 0); + memset (&sa, 0, sizeof (struct sockaddr_ll)); + sa.sll_family = AF_PACKET; + 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) + memcpy (&sa.sll_addr, ALL_L1_ISS, ETH_ALEN); + else + memcpy (&sa.sll_addr, ALL_L2_ISS, ETH_ALEN); + + + /* lets try correcting the protocol */ + sa.sll_protocol = htons (0x00FE); + 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; +} + +#endif /* ISIS_METHOD == ISIS_METHOD_PFPACKET */ diff --git a/isisd/isis_redist.c b/isisd/isis_redist.c new file mode 100644 index 0000000..ad0a31b --- /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 0000000..cc9c2e6 --- /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 new file mode 100644 index 0000000..c0ec01e --- /dev/null +++ b/isisd/isis_route.c @@ -0,0 +1,678 @@ +/* + * IS-IS Rout(e)ing protocol - isis_route.c + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * based on ../ospf6d/ospf6_route.[ch] + * by Yasuhiro Ohara + * + * 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 "thread.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 "isis_constants.h" +#include "isis_common.h" +#include "isis_flags.h" +#include "dict.h" +#include "isisd.h" +#include "isis_misc.h" +#include "isis_adjacency.h" +#include "isis_circuit.h" +#include "isis_tlv.h" +#include "isis_pdu.h" +#include "isis_lsp.h" +#include "isis_spf.h" +#include "isis_route.h" +#include "isis_zebra.h" + +static struct isis_nexthop * +isis_nexthop_create (struct in_addr *ip, ifindex_t ifindex) +{ + struct listnode *node; + struct isis_nexthop *nexthop; + + for (ALL_LIST_ELEMENTS_RO (isis->nexthops, node, nexthop)) + { + if (nexthop->ifindex != ifindex) + continue; + if (ip && memcmp (&nexthop->ip, ip, sizeof (struct in_addr)) != 0) + continue; + + nexthop->lock++; + return nexthop; + } + + nexthop = XCALLOC (MTYPE_ISIS_NEXTHOP, sizeof (struct isis_nexthop)); + + nexthop->ifindex = ifindex; + memcpy (&nexthop->ip, ip, sizeof (struct in_addr)); + listnode_add (isis->nexthops, nexthop); + nexthop->lock++; + + return nexthop; +} + +static void +isis_nexthop_delete (struct isis_nexthop *nexthop) +{ + nexthop->lock--; + if (nexthop->lock == 0) + { + listnode_delete (isis->nexthops, nexthop); + XFREE (MTYPE_ISIS_NEXTHOP, nexthop); + } + + return; +} + +static int +nexthoplookup (struct list *nexthops, struct in_addr *ip, + ifindex_t ifindex) +{ + struct listnode *node; + struct isis_nexthop *nh; + + for (ALL_LIST_ELEMENTS_RO (nexthops, node, nh)) + { + if (!(memcmp (ip, &nh->ip, sizeof (struct in_addr))) && + ifindex == nh->ifindex) + return 1; + } + + return 0; +} + +#ifdef EXTREME_DEBUG +static void +nexthop_print (struct isis_nexthop *nh) +{ + u_char buf[BUFSIZ]; + + inet_ntop (AF_INET, &nh->ip, (char *) buf, BUFSIZ); + + zlog_debug (" %s %u", buf, nh->ifindex); +} + +static void +nexthops_print (struct list *nhs) +{ + struct listnode *node; + struct isis_nexthop *nh; + + for (ALL_LIST_ELEMENTS_RO (nhs, node, nh)) + nexthop_print (nh); +} +#endif /* EXTREME_DEBUG */ + +#ifdef HAVE_IPV6 +static struct isis_nexthop6 * +isis_nexthop6_new (struct in6_addr *ip6, ifindex_t ifindex) +{ + struct isis_nexthop6 *nexthop6; + + nexthop6 = XCALLOC (MTYPE_ISIS_NEXTHOP6, sizeof (struct isis_nexthop6)); + + nexthop6->ifindex = ifindex; + memcpy (&nexthop6->ip6, ip6, sizeof (struct in6_addr)); + nexthop6->lock++; + + return nexthop6; +} + +static struct isis_nexthop6 * +isis_nexthop6_create (struct in6_addr *ip6, ifindex_t ifindex) +{ + struct listnode *node; + struct isis_nexthop6 *nexthop6; + + for (ALL_LIST_ELEMENTS_RO (isis->nexthops6, node, nexthop6)) + { + if (nexthop6->ifindex != ifindex) + continue; + if (ip6 && memcmp (&nexthop6->ip6, ip6, sizeof (struct in6_addr)) != 0) + continue; + + nexthop6->lock++; + return nexthop6; + } + + nexthop6 = isis_nexthop6_new (ip6, ifindex); + + return nexthop6; +} + +static void +isis_nexthop6_delete (struct isis_nexthop6 *nexthop6) +{ + + nexthop6->lock--; + if (nexthop6->lock == 0) + { + listnode_delete (isis->nexthops6, nexthop6); + XFREE (MTYPE_ISIS_NEXTHOP6, nexthop6); + } + + return; +} + +static int +nexthop6lookup (struct list *nexthops6, struct in6_addr *ip6, + ifindex_t ifindex) +{ + struct listnode *node; + struct isis_nexthop6 *nh6; + + for (ALL_LIST_ELEMENTS_RO (nexthops6, node, nh6)) + { + if (!(memcmp (ip6, &nh6->ip6, sizeof (struct in6_addr))) && + ifindex == nh6->ifindex) + return 1; + } + + return 0; +} + +#ifdef EXTREME_DEBUG +static void +nexthop6_print (struct isis_nexthop6 *nh6) +{ + u_char buf[BUFSIZ]; + + inet_ntop (AF_INET6, &nh6->ip6, (char *) buf, BUFSIZ); + + zlog_debug (" %s %u", buf, nh6->ifindex); +} + +static void +nexthops6_print (struct list *nhs6) +{ + struct listnode *node; + struct isis_nexthop6 *nh6; + + for (ALL_LIST_ELEMENTS_RO (nhs6, node, nh6)) + nexthop6_print (nh6); +} +#endif /* EXTREME_DEBUG */ +#endif /* HAVE_IPV6 */ + +static void +adjinfo2nexthop (struct list *nexthops, struct isis_adjacency *adj) +{ + struct isis_nexthop *nh; + struct listnode *node; + struct in_addr *ipv4_addr; + + if (adj->ipv4_addrs == NULL) + return; + + for (ALL_LIST_ELEMENTS_RO (adj->ipv4_addrs, node, ipv4_addr)) + { + if (!nexthoplookup (nexthops, ipv4_addr, + adj->circuit->interface->ifindex)) + { + nh = isis_nexthop_create (ipv4_addr, + adj->circuit->interface->ifindex); + nh->router_address = adj->router_address; + listnode_add (nexthops, nh); + } + } +} + +#ifdef HAVE_IPV6 +static void +adjinfo2nexthop6 (struct list *nexthops6, struct isis_adjacency *adj) +{ + struct listnode *node; + struct in6_addr *ipv6_addr; + struct isis_nexthop6 *nh6; + + if (!adj->ipv6_addrs) + return; + + for (ALL_LIST_ELEMENTS_RO (adj->ipv6_addrs, node, ipv6_addr)) + { + if (!nexthop6lookup (nexthops6, ipv6_addr, + adj->circuit->interface->ifindex)) + { + nh6 = isis_nexthop6_create (ipv6_addr, + adj->circuit->interface->ifindex); + nh6->router_address6 = adj->router_address6; + listnode_add (nexthops6, nh6); + } + } +} +#endif /* HAVE_IPV6 */ + +static struct isis_route_info * +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 (prefix->family == AF_INET) + { + rinfo->nexthops = list_new (); + for (ALL_LIST_ELEMENTS_RO (adjacencies, node, 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 (prefix->family == AF_INET6) + { + rinfo->nexthops6 = list_new (); + for (ALL_LIST_ELEMENTS_RO (adjacencies, node, 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 */ + + rinfo->cost = cost; + rinfo->depth = depth; + + return rinfo; +} + +static void +isis_route_info_delete (struct isis_route_info *route_info) +{ + if (route_info->nexthops) + { + route_info->nexthops->del = (void (*)(void *)) isis_nexthop_delete; + list_delete (route_info->nexthops); + } + +#ifdef HAVE_IPV6 + if (route_info->nexthops6) + { + route_info->nexthops6->del = (void (*)(void *)) isis_nexthop6_delete; + list_delete (route_info->nexthops6); + } +#endif /* HAVE_IPV6 */ + + XFREE (MTYPE_ISIS_ROUTE_INFO, route_info); +} + +static int +isis_route_info_same_attrib (struct isis_route_info *new, + struct isis_route_info *old) +{ + if (new->cost != old->cost) + return 0; + if (new->depth != old->depth) + return 0; + + return 1; +} + +static int +isis_route_info_same (struct isis_route_info *new, + struct isis_route_info *old, u_char family) +{ + struct listnode *node; + struct isis_nexthop *nexthop; +#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) + == 0) + return 0; + + for (ALL_LIST_ELEMENTS_RO (old->nexthops, node, nexthop)) + if (nexthoplookup (new->nexthops, &nexthop->ip, nexthop->ifindex) + == 0) + return 0; + } +#ifdef HAVE_IPV6 + else if (family == AF_INET6) + { + for (ALL_LIST_ELEMENTS_RO (new->nexthops6, node, nexthop6)) + if (nexthop6lookup (old->nexthops6, &nexthop6->ip6, + nexthop6->ifindex) == 0) + return 0; + + for (ALL_LIST_ELEMENTS_RO (old->nexthops6, node, nexthop6)) + if (nexthop6lookup (new->nexthops6, &nexthop6->ip6, + nexthop6->ifindex) == 0) + return 0; + } +#endif /* HAVE_IPV6 */ + + return 1; +} + +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, + int level) +{ + struct route_node *route_node; + struct isis_route_info *rinfo_new, *rinfo_old, *route_info = NULL; + u_char buff[BUFSIZ]; + u_char family; + + family = prefix->family; + /* for debugs */ + prefix2str (prefix, (char *) buff, BUFSIZ); + + 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); +#ifdef HAVE_IPV6 + else if (family == AF_INET6) + route_node = route_node_get (area->route_table6[level - 1], prefix); +#endif /* HAVE_IPV6 */ + else + { + isis_route_info_delete (rinfo_new); + 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); + route_info = rinfo_new; + UNSET_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); + } + else + { + 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 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); + route_node->info = route_info; + + return route_info; +} + +static void +isis_route_delete (struct prefix *prefix, struct route_table *table) +{ + struct route_node *rode; + struct isis_route_info *rinfo; + char buff[BUFSIZ]; + + /* for log */ + prefix2str (prefix, buff, BUFSIZ); + + + rode = route_node_get (table, prefix); + rinfo = rode->info; + + if (rinfo == NULL) + { + if (isis->debugs & DEBUG_RTE_EVENTS) + zlog_debug ("ISIS-Rte: tried to delete non-existant route %s", buff); + return; + } + + if (CHECK_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED)) + { + UNSET_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE); + if (isis->debugs & DEBUG_RTE_EVENTS) + zlog_debug ("ISIS-Rte: route delete %s", buff); + isis_zebra_route_update (prefix, rinfo); + } + isis_route_info_delete (rinfo); + rode->info = NULL; + + return; +} + +/* Validating routes in particular table. */ +static void +isis_route_validate_table (struct isis_area *area, struct route_table *table) +{ + struct route_node *rnode, *drnode; + struct isis_route_info *rinfo; + u_char buff[BUFSIZ]; + + for (rnode = route_top (table); rnode; rnode = route_next (rnode)) + { + if (rnode->info == NULL) + continue; + rinfo = rnode->info; + + if (isis->debugs & DEBUG_RTE_EVENTS) + { + prefix2str (&rnode->p, (char *) buff, BUFSIZ); + zlog_debug ("ISIS-Rte (%s): route validate: %s %s %s %s", + area->area_tag, + (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); + } + + isis_zebra_route_update (&rnode->p, rinfo); + if (!CHECK_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE)) + { + /* Area is either L1 or L2 => we use level route tables directly for + * validating => no problems with deleting routes. */ + if (area->is_type != IS_LEVEL_1_AND_2) + { + isis_route_delete (&rnode->p, table); + continue; + } + /* If area is L1L2, we work with merge table and therefore must + * delete node from level tables as well before deleting route info. + * FIXME: Is it performance problem? There has to be the better way. + * Like not to deal with it here at all (see the next comment)? */ + if (rnode->p.family == AF_INET) + { + drnode = route_node_get (area->route_table[0], &rnode->p); + if (drnode->info == rnode->info) + drnode->info = NULL; + drnode = route_node_get (area->route_table[1], &rnode->p); + if (drnode->info == rnode->info) + drnode->info = NULL; + } + +#ifdef HAVE_IPV6 + if (rnode->p.family == AF_INET6) + { + drnode = route_node_get (area->route_table6[0], &rnode->p); + if (drnode->info == rnode->info) + drnode->info = NULL; + drnode = route_node_get (area->route_table6[1], &rnode->p); + if (drnode->info == rnode->info) + drnode->info = NULL; + } +#endif + + isis_route_delete (&rnode->p, table); + } + } +} + +/* Function to validate route tables for L1L2 areas. In this case we can't use + * level route tables directly, we have to merge them at first. L1 routes are + * preferred over the L2 ones. + * + * Merge algorithm is trivial (at least for now). All L1 paths are copied into + * merge table at first, then L2 paths are added if L1 path for same prefix + * doesn't already exists there. + * + * FIXME: Is it right place to do it at all? Maybe we should push both levels + * to the RIB with different zebra route types and let RIB handle this? */ +static void +isis_route_validate_merge (struct isis_area *area, int family) +{ + struct route_table *table = NULL; + struct route_table *merge; + struct route_node *rnode, *mrnode; + + merge = route_table_init (); + + if (family == AF_INET) + table = area->route_table[0]; +#ifdef HAVE_IPV6 + else if (family == AF_INET6) + table = area->route_table6[0]; +#endif + + for (rnode = route_top (table); rnode; rnode = route_next (rnode)) + { + if (rnode->info == NULL) + continue; + mrnode = route_node_get (merge, &rnode->p); + mrnode->info = rnode->info; + } + + if (family == AF_INET) + table = area->route_table[1]; +#ifdef HAVE_IPV6 + else if (family == AF_INET6) + table = area->route_table6[1]; +#endif + + for (rnode = route_top (table); rnode; rnode = route_next (rnode)) + { + if (rnode->info == NULL) + continue; + mrnode = route_node_get (merge, &rnode->p); + if (mrnode->info != NULL) + continue; + mrnode->info = rnode->info; + } + + isis_route_validate_table (area, merge); + route_table_finish (merge); +} + +/* Walk through route tables and propagate necessary changes into RIB. In case + * of L1L2 area, level tables have to be merged at first. */ +void +isis_route_validate (struct isis_area *area) +{ + struct listnode *node; + struct isis_circuit *circuit; + + if (area->is_type == IS_LEVEL_1) + 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); + +#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)) + { + if (rode->info == NULL) + continue; + rinfo = rode->info; + + UNSET_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE); + } +} + +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 new file mode 100644 index 0000000..0d2379c --- /dev/null +++ b/isisd/isis_route.h @@ -0,0 +1,70 @@ +/* + * IS-IS Rout(e)ing protocol - isis_route.h + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * based on ../ospf6d/ospf6_route.[ch] + * by Yasuhiro Ohara + * + * 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. + */ +#ifndef _ZEBRA_ISIS_ROUTE_H +#define _ZEBRA_ISIS_ROUTE_H + +#ifdef HAVE_IPV6 +struct isis_nexthop6 +{ + ifindex_t ifindex; + struct in6_addr ip6; + struct in6_addr router_address6; + unsigned int lock; +}; +#endif /* HAVE_IPV6 */ + +struct isis_nexthop +{ + ifindex_t ifindex; + struct in_addr ip; + struct in_addr router_address; + unsigned int lock; +}; + +struct isis_route_info +{ +#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; + struct list *nexthops; +#ifdef HAVE_IPV6 + struct list *nexthops6; +#endif /* HAVE_IPV6 */ +}; + +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, int level); + +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 new file mode 100644 index 0000000..3443a0a --- /dev/null +++ b/isisd/isis_routemap.c @@ -0,0 +1,565 @@ +/* + * 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 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 "filter.h" +#include "hash.h" +#include "if.h" +#include "linklist.h" +#include "log.h" +#include "memory.h" +#include "prefix.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" +#include "isis_adjacency.h" +#include "isis_circuit.h" +#include "isis_tlv.h" +#include "isis_pdu.h" +#include "isis_lsp.h" +#include "isis_spf.h" +#include "isis_route.h" +#include "isis_zebra.h" +#include "isis_routemap.h" + +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; + + 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; + + 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) +{ + struct access_list *alist; + + 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; + + 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) + { + metric = rule; + info = object; + + info->metric = *metric; + } + return RMAP_OKAY; +} + +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) +{ + 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 +}; + +/* ------------------------------------------------------------*/ + +static int +isis_route_match_add(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg) +{ + int ret; + + ret = route_map_add_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_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) +{ + 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_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 0000000..1cb063f --- /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 new file mode 100644 index 0000000..6b2456f --- /dev/null +++ b/isisd/isis_spf.c @@ -0,0 +1,1695 @@ +/* + * IS-IS Rout(e)ing protocol - isis_spf.c + * The SPT algorithm + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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 "thread.h" +#include "linklist.h" +#include "vty.h" +#include "log.h" +#include "command.h" +#include "memory.h" +#include "prefix.h" +#include "hash.h" +#include "if.h" +#include "table.h" + +#include "isis_constants.h" +#include "isis_common.h" +#include "isis_flags.h" +#include "dict.h" +#include "isisd.h" +#include "isis_misc.h" +#include "isis_adjacency.h" +#include "isis_circuit.h" +#include "isis_tlv.h" +#include "isis_pdu.h" +#include "isis_lsp.h" +#include "isis_dynhn.h" +#include "isis_spf.h" +#include "isis_route.h" +#include "isis_csm.h" + +int isis_run_spf_l1 (struct thread *thread); +int isis_run_spf_l2 (struct thread *thread); + +/* 7.2.7 */ +static void +remove_excess_adjs (struct list *adjs) +{ + struct listnode *node, *excess = NULL; + struct isis_adjacency *adj, *candidate = NULL; + int comp; + + for (ALL_LIST_ELEMENTS_RO (adjs, node, adj)) + { + if (excess == NULL) + excess = node; + candidate = listgetdata (excess); + + if (candidate->sys_type < adj->sys_type) + { + excess = node; + candidate = adj; + continue; + } + if (candidate->sys_type > adj->sys_type) + continue; + + comp = memcmp (candidate->sysid, adj->sysid, ISIS_SYS_ID_LEN); + if (comp > 0) + { + excess = node; + candidate = adj; + continue; + } + if (comp < 0) + continue; + + if (candidate->circuit->circuit_id > adj->circuit->circuit_id) + { + excess = node; + candidate = adj; + continue; + } + + if (candidate->circuit->circuit_id < adj->circuit->circuit_id) + continue; + + comp = memcmp (candidate->snpa, adj->snpa, ETH_ALEN); + if (comp > 0) + { + excess = node; + candidate = adj; + continue; + } + } + + list_delete_node (adjs, excess); + + return; +} + +static const char * +vtype2string (enum vertextype vtype) +{ + switch (vtype) + { + case VTYPE_PSEUDO_IS: + return "pseudo_IS"; + break; + case VTYPE_PSEUDO_TE_IS: + return "pseudo_TE-IS"; + break; + case VTYPE_NONPSEUDO_IS: + return "IS"; + break; + case VTYPE_NONPSEUDO_TE_IS: + return "TE-IS"; + break; + case VTYPE_ES: + return "ES"; + break; + case VTYPE_IPREACH_INTERNAL: + return "IP internal"; + break; + case VTYPE_IPREACH_EXTERNAL: + return "IP external"; + break; + case VTYPE_IPREACH_TE: + return "IP TE"; + break; +#ifdef HAVE_IPV6 + case VTYPE_IP6REACH_INTERNAL: + return "IP6 internal"; + break; + case VTYPE_IP6REACH_EXTERNAL: + return "IP6 external"; + break; +#endif /* HAVE_IPV6 */ + default: + return "UNKNOWN"; + } + return NULL; /* Not reached */ +} + +static const char * +vid2string (struct isis_vertex *vertex, u_char * buff) +{ + switch (vertex->type) + { + case VTYPE_PSEUDO_IS: + case VTYPE_PSEUDO_TE_IS: + return print_sys_hostname (vertex->N.id); + break; + case VTYPE_NONPSEUDO_IS: + case VTYPE_NONPSEUDO_TE_IS: + case VTYPE_ES: + return print_sys_hostname (vertex->N.id); + 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 */ + prefix2str ((struct prefix *) &vertex->N.prefix, (char *) buff, BUFSIZ); + break; + default: + return "UNKNOWN"; + } + + return (char *) buff; +} + +static struct isis_vertex * +isis_vertex_new (void *id, enum vertextype vtype) +{ + struct isis_vertex *vertex; + + vertex = XCALLOC (MTYPE_ISIS_VERTEX, sizeof (struct isis_vertex)); + + vertex->type = vtype; + switch (vtype) + { + 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!"); + } + + 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; +} + +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; +} + +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) + { + if (area->spftree[0] == NULL) + area->spftree[0] = isis_spftree_new (area); +#ifdef HAVE_IPV6 + if (area->spftree6[0] == NULL) + area->spftree6[0] = isis_spftree_new (area); +#endif + } + + if (area->is_type & IS_LEVEL_2) + { + if (area->spftree[1] == NULL) + area->spftree[1] = isis_spftree_new (area); +#ifdef HAVE_IPV6 + if (area->spftree6[1] == NULL) + area->spftree6[1] = isis_spftree_new (area); +#endif + } + + return; +} + +void +spftree_area_del (struct isis_area *area) +{ + if (area->is_type & IS_LEVEL_1) + { + if (area->spftree[0] != 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 + } + + if (area->is_type & IS_LEVEL_2) + { + if (area->spftree[1] != NULL) + { + isis_spftree_del (area->spftree[1]); + area->spftree[1] = NULL; + } +#ifdef HAVE_IPV6 + if (area->spftree6[1] != NULL) + { + isis_spftree_del (area->spftree6[1]); + area->spftree6[1] = NULL; + } +#endif + } + + return; +} + +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 struct isis_vertex * +isis_spf_add_root (struct isis_spftree *spftree, int level, u_char *sysid) +{ + struct isis_vertex *vertex; + struct isis_lsp *lsp; +#ifdef EXTREME_DEBUG + u_char buff[BUFSIZ]; +#endif /* EXTREME_DEBUG */ + + 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 (!spftree->area->oldmetric) + vertex = isis_vertex_new (sysid, VTYPE_NONPSEUDO_TE_IS); + else + vertex = isis_vertex_new (sysid, VTYPE_NONPSEUDO_IS); + + listnode_add (spftree->paths, vertex); + +#ifdef EXTREME_DEBUG + zlog_debug ("ISIS-Spf: added this IS %s %s depth %d dist %d to PATHS", + vtype2string (vertex->type), vid2string (vertex, buff), + vertex->depth, vertex->d_N); +#endif /* EXTREME_DEBUG */ + + return vertex; +} + +static struct isis_vertex * +isis_find_vertex (struct list *list, void *id, enum vertextype vtype) +{ + struct listnode *node; + struct isis_vertex *vertex; + struct prefix *p1, *p2; + + for (ALL_LIST_ELEMENTS_RO (list, node, vertex)) + { + if (vertex->type != vtype) + continue; + switch (vtype) + { + case VTYPE_ES: + case VTYPE_NONPSEUDO_IS: + case VTYPE_NONPSEUDO_TE_IS: + if (memcmp ((u_char *) id, vertex->N.id, ISIS_SYS_ID_LEN) == 0) + return vertex; + break; + case VTYPE_PSEUDO_IS: + case VTYPE_PSEUDO_TE_IS: + if (memcmp ((u_char *) id, vertex->N.id, ISIS_SYS_ID_LEN + 1) == 0) + return vertex; + 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 */ + p1 = (struct prefix *) id; + p2 = (struct prefix *) &vertex->N.id; + if (p1->family == p2->family && p1->prefixlen == p2->prefixlen && + memcmp (&p1->u.prefix, &p2->u.prefix, + PSIZE (p1->prefixlen)) == 0) + return vertex; + break; + } + } + + return NULL; +} + +/* + * Add a vertex to TENT sorted by cost and by vertextype on tie break situation + */ +static struct isis_vertex * +isis_spf_add2tent (struct isis_spftree *spftree, enum vertextype vtype, + 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 (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 %s depth %d dist %d adjcount %d", + print_sys_hostname (vertex->N.id), + vtype2string (vertex->type), vid2string (vertex, buff), + vertex->depth, vertex->d_N, listcount(vertex->Adj_N)); +#endif /* EXTREME_DEBUG */ + + if (list_isempty (spftree->tents)) + { + listnode_add (spftree->tents, vertex); + return vertex; + } + + /* XXX: This cant use the standard ALL_LIST_ELEMENTS macro */ + for (node = listhead (spftree->tents); node; node = listnextnode (node)) + { + v = listgetdata (node); + if (v->d_N > vertex->d_N) + { + list_add_node_prev (spftree->tents, node, vertex); + break; + } + else if (v->d_N == vertex->d_N && v->type > vertex->type) + { + /* Tie break, add according to type */ + list_add_node_prev (spftree->tents, node, vertex); + break; + } + } + + if (node == NULL) + listnode_add (spftree->tents, vertex); + + return vertex; +} + +static void +isis_spf_add_local (struct isis_spftree *spftree, enum vertextype vtype, + void *id, struct isis_adjacency *adj, uint32_t cost, + int family, struct isis_vertex *parent) +{ + struct isis_vertex *vertex; + + vertex = isis_find_vertex (spftree->tents, id, vtype); + + if (vertex) + { + /* C.2.5 c) */ + if (vertex->d_N == cost) + { + if (adj) + listnode_add (vertex->Adj_N, adj); + /* 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; + } + else if (vertex->d_N < cost) + { + /* e) do nothing */ + return; + } + 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); + } + } + + 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, + 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) */ + 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 %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); + return; + } + + vertex = isis_find_vertex (spftree->tents, id, vtype); + /* d) */ + if (vertex) + { + /* 1) */ +#ifdef EXTREME_DEBUG + 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) + { + 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; + } + else if (vertex->d_N < dist) + { + return; + /* 4) */ + } + 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); + } + } + +#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; +} + +/* + * C.2.6 Step 1 + */ +static int +isis_spf_process_lsp (struct isis_spftree *spftree, struct isis_lsp *lsp, + uint32_t cost, uint16_t depth, int family, + u_char *root_sysid, struct isis_vertex *parent) +{ + struct listnode *node, *fragnode = NULL; + uint32_t dist; + struct is_neigh *is_neigh; + struct te_is_neigh *te_is_neigh; + struct ipv4_reachability *ipreach; + struct te_ipv4_reachability *te_ipv4_reach; + enum vertextype vtype; + struct prefix prefix; +#ifdef HAVE_IPV6 + struct ipv6_reachability *ip6reach; +#endif /* HAVE_IPV6 */ + static const u_char null_sysid[ISIS_SYS_ID_LEN]; + + 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 - 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) + { + 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_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)) + { + 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); + else + fragnode = listnextnode (fragnode); + + if (fragnode) + { + lsp = listgetdata (fragnode); + goto lspfragloop; + } + + return ISIS_OK; +} + +static int +isis_spf_process_pseudo_lsp (struct isis_spftree *spftree, + struct isis_lsp *lsp, uint32_t cost, + uint16_t depth, int family, + 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: + + if (lsp->lsp_header->seq_num == 0) + { + zlog_warn ("isis_spf_process_pseudo_lsp(): lsp with 0 seq_num" + " - do not process"); + 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)) + { + /* Two way connectivity */ + if (!memcmp (is_neigh->neigh_id, root_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)) + { + /* Two way connectivity */ + if (!memcmp (te_is_neigh->neigh_id, root_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 (fragnode == NULL) + fragnode = listhead (lsp->lspu.frags); + else + fragnode = listnextnode (fragnode); + + if (fragnode) + { + lsp = listgetdata (fragnode); + goto pseudofragloop; + } + + return ISIS_OK; +} + +static int +isis_spf_preload_tent (struct isis_spftree *spftree, int level, + int family, u_char *root_sysid, + struct isis_vertex *parent) +{ + struct isis_circuit *circuit; + struct listnode *cnode, *anode, *ipnode; + struct isis_adjacency *adj; + struct isis_lsp *lsp; + struct list *adj_list; + struct list *adjdb; + struct prefix_ipv4 *ipv4; + 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 (spftree->area->circuit_list, cnode, circuit)) + { + if (circuit->state != C_STATE_UP) + continue; + if (!(circuit->is_type & level)) + continue; + if (family == AF_INET && !circuit->ip_router) + continue; +#ifdef HAVE_IPV6 + if (family == AF_INET6 && !circuit->ipv6_router) + continue; +#endif /* HAVE_IPV6 */ + /* + * Add IP(v6) addresses of this circuit + */ + if (family == AF_INET) + { + prefix.family = AF_INET; + for (ALL_LIST_ELEMENTS_RO (circuit->ip_addrs, ipnode, ipv4)) + { + prefix.u.prefix4 = ipv4->prefix; + prefix.prefixlen = ipv4->prefixlen; + apply_mask (&prefix); + isis_spf_add_local (spftree, VTYPE_IPREACH_INTERNAL, &prefix, + NULL, 0, family, parent); + } + } +#ifdef HAVE_IPV6 + if (family == AF_INET6) + { + prefix.family = AF_INET6; + for (ALL_LIST_ELEMENTS_RO (circuit->ipv6_non_link, ipnode, ipv6)) + { + prefix.prefixlen = ipv6->prefixlen; + prefix.u.prefix6 = ipv6->prefix; + apply_mask (&prefix); + isis_spf_add_local (spftree, VTYPE_IP6REACH_INTERNAL, + &prefix, NULL, 0, family, parent); + } + } +#endif /* HAVE_IPV6 */ + if (circuit->circ_type == CIRCUIT_T_BROADCAST) + { + /* + * Add the adjacencies + */ + adj_list = list_new (); + adjdb = circuit->u.bc.adjdb[level - 1]; + isis_adj_build_up_list (adjdb, adj_list); + if (listcount (adj_list) == 0) + { + list_delete (adj_list); + if (isis->debugs & DEBUG_SPF_EVENTS) + zlog_debug ("ISIS-Spf: no L%d adjacencies on circuit %s", + level, circuit->interface->name); + continue; + } + for (ALL_LIST_ELEMENTS_RO (adj_list, anode, adj)) + { + if (!speaks (&adj->nlpids, family)) + 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, parent); + break; + case ISIS_SYSTYPE_IS: + case ISIS_SYSTYPE_L1_IS: + case ISIS_SYSTYPE_L2_IS: + 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, 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"); + } + } + list_delete (adj_list); + /* + * Add the pseudonode + */ + if (level == 1) + 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); + /* 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 from root " + "to L%d DR %s on %s (ID %d)", + level, rawlspid_print (lsp_id), + circuit->interface->name, circuit->circuit_id); + continue; + } + 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 (%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) + { + adj = circuit->u.p2p.neighbor; + if (!adj) + 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, + 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, + spftree->area->oldmetric ? + VTYPE_NONPSEUDO_IS : + VTYPE_NONPSEUDO_TE_IS, + adj->sysid, + adj, circuit->te_metric[level - 1], + family, parent); + break; + case ISIS_SYSTYPE_UNKNOWN: + default: + 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; +} + +/* + * The parent(s) for vertex is set when added to TENT list + * now we just put the child pointer(s) in place + */ +static void +add_to_paths (struct isis_spftree *spftree, struct isis_vertex *vertex, + int level) +{ + u_char buff[BUFSIZ]; + + 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 %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, spftree->area, level); + else if (isis->debugs & DEBUG_SPF_EVENTS) + 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; +} + +static void +init_spt (struct isis_spftree *spftree) +{ + spftree->tents->del = spftree->paths->del = (void (*)(void *)) isis_vertex_del; + 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, 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 route_table *table = NULL; + 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]; +#ifdef HAVE_IPV6 + 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) + table = area->route_table[level - 1]; +#ifdef HAVE_IPV6 + else if (family == AF_INET6) + table = area->route_table6[level - 1]; +#endif + + isis_route_invalidate_table (area, table); + + /* + * C.2.5 Step 0 + */ + init_spt (spftree); + /* a) */ + root_vertex = isis_spf_add_root (spftree, level, sysid); + /* b) */ + 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 SPF-root:%s", print_sys_hostname(sysid)); + goto out; + } + + while (listcount (spftree->tents) > 0) + { + node = listhead (spftree->tents); + vertex = listgetdata (node); + +#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 && 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, sysid, + vertex); + } + else + { + isis_spf_process_lsp (spftree, lsp, vertex->d_N, + vertex->depth, family, sysid, vertex); + } + } + else + { + zlog_warn ("ISIS-Spf: No LSP found for %s", + rawlspid_print (lsp_id)); + } + break; + default:; + } + } + +out: + 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; +} + +int +isis_run_spf_l1 (struct thread *thread) +{ + struct isis_area *area; + int retval = ISIS_OK; + + area = THREAD_ARG (thread); + assert (area); + + area->spftree[0]->t_spf = NULL; + area->spftree[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); + return ISIS_WARNING; + } + + if (isis->debugs & DEBUG_SPF_EVENTS) + 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, isis->sysid); + + return retval; +} + +int +isis_run_spf_l2 (struct thread *thread) +{ + struct isis_area *area; + int retval = ISIS_OK; + + area = THREAD_ARG (thread); + assert (area); + + area->spftree[1]->t_spf = NULL; + area->spftree[1]->pending = 0; + + if (!(area->is_type & IS_LEVEL_2)) + { + if (isis->debugs & DEBUG_SPF_EVENTS) + zlog_warn ("ISIS-SPF (%s) area does not share level", area->area_tag); + return ISIS_WARNING; + } + + if (isis->debugs & DEBUG_SPF_EVENTS) + 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, isis->sysid); + + return retval; +} + +int +isis_spf_schedule (struct isis_area *area, int level) +{ + struct isis_spftree *spftree = area->spftree[level - 1]; + time_t now = time (NULL); + int diff = now - spftree->last_run_timestamp; + + assert (diff >= 0); + assert (area->is_type & level); + + 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); + + if (spftree->pending) + return ISIS_OK; + + THREAD_TIMER_OFF (spftree->t_spf); + + /* 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); + + if (level == 1) + THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l1, area, + area->min_spf_interval[0] - diff); + else + THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l2, area, + area->min_spf_interval[1] - diff); + + 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 +static int +isis_run_spf6_l1 (struct thread *thread) +{ + struct isis_area *area; + int retval = ISIS_OK; + + area = THREAD_ARG (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); + return ISIS_WARNING; + } + + if (isis->debugs & DEBUG_SPF_EVENTS) + 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, isis->sysid); + + return retval; +} + +static int +isis_run_spf6_l2 (struct thread *thread) +{ + struct isis_area *area; + int retval = ISIS_OK; + + area = THREAD_ARG (thread); + assert (area); + + area->spftree6[1]->t_spf = NULL; + area->spftree6[1]->pending = 0; + + if (!(area->is_type & IS_LEVEL_2)) + { + if (isis->debugs & DEBUG_SPF_EVENTS) + zlog_warn ("ISIS-SPF (%s) area does not share level", area->area_tag); + return ISIS_WARNING; + } + + if (isis->debugs & DEBUG_SPF_EVENTS) + 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, isis->sysid); + + return retval; +} + +int +isis_spf_schedule6 (struct isis_area *area, int level) +{ + int retval = ISIS_OK; + struct isis_spftree *spftree = area->spftree6[level - 1]; + time_t now = time (NULL); + time_t diff = now - spftree->last_run_timestamp; + + assert (diff >= 0); + assert (area->is_type & level); + + 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); + + if (spftree->pending) + return ISIS_OK; + + THREAD_TIMER_OFF (spftree->t_spf); + + /* 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); + + if (level == 1) + THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf6_l1, area, + area->min_spf_interval[0] - diff); + else + THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf6_l2, area, + area->min_spf_interval[1] - diff); + + 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, u_char *root_sysid) +{ + struct listnode *node; + struct listnode *anode; + struct isis_vertex *vertex; + struct isis_adjacency *adj; + u_char buff[BUFSIZ]; + + 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 ", ""); + } + + /* 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 { + 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); + } + } +#endif + vty_out (vty, "%s", VTY_NEWLINE); + } +} + +DEFUN (show_isis_topology, + show_isis_topology_cmd, + "show isis topology", + SHOW_STR + "IS-IS information\n" + "IS-IS paths to Intermediate Systems\n") +{ + struct listnode *node; + struct isis_area *area; + int level; + + if (!isis->area_list || 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->ip_circuits > 0 && area->spftree[level] + && area->spftree[level]->paths->count > 0) + { + 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->sysid); + vty_out (vty, "%s", VTY_NEWLINE); + } +#ifdef HAVE_IPV6 + if (area->ipv6_circuits > 0 && area->spftree6[level] + && area->spftree6[level]->paths->count > 0) + { + 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->sysid); + vty_out (vty, "%s", VTY_NEWLINE); + } +#endif /* HAVE_IPV6 */ + } + + vty_out (vty, "%s", VTY_NEWLINE); + } + + return CMD_SUCCESS; +} + +DEFUN (show_isis_topology_l1, + show_isis_topology_l1_cmd, + "show isis topology level-1", + SHOW_STR + "IS-IS information\n" + "IS-IS paths to Intermediate Systems\n" + "Paths to all level-1 routers in the area\n") +{ + struct listnode *node; + struct isis_area *area; + + if (!isis->area_list || 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); + + if (area->ip_circuits > 0 && area->spftree[0] + && area->spftree[0]->paths->count > 0) + { + 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->sysid); + vty_out (vty, "%s", VTY_NEWLINE); + } +#ifdef HAVE_IPV6 + if (area->ipv6_circuits > 0 && area->spftree6[0] + && area->spftree6[0]->paths->count > 0) + { + 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->sysid); + vty_out (vty, "%s", VTY_NEWLINE); + } +#endif /* HAVE_IPV6 */ + vty_out (vty, "%s", VTY_NEWLINE); + } + + return CMD_SUCCESS; +} + +DEFUN (show_isis_topology_l2, + show_isis_topology_l2_cmd, + "show isis topology level-2", + SHOW_STR + "IS-IS information\n" + "IS-IS paths to Intermediate Systems\n" + "Paths to all level-2 routers in the domain\n") +{ + struct listnode *node; + struct isis_area *area; + + if (!isis->area_list || 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); + + if (area->ip_circuits > 0 && area->spftree[1] + && area->spftree[1]->paths->count > 0) + { + 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->sysid); + vty_out (vty, "%s", VTY_NEWLINE); + } +#ifdef HAVE_IPV6 + if (area->ipv6_circuits > 0 && area->spftree6[1] + && area->spftree6[1]->paths->count > 0) + { + 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->sysid); + vty_out (vty, "%s", VTY_NEWLINE); + } +#endif /* HAVE_IPV6 */ + vty_out (vty, "%s", VTY_NEWLINE); + } + + return CMD_SUCCESS; +} + +void +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); +} diff --git a/isisd/isis_spf.h b/isisd/isis_spf.h new file mode 100644 index 0000000..aa543b7 --- /dev/null +++ b/isisd/isis_spf.h @@ -0,0 +1,89 @@ +/* + * IS-IS Rout(e)ing protocol - isis_spf.h + * IS-IS Shortest Path First algorithm + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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. + */ + +#ifndef _ZEBRA_ISIS_SPF_H +#define _ZEBRA_ISIS_SPF_H + +enum vertextype +{ + VTYPE_PSEUDO_IS = 1, + VTYPE_PSEUDO_TE_IS, + VTYPE_NONPSEUDO_IS, + VTYPE_NONPSEUDO_TE_IS, + VTYPE_ES, + VTYPE_IPREACH_INTERNAL, + VTYPE_IPREACH_EXTERNAL, + VTYPE_IPREACH_TE +#ifdef HAVE_IPV6 + , + VTYPE_IP6REACH_INTERNAL, + VTYPE_IP6REACH_EXTERNAL +#endif /* HAVE_IPV6 */ +}; + +/* + * Triple + */ +struct isis_vertex +{ + enum vertextype type; + + union + { + u_char id[ISIS_SYS_ID_LEN + 1]; + struct prefix prefix; + } N; + + 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)} 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 */ + struct list *paths; /* the SPT */ + struct list *tents; /* TENT */ + 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 +int isis_spf_schedule6 (struct isis_area *area, int level); +#endif +#endif /* _ZEBRA_ISIS_SPF_H */ diff --git a/isisd/isis_te.c b/isisd/isis_te.c new file mode 100644 index 0000000..6cb8551 --- /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 0000000..4f29fdb --- /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 new file mode 100644 index 0000000..1d29d78 --- /dev/null +++ b/isisd/isis_tlv.c @@ -0,0 +1,1196 @@ +/* + * IS-IS Rout(e)ing protocol - isis_tlv.c + * IS-IS TLV related routines + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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 "log.h" +#include "linklist.h" +#include "stream.h" +#include "memory.h" +#include "prefix.h" +#include "vty.h" +#include "if.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/isis_tlv.h" +#include "isisd/isisd.h" +#include "isisd/isis_dynhn.h" +#include "isisd/isis_misc.h" +#include "isisd/isis_pdu.h" +#include "isisd/isis_lsp.h" +#include "isisd/isis_te.h" + +void +free_tlv (void *val) +{ + XFREE (MTYPE_ISIS_TLV, val); + + return; +} + +/* + * Called after parsing of a PDU. There shouldn't be any tlv's left, so this + * is only a caution to avoid memory leaks + */ +void +free_tlvs (struct tlvs *tlvs) +{ + if (tlvs->area_addrs) + list_delete (tlvs->area_addrs); + if (tlvs->is_neighs) + list_delete (tlvs->is_neighs); + if (tlvs->te_is_neighs) + list_delete (tlvs->te_is_neighs); + if (tlvs->es_neighs) + list_delete (tlvs->es_neighs); + if (tlvs->lsp_entries) + list_delete (tlvs->lsp_entries); + 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) + list_delete (tlvs->ipv4_int_reachs); + if (tlvs->ipv4_ext_reachs) + list_delete (tlvs->ipv4_ext_reachs); + if (tlvs->te_ipv4_reachs) + list_delete (tlvs->te_ipv4_reachs); +#ifdef HAVE_IPV6 + if (tlvs->ipv6_addrs) + list_delete (tlvs->ipv6_addrs); + if (tlvs->ipv6_reachs) + list_delete (tlvs->ipv6_reachs); +#endif /* HAVE_IPV6 */ + + memset (tlvs, 0, sizeof (struct tlvs)); + + return; +} + +/* + * Parses the tlvs found in the variant length part of the PDU. + * Caller tells with flags in "expected" which TLV's it is interested in. + */ +int +parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected, + u_int32_t * found, struct tlvs *tlvs, u_int32_t *auth_tlv_offset) +{ + u_char type, length; + struct lan_neigh *lan_nei; + struct area_addr *area_addr; + struct is_neigh *is_nei; + struct te_is_neigh *te_is_nei; + struct es_neigh *es_nei; + struct lsp_entry *lsp_entry; + struct in_addr *ipv4_addr; + struct ipv4_reachability *ipv4_reach; + struct te_ipv4_reachability *te_ipv4_reach; +#ifdef HAVE_IPV6 + struct in6_addr *ipv6_addr; + struct ipv6_reachability *ipv6_reach; + int prefix_octets; +#endif /* HAVE_IPV6 */ + int value_len, retval = ISIS_OK; + u_char *start = stream, *pnt = stream, *endpnt; + + *found = 0; + memset (tlvs, 0, sizeof (struct tlvs)); + + while (pnt < stream + size - 2) + { + type = *pnt; + length = *(pnt + 1); + pnt += 2; + value_len = 0; + if (pnt + length > stream + size) + { + zlog_warn ("ISIS-TLV (%s): TLV (type %d, length %d) exceeds packet " + "boundaries", areatag, type, length); + retval = ISIS_WARNING; + break; + } + switch (type) + { + case AREA_ADDRESSES: + /* +-------+-------+-------+-------+-------+-------+-------+-------+ + * | Address Length | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | Area Address | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * : : + */ + *found |= TLVFLAG_AREA_ADDRS; +#ifdef EXTREME_TLV_DEBUG + zlog_debug ("TLV Area Adresses len %d", length); +#endif /* EXTREME_TLV_DEBUG */ + if (*expected & TLVFLAG_AREA_ADDRS) + { + while (length > value_len) + { + area_addr = (struct area_addr *) pnt; + value_len += area_addr->addr_len + 1; + pnt += area_addr->addr_len + 1; + if (!tlvs->area_addrs) + tlvs->area_addrs = list_new (); + listnode_add (tlvs->area_addrs, area_addr); + } + } + else + { + pnt += length; + } + break; + + case IS_NEIGHBOURS: + *found |= TLVFLAG_IS_NEIGHS; +#ifdef EXTREME_TLV_DEBUG + zlog_debug ("ISIS-TLV (%s): IS Neighbours length %d", + areatag, length); +#endif /* EXTREME_TLV_DEBUG */ + if (TLVFLAG_IS_NEIGHS & *expected) + { + /* +-------+-------+-------+-------+-------+-------+-------+-------+ + * | Virtual Flag | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + */ + pnt++; + value_len++; + /* +-------+-------+-------+-------+-------+-------+-------+-------+ + * | 0 | I/E | Default Metric | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | S | I/E | Delay Metric | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | S | I/E | Expense Metric | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | S | I/E | Error Metric | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | Neighbour ID | + * +---------------------------------------------------------------+ + * : : + */ + while (length > value_len) + { + is_nei = (struct is_neigh *) pnt; + value_len += 4 + ISIS_SYS_ID_LEN + 1; + pnt += 4 + ISIS_SYS_ID_LEN + 1; + if (!tlvs->is_neighs) + tlvs->is_neighs = list_new (); + listnode_add (tlvs->is_neighs, is_nei); + } + } + else + { + pnt += length; + } + break; + + case TE_IS_NEIGHBOURS: + /* +-------+-------+-------+-------+-------+-------+-------+-------+ + * | Neighbour ID | 7 + * +---------------------------------------------------------------+ + * | TE Metric | 3 + * +---------------------------------------------------------------+ + * | SubTLVs Length | 1 + * +---------------------------------------------------------------+ + * : : + */ + *found |= TLVFLAG_TE_IS_NEIGHS; +#ifdef EXTREME_TLV_DEBUG + zlog_debug ("ISIS-TLV (%s): Extended IS Neighbours length %d", + areatag, length); +#endif /* EXTREME_TLV_DEBUG */ + if (TLVFLAG_TE_IS_NEIGHS & *expected) + { + while (length > value_len) + { + te_is_nei = (struct te_is_neigh *) pnt; + 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; + + if (!tlvs->te_is_neighs) + tlvs->te_is_neighs = list_new (); + listnode_add (tlvs->te_is_neighs, te_is_nei); + } + } + else + { + pnt += length; + } + break; + + case ES_NEIGHBOURS: + /* +-------+-------+-------+-------+-------+-------+-------+-------+ + * | 0 | I/E | Default Metric | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | S | I/E | Delay Metric | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | S | I/E | Expense Metric | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | S | I/E | Error Metric | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | Neighbour ID | + * +---------------------------------------------------------------+ + * | Neighbour ID | + * +---------------------------------------------------------------+ + * : : + */ +#ifdef EXTREME_TLV_DEBUG + zlog_debug ("ISIS-TLV (%s): ES Neighbours length %d", + areatag, length); +#endif /* EXTREME_TLV_DEBUG */ + *found |= TLVFLAG_ES_NEIGHS; + if (*expected & TLVFLAG_ES_NEIGHS) + { + es_nei = (struct es_neigh *) pnt; + value_len += 4; + pnt += 4; + while (length > value_len) + { + /* FIXME FIXME FIXME - add to the list */ + /* sys_id->id = pnt; */ + value_len += ISIS_SYS_ID_LEN; + pnt += ISIS_SYS_ID_LEN; + /* if (!es_nei->neigh_ids) es_nei->neigh_ids = sysid; */ + } + if (!tlvs->es_neighs) + tlvs->es_neighs = list_new (); + listnode_add (tlvs->es_neighs, es_nei); + } + else + { + pnt += length; + } + break; + + case LAN_NEIGHBOURS: + /* +-------+-------+-------+-------+-------+-------+-------+-------+ + * | LAN Address | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * : : + */ + *found |= TLVFLAG_LAN_NEIGHS; +#ifdef EXTREME_TLV_DEBUG + zlog_debug ("ISIS-TLV (%s): LAN Neigbours length %d", + areatag, length); +#endif /* EXTREME_TLV_DEBUG */ + if (TLVFLAG_LAN_NEIGHS & *expected) + { + while (length > value_len) + { + lan_nei = (struct lan_neigh *) pnt; + if (!tlvs->lan_neighs) + tlvs->lan_neighs = list_new (); + listnode_add (tlvs->lan_neighs, lan_nei); + value_len += ETH_ALEN; + pnt += ETH_ALEN; + } + } + else + { + pnt += length; + } + break; + + case PADDING: +#ifdef EXTREME_TLV_DEBUG + zlog_debug ("TLV padding %d", length); +#endif /* EXTREME_TLV_DEBUG */ + pnt += length; + break; + + case LSP_ENTRIES: + /* +-------+-------+-------+-------+-------+-------+-------+-------+ + * | Remaining Lifetime | 2 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | LSP ID | id+2 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | LSP Sequence Number | 4 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | Checksum | 2 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + */ +#ifdef EXTREME_TLV_DEBUG + zlog_debug ("ISIS-TLV (%s): LSP Entries length %d", areatag, length); +#endif /* EXTREME_TLV_DEBUG */ + *found |= TLVFLAG_LSP_ENTRIES; + if (TLVFLAG_LSP_ENTRIES & *expected) + { + while (length > value_len) + { + lsp_entry = (struct lsp_entry *) pnt; + value_len += 10 + ISIS_SYS_ID_LEN; + pnt += 10 + ISIS_SYS_ID_LEN; + if (!tlvs->lsp_entries) + tlvs->lsp_entries = list_new (); + listnode_add (tlvs->lsp_entries, lsp_entry); + } + } + else + { + pnt += length; + } + break; + + case CHECKSUM: + /* +-------+-------+-------+-------+-------+-------+-------+-------+ + * | 16 bit fletcher CHECKSUM | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * : : + */ + *found |= TLVFLAG_CHECKSUM; +#ifdef EXTREME_TLV_DEBUG + zlog_debug ("ISIS-TLV (%s): Checksum length %d", areatag, length); +#endif /* EXTREME_TLV_DEBUG */ + if (*expected & TLVFLAG_CHECKSUM) + { + tlvs->checksum = (struct checksum *) pnt; + } + pnt += length; + break; + + case PROTOCOLS_SUPPORTED: + /* +-------+-------+-------+-------+-------+-------+-------+-------+ + * | NLPID | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * : : + */ + *found |= TLVFLAG_NLPID; +#ifdef EXTREME_TLV_DEBUG + zlog_debug ("ISIS-TLV (%s): Protocols Supported length %d", + areatag, length); +#endif /* EXTREME_TLV_DEBUG */ + if (*expected & TLVFLAG_NLPID) + { + tlvs->nlpids = (struct nlpids *) (pnt - 1); + } + pnt += length; + break; + + case IPV4_ADDR: + /* +-------+-------+-------+-------+-------+-------+-------+-------+ + * + IP version 4 address + 4 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * : : + */ + *found |= TLVFLAG_IPV4_ADDR; +#ifdef EXTREME_TLV_DEBUG + zlog_debug ("ISIS-TLV (%s): IPv4 Address length %d", + areatag, length); +#endif /* EXTREME_TLV_DEBUG */ + if (*expected & TLVFLAG_IPV4_ADDR) + { + while (length > value_len) + { + ipv4_addr = (struct in_addr *) pnt; +#ifdef EXTREME_TLV_DEBUG + zlog_debug ("ISIS-TLV (%s) : IP ADDR %s, pnt %p", areatag, + inet_ntoa (*ipv4_addr), pnt); +#endif /* EXTREME_TLV_DEBUG */ + if (!tlvs->ipv4_addrs) + tlvs->ipv4_addrs = list_new (); + listnode_add (tlvs->ipv4_addrs, ipv4_addr); + value_len += 4; + pnt += 4; + } + } + else + { + pnt += length; + } + break; + + case AUTH_INFO: + *found |= TLVFLAG_AUTH_INFO; +#ifdef EXTREME_TLV_DEBUG + zlog_debug ("ISIS-TLV (%s): IS-IS Authentication Information", + areatag); +#endif + if (*expected & TLVFLAG_AUTH_INFO) + { + tlvs->auth_info.type = *pnt; + 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); + /* 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 + { + pnt += length; + } + break; + + case DYNAMIC_HOSTNAME: + *found |= TLVFLAG_DYN_HOSTNAME; +#ifdef EXTREME_TLV_DEBUG + zlog_debug ("ISIS-TLV (%s): Dynamic Hostname length %d", + areatag, length); +#endif /* EXTREME_TLV_DEBUG */ + if (*expected & TLVFLAG_DYN_HOSTNAME) + { + /* the length is also included in the pointed struct */ + tlvs->hostname = (struct hostname *) (pnt - 1); + } + pnt += length; + break; + + case TE_ROUTER_ID: + /* +---------------------------------------------------------------+ + * + Router ID + 4 + * +---------------------------------------------------------------+ + */ + *found |= TLVFLAG_TE_ROUTER_ID; +#ifdef EXTREME_TLV_DEBUG + zlog_debug ("ISIS-TLV (%s): TE Router ID %d", areatag, length); +#endif /* EXTREME_TLV_DEBUG */ + if (*expected & TLVFLAG_TE_ROUTER_ID) + tlvs->router_id = (struct te_router_id *) (pnt); + pnt += length; + break; + + case IPV4_INT_REACHABILITY: + /* +-------+-------+-------+-------+-------+-------+-------+-------+ + * | 0 | I/E | Default Metric | 1 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | S | I/E | Delay Metric | 1 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | S | I/E | Expense Metric | 1 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | S | I/E | Error Metric | 1 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | ip address | 4 + * +---------------------------------------------------------------+ + * | address mask | 4 + * +---------------------------------------------------------------+ + * : : + */ + *found |= TLVFLAG_IPV4_INT_REACHABILITY; +#ifdef EXTREME_TLV_DEBUG + zlog_debug ("ISIS-TLV (%s): IPv4 internal Reachability length %d", + areatag, length); +#endif /* EXTREME_TLV_DEBUG */ + if (*expected & TLVFLAG_IPV4_INT_REACHABILITY) + { + while (length > value_len) + { + ipv4_reach = (struct ipv4_reachability *) pnt; + if (!tlvs->ipv4_int_reachs) + tlvs->ipv4_int_reachs = list_new (); + listnode_add (tlvs->ipv4_int_reachs, ipv4_reach); + value_len += 12; + pnt += 12; + } + } + else + { + pnt += length; + } + break; + + case IPV4_EXT_REACHABILITY: + /* +-------+-------+-------+-------+-------+-------+-------+-------+ + * | 0 | I/E | Default Metric | 1 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | S | I/E | Delay Metric | 1 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | S | I/E | Expense Metric | 1 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | S | I/E | Error Metric | 1 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | ip address | 4 + * +---------------------------------------------------------------+ + * | address mask | 4 + * +---------------------------------------------------------------+ + * : : + */ + *found |= TLVFLAG_IPV4_EXT_REACHABILITY; +#ifdef EXTREME_TLV_DEBUG + zlog_debug ("ISIS-TLV (%s): IPv4 external Reachability length %d", + areatag, length); +#endif /* EXTREME_TLV_DEBUG */ + if (*expected & TLVFLAG_IPV4_EXT_REACHABILITY) + { + while (length > value_len) + { + ipv4_reach = (struct ipv4_reachability *) pnt; + if (!tlvs->ipv4_ext_reachs) + tlvs->ipv4_ext_reachs = list_new (); + listnode_add (tlvs->ipv4_ext_reachs, ipv4_reach); + value_len += 12; + pnt += 12; + } + } + else + { + pnt += length; + } + break; + + case TE_IPV4_REACHABILITY: + /* +-------+-------+-------+-------+-------+-------+-------+-------+ + * | TE Metric | 4 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | U/D | sTLV? | Prefix Mask Len | 1 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | Prefix | 0-4 + * +---------------------------------------------------------------+ + * | sub tlvs | + * +---------------------------------------------------------------+ + * : : + */ + *found |= TLVFLAG_TE_IPV4_REACHABILITY; +#ifdef EXTREME_TLV_DEBUG + 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); + /* this trickery is permitable since no subtlvs are defined */ + value_len += 5 + ((te_ipv4_reach->control & 0x3F) ? + ((((te_ipv4_reach->control & 0x3F) - + 1) >> 3) + 1) : 0); + pnt += 5 + ((te_ipv4_reach->control & 0x3F) ? + ((((te_ipv4_reach->control & 0x3F) - 1) >> 3) + 1) : 0); + } + } + + pnt = endpnt; + break; + +#ifdef HAVE_IPV6 + case IPV6_ADDR: + /* +-------+-------+-------+-------+-------+-------+-------+-------+ + * + IP version 6 address + 16 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * : : + */ + *found |= TLVFLAG_IPV6_ADDR; +#ifdef EXTREME_TLV_DEBUG + zlog_debug ("ISIS-TLV (%s): IPv6 Address length %d", + areatag, length); +#endif /* EXTREME_TLV_DEBUG */ + if (*expected & TLVFLAG_IPV6_ADDR) + { + while (length > value_len) + { + ipv6_addr = (struct in6_addr *) pnt; + if (!tlvs->ipv6_addrs) + tlvs->ipv6_addrs = list_new (); + listnode_add (tlvs->ipv6_addrs, ipv6_addr); + value_len += 16; + pnt += 16; + } + } + else + { + pnt += length; + } + break; + + case IPV6_REACHABILITY: + /* +-------+-------+-------+-------+-------+-------+-------+-------+ + * | Default Metric | 4 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | Control Informantion | + * +---------------------------------------------------------------+ + * | IPv6 Prefix Length |--+ + * +---------------------------------------------------------------+ | + * | IPv6 Prefix |<-+ + * +---------------------------------------------------------------+ + */ + *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; + /* FIXME: sub-tlvs */ + if (!tlvs->ipv6_reachs) + tlvs->ipv6_reachs = list_new (); + listnode_add (tlvs->ipv6_reachs, ipv6_reach); + } + } + + pnt = endpnt; + break; +#endif /* HAVE_IPV6 */ + + case WAY3_HELLO: + /* +---------------------------------------------------------------+ + * | Adjacency state | 1 + * +---------------------------------------------------------------+ + * | Extended Local Circuit ID | 4 + * +---------------------------------------------------------------+ + * | Neighbor System ID (If known) | 0-8 + * (probably 6) + * +---------------------------------------------------------------+ + * | Neighbor Local Circuit ID (If known) | 4 + * +---------------------------------------------------------------+ + */ + *found |= TLVFLAG_3WAY_HELLO; + if (*expected & TLVFLAG_3WAY_HELLO) + { + while (length > value_len) + { + /* FIXME: make this work */ +/* Adjacency State (one octet): + 0 = Up + 1 = Initializing + 2 = Down + Extended Local Circuit ID (four octets) + Neighbor System ID if known (zero to eight octets) + Neighbor Extended Local Circuit ID (four octets, if Neighbor + System ID is present) */ + pnt += length; + value_len += length; + } + } + else + { + pnt += length; + } + + break; + case GRACEFUL_RESTART: + /* +-------+-------+-------+-------+-------+-------+-------+-------+ + * | Reserved | SA | RA | RR | 1 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | Remaining Time | 2 + * +---------------------------------------------------------------+ + * | Restarting Neighbor ID (If known) | 0-8 + * +---------------------------------------------------------------+ + */ + *found |= TLVFLAG_GRACEFUL_RESTART; + if (*expected & TLVFLAG_GRACEFUL_RESTART) + { + /* FIXME: make this work */ + } + pnt += length; + break; + + default: + zlog_warn ("ISIS-TLV (%s): unsupported TLV type %d, length %d", + areatag, type, length); + + pnt += length; + break; + } + } + + return retval; +} + +int +add_tlv (u_char tag, u_char len, u_char * value, struct stream *stream) +{ + if ((stream_get_size (stream) - stream_get_endp (stream)) < + (((unsigned)len) + 2)) + { + 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; + } + + stream_putc (stream, tag); /* TAG */ + stream_putc (stream, len); /* LENGTH */ + stream_put (stream, value, (int) len); /* VALUE */ + +#ifdef EXTREME_DEBUG + zlog_debug ("Added TLV %d len %d", tag, len); +#endif /* EXTREME DEBUG */ + return ISIS_OK; +} + +int +tlv_add_area_addrs (struct list *area_addrs, struct stream *stream) +{ + struct listnode *node; + struct area_addr *area_addr; + + u_char value[255]; + u_char *pos = value; + + for (ALL_LIST_ELEMENTS_RO (area_addrs, node, area_addr)) + { + if (pos - value + area_addr->addr_len > 255) + goto err; + *pos = area_addr->addr_len; + pos++; + memcpy (pos, area_addr->area_addr, (int) area_addr->addr_len); + pos += area_addr->addr_len; + } + + return add_tlv (AREA_ADDRESSES, pos - value, value, stream); + +err: + zlog_warn ("tlv_add_area_addrs(): TLV longer than 255"); + return ISIS_WARNING; +} + +int +tlv_add_is_neighs (struct list *is_neighs, struct stream *stream) +{ + struct listnode *node; + struct is_neigh *is_neigh; + u_char value[255]; + u_char *pos = value; + int retval; + + *pos = 0; /*is_neigh->virtual; */ + pos++; + + for (ALL_LIST_ELEMENTS_RO (is_neighs, node, is_neigh)) + { + if (pos - value + IS_NEIGHBOURS_LEN > 255) + { + retval = add_tlv (IS_NEIGHBOURS, pos - value, value, stream); + if (retval != ISIS_OK) + return retval; + pos = value; + } + *pos = is_neigh->metrics.metric_default; + pos++; + *pos = is_neigh->metrics.metric_delay; + pos++; + *pos = is_neigh->metrics.metric_expense; + pos++; + *pos = is_neigh->metrics.metric_error; + pos++; + memcpy (pos, is_neigh->neigh_id, ISIS_SYS_ID_LEN + 1); + pos += ISIS_SYS_ID_LEN + 1; + } + + return add_tlv (IS_NEIGHBOURS, pos - value, value, stream); +} + +int +tlv_add_te_is_neighs (struct list *te_is_neighs, struct stream *stream) +{ + struct listnode *node; + struct te_is_neigh *te_is_neigh; + u_char value[255]; + u_char *pos = value; + int retval; + + for (ALL_LIST_ELEMENTS_RO (te_is_neighs, node, te_is_neigh)) + { + /* 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) + return retval; + pos = value; + } + + memcpy (pos, te_is_neigh->neigh_id, ISIS_SYS_ID_LEN + 1); + pos += ISIS_SYS_ID_LEN + 1; + memcpy (pos, te_is_neigh->te_metric, 3); + pos += 3; + /* 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); +} + +int +tlv_add_lan_neighs (struct list *lan_neighs, struct stream *stream) +{ + struct listnode *node; + u_char *snpa; + u_char value[255]; + u_char *pos = value; + int retval; + + for (ALL_LIST_ELEMENTS_RO (lan_neighs, node, snpa)) + { + if (pos - value + ETH_ALEN > 255) + { + retval = add_tlv (LAN_NEIGHBOURS, pos - value, value, stream); + if (retval != ISIS_OK) + return retval; + pos = value; + } + memcpy (pos, snpa, ETH_ALEN); + pos += ETH_ALEN; + } + + return add_tlv (LAN_NEIGHBOURS, pos - value, value, stream); +} + +int +tlv_add_nlpid (struct nlpids *nlpids, struct stream *stream) +{ + return add_tlv (PROTOCOLS_SUPPORTED, nlpids->count, nlpids->nlpids, stream); +} + +int +tlv_add_authinfo (u_char auth_type, u_char auth_len, u_char *auth_value, + struct stream *stream) +{ + u_char value[255]; + u_char *pos = value; + *pos++ = auth_type; + memcpy (pos, auth_value, auth_len); + + return add_tlv (AUTH_INFO, auth_len + 1, value, stream); +} + +int +tlv_add_checksum (struct checksum *checksum, struct stream *stream) +{ + u_char value[255]; + u_char *pos = value; + return add_tlv (CHECKSUM, pos - value, value, stream); +} + +int +tlv_add_ip_addrs (struct list *ip_addrs, struct stream *stream) +{ + struct listnode *node; + struct prefix_ipv4 *ipv4; + u_char value[255]; + u_char *pos = value; + + for (ALL_LIST_ELEMENTS_RO (ip_addrs, node, ipv4)) + { + if (pos - value + IPV4_MAX_BYTELEN > 255) + { + /* 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; + } + + return add_tlv (IPV4_ADDR, pos - value, value, stream); +} + +/* Used to add TLV containing just one IPv4 address - either IPv4 address TLV + * (in case of LSP) or TE router ID TLV. */ +int +tlv_add_in_addr (struct in_addr *addr, struct stream *stream, u_char tag) +{ + u_char value[255]; + u_char *pos = value; + + memcpy (pos, addr, IPV4_MAX_BYTELEN); + pos += IPV4_MAX_BYTELEN; + + return add_tlv (tag, pos - value, value, stream); +} + +int +tlv_add_dynamic_hostname (struct hostname *hostname, struct stream *stream) +{ + return add_tlv (DYNAMIC_HOSTNAME, hostname->namelen, hostname->name, + stream); +} + +int +tlv_add_lsp_entries (struct list *lsps, struct stream *stream) +{ + struct listnode *node; + struct isis_lsp *lsp; + u_char value[255]; + u_char *pos = value; + int retval; + + for (ALL_LIST_ELEMENTS_RO (lsps, node, lsp)) + { + if (pos - value + LSP_ENTRIES_LEN > 255) + { + retval = add_tlv (LSP_ENTRIES, pos - value, value, stream); + if (retval != ISIS_OK) + return retval; + pos = value; + } + *((u_int16_t *) pos) = lsp->lsp_header->rem_lifetime; + pos += 2; + memcpy (pos, lsp->lsp_header->lsp_id, ISIS_SYS_ID_LEN + 2); + pos += ISIS_SYS_ID_LEN + 2; + *((u_int32_t *) pos) = lsp->lsp_header->seq_num; + pos += 4; + *((u_int16_t *) pos) = lsp->lsp_header->checksum; + pos += 2; + } + + return add_tlv (LSP_ENTRIES, pos - value, value, stream); +} + +static int +tlv_add_ipv4_reachs (u_char tag, struct list *ipv4_reachs, struct stream *stream) +{ + struct listnode *node; + struct ipv4_reachability *reach; + u_char value[255]; + u_char *pos = value; + int retval; + + for (ALL_LIST_ELEMENTS_RO (ipv4_reachs, node, reach)) + { + if (pos - value + IPV4_REACH_LEN > 255) + { + retval = + add_tlv (tag, pos - value, value, stream); + if (retval != ISIS_OK) + return retval; + pos = value; + } + *pos = reach->metrics.metric_default; + pos++; + *pos = reach->metrics.metric_delay; + pos++; + *pos = reach->metrics.metric_expense; + pos++; + *pos = reach->metrics.metric_error; + pos++; + *(u_int32_t *) pos = reach->prefix.s_addr; + pos += IPV4_MAX_BYTELEN; + *(u_int32_t *) pos = reach->mask.s_addr; + pos += IPV4_MAX_BYTELEN; + } + + return add_tlv (tag, 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) +{ + struct listnode *node; + struct te_ipv4_reachability *te_reach; + u_char value[255]; + u_char *pos = value; + u_char prefix_size; + int retval; + + for (ALL_LIST_ELEMENTS_RO (te_ipv4_reachs, node, te_reach)) + { + prefix_size = ((((te_reach->control & 0x3F) - 1) >> 3) + 1); + + if (pos - value + (5 + prefix_size) > 255) + { + retval = + add_tlv (TE_IPV4_REACHABILITY, pos - value, value, stream); + if (retval != ISIS_OK) + return retval; + pos = value; + } + *(u_int32_t *) pos = te_reach->te_metric; + pos += 4; + *pos = te_reach->control; + pos++; + memcpy (pos, &te_reach->prefix_start, prefix_size); + pos += prefix_size; + } + + return add_tlv (TE_IPV4_REACHABILITY, pos - value, value, stream); +} + +#ifdef HAVE_IPV6 +int +tlv_add_ipv6_addrs (struct list *ipv6_addrs, struct stream *stream) +{ + struct listnode *node; + struct prefix_ipv6 *ipv6; + u_char value[255]; + u_char *pos = value; + int retval; + + for (ALL_LIST_ELEMENTS_RO (ipv6_addrs, node, ipv6)) + { + if (pos - value + IPV6_MAX_BYTELEN > 255) + { + retval = add_tlv (IPV6_ADDR, pos - value, value, stream); + if (retval != ISIS_OK) + return retval; + pos = value; + } + memcpy (pos, ipv6->prefix.s6_addr, IPV6_MAX_BYTELEN); + pos += IPV6_MAX_BYTELEN; + } + + return add_tlv (IPV6_ADDR, pos - value, value, stream); +} + +int +tlv_add_ipv6_reachs (struct list *ipv6_reachs, struct stream *stream) +{ + struct listnode *node; + struct ipv6_reachability *ip6reach; + u_char value[255]; + u_char *pos = value; + int retval, prefix_octets; + + for (ALL_LIST_ELEMENTS_RO (ipv6_reachs, node, ip6reach)) + { + if (pos - value + IPV6_MAX_BYTELEN + 6 > 255) + { + retval = add_tlv (IPV6_REACHABILITY, pos - value, value, stream); + if (retval != ISIS_OK) + return retval; + pos = value; + } + *(uint32_t *) pos = ip6reach->metric; + pos += 4; + *pos = ip6reach->control_info; + pos++; + prefix_octets = ((ip6reach->prefix_len + 7) / 8); + *pos = ip6reach->prefix_len; + pos++; + memcpy (pos, ip6reach->prefix, prefix_octets); + pos += prefix_octets; + } + + return add_tlv (IPV6_REACHABILITY, pos - value, value, stream); +} +#endif /* HAVE_IPV6 */ + +int +tlv_add_padding (struct stream *stream) +{ + int fullpads, i, left; + + /* + * How many times can we add full padding ? + */ + fullpads = (stream_get_size (stream) - stream_get_endp (stream)) / 257; + for (i = 0; i < fullpads; i++) + { + if (!stream_putc (stream, (u_char) PADDING)) /* TAG */ + goto err; + if (!stream_putc (stream, (u_char) 255)) /* LENGHT */ + goto err; + stream_put (stream, NULL, 255); /* zero padding */ + } + + left = stream_get_size (stream) - stream_get_endp (stream); + + if (left < 2) + return ISIS_OK; + + if (left == 2) + { + stream_putc (stream, PADDING); + stream_putc (stream, 0); + return ISIS_OK; + } + + stream_putc (stream, PADDING); + stream_putc (stream, left - 2); + stream_put (stream, NULL, left-2); + + return ISIS_OK; + +err: + zlog_warn ("tlv_add_padding(): no room for tlv"); + return ISIS_WARNING; +} diff --git a/isisd/isis_tlv.h b/isisd/isis_tlv.h new file mode 100644 index 0000000..5a39d56 --- /dev/null +++ b/isisd/isis_tlv.h @@ -0,0 +1,340 @@ +/* + * IS-IS Rout(e)ing protocol - isis_tlv.h + * IS-IS TLV related routines + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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. + */ + +#ifndef _ZEBRA_ISIS_TLV_H +#define _ZEBRA_ISIS_TLV_H + +/* + * The list of TLVs we (should) support. + * ____________________________________________________________________________ + * 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 + * IIS Neighbors 6 y n n ISO10589 + * Padding 8 y n n ISO10589 + * LSP Entries 9 n n y ISO10589 + * Authentication 10 y y y ISO10589, RFC3567 + * Checksum 12 y n y RFC3358 + * 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 RFC5305 + * Extended IP Reachability 135 n y n RFC5305 + * Dynamic Hostname 137 n y n RFC2763 + * 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 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 n y n RFC4971 + * + * + * IS Reachability sub-TLVs we support (See isis_te.[c,h]) + * ____________________________________________________________________________ + * 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 + * + * + * IP Reachability sub-TLVs we (should) support. + * ____________________________________________________________________________ + * Name Value Status + * ____________________________________________________________________________ + * 32bit administrative tag 1 RFC5130 + * 64bit administrative tag 2 RFC5130 + * Management prefix color 117 RFC5120 + */ + +#define AREA_ADDRESSES 1 +#define IS_NEIGHBOURS 2 +#define ES_NEIGHBOURS 3 +#define LAN_NEIGHBOURS 6 +#define PADDING 8 +#define LSP_ENTRIES 9 +#define AUTH_INFO 10 +#define CHECKSUM 12 +#define TE_IS_NEIGHBOURS 22 +#define IS_ALIAS 24 +#define IPV4_INT_REACHABILITY 128 +#define PROTOCOLS_SUPPORTED 129 +#define IPV4_EXT_REACHABILITY 130 +#define IDRP_INFO 131 +#define IPV4_ADDR 132 +#define TE_ROUTER_ID 134 +#define TE_IPV4_REACHABILITY 135 +#define DYNAMIC_HOSTNAME 137 +#define GRACEFUL_RESTART 211 +#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 +{ + struct metric metrics; + u_char neigh_id[ISIS_SYS_ID_LEN + 1]; +}; + +/* 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 +{ + struct metric metrics; + /* approximate position of first, we use the + * length ((uchar*)metric-1) to know all */ + u_char first_es_neigh[ISIS_SYS_ID_LEN]; + +}; + +struct partition_desig_level2_is +{ + struct list *isis_system_ids; +}; + +/* struct for lan neighbors */ +struct lan_neigh +{ + u_char LAN_addr[6]; +}; + +#ifdef __SUNPRO_C +#pragma pack(1) +#endif + +/* struct for LSP entry */ +struct lsp_entry +{ + u_int16_t rem_lifetime; + u_char lsp_id[ISIS_SYS_ID_LEN + 2]; + u_int32_t seq_num; + u_int16_t checksum; +} __attribute__ ((packed)); + +#ifdef __SUNPRO_C +#pragma pack() +#endif + +/* struct for checksum */ +struct checksum +{ + u_int16_t checksum; +}; + +/* ipv4 reachability */ +struct ipv4_reachability +{ + struct metric metrics; + struct in_addr prefix; + struct in_addr mask; +}; + +/* te router id */ +struct te_router_id +{ + struct in_addr id; +}; + +/* te ipv4 reachability */ +struct te_ipv4_reachability +{ + u_int32_t te_metric; + u_char control; + u_char prefix_start; /* since this is variable length by nature it only */ +}; /* points to an approximate location */ + + + +struct idrp_info +{ + u_char len; + u_char *value; +}; + +#ifdef HAVE_IPV6 +struct ipv6_reachability +{ + u_int32_t metric; + u_char control_info; + u_char prefix_len; + u_char prefix[16]; +}; + +/* bits in control_info */ +#define CTRL_INFO_DIRECTION 0x80 +#define DIRECTION_UP 0x00 +#define DIRECTION_DOWN 0x80 + +#define CTRL_INFO_DISTRIBUTION 0x40 +#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; + struct list *es_neighs; + struct list *lsp_entries; + struct list *prefix_neighs; + struct list *lan_neighs; + struct list *ipv4_addrs; + struct list *ipv4_int_reachs; + struct list *ipv4_ext_reachs; + struct list *te_ipv4_reachs; +#ifdef HAVE_IPV6 + struct list *ipv6_addrs; + struct list *ipv6_reachs; +#endif + struct isis_passwd auth_info; +}; + +/* + * Own definitions - used to bitmask found and expected + */ + +#define TLVFLAG_AREA_ADDRS (1<<0) +#define TLVFLAG_IS_NEIGHS (1<<1) +#define TLVFLAG_ES_NEIGHS (1<<2) +#define TLVFLAG_PARTITION_DESIG_LEVEL2_IS (1<<3) +#define TLVFLAG_PREFIX_NEIGHS (1<<4) +#define TLVFLAG_LAN_NEIGHS (1<<5) +#define TLVFLAG_LSP_ENTRIES (1<<6) +#define TLVFLAG_PADDING (1<<7) +#define TLVFLAG_AUTH_INFO (1<<8) +#define TLVFLAG_IPV4_INT_REACHABILITY (1<<9) +#define TLVFLAG_NLPID (1<<10) +#define TLVFLAG_IPV4_EXT_REACHABILITY (1<<11) +#define TLVFLAG_IPV4_ADDR (1<<12) +#define TLVFLAG_DYN_HOSTNAME (1<<13) +#define TLVFLAG_IPV6_ADDR (1<<14) +#define TLVFLAG_IPV6_REACHABILITY (1<<15) +#define TLVFLAG_TE_IS_NEIGHS (1<<16) +#define TLVFLAG_TE_IPV4_REACHABILITY (1<<17) +#define TLVFLAG_3WAY_HELLO (1<<18) +#define TLVFLAG_TE_ROUTER_ID (1<<19) +#define TLVFLAG_CHECKSUM (1<<20) +#define TLVFLAG_GRACEFUL_RESTART (1<<21) + +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 * 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); +int tlv_add_is_neighs (struct list *is_neighs, struct stream *stream); +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 (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_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); +int tlv_add_ipv6_reachs (struct list *ipv6_reachs, struct stream *stream); +#endif /* HAVE_IPV6 */ + +int tlv_add_padding (struct stream *stream); + +#endif /* _ZEBRA_ISIS_TLV_H */ diff --git a/isisd/isis_vty.c b/isisd/isis_vty.c new file mode 100644 index 0000000..4148eb5 --- /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 new file mode 100644 index 0000000..40157b5 --- /dev/null +++ b/isisd/isis_zebra.c @@ -0,0 +1,715 @@ +/* + * IS-IS Rout(e)ing protocol - isis_zebra.c + * + * 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 + * 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 "thread.h" +#include "command.h" +#include "memory.h" +#include "log.h" +#include "if.h" +#include "network.h" +#include "prefix.h" +#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; + +/* Router-id update message from zebra. */ +static int +isis_router_id_update_zebra (int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct isis_area *area; + struct listnode *node; + struct prefix router_id; + + /* + * 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); + + return 0; +} + +static int +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, vrf_id); + + if (isis->debugs & DEBUG_ZEBRA) + zlog_debug ("Zebra I/F add: %s index %d flags %ld metric %d mtu %d", + ifp->name, ifp->ifindex, (long)ifp->flags, ifp->metric, ifp->mtu); + + if (if_is_operative (ifp)) + isis_csm_state_change (IF_UP_FROM_Z, circuit_scan_by_ifp (ifp), ifp); + + return 0; +} + +static int +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, vrf_id); + + if (!ifp) + return 0; + + if (if_is_operative (ifp)) + zlog_warn ("Zebra: got delete of %s, but interface is still up", + ifp->name); + + if (isis->debugs & DEBUG_ZEBRA) + 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); + + ifp->ifindex = IFINDEX_INTERNAL; + + return 0; +} + +static int +isis_zebra_if_state_up (int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct interface *ifp; + + ifp = zebra_interface_state_read (zclient->ibuf, vrf_id); + + if (ifp == NULL) + return 0; + + isis_csm_state_change (IF_UP_FROM_Z, circuit_scan_by_ifp (ifp), ifp); + + return 0; +} + +static int +isis_zebra_if_state_down (int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct interface *ifp; + struct isis_circuit *circuit; + + ifp = zebra_interface_state_read (zclient->ibuf, vrf_id); + + if (ifp == NULL) + return 0; + + 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, 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, vrf_id); + + if (c == NULL) + return 0; + + p = c->address; + + prefix2str (p, buf, BUFSIZ); +#ifdef EXTREME_DEBUG + if (p->family == AF_INET) + zlog_debug ("connected IP address %s", buf); +#ifdef HAVE_IPV6 + if (p->family == AF_INET6) + zlog_debug ("connected IPv6 address %s", buf); +#endif /* HAVE_IPV6 */ +#endif /* EXTREME_DEBUG */ + if (if_is_operative (c->ifp)) + isis_circuit_add_addr (circuit_scan_by_ifp (c->ifp), c); + + return 0; +} + +static int +isis_zebra_if_address_del (int command, struct zclient *client, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct connected *c; + struct interface *ifp; +#ifdef EXTREME_DEBUG + struct prefix *p; + u_char buf[BUFSIZ]; +#endif /* EXTREME_DEBUG */ + + c = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_DELETE, + zclient->ibuf, vrf_id); + + if (c == NULL) + return 0; + + ifp = c->ifp; + +#ifdef EXTREME_DEBUG + p = c->address; + prefix2str (p, buf, BUFSIZ); + + if (p->family == AF_INET) + zlog_debug ("disconnected IP address %s", buf); +#ifdef HAVE_IPV6 + if (p->family == AF_INET6) + zlog_debug ("disconnected IPv6 address %s", buf); +#endif /* HAVE_IPV6 */ +#endif /* EXTREME_DEBUG */ + + if (if_is_operative (ifp)) + isis_circuit_del_addr (circuit_scan_by_ifp (ifp), c); + connected_free (c); + + 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) +{ + u_char message, flags; + int psize; + struct stream *stream; + struct isis_nexthop *nexthop; + struct listnode *node; + + if (CHECK_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED)) + return; + + if (vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_ISIS], VRF_DEFAULT)) + { + message = 0; + flags = 0; + + SET_FLAG (message, ZAPI_MESSAGE_NEXTHOP); + SET_FLAG (message, ZAPI_MESSAGE_METRIC); +#if 0 + SET_FLAG (message, ZAPI_MESSAGE_DISTANCE); +#endif + + stream = zclient->obuf; + stream_reset (stream); + zclient_create_header (stream, ZEBRA_IPV4_ROUTE_ADD, VRF_DEFAULT); + /* type */ + stream_putc (stream, ZEBRA_ROUTE_ISIS); + /* flags */ + stream_putc (stream, flags); + /* message */ + stream_putc (stream, message); + /* SAFI */ + stream_putw (stream, SAFI_UNICAST); + /* prefix information */ + psize = PSIZE (prefix->prefixlen); + stream_putc (stream, prefix->prefixlen); + stream_write (stream, (u_char *) & prefix->u.prefix4, psize); + + stream_putc (stream, listcount (route_info->nexthops)); + + /* Nexthop, ifindex, distance and metric information */ + for (ALL_LIST_ELEMENTS_RO (route_info->nexthops, node, nexthop)) + { + /* FIXME: can it be ? */ + if (nexthop->ip.s_addr != INADDR_ANY) + { + stream_putc (stream, ZEBRA_NEXTHOP_IPV4); + stream_put_in_addr (stream, &nexthop->ip); + } + else + { + stream_putc (stream, ZEBRA_NEXTHOP_IFINDEX); + stream_putl (stream, nexthop->ifindex); + } + } +#if 0 + if (CHECK_FLAG (message, ZAPI_MESSAGE_DISTANCE)) + stream_putc (stream, route_info->depth); +#endif + if (CHECK_FLAG (message, ZAPI_MESSAGE_METRIC)) + stream_putl (stream, route_info->cost); + + stream_putw_at (stream, 0, stream_get_endp (stream)); + zclient_send_message(zclient); + SET_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); + UNSET_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_RESYNC); + } +} + +static void +isis_zebra_route_del_ipv4 (struct prefix *prefix, + struct isis_route_info *route_info) +{ + struct zapi_ipv4 api; + struct prefix_ipv4 prefix4; + + 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; + api.safi = SAFI_UNICAST; + prefix4.family = AF_INET; + prefix4.prefixlen = prefix->prefixlen; + prefix4.prefix = prefix->u.prefix4; + zapi_ipv4_route (ZEBRA_IPV4_ROUTE_DELETE, zclient, &prefix4, &api); + } + UNSET_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); + + return; +} + +#ifdef HAVE_IPV6 +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; + 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_SYNCED)) + return; + + api.vrf_id = VRF_DEFAULT; + api.type = ZEBRA_ROUTE_ISIS; + api.flags = 0; + api.message = 0; + api.safi = SAFI_UNICAST; + SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP); + SET_FLAG (api.message, ZAPI_MESSAGE_IFINDEX); + SET_FLAG (api.message, ZAPI_MESSAGE_METRIC); + api.metric = route_info->cost; +#if 0 + SET_FLAG (api.message, ZAPI_MESSAGE_DISTANCE); + api.distance = route_info->depth; +#endif + api.nexthop_num = listcount (route_info->nexthops6); + api.ifindex_num = listcount (route_info->nexthops6); + + /* allocate memory for nexthop_list */ + size = sizeof (struct isis_nexthop6 *) * listcount (route_info->nexthops6); + nexthop_list = (struct in6_addr **) XMALLOC (MTYPE_ISIS_TMP, size); + if (!nexthop_list) + { + zlog_err ("isis_zebra_add_route_ipv6: out of memory!"); + return; + } + + /* allocate memory for ifindex_list */ + size = sizeof (unsigned int) * listcount (route_info->nexthops6); + ifindex_list = (ifindex_t *) XMALLOC (MTYPE_ISIS_TMP, size); + if (!ifindex_list) + { + zlog_err ("isis_zebra_add_route_ipv6: out of memory!"); + XFREE (MTYPE_ISIS_TMP, nexthop_list); + return; + } + + /* for each nexthop */ + i = 0; + for (ALL_LIST_ELEMENTS_RO (route_info->nexthops6, node, nexthop6)) + { + if (!IN6_IS_ADDR_LINKLOCAL (&nexthop6->ip6) && + !IN6_IS_ADDR_UNSPECIFIED (&nexthop6->ip6)) + { + api.nexthop_num--; + api.ifindex_num--; + continue; + } + + nexthop_list[i] = &nexthop6->ip6; + ifindex_list[i] = nexthop6->ifindex; + i++; + } + + api.nexthop = nexthop_list; + api.ifindex = ifindex_list; + + if (api.nexthop_num && api.ifindex_num) + { + prefix6.family = AF_INET6; + 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_SYNCED); + UNSET_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_RESYNC); + } + + XFREE (MTYPE_ISIS_TMP, nexthop_list); + XFREE (MTYPE_ISIS_TMP, ifindex_list); + + return; +} + +static void +isis_zebra_route_del_ipv6 (struct prefix *prefix, + struct isis_route_info *route_info) +{ + struct zapi_ipv6 api; + struct in6_addr **nexthop_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_SYNCED)) + return; + + api.vrf_id = VRF_DEFAULT; + api.type = ZEBRA_ROUTE_ISIS; + api.flags = 0; + api.message = 0; + api.safi = SAFI_UNICAST; + SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP); + SET_FLAG (api.message, ZAPI_MESSAGE_IFINDEX); + api.nexthop_num = listcount (route_info->nexthops6); + api.ifindex_num = listcount (route_info->nexthops6); + + /* allocate memory for nexthop_list */ + size = sizeof (struct isis_nexthop6 *) * listcount (route_info->nexthops6); + nexthop_list = (struct in6_addr **) XMALLOC (MTYPE_ISIS_TMP, size); + if (!nexthop_list) + { + zlog_err ("isis_zebra_route_del_ipv6: out of memory!"); + return; + } + + /* allocate memory for ifindex_list */ + size = sizeof (unsigned int) * listcount (route_info->nexthops6); + ifindex_list = (ifindex_t *) XMALLOC (MTYPE_ISIS_TMP, size); + if (!ifindex_list) + { + zlog_err ("isis_zebra_route_del_ipv6: out of memory!"); + XFREE (MTYPE_ISIS_TMP, nexthop_list); + return; + } + + /* for each nexthop */ + i = 0; + for (ALL_LIST_ELEMENTS_RO (route_info->nexthops6, node, nexthop6)) + { + if (!IN6_IS_ADDR_LINKLOCAL (&nexthop6->ip6) && + !IN6_IS_ADDR_UNSPECIFIED (&nexthop6->ip6)) + { + api.nexthop_num--; + api.ifindex_num--; + continue; + } + + nexthop_list[i] = &nexthop6->ip6; + ifindex_list[i] = nexthop6->ifindex; + i++; + } + + api.nexthop = nexthop_list; + api.ifindex = ifindex_list; + + if (api.nexthop_num && api.ifindex_num) + { + prefix6.family = AF_INET6; + 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_SYNCED); + } + + XFREE (MTYPE_ISIS_TMP, nexthop_list); + XFREE (MTYPE_ISIS_TMP, ifindex_list); +} + +#endif /* HAVE_IPV6 */ + +void +isis_zebra_route_update (struct prefix *prefix, + struct isis_route_info *route_info) +{ + if (zclient->sock < 0) + return; + + if (!vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_ISIS], VRF_DEFAULT)) + return; + + if (CHECK_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ACTIVE)) + { + if (prefix->family == AF_INET) + isis_zebra_route_add_ipv4 (prefix, route_info); +#ifdef HAVE_IPV6 + else if (prefix->family == AF_INET6) + isis_zebra_route_add_ipv6 (prefix, route_info); +#endif /* HAVE_IPV6 */ + } + else + { + if (prefix->family == AF_INET) + isis_zebra_route_del_ipv4 (prefix, route_info); +#ifdef HAVE_IPV6 + else if (prefix->family == AF_INET6) + isis_zebra_route_del_ipv6 (prefix, route_info); +#endif /* HAVE_IPV6 */ + } + return; +} + +static int +isis_zebra_read_ipv4 (int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct stream *stream; + struct zapi_ipv4 api; + struct prefix_ipv4 p; + 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); + api.flags = stream_getc (stream); + api.message = stream_getc (stream); + + p.family = AF_INET; + 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)) + { + api.nexthop_num = stream_getc (stream); + nexthop.s_addr = stream_get_ipv4 (stream); + } + 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_IPV4_ROUTE_DELETE; + + if (command == ZEBRA_IPV4_ROUTE_ADD) + isis_redist_add(api.type, p_generic, api.distance, api.metric); + else + isis_redist_delete(api.type, p_generic); + + return 0; +} + +static int +isis_zebra_read_ipv6 (int command, struct zclient *zclient, + 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; +} + +int +isis_distribute_list_update (int routetype) +{ + return 0; +} + +void +isis_zebra_redistribute_set(int type) +{ + 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); +} + +void +isis_zebra_init (struct thread_master *master) +{ + 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; + zclient->interface_up = isis_zebra_if_state_up; + 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 + zclient->ipv6_route_add = isis_zebra_read_ipv6; + zclient->ipv6_route_delete = isis_zebra_read_ipv6; +#endif /* HAVE_IPV6 */ + + return; +} diff --git a/isisd/isis_zebra.h b/isisd/isis_zebra.h new file mode 100644 index 0000000..dd36d7d --- /dev/null +++ b/isisd/isis_zebra.h @@ -0,0 +1,34 @@ +/* + * IS-IS Rout(e)ing protocol - isis_zebra.h + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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. + */ +#ifndef _ZEBRA_ISIS_ZEBRA_H +#define _ZEBRA_ISIS_ZEBRA_H + +extern struct zclient *zclient; + +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 new file mode 100644 index 0000000..9844ae5 --- /dev/null +++ b/isisd/isisd.c @@ -0,0 +1,2406 @@ +/* + * IS-IS Rout(e)ing protocol - isisd.c + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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 "thread.h" +#include "vty.h" +#include "command.h" +#include "log.h" +#include "memory.h" +#include "time.h" +#include "linklist.h" +#include "if.h" +#include "hash.h" +#include "stream.h" +#include "prefix.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_csm.h" +#include "isisd/isisd.h" +#include "isisd/isis_dynhn.h" +#include "isisd/isis_adjacency.h" +#include "isisd/isis_pdu.h" +#include "isisd/isis_misc.h" +#include "isisd/isis_constants.h" +#include "isisd/isis_tlv.h" +#include "isisd/isis_lsp.h" +#include "isisd/isis_spf.h" +#include "isisd/isis_route.h" +#include "isisd/isis_zebra.h" +#include "isisd/isis_events.h" +#include "isisd/isis_te.h" + +#ifdef TOPOLOGY_GENERATE +#include "spgrid.h" +u_char DEFAULT_TOPOLOGY_BASEIS[6] = { 0xFE, 0xED, 0xFE, 0xED, 0x00, 0x00 }; +#endif /* TOPOLOGY_GENERATE */ + +struct isis *isis = NULL; + +/* + * Prototypes. + */ +int isis_area_get(struct vty *, const char *); +int isis_area_destroy(struct vty *, const char *); +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 *); + + + +void +isis_new (unsigned long process_id) +{ + isis = XCALLOC (MTYPE_ISIS, sizeof (struct isis)); + /* + * 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); + isis->nexthops = list_new (); +#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 (const char *area_tag) +{ + struct isis_area *area; + + area = XCALLOC (MTYPE_ISIS_AREA, sizeof (struct isis_area)); + + /* + * The first instance is level-1-2 rest are level-1, unless otherwise + * configured + */ + if (listcount (isis->area_list) > 0) + area->is_type = IS_LEVEL_1; + else + area->is_type = IS_LEVEL_1_AND_2; + + /* + * intialize the databases + */ + 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[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] = 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 = 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 */ + + area->area_tag = strdup (area_tag); + listnode_add (isis->area_list, area); + area->isis = isis; + + return area; +} + +struct isis_area * +isis_area_lookup (const char *area_tag) +{ + struct isis_area *area; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area)) + if ((area->area_tag == NULL && area_tag == NULL) || + (area->area_tag && area_tag + && strcmp (area->area_tag, area_tag) == 0)) + return area; + + return NULL; +} + +int +isis_area_get (struct vty *vty, const char *area_tag) +{ + struct isis_area *area; + + area = isis_area_lookup (area_tag); + + if (area) + { + vty->node = ISIS_NODE; + vty->index = area; + return CMD_SUCCESS; + } + + area = isis_area_create (area_tag); + + if (isis->debugs & DEBUG_EVENTS) + zlog_debug ("New IS-IS area instance %s", area->area_tag); + + vty->node = ISIS_NODE; + vty->index = area; + + return CMD_SUCCESS; +} + +int +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_ERR_NO_MATCH; + } + + if (area->circuit_list) + { + for (ALL_LIST_ELEMENTS (area->circuit_list, node, nnode, circuit)) + { + 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; + } + + 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); + THREAD_TIMER_OFF (area->t_lsp_refresh[0]); + THREAD_TIMER_OFF (area->t_lsp_refresh[1]); + + thread_cancel_event (master, area); + + listnode_delete (isis->area_list, area); + + free (area->area_tag); + + XFREE (MTYPE_ISIS_AREA, area); + + 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 char *net_title) +{ + struct isis_area *area; + struct area_addr *addr; + struct area_addr *addrp; + struct listnode *node; + + u_char buff[255]; + area = vty->index; + + if (!area) + { + vty_out (vty, "Can't find ISIS instance %s", VTY_NEWLINE); + return CMD_ERR_NO_MATCH; + } + + /* We check that we are not over the maximal number of addresses */ + if (listcount (area->area_addrs) >= isis->max_area_addrs) + { + vty_out (vty, "Maximum of area addresses (%d) already reached %s", + isis->max_area_addrs, VTY_NEWLINE); + return CMD_ERR_NOTHING_TODO; + } + + addr = XMALLOC (MTYPE_ISIS_AREA_ADDR, sizeof (struct area_addr)); + addr->addr_len = dotformat2buff (buff, net_title); + memcpy (addr->area_addr, buff, addr->addr_len); +#ifdef EXTREME_DEBUG + zlog_debug ("added area address %s for area %s (address length %d)", + net_title, area->area_tag, addr->addr_len); +#endif /* EXTREME_DEBUG */ + if (addr->addr_len < 8 || addr->addr_len > 20) + { + 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_ERR_AMBIGUOUS; + } + + if (isis->sysid_set == 0) + { + /* + * First area address - get the SystemID for this router + */ + 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)); + } + else + { + /* + * Check that the SystemID portions match + */ + 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_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 + ISIS_NSEL_LEN) != (addr->addr_len)) + continue; + if (!memcmp (addrp->area_addr, addr->area_addr, addr->addr_len)) + { + XFREE (MTYPE_ISIS_AREA_ADDR, addr); + return CMD_SUCCESS; /* silent fail */ + } + } + } + + /* + * Forget the systemID part of the address + */ + 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) + { + 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 char *net_title) +{ + struct isis_area *area; + struct area_addr addr, *addrp = NULL; + struct listnode *node; + u_char buff[255]; + + area = vty->index; + if (!area) + { + vty_out (vty, "Can't find ISIS instance %s", VTY_NEWLINE); + return CMD_ERR_NO_MATCH; + } + + addr.addr_len = dotformat2buff (buff, net_title); + if (addr.addr_len < 8 || addr.addr_len > 20) + { + vty_out (vty, "Unsupported area address length %d, should be 8...20 %s", + addr.addr_len, VTY_NEWLINE); + 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 + ISIS_SYS_ID_LEN + 1) == addr.addr_len && + !memcmp (addrp->area_addr, addr.area_addr, addr.addr_len)) + break; + + if (!addrp) + { + vty_out (vty, "No area address %s for area %s %s", net_title, + area->area_tag, VTY_NEWLINE); + 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 isis interface' command + */ + +int +show_isis_interface_common (struct vty *vty, const char *ifname, char detail) +{ + struct listnode *anode, *cnode; + struct isis_area *area; + struct isis_circuit *circuit; + + if (!isis) + { + vty_out (vty, "IS-IS Routing Process not enabled%s", VTY_NEWLINE); + return CMD_SUCCESS; + } + + 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, " Interface CircId State Type Level%s", + VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO (area->circuit_list, cnode, circuit)) + 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_isis_interface, + show_isis_interface_cmd, + "show isis interface", + SHOW_STR + "ISIS network information\n" + "ISIS interface\n") +{ + return show_isis_interface_common (vty, NULL, ISIS_UI_LEVEL_BRIEF); +} + +DEFUN (show_isis_interface_detail, + show_isis_interface_detail_cmd, + "show isis interface detail", + SHOW_STR + "ISIS network information\n" + "ISIS interface\n" + "show detailed information\n") +{ + return show_isis_interface_common (vty, NULL, ISIS_UI_LEVEL_DETAIL); +} + +DEFUN (show_isis_interface_arg, + show_isis_interface_arg_cmd, + "show isis interface WORD", + SHOW_STR + "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_isis_neighbor_common (vty, NULL, ISIS_UI_LEVEL_BRIEF); +} + +DEFUN (show_isis_neighbor_detail, + show_isis_neighbor_detail_cmd, + "show isis neighbor detail", + SHOW_STR + "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' + */ +void +print_debug (struct vty *vty, int flags, int onoff) +{ + char onoffs[4]; + if (onoff) + strcpy (onoffs, "on"); + else + strcpy (onoffs, "off"); + + if (flags & DEBUG_ADJ_PACKETS) + vty_out (vty, "IS-IS Adjacency related packets debugging is %s%s", onoffs, + VTY_NEWLINE); + if (flags & DEBUG_CHECKSUM_ERRORS) + vty_out (vty, "IS-IS checksum errors debugging is %s%s", onoffs, + VTY_NEWLINE); + if (flags & DEBUG_LOCAL_UPDATES) + vty_out (vty, "IS-IS local updates debugging is %s%s", onoffs, + VTY_NEWLINE); + if (flags & DEBUG_PROTOCOL_ERRORS) + vty_out (vty, "IS-IS protocol errors debugging is %s%s", onoffs, + VTY_NEWLINE); + if (flags & DEBUG_SNP_PACKETS) + vty_out (vty, "IS-IS CSNP/PSNP packets debugging is %s%s", onoffs, + VTY_NEWLINE); + if (flags & DEBUG_SPF_EVENTS) + vty_out (vty, "IS-IS SPF events debugging is %s%s", onoffs, VTY_NEWLINE); + if (flags & DEBUG_SPF_STATS) + vty_out (vty, "IS-IS SPF Timing and Statistics Data debugging is %s%s", + onoffs, VTY_NEWLINE); + if (flags & DEBUG_SPF_TRIGGERS) + vty_out (vty, "IS-IS SPF triggering events debugging is %s%s", onoffs, + VTY_NEWLINE); + if (flags & DEBUG_UPDATE_PACKETS) + vty_out (vty, "IS-IS Update related packet debugging is %s%s", onoffs, + VTY_NEWLINE); + if (flags & DEBUG_RTE_EVENTS) + vty_out (vty, "IS-IS Route related debuggin is %s%s", onoffs, + 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_isis_cmd, + "show debugging isis", + SHOW_STR + "State of each debugging option\n") +{ + if (isis->debugs) { + vty_out (vty, "IS-IS:%s", VTY_NEWLINE); + print_debug (vty, isis->debugs, 1); + } + return CMD_SUCCESS; +} + +/* Debug node. */ +static struct cmd_node debug_node = { + DEBUG_NODE, + "", + 1 +}; + +static int +config_write_debug (struct vty *vty) +{ + int write = 0; + int flags = isis->debugs; + + if (flags & DEBUG_ADJ_PACKETS) + { + vty_out (vty, "debug isis adj-packets%s", VTY_NEWLINE); + write++; + } + if (flags & DEBUG_CHECKSUM_ERRORS) + { + vty_out (vty, "debug isis checksum-errors%s", VTY_NEWLINE); + write++; + } + if (flags & DEBUG_LOCAL_UPDATES) + { + vty_out (vty, "debug isis local-updates%s", VTY_NEWLINE); + write++; + } + if (flags & DEBUG_PROTOCOL_ERRORS) + { + vty_out (vty, "debug isis protocol-errors%s", VTY_NEWLINE); + write++; + } + if (flags & DEBUG_SNP_PACKETS) + { + vty_out (vty, "debug isis snp-packets%s", VTY_NEWLINE); + write++; + } + if (flags & DEBUG_SPF_EVENTS) + { + vty_out (vty, "debug isis spf-events%s", VTY_NEWLINE); + write++; + } + if (flags & DEBUG_SPF_STATS) + { + vty_out (vty, "debug isis spf-statistics%s", VTY_NEWLINE); + write++; + } + if (flags & DEBUG_SPF_TRIGGERS) + { + vty_out (vty, "debug isis spf-triggers%s", VTY_NEWLINE); + write++; + } + if (flags & DEBUG_UPDATE_PACKETS) + { + vty_out (vty, "debug isis update-packets%s", VTY_NEWLINE); + write++; + } + if (flags & DEBUG_RTE_EVENTS) + { + vty_out (vty, "debug isis route-events%s", VTY_NEWLINE); + write++; + } + if (flags & DEBUG_EVENTS) + { + 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; +} + +DEFUN (debug_isis_adj, + debug_isis_adj_cmd, + "debug isis adj-packets", + DEBUG_STR + "IS-IS information\n" + "IS-IS Adjacency related packets\n") +{ + isis->debugs |= DEBUG_ADJ_PACKETS; + print_debug (vty, DEBUG_ADJ_PACKETS, 1); + + return CMD_SUCCESS; +} + +DEFUN (no_debug_isis_adj, + no_debug_isis_adj_cmd, + "no debug isis adj-packets", + UNDEBUG_STR + "IS-IS information\n" + "IS-IS Adjacency related packets\n") +{ + isis->debugs &= ~DEBUG_ADJ_PACKETS; + print_debug (vty, DEBUG_ADJ_PACKETS, 0); + + return CMD_SUCCESS; +} + +DEFUN (debug_isis_csum, + debug_isis_csum_cmd, + "debug isis checksum-errors", + DEBUG_STR + "IS-IS information\n" + "IS-IS LSP checksum errors\n") +{ + isis->debugs |= DEBUG_CHECKSUM_ERRORS; + print_debug (vty, DEBUG_CHECKSUM_ERRORS, 1); + + return CMD_SUCCESS; +} + +DEFUN (no_debug_isis_csum, + no_debug_isis_csum_cmd, + "no debug isis checksum-errors", + UNDEBUG_STR + "IS-IS information\n" + "IS-IS LSP checksum errors\n") +{ + isis->debugs &= ~DEBUG_CHECKSUM_ERRORS; + print_debug (vty, DEBUG_CHECKSUM_ERRORS, 0); + + return CMD_SUCCESS; +} + +DEFUN (debug_isis_lupd, + debug_isis_lupd_cmd, + "debug isis local-updates", + DEBUG_STR + "IS-IS information\n" + "IS-IS local update packets\n") +{ + isis->debugs |= DEBUG_LOCAL_UPDATES; + print_debug (vty, DEBUG_LOCAL_UPDATES, 1); + + return CMD_SUCCESS; +} + +DEFUN (no_debug_isis_lupd, + no_debug_isis_lupd_cmd, + "no debug isis local-updates", + UNDEBUG_STR + "IS-IS information\n" + "IS-IS local update packets\n") +{ + isis->debugs &= ~DEBUG_LOCAL_UPDATES; + print_debug (vty, DEBUG_LOCAL_UPDATES, 0); + + return CMD_SUCCESS; +} + +DEFUN (debug_isis_err, + debug_isis_err_cmd, + "debug isis protocol-errors", + DEBUG_STR + "IS-IS information\n" + "IS-IS LSP protocol errors\n") +{ + isis->debugs |= DEBUG_PROTOCOL_ERRORS; + print_debug (vty, DEBUG_PROTOCOL_ERRORS, 1); + + return CMD_SUCCESS; +} + +DEFUN (no_debug_isis_err, + no_debug_isis_err_cmd, + "no debug isis protocol-errors", + UNDEBUG_STR + "IS-IS information\n" + "IS-IS LSP protocol errors\n") +{ + isis->debugs &= ~DEBUG_PROTOCOL_ERRORS; + print_debug (vty, DEBUG_PROTOCOL_ERRORS, 0); + + return CMD_SUCCESS; +} + +DEFUN (debug_isis_snp, + debug_isis_snp_cmd, + "debug isis snp-packets", + DEBUG_STR + "IS-IS information\n" + "IS-IS CSNP/PSNP packets\n") +{ + isis->debugs |= DEBUG_SNP_PACKETS; + print_debug (vty, DEBUG_SNP_PACKETS, 1); + + return CMD_SUCCESS; +} + +DEFUN (no_debug_isis_snp, + no_debug_isis_snp_cmd, + "no debug isis snp-packets", + UNDEBUG_STR + "IS-IS information\n" + "IS-IS CSNP/PSNP packets\n") +{ + isis->debugs &= ~DEBUG_SNP_PACKETS; + print_debug (vty, DEBUG_SNP_PACKETS, 0); + + return CMD_SUCCESS; +} + +DEFUN (debug_isis_upd, + debug_isis_upd_cmd, + "debug isis update-packets", + DEBUG_STR + "IS-IS information\n" + "IS-IS Update related packets\n") +{ + isis->debugs |= DEBUG_UPDATE_PACKETS; + print_debug (vty, DEBUG_UPDATE_PACKETS, 1); + + return CMD_SUCCESS; +} + +DEFUN (no_debug_isis_upd, + no_debug_isis_upd_cmd, + "no debug isis update-packets", + UNDEBUG_STR + "IS-IS information\n" + "IS-IS Update related packets\n") +{ + isis->debugs &= ~DEBUG_UPDATE_PACKETS; + print_debug (vty, DEBUG_UPDATE_PACKETS, 0); + + return CMD_SUCCESS; +} + +DEFUN (debug_isis_spfevents, + debug_isis_spfevents_cmd, + "debug isis spf-events", + DEBUG_STR + "IS-IS information\n" + "IS-IS Shortest Path First Events\n") +{ + isis->debugs |= DEBUG_SPF_EVENTS; + print_debug (vty, DEBUG_SPF_EVENTS, 1); + + return CMD_SUCCESS; +} + +DEFUN (no_debug_isis_spfevents, + no_debug_isis_spfevents_cmd, + "no debug isis spf-events", + UNDEBUG_STR + "IS-IS information\n" + "IS-IS Shortest Path First Events\n") +{ + isis->debugs &= ~DEBUG_SPF_EVENTS; + print_debug (vty, DEBUG_SPF_EVENTS, 0); + + return CMD_SUCCESS; +} + +DEFUN (debug_isis_spfstats, + debug_isis_spfstats_cmd, + "debug isis spf-statistics ", + DEBUG_STR + "IS-IS information\n" + "IS-IS SPF Timing and Statistic Data\n") +{ + isis->debugs |= DEBUG_SPF_STATS; + print_debug (vty, DEBUG_SPF_STATS, 1); + + return CMD_SUCCESS; +} + +DEFUN (no_debug_isis_spfstats, + no_debug_isis_spfstats_cmd, + "no debug isis spf-statistics", + UNDEBUG_STR + "IS-IS information\n" + "IS-IS SPF Timing and Statistic Data\n") +{ + isis->debugs &= ~DEBUG_SPF_STATS; + print_debug (vty, DEBUG_SPF_STATS, 0); + + return CMD_SUCCESS; +} + +DEFUN (debug_isis_spftrigg, + debug_isis_spftrigg_cmd, + "debug isis spf-triggers", + DEBUG_STR + "IS-IS information\n" + "IS-IS SPF triggering events\n") +{ + isis->debugs |= DEBUG_SPF_TRIGGERS; + print_debug (vty, DEBUG_SPF_TRIGGERS, 1); + + return CMD_SUCCESS; +} + +DEFUN (no_debug_isis_spftrigg, + no_debug_isis_spftrigg_cmd, + "no debug isis spf-triggers", + UNDEBUG_STR + "IS-IS information\n" + "IS-IS SPF triggering events\n") +{ + isis->debugs &= ~DEBUG_SPF_TRIGGERS; + print_debug (vty, DEBUG_SPF_TRIGGERS, 0); + + return CMD_SUCCESS; +} + +DEFUN (debug_isis_rtevents, + debug_isis_rtevents_cmd, + "debug isis route-events", + DEBUG_STR + "IS-IS information\n" + "IS-IS Route related events\n") +{ + isis->debugs |= DEBUG_RTE_EVENTS; + print_debug (vty, DEBUG_RTE_EVENTS, 1); + + return CMD_SUCCESS; +} + +DEFUN (no_debug_isis_rtevents, + no_debug_isis_rtevents_cmd, + "no debug isis route-events", + UNDEBUG_STR + "IS-IS information\n" + "IS-IS Route related events\n") +{ + isis->debugs &= ~DEBUG_RTE_EVENTS; + print_debug (vty, DEBUG_RTE_EVENTS, 0); + + return CMD_SUCCESS; +} + +DEFUN (debug_isis_events, + debug_isis_events_cmd, + "debug isis events", + DEBUG_STR + "IS-IS information\n" + "IS-IS Events\n") +{ + isis->debugs |= DEBUG_EVENTS; + print_debug (vty, DEBUG_EVENTS, 1); + + return CMD_SUCCESS; +} + +DEFUN (no_debug_isis_events, + no_debug_isis_events_cmd, + "no debug isis events", + UNDEBUG_STR + "IS-IS information\n" + "IS-IS Events\n") +{ + isis->debugs &= ~DEBUG_EVENTS; + print_debug (vty, DEBUG_EVENTS, 0); + + 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", + SHOW_STR + "IS-IS information\n" + "IS-IS Dynamic hostname mapping\n") +{ + dynhn_print_all (vty); + + return CMD_SUCCESS; +} + +static void +vty_out_timestr(struct vty *vty, time_t uptime) +{ + 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; + struct isis_spftree *spftree; + int level; + + 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) + { + 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; + + 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, " 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; +} + +/* + * 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; + + memset (&lspid, 0, ISIS_SYS_ID_LEN); + memset (&sysid, 0, 255); + + /* + * 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 + */ +DEFUN (router_isis, + router_isis_cmd, + "router isis WORD", + ROUTER_STR + "ISO IS-IS\n" + "ISO Routing area tag") +{ + return isis_area_get (vty, argv[0]); +} + +/* + *'no router isis' command + */ +DEFUN (no_router_isis, + no_router_isis_cmd, + "no router isis WORD", + "no\n" ROUTER_STR "ISO IS-IS\n" "ISO Routing area tag") +{ + return isis_area_destroy (vty, argv[0]); +} + +/* + * 'net' command + */ +DEFUN (net, + net_cmd, + "net WORD", + "A Network Entity Title for this process (OSI only)\n" + "XX.XXXX. ... .XXX.XX Network entity title (NET)\n") +{ + return area_net_title (vty, argv[0]); +} + +/* + * 'no net' command + */ +DEFUN (no_net, + no_net_cmd, + "no net WORD", + NO_STR + "A Network Entity Title for this process (OSI only)\n" + "XX.XXXX. ... .XXX.XX Network entity title (NET)\n") +{ + return area_clear_net_title (vty, argv[0]); +} + +void isis_area_lsp_mtu_set(struct isis_area *area, unsigned int lsp_mtu) +{ + 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; + + 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 (passwd_type != ISIS_PASSWD_TYPE_UNUSED) + { + if (!passwd) + return -1; + + len = strlen(passwd); + if (len > 254) + return -1; + + modified.len = len; + strncpy((char*)modified.passwd, passwd, 255); + modified.type = passwd_type; + modified.snp_auth = snp_auth; + } + + if (memcmp(&modified, dest, sizeof(modified))) + { + memcpy(dest, &modified, sizeof(modified)); + lsp_regenerate_schedule(area, IS_LEVEL_1|IS_LEVEL_2, 1); + } + + return 0; +} + +int +isis_area_passwd_unset (struct isis_area *area, int level) +{ + return isis_area_passwd_set (area, level, ISIS_PASSWD_TYPE_UNUSED, NULL, 0); +} + +int +isis_area_passwd_cleartext_set (struct isis_area *area, int level, + const char *passwd, u_char snp_auth) +{ + return isis_area_passwd_set (area, level, ISIS_PASSWD_TYPE_CLEARTXT, + passwd, snp_auth); +} + +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); +} + +static void +area_resign_level (struct isis_area *area, int level) +{ + if (area->lspdb[level - 1]) + { + lsp_db_destroy (area->lspdb[level - 1]); + area->lspdb[level - 1] = NULL; + } + if (area->spftree[level - 1]) + { + isis_spftree_del (area->spftree[level - 1]); + area->spftree[level - 1] = NULL; + } +#ifdef HAVE_IPV6 + if (area->spftree6[level - 1]) + { + isis_spftree_del (area->spftree6[level - 1]); + area->spftree6[level - 1] = NULL; + } +#endif + if (area->route_table[level - 1]) + { + route_table_finish (area->route_table[level - 1]); + area->route_table[level - 1] = NULL; + } +#ifdef HAVE_IPV6 + if (area->route_table6[level - 1]) + { + route_table_finish (area->route_table6[level - 1]); + area->route_table6[level - 1] = NULL; + } +#endif /* HAVE_IPV6 */ + + 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; +} + +void +isis_area_is_type_set(struct isis_area *area, int is_type) +{ + 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 (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; + + 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; + + case IS_LEVEL_2: + if (is_type == IS_LEVEL_1) + area_resign_level (area, IS_LEVEL_2); + + 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; + + default: + break; + } + + area->is_type = is_type; + + /* 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); + } + + spftree_area_init (area); + + 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; +} + +void isis_area_metricstyle_set(struct isis_area *area, bool old_metric, + bool new_metric) +{ + 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); + } +} + +void isis_area_overload_bit_set(struct isis_area *area, bool overload_bit) +{ + char new_overload_bit = overload_bit ? LSPBIT_OL : 0; + + if (new_overload_bit != area->overload_bit) + { + area->overload_bit = new_overload_bit; + lsp_regenerate_schedule(area, IS_LEVEL_1 | IS_LEVEL_2, 1); + } +} + +void isis_area_attached_bit_set(struct isis_area *area, bool attached_bit) +{ + char new_attached_bit = attached_bit ? LSPBIT_ATT : 0; + + if (new_attached_bit != area->attached_bit) + { + area->attached_bit = new_attached_bit; + lsp_regenerate_schedule(area, IS_LEVEL_1 | IS_LEVEL_2, 1); + } +} + +void isis_area_dynhostname_set(struct isis_area *area, bool dynhostname) +{ + if (area->dynhostname != dynhostname) + { + area->dynhostname = dynhostname; + lsp_regenerate_schedule(area, IS_LEVEL_1 | IS_LEVEL_2, 0); + } +} + +void +isis_area_max_lsp_lifetime_set(struct isis_area *area, int level, + uint16_t max_lsp_lifetime) +{ + assert((level == IS_LEVEL_1) || (level == IS_LEVEL_2)); + + if (area->max_lsp_lifetime[level-1] == max_lsp_lifetime) + return; + + area->max_lsp_lifetime[level-1] = max_lsp_lifetime; + lsp_regenerate_schedule(area, level, 1); +} + +void +isis_area_lsp_refresh_set(struct isis_area *area, int level, + uint16_t lsp_refresh) +{ + assert((level == IS_LEVEL_1) || (level == IS_LEVEL_2)); + + if (area->lsp_refresh[level-1] == lsp_refresh) + return; + + area->lsp_refresh[level-1] = lsp_refresh; + lsp_regenerate_schedule(area, level, 1); +} + +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->log_adj_changes = 1; + + return CMD_SUCCESS; +} + +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->log_adj_changes = 0; + + return CMD_SUCCESS; +} + +#ifdef TOPOLOGY_GENERATE + +DEFUN (topology_generate_grid, + topology_generate_grid_cmd, + "topology generate grid <1-100> <1-100> <1-65000> [param] [param] " + "[param]", + "Topology generation for IS-IS\n" + "Topology generation\n" + "Grid topology\n" + "X parameter of the grid\n" + "Y parameter of the grid\n" + "Random seed\n" + "Optional param 1\n" + "Optional param 2\n" + "Optional param 3\n" + "Topology\n") +{ + struct isis_area *area; + + area = vty->index; + assert (area); + + if (!spgrid_check_params (vty, argc, argv)) + { + if (area->topology) + list_delete (area->topology); + area->topology = list_new (); + memcpy (area->top_params, vty->buf, 200); + gen_spgrid_topology (vty, area->topology); + remove_topology_lsps (area); + generate_topology_lsps (area); + /* Regenerate L1 LSP to get two way connection to the generated + * topology. */ + lsp_regenerate_schedule (area, IS_LEVEL_1 | IS_LEVEL_2, 1); + } + + return CMD_SUCCESS; +} + +DEFUN (show_isis_generated_topology, + show_isis_generated_topology_cmd, + "show isis generated-topologies", + SHOW_STR + "ISIS network information\n" + "Show generated topologies\n") +{ + struct isis_area *area; + struct listnode *node; + struct listnode *node2; + struct arc *arc; + + for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area)) + { + if (!area->topology) + continue; + + vty_out (vty, "Topology for isis area: %s%s", area->area_tag, + VTY_NEWLINE); + vty_out (vty, "From node To node Distance%s", VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO (area->topology, node2, arc)) + vty_out (vty, "%9ld %11ld %12ld%s", arc->from_node, arc->to_node, + arc->distance, VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +/* Base IS for topology generation. */ +DEFUN (topology_baseis, + topology_baseis_cmd, + "topology base-is WORD", + "Topology generation for IS-IS\n" + "A Network IS Base for this topology\n" + "XXXX.XXXX.XXXX Network entity title (NET)\n") +{ + struct isis_area *area; + u_char buff[ISIS_SYS_ID_LEN]; + + area = vty->index; + assert (area); + + if (sysid2buff (buff, argv[0])) + sysid2buff (area->topology_baseis, argv[0]); + + return CMD_SUCCESS; +} + +DEFUN (no_topology_baseis, + no_topology_baseis_cmd, + "no topology base-is WORD", + NO_STR + "Topology generation for IS-IS\n" + "A Network IS Base for this topology\n" + "XXXX.XXXX.XXXX Network entity title (NET)\n") +{ + struct isis_area *area; + + area = vty->index; + assert (area); + + memcpy (area->topology_baseis, DEFAULT_TOPOLOGY_BASEIS, ISIS_SYS_ID_LEN); + return CMD_SUCCESS; +} + +ALIAS (no_topology_baseis, + no_topology_baseis_noid_cmd, + "no topology base-is", + NO_STR + "Topology generation for IS-IS\n" + "A Network IS Base for this topology\n") + +DEFUN (topology_basedynh, + topology_basedynh_cmd, + "topology base-dynh WORD", + "Topology generation for IS-IS\n" + "Dynamic hostname base for this topology\n" + "Dynamic hostname base\n") +{ + struct isis_area *area; + + area = vty->index; + assert (area); + + /* I hope that it's enough. */ + area->topology_basedynh = strndup (argv[0], 16); + return CMD_SUCCESS; +} + +#endif /* TOPOLOGY_GENERATE */ + +/* IS-IS configuration write function */ +int +isis_config_write (struct vty *vty) +{ + int write = 0; + + if (isis != NULL) + { + struct isis_area *area; + struct listnode *node, *node2; + + for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area)) + { + /* ISIS - Area name */ + vty_out (vty, "router isis %s%s", area->area_tag, VTY_NEWLINE); + write++; + /* ISIS - Net */ + if (listcount (area->area_addrs) > 0) + { + 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); + write++; + } + } + /* ISIS - Dynamic hostname - Defaults to true so only display if + * false. */ + if (!area->dynhostname) + { + vty_out (vty, " no hostname dynamic%s", VTY_NEWLINE); + write++; + } + /* ISIS - Metric-Style - when true displays wide */ + if (area->newmetric) + { + if (!area->oldmetric) + vty_out (vty, " metric-style wide%s", VTY_NEWLINE); + else + 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 if (area->is_type == IS_LEVEL_2) + { + 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] != DEFAULT_MIN_LSP_GEN_INTERVAL) + { + vty_out (vty, " lsp-gen-interval %d%s", + area->lsp_gen_interval[0], VTY_NEWLINE); + write++; + } + } + else + { + 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] != DEFAULT_MIN_LSP_GEN_INTERVAL) + { + vty_out (vty, " lsp-gen-interval level-2 %d%s", + area->lsp_gen_interval[1], VTY_NEWLINE); + write++; + } + } + /* ISIS - LSP lifetime */ + if (area->max_lsp_lifetime[0] == area->max_lsp_lifetime[1]) + { + if (area->max_lsp_lifetime[0] != DEFAULT_LSP_LIFETIME) + { + vty_out (vty, " max-lsp-lifetime %u%s", area->max_lsp_lifetime[0], + VTY_NEWLINE); + write++; + } + } + else + { + if (area->max_lsp_lifetime[0] != DEFAULT_LSP_LIFETIME) + { + vty_out (vty, " max-lsp-lifetime level-1 %u%s", + area->max_lsp_lifetime[0], VTY_NEWLINE); + write++; + } + if (area->max_lsp_lifetime[1] != DEFAULT_LSP_LIFETIME) + { + 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]) + { + if (area->min_spf_interval[0] != MINIMUM_SPF_INTERVAL) + { + vty_out (vty, " spf-interval %d%s", + area->min_spf_interval[0], VTY_NEWLINE); + write++; + } + } + else + { + if (area->min_spf_interval[0] != MINIMUM_SPF_INTERVAL) + { + vty_out (vty, " spf-interval level-1 %d%s", + area->min_spf_interval[0], VTY_NEWLINE); + write++; + } + if (area->min_spf_interval[1] != MINIMUM_SPF_INTERVAL) + { + vty_out (vty, " spf-interval level-2 %d%s", + area->min_spf_interval[1], VTY_NEWLINE); + write++; + } + } + /* Authentication passwords. */ + if (area->area_passwd.type == ISIS_PASSWD_TYPE_HMAC_MD5) + { + 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 "); + 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++; + } + if (area->domain_passwd.type == ISIS_PASSWD_TYPE_HMAC_MD5) + { + 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 "); + 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++; + } + + 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 ((u_char *)area->topology_baseis), VTY_NEWLINE); + write++; + } + if (area->topology_basedynh) + { + vty_out (vty, " topology base-dynh %s%s", + area->topology_basedynh, VTY_NEWLINE); + write++; + } + /* We save the whole command line here. */ + if (strlen(area->top_params)) + { + vty_out (vty, " %s%s", area->top_params, VTY_NEWLINE); + write++; + } +#endif /* TOPOLOGY_GENERATE */ + + } + isis_mpls_te_config_write_router(vty); + } + + return write; +} + +struct cmd_node isis_node = { + ISIS_NODE, + "%s(config-router)# ", + 1 +}; + +void +isis_init () +{ + /* Install IS-IS top node */ + install_node (&isis_node, isis_config_write); + + 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_debugging_isis_cmd); + + install_node (&debug_node, config_write_debug); + + install_element (ENABLE_NODE, &debug_isis_adj_cmd); + install_element (ENABLE_NODE, &no_debug_isis_adj_cmd); + install_element (ENABLE_NODE, &debug_isis_csum_cmd); + install_element (ENABLE_NODE, &no_debug_isis_csum_cmd); + install_element (ENABLE_NODE, &debug_isis_lupd_cmd); + install_element (ENABLE_NODE, &no_debug_isis_lupd_cmd); + install_element (ENABLE_NODE, &debug_isis_err_cmd); + install_element (ENABLE_NODE, &no_debug_isis_err_cmd); + install_element (ENABLE_NODE, &debug_isis_snp_cmd); + install_element (ENABLE_NODE, &no_debug_isis_snp_cmd); + install_element (ENABLE_NODE, &debug_isis_upd_cmd); + install_element (ENABLE_NODE, &no_debug_isis_upd_cmd); + install_element (ENABLE_NODE, &debug_isis_spfevents_cmd); + install_element (ENABLE_NODE, &no_debug_isis_spfevents_cmd); + install_element (ENABLE_NODE, &debug_isis_spfstats_cmd); + install_element (ENABLE_NODE, &no_debug_isis_spfstats_cmd); + install_element (ENABLE_NODE, &debug_isis_spftrigg_cmd); + install_element (ENABLE_NODE, &no_debug_isis_spftrigg_cmd); + install_element (ENABLE_NODE, &debug_isis_rtevents_cmd); + 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); + install_element (CONFIG_NODE, &debug_isis_csum_cmd); + install_element (CONFIG_NODE, &no_debug_isis_csum_cmd); + install_element (CONFIG_NODE, &debug_isis_lupd_cmd); + install_element (CONFIG_NODE, &no_debug_isis_lupd_cmd); + install_element (CONFIG_NODE, &debug_isis_err_cmd); + install_element (CONFIG_NODE, &no_debug_isis_err_cmd); + install_element (CONFIG_NODE, &debug_isis_snp_cmd); + install_element (CONFIG_NODE, &no_debug_isis_snp_cmd); + install_element (CONFIG_NODE, &debug_isis_upd_cmd); + install_element (CONFIG_NODE, &no_debug_isis_upd_cmd); + install_element (CONFIG_NODE, &debug_isis_spfevents_cmd); + install_element (CONFIG_NODE, &no_debug_isis_spfevents_cmd); + install_element (CONFIG_NODE, &debug_isis_spfstats_cmd); + install_element (CONFIG_NODE, &no_debug_isis_spfstats_cmd); + install_element (CONFIG_NODE, &debug_isis_spftrigg_cmd); + install_element (CONFIG_NODE, &no_debug_isis_spftrigg_cmd); + install_element (CONFIG_NODE, &debug_isis_rtevents_cmd); + 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); + + install_default (ISIS_NODE); + + install_element (ISIS_NODE, &net_cmd); + install_element (ISIS_NODE, &no_net_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); + install_element (ISIS_NODE, &topology_basedynh_cmd); + 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); +#endif /* TOPOLOGY_GENERATE */ +} diff --git a/isisd/isisd.conf.sample b/isisd/isisd.conf.sample new file mode 100644 index 0000000..47b1595 --- /dev/null +++ b/isisd/isisd.conf.sample @@ -0,0 +1,39 @@ +! -*- isis -*- +! +! ISISd sample configuration file +! +hostname isisd +password foo +enable password foo +log stdout +!log file /tmp/isisd.log +! +! +router isis DEAD + net 47.0023.0000.0003.0300.0100.0102.0304.0506.00 +! is-type level-1 + +! -- set the lifetime either for level-1, level-2 or both +! lsp-lifetime level-1 65535 +! lsp-lifetime level-2 65535 +! lsp-lifetime 65535 + +! hostname isisd-router +! area-password foobar +! domain-password foobar + +interface eth0 + ip router isis DEAD +! isis hello-interval 5 +! isis lsp-interval 1000 + +! -- optional +! isis circuit-type level-1 +! isis password lallaa level-1 +! isis metric 1 level-1 +! isis csnp-interval 5 level-1 +! isis retransmit-interval 10 +! isis retransmit-throttle-interval +! isis hello-multiplier 2 level-1 +! isis priority 64 +! diff --git a/isisd/isisd.h b/isisd/isisd.h new file mode 100644 index 0000000..eedb451 --- /dev/null +++ b/isisd/isisd.h @@ -0,0 +1,202 @@ +/* + * IS-IS Rout(e)ing protocol - isisd.h + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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. + */ + +#ifndef ISISD_H +#define ISISD_H + +#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 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 */ +#ifdef HAVE_IPV6 + struct list *nexthops6; /* IPv6 next hops from this IS */ +#endif /* HAVE_IPV6 */ + u_char max_area_addrs; /* maximumAreaAdresses */ + struct area_addr *man_area_addrs; /* manualAreaAddresses */ + u_int32_t debugs; /* bitmap for debug */ + time_t uptime; /* when did we start */ + struct thread *t_dync_clean; /* dynamic hostname cache cleanup thread */ + + struct route_table *ext_info[REDIST_PROTOCOL_COUNT]; +}; + +extern struct isis *isis; + +struct isis_area +{ + struct isis *isis; /* back pointer */ + dict_t *lspdb[ISIS_LEVELS]; /* link-state dbs */ + struct isis_spftree *spftree[ISIS_LEVELS]; /* The v4 SPTs */ + struct route_table *route_table[ISIS_LEVELS]; /* IPv4 routes */ +#ifdef HAVE_IPV6 + struct isis_spftree *spftree6[ISIS_LEVELS]; /* The v6 SPTs */ + struct route_table *route_table6[ISIS_LEVELS]; /* IPv6 routes */ +#endif +#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_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 + */ + struct isis_passwd area_passwd; + struct isis_passwd domain_passwd; + /* do we support dynamic hostnames? */ + char dynhostname; + /* do we support new style metrics? */ + char newmetric; + char oldmetric; + /* identifies the routing instance */ + char *area_tag; + /* area addresses for this 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]; + /* min interval between between consequtive SPFs */ + u_int16_t min_spf_interval[ISIS_LEVELS]; + /* 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; + 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) +#define DEBUG_LOCAL_UPDATES (1<<2) +#define DEBUG_PROTOCOL_ERRORS (1<<3) +#define DEBUG_SNP_PACKETS (1<<4) +#define DEBUG_UPDATE_PACKETS (1<<5) +#define DEBUG_SPF_EVENTS (1<<6) +#define DEBUG_SPF_STATS (1<<7) +#define DEBUG_SPF_TRIGGERS (1<<8) +#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/iso_checksum.c b/isisd/iso_checksum.c new file mode 100644 index 0000000..294fe99 --- /dev/null +++ b/isisd/iso_checksum.c @@ -0,0 +1,77 @@ +/* + * IS-IS Rout(e)ing protocol - iso_checksum.c + * ISO checksum related routines + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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 "iso_checksum.h" +#include "checksum.h" + +/* + * Calculations of the OSI checksum. + * ISO/IEC 8473 defines the sum as + * + * L + * sum a (mod 255) = 0 + * 1 i + * + * L + * sum (L-i+1)a (mod 255) = 0 + * 1 i + * + */ + +/* + * Verifies that the checksum is correct. + * Return 0 on correct and 1 on invalid checksum. + * Based on Annex C.4 of ISO/IEC 8473 + */ + +int +iso_csum_verify (u_char * buffer, int len, uint16_t * csum) +{ + u_int16_t checksum; + u_int32_t c0; + u_int32_t c1; + + c0 = *csum & 0xff00; + c1 = *csum & 0x00ff; + + /* + * If both are zero return correct + */ + if (c0 == 0 && c1 == 0) + return 0; + + /* + * If either, but not both are zero return incorrect + */ + if (c0 == 0 || c1 == 0) + return 1; + + /* Offset of checksum from the start of the buffer */ + int offset = (u_char *) csum - buffer; + + checksum = fletcher_checksum(buffer, len, offset); + if (checksum == *csum) + return 0; + return 1; +} diff --git a/isisd/iso_checksum.h b/isisd/iso_checksum.h new file mode 100644 index 0000000..5f8d41f --- /dev/null +++ b/isisd/iso_checksum.h @@ -0,0 +1,28 @@ +/* + * IS-IS Rout(e)ing protocol - iso_checksum.c + * ISO checksum related routines + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * 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. + */ +#ifndef _ZEBRA_ISO_CSUM_H +#define _ZEBRA_ISO_CSUM_H + +int iso_csum_verify (u_char * buffer, int len, uint16_t * csum); + +#endif /* _ZEBRA_ISO_CSUM_H */ diff --git a/isisd/topology/.gitignore b/isisd/topology/.gitignore new file mode 100644 index 0000000..dd75c27 --- /dev/null +++ b/isisd/topology/.gitignore @@ -0,0 +1,12 @@ +Makefile +Makefile.in +*.o +tags +TAGS +.deps +.nfs* +.arch-inventory +.arch-ids +*~ +*.loT + diff --git a/isisd/topology/Makefile.am b/isisd/topology/Makefile.am new file mode 100644 index 0000000..fe73ae5 --- /dev/null +++ b/isisd/topology/Makefile.am @@ -0,0 +1,21 @@ +## Process this file with automake to produce Makefile.in. + +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" + +AM_CFLAGS = $(PICFLAGS) +AM_LDFLAGS = $(PILDFLAGS) + +noinst_LIBRARIES = libtopology.a + +libtopology_a_SOURCES = \ + spgrid.c random.c + +libtopology_a_DEPENDENCIES = @LIB_REGEX@ + +libtopology_a_LIBADD = @LIB_REGEX@ ../../lib/libzebra.la + +noinst_HEADERS = \ + spgrid.h + +## File dependency. diff --git a/isisd/topology/random.c b/isisd/topology/random.c new file mode 100644 index 0000000..2c457c5 --- /dev/null +++ b/isisd/topology/random.c @@ -0,0 +1,166 @@ +/*********************************************************************/ +/* */ +/* current processor time in seconds */ +/* difference between two calls is processor time spent by your code */ +/* needs: , */ +/* depends on compiler and OS */ +/* */ +/*********************************************************************/ + +#include +#include +#include + +/* + * Prototypes. + */ +unsigned long timer(void); +void init_rand(long); +double rand01(void); +double randg01(void); +long nrand(long); +void free_arc(void *); + +unsigned long timer () + { struct tms hold; + + times(&hold); + return (unsigned long) ((float) (hold.tms_utime) / 60.0); + } + + +/*********************************************************************/ +/* */ +/* Family of random number generators */ +/* */ +/* Initialisation: */ +/* void init_rand ( seed ); */ +/* long seed - any positive number */ +/* if seed<=0 init_rand takes time */ +/* from timer instead of seed */ +/* */ +/* Whole number uniformly distributed on [0,n): */ +/* long nrand (n); */ +/* long n */ +/* */ +/* Real number uniformly distributed on [0,1] */ +/* double rand01(); */ +/* */ +/* Real number with Gauss(0,1) disitribution: */ +/* double randg01(); */ +/* */ +/* Algorithm: */ +/* x(n+1) = (x(n) * 5^13) mod 2^31 */ +/* */ +/*********************************************************************/ + +unsigned long internal_seed; + +void init_rand ( init_seed ) + +long init_seed; + +{ internal_seed = ( init_seed > 0 ) + ? (unsigned long) init_seed + : (unsigned long) timer(); + + + /* only odd numbers are acceptable */ + if ( internal_seed % 2 == 0 ) internal_seed --; +} + +/*********************************************************************/ +/* */ +/* Internal function irand may depend on OS and compiler */ +/* */ +/* irand assumption: */ +/* unsigned long i,j; */ +/* if i*j > max(unsigned long) */ +/* 1. No overflow interruption */ +/* 2. i*j = i*j mod max(unsigned long) */ +/* */ +/* This assumption is true for a lot of computers. */ +/* If your computer fails: */ +/* rename: irand <---> xrand */ +/* */ +/*********************************************************************/ + +#define A 1220703125 +#define B 2147483647 +#define BF 2147483647. + +static long irand () + +{ internal_seed = ( internal_seed * A ) & B; + return (long) internal_seed ; +} + +#if 0 /* Not used. */ +/*********************************************************************/ +/* */ +/* computer independent variant of irand */ +/* */ +/*********************************************************************/ + + +#define T15 32768 +#define T16 65536 +#define A1 37252 +#define A2 29589 + +static long xrand() + +{ unsigned long is1, is2; + + is1 = internal_seed / T15; + is2 = internal_seed % T15; + + internal_seed = ( (((is2 * A1) + (is1 * A2))% T16 )* T15 + (is2 * A2) ) & B; + return (long) ( internal_seed ) ; +} +#endif + +/*********************************************************************/ + + +double rand01() + +{ return (double) (irand() / BF) ; +} + +/*********************************************************************/ + +#define NK 12 + +double randg01() + +{ int i; + double sum = 0; + + for ( i = 0; i < NK; i++ ) sum += rand01(); + return sum - 6.; + + /* if NK != 12 then you must return (12/NK)*sum - (NK/2) */ +} + +#undef NK + + +/*********************************************************************/ + +long nrand ( n ) + +long n; + +{ return (long) ( rand01() * (double) n ); +} + +/*********************************************************************/ + +#undef A +#undef A1 +#undef A2 +#undef B +#undef BF +#undef T15 +#undef T16 diff --git a/isisd/topology/spacyc.c b/isisd/topology/spacyc.c new file mode 100644 index 0000000..91a4799 --- /dev/null +++ b/isisd/topology/spacyc.c @@ -0,0 +1,484 @@ +#include +#include +#include +#include +#include + +#include "random.c" + +#define DASH '-' +#define VERY_FAR 100000000 + + +/* generator of acyclic random networks for the shortest paths problem; + extended DIMACS format for output */ + +main ( argc, argv ) + +int argc; +char* argv[]; + +{ + +char args[30]; + +long n, + n0, + source, + i, + i0, + j, + dij; + +long m, + m0, + mc, + k; + +long *p, + p_t, + l, + lx; + +long seed, + seed1, + seed2; + +int ext=0; + +FILE *fout; + +/* variables for lengths generating */ +/* initialized by default values */ +int l_f = 0, ll_f = 0, lm_f = 0, ln_f = 0, ls_f = 0; +long ll = 10000, /* upper bound of the interval */ + lm = 0; /* lower bound of the interval */ +double ln = 0, /* l += ln * |i-j| */ + ls = 0; /* l += ls * |i-j|^2 */ + +/* variables for connecting path(s) */ +int c_f = 0, cl_f = 0, ch_f = 0, c_rand = 1; +long cl = 1; /* length of path arc */ +long ch; /* number of arcs in the path + n - by default */ + +/* variables for artifical source */ +int s_f = 0, sl_f = 0, sm_f = 0; +long sl = VERY_FAR, /* upper bound of artifical arc */ + sm, /* lower bound of artifical arc */ + s; + +/* variables for potentials */ +int p_f = 0, pl_f = 0, pm_f = 0, pn_f = 0, ps_f = 0, + pa_f = 0, pap_f = 0, pac_f = 0; +long pl, /* upper bound of the interval */ + pm; /* lower bound of the interval */ +double pn = 0, /* l += ln * |i-j| */ + ps = 0, /* l += ls * |i-j|^2 */ + pap = 0, /* part of nodes with alternative dustribution */ + pac = -1; /* multiplier for alternative distribution */ + +int np; /* number of parameter parsing now */ + +#define PRINT_ARC( i, j, length )\ +{\ +l = length;\ +if ( p_f ) l += ( p[i] - p[j] );\ +printf ("a %8ld %8ld %12ld\n", i, j, l );\ +} + + /* parsing parameters */ + +if ( argc < 2 ) goto usage; + +np = 0; + +strcpy ( args, argv[1] ); + + if ( ( args[0] == DASH ) && ( args[1] == 'h') + ) + goto help; + +if ( argc < 4 ) goto usage; + +/* first parameter - number of nodes */ +np = 1; +if ( ( n = atoi ( argv[1] ) ) < 2 ) goto usage; + +/* second parameter - number of arcs */ +np = 2; +if ( ( m = atoi ( argv[2] ) ) < n ) goto usage; + +/* third parameter - seed */ +np=3; +if ( ( seed = atoi ( argv[3] ) ) <= 0 ) goto usage; + +/* other parameters */ + +for ( np = 4; np < argc; np ++ ) + { + strcpy ( args, argv[np] ); + if ( args[0] != DASH ) goto usage; + + switch ( args[1] ) + { + + case 'l' : /* an interval for arc length */ + l_f = 1; + switch ( args[2] ) + { + case 'l': /* length of the interval */ + ll_f = 1; + ll = (long) atof ( &args[3] ); + break; + case 'm': /* minimal bound */ + lm_f = 1; + lm = (long ) atof ( &args[3] ); + break; + case 'n': /* additional length: l*|i-j| */ + ln_f = 1; + ln = atof ( &args[3] ); + break; + case 's': /* additional length: l*|i-j|^2 */ + ls_f = 1; + ls = atof ( &args[3] ); + break; + default: /* unknown switch value */ + goto usage; + } + break; + + case 'c' : /* connecting path(s) */ + c_f = 1; + switch ( args[2] ) + { + case 'l': /* length of path arc */ + c_rand = 0; /* fixed arc length */ + cl_f = 1; + cl = (long) atof ( &args[3] ); + break; + case 'h': /* number of arcs in connecting path */ + ch_f = 1; + ch = (long) atof ( &args[3] ); + if ( ch < 1 || ch > n ) goto usage; + break; + default: /* unknown switch value */ + goto usage; + } + break; + + case 's' : /* additional source */ + s_f = 1; + if ( strlen ( args ) > 2 ) + { + switch ( args[2] ) + { + case 'l': /* upper bound of art. arc */ + sl_f = 1; + sl = (long) atof ( &args[3] ); + break; + case 'm': /* lower bound of art. arc */ + sm_f = 1; + sm = (long) atof ( &args[3] ); + break; + default: /* unknown switch value */ + goto usage; + } + } + break; + + case 'p' : /* potentials */ + p_f = 1; + if ( strlen ( args ) > 2 ) + { + switch ( args[2] ) + { + case 'l': /* length of the interval */ + pl_f = 1; + pl = (long) atof ( &args[3] ); + break; + case 'm': /* minimal bound */ + pm_f = 1; + pm = (long ) atof ( &args[3] ); + break; + case 'n': /* additional length: l*|i-j| */ + pn_f = 1; + pn = atof ( &args[3] ); + break; + case 's': /* additional length: l*|i-j|^2 */ + ps_f = 1; + ps = atof ( &args[3] ); + break; + case 'a': /* bipolar distribution */ + pa_f = 1; + switch ( args[3] ) + { + case 'p': /* % of alternative potentials */ + pap_f = 1; + pap = atof ( &args[4] ); + if ( pap < 0 ) pap = 0; + if ( pap > 100 ) pap = 100; + pap /= 100; + break; + case 'c': /* multiplier */ + pac_f = 1; + pac = atof ( &args[4] ); + break; + default: /* unknown switch value */ + goto usage; + } + break; + default: /* unknown switch value */ + goto usage; + } + } + break; + + default : /* unknoun case */ + goto usage; + } + } + +/* ----- ajusting parameters ----- */ + +n0 = n; m0 = m; + +/* length parameters */ +if ( ll < lm ) { lx = ll; ll = lm; lm = lx; } + +/* potential parameters */ +if ( p_f ) + { + if ( ! pl_f ) pl = ll; + if ( ! pm_f ) pm = lm; + if ( pl < pm ) { lx = pl; pl = pm; pm = lx; } + } + +/* path(s) parameters */ +if ( ! ch_f ) ch = n - 1; +mc = n - 1; + + /* artifical source parameters */ +if ( s_f ) + { m0 += n; n0 ++ ; + if ( ! sm_f ) sm = sl; + if ( sl < sm ) { lx = sl; sl = sm; sm = lx; } + } + +/*----- printing title -----*/ + +printf ("c acyclic network for shortest paths problem\n"); +printf ("c extended DIMACS format\nc\n" ); + + +/* name of the problem */ +printf ("t ac_%ld_%ld_%ld_", n, m, seed ); +if ( l_f ) + printf ("%c", 'l'); +if ( c_f ) + printf ("%c", 'c'); +if ( s_f ) + printf ("%c", 's'); +if ( p_f ) + printf ("%c", 'p'); +printf ("\nc\n"); + +/* printing additional information */ +if ( l_f ) + printf ("c length -> min: %ld max: %ld k1: %.2f k2: %.2f\n", + lm, ll, ln, ls ); +if ( c_f ) + printf ("c path(s) -> number of arcs: %ld arc length: %ld\n", + ch, cl ); +if ( s_f ) + printf ("c length of arcs from artifical source -> min: %ld max: %ld\n", + sm, sl ); +if ( p_f ) + { + printf ("c potentials -> min: %ld max: %ld k1: %.2f k2: %.2f\n", + pm, pl, pn, ps ); + if ( pa_f ) + printf ("c potentials -> part of alternative distribution: %.2f k: %.2f\n", + pap, pac ); + } +printf ("c\n" ); + +printf ("p sp %8ld %8ld\nc\n", n0, m0 ); + +source = ( s_f ) ? n0 : 1; +printf ("n %8ld\nc\n", source ); + + +if ( p_f ) /* generating potentials */ + { + seed1 = 2*seed + 1; + p = (long*) calloc ( n+2, sizeof (long) ); + init_rand ( seed1); + pl = pl - pm + 1; + + for ( i = 0; i <= n; i ++ ) + { + p_t = pm + nrand ( pl ); + if ( pn_f ) p_t += (long) ( i * pn ); + if ( ps_f ) p_t += (long) ( i * ( i * ps )); + if ( pap_f ) + if ( rand01() < pap ) + p_t = (long) ( p_t * pac ); + p[i] = p_t; + } + p[n+1] = 0; + } + + +if ( s_f ) /* additional arcs from artifical source */ + { + seed2 = 3*seed + 1; + init_rand ( seed2 ); + sl = sl - sm + 1; + + for ( i = n; i > 1; i -- ) + { + s = sm + nrand ( sl ); + PRINT_ARC ( n0, i, s ) + } + + PRINT_ARC ( n0, 1, 0 ) + } + +/* initialize random number generator */ +init_rand ( seed ); +ll = ll - lm + 1; + +/* generating connecting path(s) */ +for ( i = 1; i < n; i ++ ) + { + if ( ( (i-1) % ch ) != 0 ) + i0 = i; + else + i0 = 1; + + if (c_rand) + cl = lm + nrand(ll); + PRINT_ARC ( i0, i+1, cl ) + } + +/* generating random arcs */ + + +for ( k = 1; k <= m - mc; k ++ ) + { + i = 1 + nrand ( n ); + + do + j = 1 + nrand ( n ); + while ( j == i ); + + if ( i > j ) + { i0 = i; i = j; j = i0; } + + dij = j - i; + l = lm + nrand ( ll ); + if ( ln_f ) l += (long) ( dij * ln ); + if ( ls_f ) l += (long) ( dij * ( dij * ls ) ); + PRINT_ARC ( i, j, l ); + } + +/* all is done */ +exit (ext); + +/* ----- wrong usage ----- */ + + usage: +fprintf ( stderr, +"\nusage: %s n m seed [ -ll#i -lm#i -cl#i -p -pl#i -pm#i ... ]\n\ +help: %s -h\n\n", argv[0], argv[0] ); + +if ( np > 0 ) + fprintf ( stderr, "error in parameter # %d\n\n", np ); +exit (4); + +/* ---- help ---- */ + + help: + +if ( args[2] == 'h') goto hhelp; + +fprintf ( stderr, +"\n'%s' - acyclic network generator for shortest paths problem.\n\ +Generates problems in extended DIMACS format.\n\ +\n\ + %s n m seed [ -ll#i -lm#i -cl#i -p -pl#i -pm#i ... ]\n\ + %s -hh\n\ +\n\ + #i - integer number #f - real number\n\ +\n\ +-ll#i - #i is the upper bound on arc lengths (default 10000)\n\ +-lm#i - #i is the lower bound on arc lengths (default 0)\n\ +-cl#i - #i is length of arcs in connecting path(s) (default random)\n\ +-p - generate potentials \n\ +-pl#i - #i is the upper bound on potentials (default ll)\n\ +-pm#i - #i is the lower bound on potentials (default lm)\n\ +\n\ +-hh - extended help \n\n", +argv[0], argv[0], argv[0] ); + +exit (0); + +/* --------- sophisticated help ------------ */ + hhelp: + +if ( argc < 3 ) + fout = stderr; +else + fout = fopen ( argv[2], "w" ); + +if ( fout == NULL ) +{ fprintf ( stderr, "\nCan't open file '%s' for writing help\n\n", argv[2] ); + exit ( 2 ); +} + +fprintf (fout, +"\n'%s' - acyclic network generator for shortest paths problem.\n\ +Generates problems in extended DIMACS format.\n\ +\n\ + %s n m seed [ -ll#i -lm#i -ln#f -ls#f\n\ + -p -pl#i -pm#i -pn#f -ps#f -pap#i -pac#f\n\ + -cl#i -ch#i\n\ + -s -sl#i -sm#i\n\ + ]\n\ + %s -hh file_name\n\ +\n\ + #i - integer number #f - real number\n\ +\n\ + Arc length parameters:\n\ +-ll#i - #i is the upper bound on arc lengths (default 10000)\n\ +-lm#i - #i is the lower bound on arc lengths (default 0)\n\ +-ln#f - multipliy l(i, j) by #f * |i-j| (default 0)\n\ +-ls#f - multipliy l(i, j) by #f * |i-j|^2 (default 0)\n\ +\n\ + Potential parameters:\n\ +-p - generate potentials \n\ +-pl#i - #i is the upper bound on potentials (default ll)\n\ +-pm#i - #i is the lower bound on potentials (default lm)\n\ +-pn#f - multiply p(i) by #f * i (default 0)\n\ +-ps#f - multiply p(i) by #f * i^2 (default 0)\n\ +-pap#i - percentage of alternative potential nodes (default 0)\n\ +-pac#f - if i is alternative, multiply p(i) by #f (default -1)\n\ +\n\ + Connecting path(s) parameters:\n\ +-cl#i - #i is length of arcs in connecting path(s) (default random)\n\ +-ch#i - #i is length of connecting path(s) (default n-1)\n\ +\n\ + Artificial source parameters:\n\ +-s - generate artificial source with default connecting arc lengths\n\ +-sl#i - #i is the upper bound on art. arc lengths (default 100000000)\n\ +-sm#i - #i is the lower bound on art. arc lengths (default sl)\n\ +\n\ +-hh file_name - save this help in the file 'file_name'\n\n", +argv[0], argv[0], argv[0] ); + +exit (0); +} + + + diff --git a/isisd/topology/spgrid.c b/isisd/topology/spgrid.c new file mode 100644 index 0000000..e1c87ab --- /dev/null +++ b/isisd/topology/spgrid.c @@ -0,0 +1,738 @@ +#include + +#include +#include +#include + +#include "random.c" + +#include "thread.h" +#include "vty.h" +#include "log.h" +#include "linklist.h" + +#include "spgrid.h" + + +#define DASH '-' +#define VERY_FAR 100000000 + +#define DOUBLE_CYCLE 0 +#define CYCLE 1 +#define PATH 2 + +#define NO 0 +#define YES 1 + +#define NODE( x, y ) (x*Y + y + 1) + +/* + * Prototypes. + */ +void free_arc(void *); +void help(struct vty *); +void print_arc(struct vty *, struct list *, long, long, long); +void hhelp(struct vty *); +void usage(struct vty *); + +const char *graph_type[] = { + "double cycle", + "cycle", + "path" +}; + +struct arc *arc; + +char args[30]; + +long X, /* horizontal size of grid */ + Y; /* vertical size of grid */ + +long x, + y, + yy1, yy2, yyp, + dl, dx, xn, yyn, count, + *mess; + +double n; +long n0, + source, + i, + i0, + j, + dij; + +double m; +long m0, + mc, + k; + +long *p, + p_t, + l, + lx; + +long seed, + seed1, + seed2; + +int ext=0; + +/* initialized by default values */ + +/* variables for generating one layer */ + +/* variables for generating spanning graph */ +int c_f = 0, cw_f = 0, cm_f = 0, cl_f = 0; + +int cw = DOUBLE_CYCLE; /* type of spanning graph */ +long cm = 0, /* lower bound of the interval */ + cl = 100; /* upper bound of the interval */ + +/* variables for generating additional arcs */ +int a_f = 0, ax_f = 0, am_f = 0, al_f = 0; + +long ax = 0, /* number of additional arcs */ + am = 0, /* lower bound of the interval */ + al = 100; /* upper bound of the interval */ + +/* variables for inter-layer arcs */ +int i_f = 0, ip_f = 0, ix_f = 0, ih_f = 0, + im_f = 0, il_f = 0, in_f = 0, is_f = 0; + +int ip = NO; /* to mess or not to mess */ +long ix = 1, /* number of interlayered arcs in a NODE */ + ih = 1, /* step between two layeres */ + il = 10000, /* upper bound of the interval */ + im = 1000; /* lower bound of the interval */ +double in = 1, /* l *= in * |x1-x2| */ + is = 0; /* l *= is * |x1-x2|^2 */ + +/* variables for artifical source */ +int s_f = 0, sl_f = 0, sm_f = 0; +long sl = VERY_FAR, /* upper bound of artifical arc */ + sm, /* lower bound of artifical arc */ + s; + +/* variables for potentials */ +int p_f = 0, pl_f = 0, pm_f = 0, pn_f = 0, ps_f = 0; + +long pl, /* upper bound of the interval */ + pm; /* lower bound of the interval */ +double pn = 0, /* p += ln * (x+1) */ + ps = 0; /* p += ls * (x+1)^2 */ + +int np; /* number of parameter parsing now */ + + +void +free_arc (void *val) { + free(val); +} + +void +print_arc (struct vty *vty, struct list *topology, long i, long j, long length) +{ + struct arc *myarc; + + l = length; + if ( p_f ) l += ( p[i] - p[j] ); +// vty_out (vty,"a %8ld %8ld %12ld%s", i, j, l ,VTY_NEWLINE); + myarc = malloc (sizeof(struct arc)); + myarc->from_node = i; + myarc->to_node = j; + myarc->distance = l; + topology->del = free_arc; + listnode_add (topology, myarc); +} + +/* ---- help ---- */ +void +help (struct vty *vty) { +// if ( args[2] == 'h') hhelp (vty); + vty_out (vty,"grid network generator for shortest paths problem.%s",VTY_NEWLINE); + vty_out (vty,"Generates problems in extended DIMACS format.%s",VTY_NEWLINE); + vty_out (vty,"X Y seed [ -cl#i -cm#i -c{c|d|p} -ip -il#i -im#i -p -pl#i -pm#i... ]%s",VTY_NEWLINE); + vty_out (vty,"#i - integer number%s",VTY_NEWLINE); + vty_out (vty,"-cl#i - #i is the upper bound on layer arc lengths (default 100)%s",VTY_NEWLINE); + vty_out (vty,"-cm#i - #i is the lower bound on layer arc lengths (default 0)%s",VTY_NEWLINE); + vty_out (vty,"-c#t - #t is the type of connecting graph: { c | d | p }%s",VTY_NEWLINE); + vty_out (vty," c - cycle, d - double cycle, p - path (default d)%s",VTY_NEWLINE); + vty_out (vty,"-ip - shuffle inter-layer arcs (default NO)%s",VTY_NEWLINE); + vty_out (vty,"-il#i - #i is the upper bound on inter-layer arc lengths (default 10000)%s",VTY_NEWLINE); + vty_out (vty,"-im#i - #i is the lower bound on inter-layer arc lengths (default 1000)%s",VTY_NEWLINE); + vty_out (vty,"-p - generate potentials%s",VTY_NEWLINE); + vty_out (vty,"-pl#i - #i is the upper bound on potentials (default il)%s",VTY_NEWLINE); + vty_out (vty,"-pm#i - #i is the lower bound on potentials (default im)%s",VTY_NEWLINE); + vty_out (vty,"%s",VTY_NEWLINE); + vty_out (vty,"-hh - extended help%s",VTY_NEWLINE); +} + +/* --------- sophisticated help ------------ */ +void +hhelp (struct vty *vty) { +/* +zlog_info ( +"\n'%s' - grid network generator for shortest paths problem.\n\ +Generates problems in extended DIMACS format.\n\ +\n\ + %s X Y seed [ -cl#i -cm#i -c{c|d|p}\n\ + -ax#i -al#i -am#i\n\ + -ip -il#i -im#i -in#i -is#i -ix#i -ih#i\n\ + -p -pl#i -pm#i -pn#f -ps#f\n\ + -s -sl#i -sm#i\n\ + ]\n\ + %s -hh file_name\n\ +\n\ + #i - integer number #f - real number\n\ +\n\ + Parameters of connecting arcs within one layer:\n\ +-cl#i - #i is the upper bound on arc lengths (default 100)\n\ +-cm#i - #i is the lower bound on arc lengths (default 0)\n\ +-c#t - #t is the type of connecting graph: { c | d | p }\n\ + c - cycle, d - double cycle, p - path (default d)\n\ +\n\ + Parameters of additional arcs within one layer:\n\ +-ax#i - #i is the number of additional arcs (default 0)\n\ +-al#i - #i is the upper bound on arc lengths (default 100)\n\ +-am#i - #i is the lower bound on arc lengths (default 0)\n\ +\n\ + Interlayerd arc parameters:\n\ +-ip - shuffle inter-layer arcs (default NO)\n\ +-il#i - #i is the upper bound on arc lengths (default 10000)\n\ +-im#i - #i is the lower bound on arc lengths (default 1000)\n\ +-in#f - multiply l(i, j) by #f * x(j)-x(i) (default 1)\n\ + if #f=0 - don't multiply\n\ +-is#f - multiply l(i, j) by #f * (x(j)-x(i))^2 (default NO)\n\ +-ix#i - #i - is the number of arcs from a node (default 1)\n\ +-ih#i - #i - is the step between connected layers (default 1)\n\ +\n\ + Potential parameters:\n\ +-p - generate potentials \n\ +-pl#i - #i is the upper bound on potentials (default ll)\n\ +-pm#i - #i is the lower bound on potentials (default lm)\n\ +-pn#f - multiply p(i) by #f * x(i) (default NO)\n\ +-ps#f - multiply p(i) by #f * x(i)^2 (default NO)\n\ +\n"); +zlog_info ( +" Artificial source parameters:\n\ +-s - generate artificial source with default connecting arc lengths\n\ +-sl#i - #i is the upper bound on art. arc lengths (default 100000000)\n\ +-sm#i - #i is the lower bound on art. arc lengths (default sl)\n\" +);*/ +} + +/* ----- wrong usage ----- */ +void +usage (struct vty *vty) { + vty_out (vty,"usage: X Y seed [-ll#i -lm#i -cl#i -p -pl#i -pm#i ...]%s",VTY_NEWLINE); + vty_out (vty,"help: -h or -hh%s",VTY_NEWLINE); + + if ( np > 0 ) + zlog_err ("error in parameter # %d\n\n", np ); +} + + +/* parsing parameters */ +/* checks the validity of incoming parameters */ +int +spgrid_check_params ( struct vty *vty, int argc, const char **argv) +{ +/* initialized by default values */ + ext=0; + +/* variables for generating one layer */ + +/* variables for generating spanning graph */ + c_f = 0; + cw_f = 0; + cm_f = 0; + cl_f = 0; + + cw = PATH; /* type of spanning graph */ + cm = 0; /* lower bound of the interval */ + cl = 63; /* upper bound of the interval */ + +/* variables for generating additional arcs */ + a_f = 0; + ax_f = 0; + am_f = 0; + al_f = 0; + + ax = 0; /* number of additional arcs */ + am = 0; /* lower bound of the interval */ + al = 63; /* upper bound of the interval */ + +/* variables for inter-layer arcs */ + i_f = 0; + ip_f = 0; + ix_f = 0; + ih_f = 0; + im_f = 0; + il_f = 0; + in_f = 0; + is_f = 0; + + ip = NO; /* to mess or not to mess */ + ix = 1; /* number of interlayered arcs in a NODE */ + ih = 1; /* step between two layeres */ + il = 63; //was 10000; /* upper bound of the interval */ + im = 0; //was 1000; /* lower bound of the interval */ + in = 1; /* l *= in * |x1-x2| */ + is = 0; /* l *= is * |x1-x2|^2 */ + +/* variables for artifical source */ + s_f = 0; + sl_f = 0; + sm_f = 0; + sl = VERY_FAR; /* upper bound of artifical arc */ + +/* variables for potentials */ + p_f = 0; + pl_f = 0; + pm_f = 0; + pn_f = 0; + ps_f = 0; + + pn = 0; /* p += ln * (x+1) */ + ps = 0; /* p += ls * (x+1)^2 */ + + + if ( argc < 1 ) { + usage (vty); + return 1; + } + + np = 0; + + strcpy ( args, argv[0] ); + + if ((args[0] == DASH) && (args[1] == 'h')) + help (vty); + + if ( argc < 3 ) { + usage (vty); + return 1; + } + + /* first parameter - horizontal size */ + np = 1; + if ( ( X = atoi ( argv[0] ) ) < 1 ) { + usage (vty); + return 1; + } + + /* second parameter - vertical size */ + np = 2; + if ( ( Y = atoi ( argv[1] ) ) < 1 ) { + usage (vty); + return 1; + } + + /* third parameter - seed */ + np=3; + if ( ( seed = atoi ( argv[2] ) ) <= 0 ) { + usage (vty); + return 1; + } + + /* other parameters */ + for ( np = 3; np < argc; np ++ ) { + strcpy ( args, argv[np] ); + if ( args[0] != DASH ) { + usage (vty); + return 1; + } + + switch ( args[1] ) { + case 'c' : /* spanning graph in one layer */ + c_f = 1; + switch ( args[2] ) { + case 'l': /* upper bound of the interval */ + cl_f = 1; + cl = atol ( &args[3] ); + break; + case 'm': /* lower bound */ + cm_f = 1; + cm = atol ( &args[3] ); + break; + case 'c': /* type - cycle */ + cw_f = 1; + cw = CYCLE; + break; + case 'd': /* type - double cycle */ + cw_f = 1; + cw = DOUBLE_CYCLE; + break; + case 'p': /* type - path */ + cw_f = 1; + cw = PATH; + break; + + default: /* unknown switch value */ + usage (vty); + return 1; + } + break; + + case 'a' : /* additional arcs in one layer */ + a_f = 1; + switch ( args[2] ) + { + case 'l': /* upper bound of the interval */ + al_f = 1; + al = atol ( &args[3] ); + break; + case 'm': /* lower bound */ + am_f = 1; + am = atol ( &args[3] ); + break; + case 'x': /* number of additional arcs */ + ax_f = 1; + ax = atol ( &args[3] ); + if ( ax < 0 ) + { + usage (vty); + return 1; + } + break; + + default: /* unknown switch value */ + { + usage (vty); + return 1; + } + } + break; + + + case 'i' : /* interlayered arcs */ + i_f = 1; + + switch ( args[2] ) + { + case 'l': /* upper bound */ + il_f = 1; + il = atol ( &args[3] ); + break; + case 'm': /* lower bound */ + im_f = 1; + im = atol ( &args[3] ); + break; + case 'n': /* additional length: l *= in*|i1-i2| */ + in_f = 1; + in = atof ( &args[3] ); + break; + case 's': /* additional length: l *= is*|i1-i2|^2 */ + is_f = 1; + is = atof ( &args[3] ); + break; + case 'p': /* mess interlayered arcs */ + ip_f = 1; + ip = YES; + break; + case 'x': /* number of interlayered arcs */ + ix_f = 1; + ix = atof ( &args[3] ); + if ( ix < 1 ) { + usage (vty); + return 1; + } + break; + case 'h': /* step between two layeres */ + ih_f = 1; + ih = atof ( &args[3] ); + if ( ih < 1 ) { + usage (vty); + return 1; + } + break; + default: /* unknown switch value */ + usage (vty); + return 1; + } + break; + + case 's' : /* additional source */ + s_f = 1; + if ( strlen ( args ) > 2 ) + { + switch ( args[2] ) + { + case 'l': /* upper bound of art. arc */ + sl_f = 1; + sl = atol ( &args[3] ); + break; + case 'm': /* lower bound of art. arc */ + sm_f = 1; + sm = atol ( &args[3] ); + break; + default: /* unknown switch value */ + usage (vty); + return 1; + } + } + break; + + case 'p' : /* potentials */ + p_f = 1; + if ( strlen ( args ) > 2 ) + { + switch ( args[2] ) + { + case 'l': /* upper bound */ + pl_f = 1; + pl = atol ( &args[3] ); + break; + case 'm': /* lower bound */ + pm_f = 1; + pm = atol ( &args[3] ); + break; + case 'n': /* additional: p *= pn*(x+1) */ + pn_f = 1; + pn = atof ( &args[3] ); + break; + case 's': /* additional: p = ps* (x+1)^2 */ + ps_f = 1; + ps = atof ( &args[3] ); + break; + default: /* unknown switch value */ + usage (vty); + return 1; + } + } + break; + + default: /* unknoun case */ + usage (vty); + return 1; + } + } + + + return 0; +} + + +/* generator of layered networks for the shortest paths problem; + extended DIMACS format for output */ +int +gen_spgrid_topology (struct vty *vty, struct list *topology) +{ + /* ----- ajusting parameters ----- */ + + /* spanning */ + if ( cl < cm ) { lx = cl; cl = cm; cm = lx; } + + /* additional arcs */ + if ( al < am ) { lx = al; al = am; am = lx; } + + /* interlayered arcs */ + if ( il < im ) { lx = il; il = im; im = lx; } + + /* potential parameters */ + if ( p_f ) + { + if ( ! pl_f ) pl = il; + if ( ! pm_f ) pm = im; + if ( pl < pm ) { lx = pl; pl = pm; pm = lx; } + } + + /* number of nodes and arcs */ + + n = (double)X *(double)Y + 1; + + m = (double)Y; /* arcs from source */ + + switch ( cw ) + { + case PATH: + mc = (double)Y - 1; + break; + case CYCLE: + mc = (double)Y; + break; + case DOUBLE_CYCLE: + mc = 2*(double)Y; + } + + m += (double)X * (double)mc; /* spanning arcs */ + m += (double)X * (double)ax; /* additional arcs */ + + /* interlayered arcs */ + for ( x = 0; x < X; x ++ ) + { + dl = ( ( X - x - 1 ) + ( ih - 1 ) ) / ih; + if ( dl > ix ) dl = ix; + m += (double)Y * (double)dl; + } + + /* artifical source parameters */ + if ( s_f ) { + m += n; n ++ ; + if ( ! sm_f ) sm = sl; + if ( sl < sm ) { lx = sl; sl = sm; sm = lx; } + } + + if ( n >= (double)LONG_MAX || m >= (double)LONG_MAX ) + { + zlog_err ("Too large problem. It can't be generated\n"); + exit (4); + } + else + { + n0 = (long)n; m0 = (long)m; + } + + if ( ip_f ) + mess = (long*) calloc ( Y, sizeof ( long ) ); + + /* printing title */ + zlog_info ("Generating topology for ISIS"); + + source = ( s_f ) ? n0-1 : n0; + + if ( p_f ) /* generating potentials */ { + p = (long*) calloc ( n0+1, sizeof (long) ); + seed1 = 2*seed + 1; + init_rand ( seed1); + pl = pl - pm + 1; + + 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 ); + if ( ps_f ) p_t *= (long) ( (1 + x) * ( (1 + x) * ps )); + + p[ NODE ( x, y ) ] = p_t; + } + } + p[n0] = 0; + if ( s_f ) p[n0-1] = 0; + } + + if ( s_f ) /* additional arcs from artifical source */ + { + seed2 = 3*seed + 1; + init_rand ( seed2 ); + sl = sl - sm + 1; + + for ( x = X - 1; x >= 0; x -- ) + for ( y = Y - 1; y >= 0; y -- ) + { + i = NODE ( x, y ); + s = sm + nrand ( sl ); + print_arc (vty, topology, n0, i, s ); + } + + print_arc (vty, topology, n0, n0-1, 0 ); + } + + + /* ----- generating arcs within layers ----- */ + + init_rand ( seed ); + cl = cl - cm + 1; + al = al - am + 1; + + for ( x = 0; x < X; x ++ ) + { + /* generating arcs within one layer */ + for ( y = 0; y < Y-1; y ++ ) + { + /* generating spanning graph */ + i = NODE ( x, y ); + j = NODE ( x, y+1 ); + l = cm + nrand ( cl ); + print_arc (vty, topology, i, j, l ); + + if ( cw == DOUBLE_CYCLE ) + { + l = cm + nrand ( cl ); + print_arc (vty, topology, j, i, l ); + } + } + + if ( cw <= CYCLE ) + { + i = NODE ( x, Y-1 ); + j = NODE ( x, 0 ); + l = cm + nrand ( cl ); + print_arc (vty, topology, i, j, l ); + + if ( cw == DOUBLE_CYCLE ) + { + l = cm + nrand ( cl ); + print_arc (vty, topology, j, i, l ); + } + } + + /* generating additional arcs */ + + for ( k = ax; k > 0; k -- ) + { + yy1 = nrand ( Y ); + do + 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 ); + } + } + + /* ----- generating interlayered arcs ------ */ + + il = il - im + 1; + + /* arcs from the source */ + + for ( y = 0; y < Y; y ++ ) + { + l = im + nrand ( il ); + i = NODE ( 0, y ); + print_arc (vty, topology, source, i, l ); + } + + for ( x = 0; x < X-1; x ++ ) + { + /* generating arcs from one layer */ + for ( count = 0, xn = x + 1; + count < ix && xn < X; + count ++, xn += ih ) + { + if ( ip_f ) + for ( y = 0; y < Y; y ++ ) + mess[y] = y; + + for ( y = 0; y < Y; y ++ ) + { + i = NODE ( x, y ); + dx = xn - x; + if ( ip_f ) + { + yyp = nrand(Y-y); + yyn = mess[ yyp ]; + mess[ yyp ] = mess[ Y - y - 1 ]; + } + else + yyn = y; + j = NODE ( xn, yyn ); + l = im + nrand ( il ); + if ( in != 0 ) + l *= (long) ( in * dx ); + if ( is_f ) + l *= (long) ( ( is * dx ) * dx ); + print_arc (vty, topology, i, j, l ); + } + } + } + /* all is done */ + return ext; + +return 0; +} + + + diff --git a/isisd/topology/spgrid.h b/isisd/topology/spgrid.h new file mode 100644 index 0000000..1c1ceea --- /dev/null +++ b/isisd/topology/spgrid.h @@ -0,0 +1,45 @@ +/* + * IS-IS Rout(e)ing protocol - topology/spgrid.h + Routines for manipulation of SSN and SRM flags + * Copyright (C) 2001 Sampo Saaristo, Ofer Wald + * + * 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. + */ + +/* + * Based on: + * SPLIB Copyright C 1994 by Cherkassky, Goldberg, and Radzik + * + */ +#ifndef _ZEBRA_ISIS_TOPOLOGY_SPGRID_H +#define _ZEBRA_ISIS_TOPOLOGY_SPGRID_H + +struct arc { + long from_node; + long to_node; + long distance; +}; + +int gen_spgrid_topology (struct vty *vty, struct list *topology); +int spgrid_check_params (struct vty *vty, int argc, const char **argv); + + +#endif /* _ZEBRA_ISIS_TOPOLOGY_SPGRID_H */ + + + + + + diff --git a/isisd/topology/sprand.c b/isisd/topology/sprand.c new file mode 100644 index 0000000..1c1eb19 --- /dev/null +++ b/isisd/topology/sprand.c @@ -0,0 +1,501 @@ +#include + +#include +#include +#include +#include + +#include "random.c" + +#define DASH '-' +#define VERY_FAR 100000000 + +/* generator of random networks for the shortest paths problem; + extended DIMACS format for output */ + +main ( argc, argv ) + +int argc; +char* argv[]; + +{ + +char args[30]; + +long n, + n0, + source, + i, + i0, + j, + dij; + +long m, + m0, + mc, + k; + +long *p, + p_t, + l, + lx; + +long seed, + seed1, + seed2; + +int ext=0; + +FILE *fout; + +/* variables for lengths generating */ +/* initialized by default values */ +int l_f = 0, ll_f = 0, lm_f = 0, ln_f = 0, ls_f = 0; +long ll = 10000, /* length of the interval */ + lm = 0; /* minimal bound of the interval */ +double ln = 0, /* l += ln * |i-j| */ + ls = 0; /* l += ls * |i-j|^2 */ + +/* variables for connecting cycle(s) */ +int c_f = 0, cl_f = 0, ch_f = 0, c_random = 1; +long cl = 1; /* length of cycle arc */ +long ch; /* number of arcs in the cycle + n - by default */ + +/* variables for artifical source */ +int s_f = 0, sl_f = 0, sm_f = 0; +long sl = VERY_FAR, /* upper bound of artifical arc */ + sm, /* lower bound of artifical arc */ + s; + +/* variables for potentials */ +int p_f = 0, pl_f = 0, pm_f = 0, pn_f = 0, ps_f = 0, + pa_f = 0, pap_f = 0, pac_f = 0; +long pl, /* length of the interval */ + pm; /* minimal bound of the interval */ +double pn = 0, /* l += ln * |i-j| */ + ps = 0, /* l += ls * |i-j|^2 */ + pap = 0, /* part of nodes with alternative dustribution */ + pac = -1; /* multiplier for alternative distribution */ + +int np; /* number of parameter parsing now */ + +#define PRINT_ARC( i, j, length )\ +{\ +l = length;\ +if ( p_f ) l += ( p[i] - p[j] );\ +printf ("a %8ld %8ld %12ld\n", i, j, l );\ +} + + /* parsing parameters */ + +if ( argc < 2 ) goto usage; + +np = 0; + +strcpy ( args, argv[1] ); + + if ( ( args[0] == DASH ) && ( args[1] == 'h') + ) + goto help; + +if ( argc < 4 ) goto usage; + +/* first parameter - number of nodes */ +np = 1; +if ( ( n = atoi ( argv[1] ) ) < 2 ) goto usage; + +/* second parameter - number of arcs */ +np = 2; +if ( ( m = atoi ( argv[2] ) ) < n ) goto usage; + +/* third parameter - seed */ +np=3; +if ( ( seed = atoi ( argv[3] ) ) <= 0 ) goto usage; + +/* other parameters */ + +for ( np = 4; np < argc; np ++ ) + { + strcpy ( args, argv[np] ); + if ( args[0] != DASH ) goto usage; + + switch ( args[1] ) + { + + case 'l' : /* an interval for arc length */ + l_f = 1; + switch ( args[2] ) + { + case 'l': /* length of the interval */ + ll_f = 1; + ll = (long) atof ( &args[3] ); + break; + case 'm': /* minimal bound */ + lm_f = 1; + lm = (long ) atof ( &args[3] ); + break; + case 'n': /* additional length: l*|i-j| */ + ln_f = 1; + ln = atof ( &args[3] ); + break; + case 's': /* additional length: l*|i-j|^2 */ + ls_f = 1; + ls = atof ( &args[3] ); + break; + default: /* unknown switch value */ + goto usage; + } + break; + + case 'c' : /* connecting cycle(s) */ + c_f = 1; + switch ( args[2] ) + { + case 'l': + c_random = 0; + cl_f = 1; + cl = (long) atof ( &args[3] ); + if ( cl < 0 ) goto usage; + break; + case 'h': + ch_f = 1; + ch = (long) atof ( &args[3] ); + if ( ch < 2 || ch > n ) goto usage; + break; + default: /* unknown switch value */ + goto usage; + } + break; + + case 's' : /* additional source */ + s_f = 1; + if ( strlen ( args ) > 2 ) + { + switch ( args[2] ) + { + case 'l': /* upper bound of art. arc */ + sl_f = 1; + sl = (long) atof ( &args[3] ); + break; + case 'm': /* lower bound of art. arc */ + sm_f = 1; + sm = (long) atof ( &args[3] ); + break; + default: /* unknown switch value */ + goto usage; + } + } + break; + + case 'p' : /* potentials */ + p_f = 1; + if ( strlen ( args ) > 2 ) + { + switch ( args[2] ) + { + case 'l': /* length of the interval */ + pl_f = 1; + pl = (long) atof ( &args[3] ); + break; + case 'm': /* minimal bound */ + pm_f = 1; + pm = (long ) atof ( &args[3] ); + break; + case 'n': /* additional length: l*|i-j| */ + pn_f = 1; + pn = atof ( &args[3] ); + break; + case 's': /* additional length: l*|i-j|^2 */ + ps_f = 1; + ps = atof ( &args[3] ); + break; + case 'a': /* bipolar distribution */ + pa_f = 1; + switch ( args[3] ) + { + case 'p': /* % of alternative potentials */ + pap_f = 1; + pap = atof ( &args[4] ); + if ( pap < 0 ) pap = 0; + if ( pap > 100 ) pap = 100; + pap /= 100; + break; + case 'c': /* multiplier */ + pac_f = 1; + pac = atof ( &args[4] ); + break; + default: /* unknown switch value */ + goto usage; + } + break; + default: /* unknown switch value */ + goto usage; + } + } + break; + + default : /* unknoun case */ + goto usage; + } + } + + +/* ----- ajusting parameters ----- */ + +n0 = n; m0 = m; + +/* length parameters */ +if ( ll < lm ) { lx = ll; ll = lm; lm = lx; } + +/* potential parameters */ +if ( p_f ) + { + if ( ! pl_f ) pl = ll; + if ( ! pm_f ) pm = lm; + if ( pl < pm ) { lx = pl; pl = pm; pm = lx; } + } + +/* path(s) parameters */ +if ( ! ch_f ) ch = n; + +mc = n + (n-2) / (ch-1); +if ( mc > m ) + { fprintf ( stderr, + "Error: not enough arcs for generating connecting cycle(s)\n" ); + exit (4); + } + + /* artifical source parameters */ +if ( s_f ) + { m0 += n; n0 ++ ; + if ( ! sm_f ) sm = sl; + if ( sl < sm ) { lx = sl; sl = sm; sm = lx; } + } + +/* printing title */ +printf ("c random network for shortest paths problem\n"); +printf ("c extended DIMACS format\nc\n" ); + +/* name of the problem */ +printf ("t rd_%ld_%ld_%ld_", n, m, seed ); +if ( l_f ) + printf ("%c", 'l'); +if ( c_f ) + printf ("%c", 'c'); +if ( s_f ) + printf ("%c", 's'); +if ( p_f ) + printf ("%c", 'p'); +printf ("\nc\n"); + +/* printing additional information */ +if ( l_f ) + printf ("c length -> min: %ld max: %ld k1: %.2f k2: %.2f\n", + lm, ll, ln, ls ); +if ( c_f ) + { + if ( c_random ) + printf ("c cycle -> number of arcs: %ld arc length: random\n", ch); + else + printf ("c cycle -> number of arcs: %ld arc length: %ld\n", + ch, cl ); + } +if ( s_f ) + printf ("c length of arcs from artifical source -> min: %ld max: %ld\n", + sm, sl ); +if ( p_f ) + { + printf ("c potentials -> min: %ld max: %ld k1: %.2f k2: %.2f\n", + pm, pl, pn, ps ); + if ( pa_f ) + printf ("c potentials -> part of alternative distribution: %.2f k: %.2f\n", + pap, pac ); + } +printf ("c\n" ); + + +printf ("p sp %8ld %8ld\nc\n", n0, m0 ); + +source = ( s_f ) ? n0 : 1; +printf ("n %8ld\nc\n", source ); + +if ( p_f ) /* generating potentials */ + { + p = (long*) calloc ( n+2, sizeof (long) ); + seed1 = 2*seed + 1; + init_rand ( seed1); + pl = pl - pm + 1; + + for ( i = 0; i <= n; i ++ ) + { + p_t = pm + nrand ( pl ); + if ( pn_f ) p_t += (long) ( i * pn ); + if ( ps_f ) p_t += (long) ( i * ( i * ps )); + if ( pap_f ) + if ( rand01() < pap ) + p_t = (long) ( p_t * pac ); + p[i] = p_t; + } + p[n+1] = 0; + } + + +if ( s_f ) /* additional arcs from artifical source */ + { + seed2 = 3*seed + 1; + init_rand ( seed2 ); + sl = sl - sm + 1; + + for ( i = n; i > 1; i -- ) + { + s = sm + nrand ( sl ); + PRINT_ARC ( n0, i, s ) + } + + PRINT_ARC ( n0, 1, 0 ) + } + +/* initialize random number generator */ +init_rand ( seed ); +ll = ll - lm + 1; + +/* generating connecting cycle(s) */ +if (c_random) + cl = lm + nrand ( ll ); +PRINT_ARC ( 1, 2, cl ) +if (c_random) + cl = lm + nrand ( ll ); +PRINT_ARC ( n, 1, cl ) + +for ( i = 2; i < n; i ++ ) + { + if (c_random) + cl = lm + nrand ( ll ); + + if ( ( (i-1) % (ch-1) ) != 0 ) + PRINT_ARC ( i, i+1, cl ) + else + { PRINT_ARC ( i, 1, cl ) + if (c_random) + cl = lm + nrand ( ll ); + PRINT_ARC ( 1, i+1, cl ) + } + } + +/* generating random arcs */ + +for ( k = 1; k <= m - mc; k ++ ) + { + i = 1 + nrand ( n ); + + do + j = 1 + nrand ( n ); + while ( j == i ); + + dij = ( i > j ) ? ( i - j ) : ( j - i ); + l = lm + nrand ( ll ); + if ( ln_f ) l += (long) ( dij * ln ); + if ( ls_f ) l += (long) ( dij * ( dij * ls ) ); + PRINT_ARC ( i, j, l ); + } + +/* all is done */ +exit (ext); + +/* ----- wrong usage ----- */ + + usage: +fprintf ( stderr, +"\nusage: %s n m seed [ -ll#i -lm#i -cl#i -p -pl#i -pm#i ... ]\n\ +help: %s -h\n\n", argv[0], argv[0] ); + +if ( np > 0 ) + fprintf ( stderr, "error in parameter # %d\n\n", np ); +exit (4); + +/* ---- help ---- */ + + help: + +if ( args[2] == 'h') goto hhelp; + +fprintf ( stderr, +"\n'%s' - random network generator for shortest paths problem.\n\ +Generates problems in extended DIMACS format.\n\ +\n\ + %s n m seed [ -ll#i -lm#i -cl#i -p -pl#i -pm#i ... ]\n\ + %s -hh\n\ +\n\ + #i - integer number #f - real number\n\ +\n\ +-ll#i - #i is the upper bound on arc lengths (default 10000)\n\ +-lm#i - #i is the lower bound on arc lengths (default 0)\n\ +-cl#i - #i is length of arcs in connecting cycle(s) (default random)\n\ +-p - generate potentials \n\ +-pl#i - #i is the upper bound on potentials (default ll)\n\ +-pm#i - #i is the lower bound on potentials (default lm)\n\ +\n\ +-hh - extended help \n\n", +argv[0], argv[0], argv[0] ); + +exit (0); + +/* --------- sophisticated help ------------ */ + hhelp: + +if ( argc < 3 ) + fout = stderr; +else + fout = fopen ( argv[2], "w" ); + +if ( fout == NULL ) +{ fprintf ( stderr, "\nCan't open file '%s' for writing help\n\n", argv[2] ); + exit ( 2 ); +} + +fprintf (fout, +"\n'%s' - random network generator for shortest paths problem.\n\ +Generates problems in extended DIMACS format.\n\ +\n\ + %s n m seed [ -ll#i -lm#i -ln#f -ls#f\n\ + -p -pl#i -pm#i -pn#f -ps#f -pap#i -pac#f\n\ + -cl#i -ch#i\n\ + -s -sl#i -sm#i\n\ + ]\n\ + %s -hh file_name\n\ +\n\ + #i - integer number #f - real number\n\ +\n\ + Arc length parameters:\n\ +-ll#i - #i is the upper bound on arc lengths (default 10000)\n\ +-lm#i - #i is the lower bound on arc lengths (default 0)\n\ +-ln#f - multipliy l(i, j) by #f * |i-j| (default 0)\n\ +-ls#f - multipliy l(i, j) by #f * |i-j|^2 (default 0)\n\ +\n\ + Potential parameters:\n\ +-p - generate potentials \n\ +-pl#i - #i is the upper bound on potentials (default ll)\n\ +-pm#i - #i is the lower bound on potentials (default lm)\n\ +-pn#f - multiply p(i) by #f * i (default 0)\n\ +-ps#f - multiply p(i) by #f * i^2 (default 0)\n\ +-pap#i - percentage of alternative potential nodes (default 0)\n\ +-pac#f - if i is alternative, multiply p(i) by #f (default -1)\n\ +\n\ + Connecting cycle(s) parameters:\n\ +-cl#i - #i is length of arcs in connecting cycle(s) (default random)\n\ +-ch#i - #i is length of connecting cycles (default n)\n\ +\n\ + Artificial source parameters:\n\ +-s - generate artificial source with default connecting arc lengths\n\ +-sl#i - #i is the upper bound on art. arc lengths (default 100000000)\n\ +-sm#i - #i is the lower bound on art. arc lengths (default sl)\n\ +\n\ +-hh file_name - save this help in the file 'file_name'\n\n", +argv[0], argv[0], argv[0] ); + +exit (0); +} + + + diff --git a/lib/.gitignore b/lib/.gitignore new file mode 100644 index 0000000..02aa432 --- /dev/null +++ b/lib/.gitignore @@ -0,0 +1,18 @@ +Makefile +Makefile.in +*.o +*.lo +*.la +version.c +version.h +gitversion.h +gitversion.h.tmp +.deps +.nfs* +.libs +.arch-inventory +.arch-ids +*~ +*.loT +memtypes.h +route_types.h diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 0000000..cb9e1fb --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,71 @@ +## Process this file with automake to produce Makefile.in. + +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 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 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 gitversion.h + +libzebra_la_DEPENDENCIES = @LIB_REGEX@ + +libzebra_la_LIBADD = @LIB_REGEX@ @LIBCAP@ + +pkginclude_HEADERS = \ + buffer.h checksum.h command.h filter.h getopt.h hash.h \ + if.h linklist.h log.h \ + memory.h network.h prefix.h routemap.h distribute.h sockunion.h \ + 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 libospf.h vrf.h fifo.h event_counter.h \ + nexthop.h + +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 0000000..5d7d057 --- /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 new file mode 100644 index 0000000..ee93101 --- /dev/null +++ b/lib/buffer.c @@ -0,0 +1,497 @@ +/* + * Buffering of output and input. + * Copyright (C) 1998 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 "memory.h" +#include "buffer.h" +#include "log.h" +#include "network.h" +#include + + + +/* Buffer master. */ +struct buffer +{ + /* Data list. */ + struct buffer_data *head; + struct buffer_data *tail; + + /* Size of each buffer_data chunk. */ + size_t size; +}; + +/* Data container. */ +struct buffer_data +{ + struct buffer_data *next; + + /* Location to add new data. */ + size_t cp; + + /* Pointer to data not yet flushed. */ + size_t sp; + + /* Actual data stream (variable length). */ + unsigned char data[]; /* real dimension is buffer->size */ +}; + +/* It should always be true that: 0 <= sp <= cp <= size */ + +/* Default buffer size (used if none specified). It is rounded up to the + next page boundery. */ +#define BUFFER_SIZE_DEFAULT 4096 + + +#define BUFFER_DATA_FREE(D) XFREE(MTYPE_BUFFER_DATA, (D)) + +/* Make new buffer. */ +struct buffer * +buffer_new (size_t size) +{ + struct buffer *b; + + b = XCALLOC (MTYPE_BUFFER, sizeof (struct buffer)); + + if (size) + b->size = size; + else + { + static size_t default_size; + if (!default_size) + { + long pgsz = sysconf(_SC_PAGESIZE); + default_size = ((((BUFFER_SIZE_DEFAULT-1)/pgsz)+1)*pgsz); + } + b->size = default_size; + } + + return b; +} + +/* Free buffer. */ +void +buffer_free (struct buffer *b) +{ + buffer_reset(b); + XFREE (MTYPE_BUFFER, b); +} + +/* Make string clone. */ +char * +buffer_getstr (struct buffer *b) +{ + size_t totlen = 0; + struct buffer_data *data; + char *s; + char *p; + + for (data = b->head; data; data = data->next) + totlen += data->cp - data->sp; + if (!(s = XMALLOC(MTYPE_TMP, totlen+1))) + return NULL; + p = s; + for (data = b->head; data; data = data->next) + { + memcpy(p, data->data + data->sp, data->cp - data->sp); + p += data->cp - data->sp; + } + *p = '\0'; + return s; +} + +/* Return 1 if buffer is empty. */ +int +buffer_empty (struct buffer *b) +{ + return (b->head == NULL); +} + +/* Clear and free all allocated data. */ +void +buffer_reset (struct buffer *b) +{ + struct buffer_data *data; + struct buffer_data *next; + + for (data = b->head; data; data = next) + { + next = data->next; + BUFFER_DATA_FREE(data); + } + b->head = b->tail = NULL; +} + +/* Add buffer_data to the end of buffer. */ +static struct buffer_data * +buffer_add (struct buffer *b) +{ + struct buffer_data *d; + + d = XMALLOC(MTYPE_BUFFER_DATA, offsetof(struct buffer_data, data) + b->size); + d->cp = d->sp = 0; + d->next = NULL; + + if (b->tail) + b->tail->next = d; + else + b->head = d; + b->tail = d; + + return d; +} + +/* Write data to buffer. */ +void +buffer_put(struct buffer *b, const void *p, size_t size) +{ + struct buffer_data *data = b->tail; + const char *ptr = p; + + /* We use even last one byte of data buffer. */ + while (size) + { + size_t chunk; + + /* If there is no data buffer add it. */ + if (data == NULL || data->cp == b->size) + data = buffer_add (b); + + chunk = ((size <= (b->size - data->cp)) ? size : (b->size - data->cp)); + memcpy ((data->data + data->cp), ptr, chunk); + size -= chunk; + ptr += chunk; + data->cp += chunk; + } +} + +/* Insert character into the buffer. */ +void +buffer_putc (struct buffer *b, u_char c) +{ + buffer_put(b, &c, 1); +} + +/* Put string to the buffer. */ +void +buffer_putstr (struct buffer *b, const char *c) +{ + buffer_put(b, c, strlen(c)); +} + +/* Keep flushing data to the fd until the buffer is empty or an error is + encountered or the operation would block. */ +buffer_status_t +buffer_flush_all (struct buffer *b, int fd) +{ + buffer_status_t ret; + struct buffer_data *head; + size_t head_sp; + + if (!b->head) + return BUFFER_EMPTY; + head_sp = (head = b->head)->sp; + /* Flush all data. */ + while ((ret = buffer_flush_available(b, fd)) == BUFFER_PENDING) + { + if ((b->head == head) && (head_sp == head->sp) && (errno != EINTR)) + /* No data was flushed, so kernel buffer must be full. */ + return ret; + head_sp = (head = b->head)->sp; + } + + return ret; +} + +/* Flush enough data to fill a terminal window of the given scene (used only + by vty telnet interface). */ +buffer_status_t +buffer_flush_window (struct buffer *b, int fd, int width, int height, + int erase_flag, int no_more_flag) +{ + int nbytes; + int iov_alloc; + int iov_index; + struct iovec *iov; + struct iovec small_iov[3]; + char more[] = " --More-- "; + char erase[] = { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}; + struct buffer_data *data; + int column; + + if (!b->head) + return BUFFER_EMPTY; + + if (height < 1) + { + zlog_warn("%s called with non-positive window height %d, forcing to 1", + __func__, height); + height = 1; + } + else if (height >= 2) + height--; + if (width < 1) + { + zlog_warn("%s called with non-positive window width %d, forcing to 1", + __func__, width); + width = 1; + } + + /* For erase and more data add two to b's buffer_data count.*/ + if (b->head->next == NULL) + { + iov_alloc = array_size(small_iov); + iov = small_iov; + } + else + { + iov_alloc = ((height*(width+2))/b->size)+10; + iov = XMALLOC(MTYPE_TMP, iov_alloc*sizeof(*iov)); + } + iov_index = 0; + + /* Previously print out is performed. */ + if (erase_flag) + { + iov[iov_index].iov_base = erase; + iov[iov_index].iov_len = sizeof erase; + iov_index++; + } + + /* Output data. */ + column = 1; /* Column position of next character displayed. */ + for (data = b->head; data && (height > 0); data = data->next) + { + size_t cp; + + cp = data->sp; + while ((cp < data->cp) && (height > 0)) + { + /* Calculate lines remaining and column position after displaying + this character. */ + if (data->data[cp] == '\r') + column = 1; + else if ((data->data[cp] == '\n') || (column == width)) + { + column = 1; + height--; + } + else + column++; + cp++; + } + iov[iov_index].iov_base = (char *)(data->data + data->sp); + iov[iov_index++].iov_len = cp-data->sp; + data->sp = cp; + + if (iov_index == iov_alloc) + /* This should not ordinarily happen. */ + { + iov_alloc *= 2; + if (iov != small_iov) + { + zlog_warn("%s: growing iov array to %d; " + "width %d, height %d, size %lu", + __func__, iov_alloc, width, height, (u_long)b->size); + iov = XREALLOC(MTYPE_TMP, iov, iov_alloc*sizeof(*iov)); + } + else + { + /* This should absolutely never occur. */ + zlog_err("%s: corruption detected: iov_small overflowed; " + "head %p, tail %p, head->next %p", + __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)); + } + } + } + + /* In case of `more' display need. */ + if (b->tail && (b->tail->sp < b->tail->cp) && !no_more_flag) + { + iov[iov_index].iov_base = more; + iov[iov_index].iov_len = sizeof more; + iov_index++; + } + + +#ifdef IOV_MAX + /* IOV_MAX are normally defined in , Posix.1g. + example: Solaris2.6 are defined IOV_MAX size at 16. */ + { + struct iovec *c_iov = iov; + nbytes = 0; /* Make sure it's initialized. */ + + while (iov_index > 0) + { + int iov_size; + + iov_size = ((iov_index > IOV_MAX) ? IOV_MAX : iov_index); + if ((nbytes = writev(fd, c_iov, iov_size)) < 0) + { + zlog_warn("%s: writev to fd %d failed: %s", + __func__, fd, safe_strerror(errno)); + break; + } + + /* move pointer io-vector */ + c_iov += iov_size; + iov_index -= iov_size; + } + } +#else /* IOV_MAX */ + if ((nbytes = writev (fd, iov, iov_index)) < 0) + zlog_warn("%s: writev to fd %d failed: %s", + __func__, fd, safe_strerror(errno)); +#endif /* IOV_MAX */ + + /* Free printed buffer data. */ + while (b->head && (b->head->sp == b->head->cp)) + { + struct buffer_data *del; + if (!(b->head = (del = b->head)->next)) + b->tail = NULL; + BUFFER_DATA_FREE(del); + } + + if (iov != small_iov) + XFREE (MTYPE_TMP, iov); + + return (nbytes < 0) ? BUFFER_ERROR : + (b->head ? BUFFER_PENDING : BUFFER_EMPTY); +} + +/* This function (unlike other buffer_flush* functions above) is designed +to work with non-blocking sockets. It does not attempt to write out +all of the queued data, just a "big" chunk. It returns 0 if it was +able to empty out the buffers completely, 1 if more flushing is +required later, or -1 on a fatal write error. */ +buffer_status_t +buffer_flush_available(struct buffer *b, int fd) +{ + +/* These are just reasonable values to make sure a significant amount of +data is written. There's no need to go crazy and try to write it all +in one shot. */ +#ifdef IOV_MAX +#define MAX_CHUNKS ((IOV_MAX >= 16) ? 16 : IOV_MAX) +#else +#define MAX_CHUNKS 16 +#endif +#define MAX_FLUSH 131072 + + struct buffer_data *d; + size_t written; + struct iovec iov[MAX_CHUNKS]; + size_t iovcnt = 0; + size_t nbyte = 0; + + for (d = b->head; d && (iovcnt < MAX_CHUNKS) && (nbyte < MAX_FLUSH); + d = d->next, iovcnt++) + { + iov[iovcnt].iov_base = d->data+d->sp; + nbyte += (iov[iovcnt].iov_len = d->cp-d->sp); + } + + if (!nbyte) + /* No data to flush: should we issue a warning message? */ + return BUFFER_EMPTY; + + /* only place where written should be sign compared */ + if ((ssize_t)(written = writev(fd,iov,iovcnt)) < 0) + { + if (ERRNO_IO_RETRY(errno)) + /* Calling code should try again later. */ + return BUFFER_PENDING; + zlog_warn("%s: write error on fd %d: %s", + __func__, fd, safe_strerror(errno)); + return BUFFER_ERROR; + } + + /* Free printed buffer data. */ + while (written > 0) + { + struct buffer_data *d; + if (!(d = b->head)) + { + zlog_err("%s: corruption detected: buffer queue empty, " + "but written is %lu", __func__, (u_long)written); + break; + } + if (written < d->cp-d->sp) + { + d->sp += written; + return BUFFER_PENDING; + } + + written -= (d->cp-d->sp); + if (!(b->head = d->next)) + b->tail = NULL; + BUFFER_DATA_FREE(d); + } + + return b->head ? BUFFER_PENDING : BUFFER_EMPTY; + +#undef MAX_CHUNKS +#undef MAX_FLUSH +} + +buffer_status_t +buffer_write(struct buffer *b, int fd, const void *p, size_t size) +{ + ssize_t nbytes; + +#if 0 + /* Should we attempt to drain any previously buffered data? This could help + reduce latency in pushing out the data if we are stuck in a long-running + thread that is preventing the main select loop from calling the flush + thread... */ + if (b->head && (buffer_flush_available(b, fd) == BUFFER_ERROR)) + return BUFFER_ERROR; +#endif + if (b->head) + /* Buffer is not empty, so do not attempt to write the new data. */ + nbytes = 0; + else if ((nbytes = write(fd, p, size)) < 0) + { + if (ERRNO_IO_RETRY(errno)) + nbytes = 0; + else + { + zlog_warn("%s: write error on fd %d: %s", + __func__, fd, safe_strerror(errno)); + return BUFFER_ERROR; + } + } + /* Add any remaining data to the buffer. */ + { + size_t written = nbytes; + if (written < size) + buffer_put(b, ((const char *)p)+written, size-written); + } + return b->head ? BUFFER_PENDING : BUFFER_EMPTY; +} diff --git a/lib/buffer.h b/lib/buffer.h new file mode 100644 index 0000000..6c3dc76 --- /dev/null +++ b/lib/buffer.h @@ -0,0 +1,102 @@ +/* + * Buffering to output and input. + * Copyright (C) 1998 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_BUFFER_H +#define _ZEBRA_BUFFER_H + + +/* Create a new buffer. Memory will be allocated in chunks of the given + size. If the argument is 0, the library will supply a reasonable + default size suitable for buffering socket I/O. */ +extern struct buffer *buffer_new (size_t); + +/* Free all data in the buffer. */ +extern void buffer_reset (struct buffer *); + +/* This function first calls buffer_reset to release all buffered data. + Then it frees the struct buffer itself. */ +extern void buffer_free (struct buffer *); + +/* Add the given data to the end of the buffer. */ +extern void buffer_put (struct buffer *, const void *, size_t); +/* Add a single character to the end of the buffer. */ +extern void buffer_putc (struct buffer *, u_char); +/* Add a NUL-terminated string to the end of the buffer. */ +extern void buffer_putstr (struct buffer *, const char *); + +/* Combine all accumulated (and unflushed) data inside the buffer into a + single NUL-terminated string allocated using XMALLOC(MTYPE_TMP). Note + that this function does not alter the state of the buffer, so the data + is still inside waiting to be flushed. */ +char *buffer_getstr (struct buffer *); + +/* Returns 1 if there is no pending data in the buffer. Otherwise returns 0. */ +int buffer_empty (struct buffer *); + +typedef enum + { + /* An I/O error occurred. The buffer should be destroyed and the + file descriptor should be closed. */ + BUFFER_ERROR = -1, + + /* The data was written successfully, and the buffer is now empty + (there is no pending data waiting to be flushed). */ + BUFFER_EMPTY = 0, + + /* There is pending data in the buffer waiting to be flushed. Please + try flushing the buffer when select indicates that the file descriptor + is writeable. */ + BUFFER_PENDING = 1 + } buffer_status_t; + +/* Try to write this data to the file descriptor. Any data that cannot + be written immediately is added to the buffer queue. */ +extern buffer_status_t buffer_write(struct buffer *, int fd, + const void *, size_t); + +/* This function attempts to flush some (but perhaps not all) of + the queued data to the given file descriptor. */ +extern buffer_status_t buffer_flush_available(struct buffer *, int fd); + +/* The following 2 functions (buffer_flush_all and buffer_flush_window) + are for use in lib/vty.c only. They should not be used elsewhere. */ + +/* Call buffer_flush_available repeatedly until either all data has been + flushed, or an I/O error has been encountered, or the operation would + block. */ +extern buffer_status_t buffer_flush_all (struct buffer *, int fd); + +/* Attempt to write enough data to the given fd to fill a window of the + given width and height (and remove the data written from the buffer). + + If !no_more, then a message saying " --More-- " is appended. + If erase is true, then first overwrite the previous " --More-- " message + with spaces. + + Any write error (including EAGAIN or EINTR) will cause this function + to return -1 (because the logic for handling the erase and more features + is too complicated to retry the write later). +*/ +extern buffer_status_t buffer_flush_window (struct buffer *, int fd, int width, + int height, int erase, int no_more); + +#endif /* _ZEBRA_BUFFER_H */ diff --git a/lib/checksum.c b/lib/checksum.c new file mode 100644 index 0000000..43940b7 --- /dev/null +++ b/lib/checksum.c @@ -0,0 +1,124 @@ +/* + * Checksum routine for Internet Protocol family headers (C Version). + * + * Refer to "Computing the Internet Checksum" by R. Braden, D. Borman and + * C. Partridge, Computer Communication Review, Vol. 19, No. 2, April 1989, + * pp. 86-101, for additional details on computing this checksum. + */ + +#include +#include "checksum.h" + +int /* return checksum in low-order 16 bits */ +in_cksum(void *parg, int nbytes) +{ + u_short *ptr = parg; + register long sum; /* assumes long == 32 bits */ + u_short oddbyte; + register u_short answer; /* assumes u_short == 16 bits */ + + /* + * Our algorithm is simple, using a 32-bit accumulator (sum), + * we add sequential 16-bit words to it, and at the end, fold back + * all the carry bits from the top 16 bits into the lower 16 bits. + */ + + sum = 0; + while (nbytes > 1) { + sum += *ptr++; + nbytes -= 2; + } + + /* mop up an odd byte, if necessary */ + if (nbytes == 1) { + oddbyte = 0; /* make sure top half is zero */ + *((u_char *) &oddbyte) = *(u_char *)ptr; /* one byte only */ + sum += oddbyte; + } + + /* + * Add back carry outs from top 16 bits to low 16 bits. + */ + + sum = (sum >> 16) + (sum & 0xffff); /* add high-16 to low-16 */ + sum += (sum >> 16); /* add carry */ + answer = ~sum; /* ones-complement, then truncate to 16 bits */ + return(answer); +} + +/* Fletcher Checksum -- Refer to RFC1008. */ +#define MODX 4102 /* 5802 should be fine */ + +/* 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) +{ + u_int8_t *p; + int x, y, c0, c1; + u_int16_t checksum; + u_int16_t *csum; + size_t partial_len, i, left = len; + + checksum = 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; + c1 = 0; + + while (left != 0) + { + partial_len = MIN(left, MODX); + + for (i = 0; i < partial_len; i++) + { + c0 = c0 + *(p++); + c1 += c0; + } + + c0 = c0 % 255; + c1 = c1 % 255; + + 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; + + if (x <= 0) + x += 255; + y = 510 - c0 - x; + if (y > 255) + y -= 255; + + 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 new file mode 100644 index 0000000..b310f74 --- /dev/null +++ b/lib/checksum.h @@ -0,0 +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 new file mode 100644 index 0000000..662f8a3 --- /dev/null +++ b/lib/command.c @@ -0,0 +1,4334 @@ +/* + 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. + +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 "memory.h" +#include "log.h" +#include +#include "thread.h" +#include "vector.h" +#include "vty.h" +#include "command.h" +#include "workqueue.h" + +/* Command vector which includes some level of command lists. Normally + each daemon maintains each own cmdvec. */ +vector cmdvec = NULL; + +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; + +/* Standard command node structures. */ +static struct cmd_node auth_node = +{ + AUTH_NODE, + "Password: ", +}; + +static struct cmd_node view_node = +{ + VIEW_NODE, + "%s> ", +}; + +static struct cmd_node restricted_node = +{ + RESTRICTED_NODE, + "%s$ ", +}; + +static struct cmd_node auth_enable_node = +{ + AUTH_ENABLE_NODE, + "Password: ", +}; + +static struct cmd_node enable_node = +{ + ENABLE_NODE, + "%s# ", +}; + +static struct cmd_node config_node = +{ + CONFIG_NODE, + "%s(config)# ", + 1 +}; + +/* Default motd string. */ +static const char *default_motd = +"\r\n\ +Hello, this is " QUAGGA_PROGNAME " (version " QUAGGA_VERSION ").\r\n\ +" QUAGGA_COPYRIGHT "\r\n\ +" GIT_INFO "\r\n"; + + +static const struct facility_map { + int facility; + const char *name; + size_t match; +} syslog_facilities[] = + { + { LOG_KERN, "kern", 1 }, + { LOG_USER, "user", 2 }, + { LOG_MAIL, "mail", 1 }, + { LOG_DAEMON, "daemon", 1 }, + { LOG_AUTH, "auth", 1 }, + { LOG_SYSLOG, "syslog", 1 }, + { LOG_LPR, "lpr", 2 }, + { LOG_NEWS, "news", 1 }, + { LOG_UUCP, "uucp", 2 }, + { LOG_CRON, "cron", 1 }, +#ifdef LOG_FTP + { LOG_FTP, "ftp", 1 }, +#endif + { LOG_LOCAL0, "local0", 6 }, + { LOG_LOCAL1, "local1", 6 }, + { LOG_LOCAL2, "local2", 6 }, + { LOG_LOCAL3, "local3", 6 }, + { LOG_LOCAL4, "local4", 6 }, + { LOG_LOCAL5, "local5", 6 }, + { LOG_LOCAL6, "local6", 6 }, + { LOG_LOCAL7, "local7", 6 }, + { 0, NULL, 0 }, + }; + +static const char * +facility_name(int facility) +{ + const struct facility_map *fm; + + for (fm = syslog_facilities; fm->name; fm++) + if (fm->facility == facility) + return fm->name; + return ""; +} + +static int +facility_match(const char *str) +{ + const struct facility_map *fm; + + for (fm = syslog_facilities; fm->name; fm++) + if (!strncmp(str,fm->name,fm->match)) + return fm->facility; + return -1; +} + +static int +level_match(const char *s) +{ + int level ; + + for ( level = 0 ; zlog_priority [level] != NULL ; level ++ ) + if (!strncmp (s, zlog_priority[level], 2)) + return level; + return ZLOG_DISABLED; +} + +/* This is called from main when a daemon is invoked with -v or --version. */ +void +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 * +argv_concat (const char **argv, int argc, int shift) +{ + int i; + size_t len; + char *str; + char *p; + + len = 0; + for (i = shift; i < argc; i++) + len += strlen(argv[i])+1; + if (!len) + return NULL; + p = str = XMALLOC(MTYPE_TMP, len); + for (i = shift; i < argc; i++) + { + size_t arglen; + memcpy(p, argv[i], (arglen = strlen(argv[i]))); + p += arglen; + *p++ = ' '; + } + *(p-1) = '\0'; + return str; +} + +static unsigned int +cmd_hash_key (void *p) +{ + return (uintptr_t) p; +} + +static int +cmd_hash_cmp (const void *a, const void *b) +{ + return a == b; +} + +/* 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); + node->cmd_hash = hash_create (cmd_hash_key, cmd_hash_cmp); +} + +/* Breaking up string into each command piece. I assume given + character is separated by a space character. Return value is a + vector which includes char ** data element. */ +vector +cmd_make_strvec (const char *string) +{ + const char *cp, *start; + char *token; + int strlen; + vector strvec; + + if (string == NULL) + return NULL; + + cp = string; + + /* Skip white spaces. */ + while (isspace ((int) *cp) && *cp != '\0') + cp++; + + /* Return if there is only white spaces */ + if (*cp == '\0') + return NULL; + + if (*cp == '!' || *cp == '#') + return NULL; + + /* Prepare return vector. */ + strvec = vector_init (VECTOR_MIN_SIZE); + + /* Copy each command piece and set into vector. */ + while (1) + { + start = cp; + while (!(isspace ((int) *cp) || *cp == '\r' || *cp == '\n') && + *cp != '\0') + cp++; + strlen = cp - start; + token = XMALLOC (MTYPE_STRVEC, strlen + 1); + memcpy (token, start, strlen); + *(token + strlen) = '\0'; + vector_set (strvec, token); + + while ((isspace ((int) *cp) || *cp == '\n' || *cp == '\r') && + *cp != '\0') + cp++; + + if (*cp == '\0') + return strvec; + } +} + +/* Free allocated string vector. */ +void +cmd_free_strvec (vector v) +{ + unsigned int i; + char *cp; + + if (!v) + return; + + for (i = 0; i < vector_active (v); i++) + if ((cp = vector_slot (v, i)) != NULL) + XFREE (MTYPE_STRVEC, cp); + + vector_free (v); +} + +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 * +format_parser_desc_str(struct format_parser_state *state) +{ + const char *cp, *start; + char *token; + int strlen; + + cp = state->dp; + + if (cp == NULL) + return NULL; + + /* Skip white spaces. */ + while (isspace ((int) *cp) && *cp != '\0') + cp++; + + /* Return if there is only white spaces */ + if (*cp == '\0') + return NULL; + + start = cp; + + while (!(*cp == '\r' || *cp == '\n') && *cp != '\0') + cp++; + + strlen = cp - start; + token = XMALLOC (MTYPE_CMD_TOKENS, strlen + 1); + memcpy (token, start, strlen); + *(token + strlen) = '\0'; + + state->dp = cp; + + return token; +} + +static void +format_parser_begin_keyword(struct format_parser_state *state) +{ + struct cmd_token *token; + vector keyword_vect; + + if (state->in_keyword + || state->in_multiple) + format_parser_error(state, "Unexpected '{'"); + + state->cp++; + state->in_keyword = 1; + + token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token)); + token->type = TOKEN_KEYWORD; + token->keyword = vector_init(VECTOR_MIN_SIZE); + + keyword_vect = vector_init(VECTOR_MIN_SIZE); + vector_set(token->keyword, keyword_vect); + + vector_set(state->curvect, token); + state->curvect = keyword_vect; +} + +static void +format_parser_begin_multiple(struct format_parser_state *state) +{ + struct cmd_token *token; + + if (state->in_keyword == 1) + format_parser_error(state, "Keyword starting with '('"); + + if (state->in_multiple) + format_parser_error(state, "Nested group"); + + state->cp++; + state->in_multiple = 1; + state->just_read_word = 0; + + token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token)); + token->type = TOKEN_MULTIPLE; + token->multiple = vector_init(VECTOR_MIN_SIZE); + + vector_set(state->curvect, token); + if (state->curvect != state->topvect) + state->intvect = state->curvect; + state->curvect = token->multiple; +} + +static void +format_parser_end_keyword(struct format_parser_state *state) +{ + if (state->in_multiple + || !state->in_keyword) + format_parser_error(state, "Unexpected '}'"); + + 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; +} + +static void +format_parser_handle_pipe(struct format_parser_state *state) +{ + struct cmd_token *keyword_token; + vector keyword_vect; + + if (state->in_multiple) + { + 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 prompt character of specified node. */ +const char * +cmd_prompt (enum node_type node) +{ + struct cmd_node *cnode; + + cnode = vector_slot (cmdvec, node); + return cnode->prompt; +} + +/* Install a command into a node. */ +void +install_element (enum node_type ntype, struct cmd_element *cmd) +{ + struct cmd_node *cnode; + + /* cmd_init hasn't been called */ + if (!cmdvec) + { + fprintf (stderr, "%s called before cmd_init, breakage likely\n", + __func__); + return; + } + + cnode = vector_slot (cmdvec, ntype); + + if (cnode == NULL) + { + fprintf (stderr, "Command node %d doesn't exist, please check it\n", + 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 (ntype == VIEW_NODE) + install_element (ENABLE_NODE, cmd); +} + +static const unsigned char itoa64[] = +"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +static void +to64(char *s, long v, int n) +{ + while (--n >= 0) + { + *s++ = itoa64[v&0x3f]; + v >>= 6; + } +} + +static char * +zencrypt (const char *passwd) +{ + char salt[6]; + struct timeval tv; + char *crypt (const char *, const char *); + + gettimeofday(&tv,0); + + to64(&salt[0], random(), 3); + to64(&salt[3], tv.tv_usec, 3); + salt[5] = '\0'; + + return crypt (passwd, salt); +} + +/* This function write configuration of this host. */ +static int +config_write_host (struct vty *vty) +{ + if (host.name) + vty_out (vty, "hostname %s%s", host.name, VTY_NEWLINE); + + if (host.encrypt) + { + if (host.password_encrypt) + vty_out (vty, "password 8 %s%s", host.password_encrypt, VTY_NEWLINE); + if (host.enable_encrypt) + vty_out (vty, "enable password 8 %s%s", host.enable_encrypt, VTY_NEWLINE); + } + else + { + if (host.password) + vty_out (vty, "password %s%s", host.password, VTY_NEWLINE); + if (host.enable) + vty_out (vty, "enable password %s%s", host.enable, VTY_NEWLINE); + } + + if (zlog_default->default_lvl != LOG_DEBUG) + { + vty_out (vty, "! N.B. The 'log trap' command is deprecated.%s", + VTY_NEWLINE); + vty_out (vty, "log trap %s%s", + zlog_priority[zlog_default->default_lvl], VTY_NEWLINE); + } + + if (host.logfile && (zlog_default->maxlvl[ZLOG_DEST_FILE] != ZLOG_DISABLED)) + { + vty_out (vty, "log file %s", host.logfile); + if (zlog_default->maxlvl[ZLOG_DEST_FILE] != zlog_default->default_lvl) + vty_out (vty, " %s", + zlog_priority[zlog_default->maxlvl[ZLOG_DEST_FILE]]); + vty_out (vty, "%s", VTY_NEWLINE); + } + + if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != ZLOG_DISABLED) + { + vty_out (vty, "log stdout"); + if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != zlog_default->default_lvl) + vty_out (vty, " %s", + zlog_priority[zlog_default->maxlvl[ZLOG_DEST_STDOUT]]); + vty_out (vty, "%s", VTY_NEWLINE); + } + + if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED) + vty_out(vty,"no log monitor%s",VTY_NEWLINE); + else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] != zlog_default->default_lvl) + vty_out(vty,"log monitor %s%s", + zlog_priority[zlog_default->maxlvl[ZLOG_DEST_MONITOR]],VTY_NEWLINE); + + if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED) + { + vty_out (vty, "log syslog"); + if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != zlog_default->default_lvl) + vty_out (vty, " %s", + zlog_priority[zlog_default->maxlvl[ZLOG_DEST_SYSLOG]]); + vty_out (vty, "%s", VTY_NEWLINE); + } + + if (zlog_default->facility != LOG_DAEMON) + vty_out (vty, "log facility %s%s", + facility_name(zlog_default->facility), VTY_NEWLINE); + + if (zlog_default->record_priority == 1) + vty_out (vty, "log record-priority%s", VTY_NEWLINE); + + if (zlog_default->timestamp_precision > 0) + vty_out (vty, "log timestamp precision %d%s", + zlog_default->timestamp_precision, VTY_NEWLINE); + + if (host.advanced) + vty_out (vty, "service advanced-vty%s", VTY_NEWLINE); + + if (host.encrypt) + vty_out (vty, "service password-encryption%s", VTY_NEWLINE); + + if (host.lines >= 0) + vty_out (vty, "service terminal-length %d%s", host.lines, + VTY_NEWLINE); + + if (host.motdfile) + vty_out (vty, "banner motd file %s%s", host.motdfile, VTY_NEWLINE); + else if (! host.motd) + vty_out (vty, "no banner motd%s", VTY_NEWLINE); + + return 1; +} + +/* Utility function for getting command vector. */ +static vector +cmd_node_vector (vector v, enum node_type ntype) +{ + struct cmd_node *cnode = vector_slot (v, ntype); + return cnode->cmd_vector; +} + +/* Completion match types. */ +enum match_type +{ + no_match, + extend_match, + ipv4_prefix_match, + ipv4_match, + ipv6_prefix_match, + ipv6_match, + range_match, + vararg_match, + partly_match, + exact_match +}; + +static enum match_type +cmd_ipv4_match (const char *str) +{ + const char *sp; + int dots = 0, nums = 0; + char buf[4]; + + if (str == NULL) + return partly_match; + + for (;;) + { + memset (buf, 0, sizeof (buf)); + sp = str; + while (*str != '\0') + { + if (*str == '.') + { + if (dots >= 3) + return no_match; + + if (*(str + 1) == '.') + return no_match; + + if (*(str + 1) == '\0') + return partly_match; + + dots++; + break; + } + if (!isdigit ((int) *str)) + return no_match; + + str++; + } + + if (str - sp > 3) + return no_match; + + strncpy (buf, sp, str - sp); + if (atoi (buf) > 255) + return no_match; + + nums++; + + if (*str == '\0') + break; + + str++; + } + + if (nums < 4) + return partly_match; + + return exact_match; +} + +static enum match_type +cmd_ipv4_prefix_match (const char *str) +{ + const char *sp; + int dots = 0; + char buf[4]; + + if (str == NULL) + return partly_match; + + for (;;) + { + memset (buf, 0, sizeof (buf)); + sp = str; + while (*str != '\0' && *str != '/') + { + if (*str == '.') + { + if (dots == 3) + return no_match; + + if (*(str + 1) == '.' || *(str + 1) == '/') + return no_match; + + if (*(str + 1) == '\0') + return partly_match; + + dots++; + break; + } + + if (!isdigit ((int) *str)) + return no_match; + + str++; + } + + if (str - sp > 3) + return no_match; + + strncpy (buf, sp, str - sp); + if (atoi (buf) > 255) + return no_match; + + if (dots == 3) + { + if (*str == '/') + { + if (*(str + 1) == '\0') + return partly_match; + + str++; + break; + } + else if (*str == '\0') + return partly_match; + } + + if (*str == '\0') + return partly_match; + + str++; + } + + sp = str; + while (*str != '\0') + { + if (!isdigit ((int) *str)) + return no_match; + + str++; + } + + if (atoi (sp) > 32) + return no_match; + + return exact_match; +} + +#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%" +#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/" +#define STATE_START 1 +#define STATE_COLON 2 +#define STATE_DOUBLE 3 +#define STATE_ADDR 4 +#define STATE_DOT 5 +#define STATE_SLASH 6 +#define STATE_MASK 7 + +#ifdef HAVE_IPV6 + +static enum match_type +cmd_ipv6_match (const char *str) +{ + struct sockaddr_in6 sin6_dummy; + int ret; + + if (str == NULL) + return partly_match; + + if (strspn (str, IPV6_ADDR_STR) != strlen (str)) + return no_match; + + /* use inet_pton that has a better support, + * for example inet_pton can support the automatic addresses: + * ::1.2.3.4 + */ + ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr); + + if (ret == 1) + return exact_match; + + return no_match; +} + +static enum match_type +cmd_ipv6_prefix_match (const char *str) +{ + int state = STATE_START; + int colons = 0, nums = 0, double_colon = 0; + int mask; + const char *sp = NULL; + char *endptr = NULL; + + if (str == NULL) + return partly_match; + + if (strspn (str, IPV6_PREFIX_STR) != strlen (str)) + return no_match; + + while (*str != '\0' && state != STATE_MASK) + { + 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) == '/') + return no_match; + else 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' && *(str + 1) != '/') + colons++; + sp = str + 1; + + if (*(str + 1) == '/') + state = STATE_SLASH; + else + state = STATE_ADDR; + } + + double_colon++; + nums += 1; + break; + case STATE_ADDR: + if (*(str + 1) == ':' || *(str + 1) == '.' + || *(str + 1) == '\0' || *(str + 1) == '/') + { + if (str - sp > 3) + return no_match; + + for (; sp <= str; sp++) + if (*sp == '/') + return no_match; + + nums++; + + if (*(str + 1) == ':') + state = STATE_COLON; + else if (*(str + 1) == '.') + { + if (colons || double_colon) + state = STATE_DOT; + else + return no_match; + } + else if (*(str + 1) == '/') + state = STATE_SLASH; + } + break; + case STATE_DOT: + state = STATE_ADDR; + break; + case STATE_SLASH: + if (*(str + 1) == '\0') + return partly_match; + + state = STATE_MASK; + break; + default: + break; + } + + if (nums > 11) + return no_match; + + if (colons > 7) + return no_match; + + str++; + } + + if (state < STATE_MASK) + return partly_match; + + mask = strtol (str, &endptr, 10); + if (*endptr != '\0') + return no_match; + + if (mask < 0 || mask > 128) + return no_match; + + return exact_match; +} + +#endif /* HAVE_IPV6 */ + +#define DECIMAL_STRLEN_MAX 10 + +static int +cmd_range_match (const char *range, const char *str) +{ + char *p; + char buf[DECIMAL_STRLEN_MAX + 1]; + char *endptr = NULL; + unsigned long min, max, val; + + if (str == NULL) + return 1; + + val = strtoul (str, &endptr, 10); + if (*endptr != '\0') + return 0; + + range++; + p = strchr (range, '-'); + if (p == NULL) + return 0; + if (p - range > DECIMAL_STRLEN_MAX) + return 0; + strncpy (buf, range, p - range); + buf[p - range] = '\0'; + min = strtoul (buf, &endptr, 10); + if (*endptr != '\0') + return 0; + + range = p + 1; + p = strchr (range, '>'); + if (p == NULL) + return 0; + if (p - range > DECIMAL_STRLEN_MAX) + return 0; + strncpy (buf, range, p - range); + buf[p - range] = '\0'; + max = strtoul (buf, &endptr, 10); + if (*endptr != '\0') + return 0; + + if (val < min || val > max) + return 0; + + return 1; +} + +static enum match_type +cmd_word_match(struct cmd_token *token, + enum filter_type filter, + const char *word) +{ + const char *str; + enum match_type match_type; + + str = token->cmd; + + if (filter == FILTER_RELAXED) + if (!word || !strlen(word)) + return partly_match; + + if (!word) + return no_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; + + default: + assert (0); + } + + return no_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 */ + + /* 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 MATCHER_OK; +} + +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; + struct cmd_element *cmd_element; + enum match_type best_match; + enum match_type element_match; + enum matcher_rv matcher_rv; + + best_match = no_match; + *matches = vector_init(VECTOR_MIN_SIZE); + + for (i = 0; i < vector_active (commands); i++) + if ((cmd_element = vector_slot (commands, i)) != NULL) + { + 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; +} + +/** + * 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); +} + +/** + * 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; + + 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 (vector cmd_vector, + const char *command, + vector matches, + enum match_type type) +{ + unsigned int i; + unsigned int j; + const char *str = NULL; + const char *matched = NULL; + vector match_vector; + struct cmd_token *cmd_token; + + if (command == NULL) + command = ""; + + for (i = 0; i < vector_active (matches); i++) + if ((match_vector = vector_slot (matches, i)) != NULL) + { + int match = 0; + + for (j = 0; j < vector_active (match_vector); j++) + if ((cmd_token = vector_slot (match_vector, j)) != NULL) + { + enum match_type ret; + + assert(cmd_token->type == TOKEN_TERMINAL); + if (cmd_token->type != TOKEN_TERMINAL) + continue; + + str = cmd_token->cmd; + + switch (type) + { + case exact_match: + if (!TERMINAL_RECORD (cmd_token->terminal) + && strcmp (command, str) == 0) + match++; + break; + case partly_match: + if (!TERMINAL_RECORD (cmd_token->terminal) + && strncmp (command, str, strlen (command)) == 0) + { + if (matched && strcmp (matched, str) != 0) + return 1; /* There is ambiguous match. */ + else + matched = str; + match++; + } + break; + case range_match: + if (cmd_range_match (str, command)) + { + if (matched && strcmp (matched, str) != 0) + return 1; + else + matched = str; + match++; + } + break; +#ifdef HAVE_IPV6 + case ipv6_match: + if (cmd_token->terminal == TERMINAL_IPV6) + match++; + break; + case ipv6_prefix_match: + if ((ret = cmd_ipv6_prefix_match (command)) != no_match) + { + if (ret == partly_match) + return 2; /* There is incomplete match. */ + + match++; + } + break; +#endif /* HAVE_IPV6 */ + case ipv4_match: + if (cmd_token->terminal == TERMINAL_IPV4) + match++; + break; + case ipv4_prefix_match: + if ((ret = cmd_ipv4_prefix_match (command)) != no_match) + { + if (ret == partly_match) + return 2; /* There is incomplete match. */ + + match++; + } + break; + case extend_match: + if (TERMINAL_RECORD (cmd_token->terminal)) + match++; + break; + case no_match: + default: + break; + } + } + if (!match) + 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, struct cmd_token *token) +{ + const char *dst = token->cmd; + + /* Skip variable arguments. */ + if (TERMINAL_RECORD (token->terminal)) + return NULL; + + /* In case of 'command \t', given src is NULL string. */ + if (src == NULL) + return dst; + + /* Matched with input string. */ + if (strncmp (src, dst, strlen (src)) == 0) + return dst; + + return NULL; +} + +/* If src matches dst return dst string, otherwise return NULL */ +/* 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, struct cmd_token *token) +{ + const char *dst = token->cmd; + + switch (token->terminal) + { + 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; + } +} + +/** + * 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) +{ + unsigned int i; + char *match; + + for (i = 0; i < vector_active (v); i++) + if ((match = vector_slot (v, i)) != NULL) + if (strcmp (match, str) == 0) + return 0; + return 1; +} + +/** + * 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 cmd_token *token; + + for (i = 0; i < vector_active (v); i++) + if ((token = vector_slot (v, i)) != NULL) + if (strcmp (token->cmd, str) == 0) + return 0; + return 1; +} + +static int +cmd_try_do_shortcut (enum node_type node, char* first_word) { + if ( first_word != NULL && + node != AUTH_NODE && + node != VIEW_NODE && + node != AUTH_ENABLE_NODE && + node != ENABLE_NODE && + node != RESTRICTED_NODE && + 0 == strcmp( "do", first_word ) ) + return 1; + 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) +{ + unsigned int i; + vector cmd_vector; +#define INIT_MATCHVEC_SIZE 10 + vector matchvec; + struct cmd_element *cmd_element; + unsigned int index; + 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) + { + *status = CMD_ERR_NO_MATCH; + return NULL; + } + + 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 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; + + 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)) + { + if (match != vararg_match) + vector_set(matchvec, &token_cr); + } + + vector_free(vline_trimmed); + } + + match_vector = vector_slot (matches, i); + if (match_vector) + { + for (j = 0; j < vector_active(match_vector); j++) + { + struct cmd_token *token = vector_slot(match_vector, j); + const char *string; + + string = cmd_entry_function_desc(command, token); + if (string && desc_unique_string(matchvec, string)) + vector_set(matchvec, token); + } + } + } + } + + /* + * 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); + + vector_free (cmd_vector); + cmd_matches_free(&matches); + + if (vector_slot (matchvec, 0) == NULL) + { + vector_free (matchvec); + *status = CMD_ERR_NO_MATCH; + return NULL; + } + + *status = CMD_SUCCESS; + cmd_describe_sort(matchvec); + return matchvec; +} + +vector +cmd_describe_command (vector vline, struct vty *vty, int *status) +{ + vector ret; + + if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) ) + { + enum node_type onode; + vector shifted_vline; + unsigned int index; + + onode = vty->node; + vty->node = ENABLE_NODE; + /* We can try it on enable node, cos' the vty is authenticated */ + + shifted_vline = vector_init (vector_count(vline)); + /* use memcpy? */ + for (index = 1; index < vector_active (vline); index++) + { + vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); + } + + ret = cmd_describe_command_real (shifted_vline, vty, status); + + vector_free(shifted_vline); + vty->node = onode; + return ret; + } + + + return cmd_describe_command_real (vline, vty, status); +} + + +/* Check LCD of matched command. */ +static int +cmd_lcd (char **matched) +{ + int i; + int j; + int lcd = -1; + char *s1, *s2; + char c1, c2; + + if (matched[0] == NULL || matched[1] == NULL) + return 0; + + for (i = 1; matched[i] != NULL; i++) + { + s1 = matched[i - 1]; + s2 = matched[i]; + + for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++) + if (c1 != c2) + break; + + if (lcd < 0) + lcd = j; + else + { + if (lcd > j) + lcd = j; + } + } + 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, int islib) +{ + unsigned int i; + vector cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); +#define INIT_MATCHVEC_SIZE 10 + vector matchvec; + unsigned int index; + char **match_str; + struct cmd_token *token; + char *command; + int lcd; + vector matches = NULL; + vector match_vector; + + if (vector_active (vline) == 0) + { + vector_free (cmd_vector); + *status = CMD_ERR_NO_MATCH; + return NULL; + } + else + index = vector_active (vline) - 1; + + /* First, filter by command string */ + for (i = 0; i <= index; i++) + { + command = vector_slot (vline, i); + enum match_type match; + int ret; + + if (matches) + cmd_matches_free(&matches); + + /* 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); + + /* 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; + 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) + { + vector_free (matchvec); + + /* In case of 'command \t' pattern. Do you need '?' command at + the end of the line. */ + if (vector_slot (vline, index) == '\0') + *status = CMD_ERR_NOTHING_TODO; + else + *status = CMD_ERR_NO_MATCH; + return NULL; + } + + /* Only one matched */ + if (vector_slot (matchvec, 1) == NULL) + { + match_str = (char **) matchvec->index; + vector_only_wrapper_free (matchvec); + *status = CMD_COMPLETE_FULL_MATCH; + return match_str; + } + /* Make it sure last element is NULL. */ + vector_set (matchvec, NULL); + + /* Check LCD of matched strings. */ + if (vector_slot (vline, index) != NULL) + { + lcd = cmd_lcd ((char **) matchvec->index); + + if (lcd) + { + int len = strlen (vector_slot (vline, index)); + + if (len < lcd) + { + char *lcdstr; + + lcdstr = (islib != 0 ? + XMALLOC (MTYPE_TMP, lcd + 1) : + malloc(lcd + 1)); + memcpy (lcdstr, matchvec->index[0], lcd); + lcdstr[lcd] = '\0'; + + /* Free matchvec. */ + for (i = 0; i < vector_active (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. */ + matchvec = vector_init (INIT_MATCHVEC_SIZE); + vector_set (matchvec, lcdstr); + match_str = (char **) matchvec->index; + vector_only_wrapper_free (matchvec); + + *status = CMD_COMPLETE_MATCH; + return match_str; + } + } + } + + 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_lib (vector vline, struct vty *vty, int *status, int islib) +{ + char **ret; + + if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) ) + { + enum node_type onode; + vector shifted_vline; + unsigned int index; + + onode = vty->node; + vty->node = ENABLE_NODE; + /* We can try it on enable node, cos' the vty is authenticated */ + + shifted_vline = vector_init (vector_count(vline)); + /* use memcpy? */ + for (index = 1; index < vector_active (vline); index++) + { + vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); + } + + 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); +} + +char ** +cmd_complete_command (vector vline, struct vty *vty, int *status) +{ + return cmd_complete_command_lib (vline, vty, status, 0); +} + +/* return parent node */ +/* MUST eventually converge on CONFIG_NODE */ +enum node_type +node_parent ( enum node_type node ) +{ + enum node_type ret; + + assert (node > CONFIG_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: + case BGP_IPV6M_NODE: + ret = BGP_NODE; + break; + case KEYCHAIN_KEY_NODE: + ret = KEYCHAIN_NODE; + break; + case LINK_PARAMS_NODE: + ret = INTERFACE_NODE; + break; + default: + ret = CONFIG_NODE; + break; + } + + return ret; +} + +/* Execute command by argument vline vector. */ +static int +cmd_execute_command_real (vector vline, + enum filter_type filter, + 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]; + enum match_type match = 0; + 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++) + { + 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) + { + cmd_matches_free(&matches); + break; + } + + 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; + matched_count = 0; + incomplete_count = 0; + + for (i = 0; i < vector_active (cmd_vector); i++) + if ((cmd_element = vector_slot (cmd_vector, i))) + { + if (cmd_is_complete(cmd_element, vline)) + { + matched_element = cmd_element; + matched_count++; + } + else + { + incomplete_count++; + } + } + + /* Finish of using cmd_vector. */ + vector_free (cmd_vector); + + /* To execute command, matched_count must be 1. */ + if (matched_count == 0) + { + if (incomplete_count) + return CMD_ERR_INCOMPLETE; + else + return CMD_ERR_NO_MATCH; + } + + if (matched_count > 1) + return CMD_ERR_AMBIGUOUS; + + ret = cmd_parse(matched_element, vline, &argc, argv); + if (ret != CMD_SUCCESS) + return ret; + + /* For vtysh execution. */ + if (cmd) + *cmd = matched_element; + + if (matched_element->daemon) + return CMD_SUCCESS_DAEMON; + + /* Execute matched command. */ + 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) { + int ret, saved_ret, tried = 0; + enum node_type onode, try_node; + + onode = try_node = vty->node; + + if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) ) + { + vector shifted_vline; + unsigned int index; + + vty->node = ENABLE_NODE; + /* We can try it on enable node, cos' the vty is authenticated */ + + shifted_vline = vector_init (vector_count(vline)); + /* use memcpy? */ + for (index = 1; index < vector_active (vline); index++) + { + vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); + } + + ret = cmd_execute_command_real (shifted_vline, FILTER_RELAXED, vty, cmd); + + vector_free(shifted_vline); + vty->node = onode; + return ret; + } + + + saved_ret = ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd); + + if (vtysh) + return saved_ret; + + /* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */ + while ( ret != CMD_SUCCESS && ret != CMD_WARNING + && vty->node > CONFIG_NODE ) + { + try_node = node_parent(try_node); + vty->node = try_node; + ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd); + tried = 1; + if (ret == CMD_SUCCESS || ret == CMD_WARNING) + { + /* succesfull command, leave the node as is */ + return ret; + } + } + /* no command succeeded, reset the vty to the original node and + return the error for this node */ + if ( tried ) + vty->node = onode; + return saved_ret; +} + +/** + * 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) +{ + return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd); +} + +/** + * 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; + + vline = cmd_make_strvec (vty->buf); + + /* In case of comment line */ + if (vline == NULL) + return CMD_SUCCESS; + + /* Execute configuration command : this is strict match */ + ret = cmd_execute_command_strict (vline, vty, cmd); + + saved_node = vty->node; + + 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); + } + + // 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) + { + vty->node = saved_node; + } + + cmd_free_strvec (vline); + + return ret; +} + +/* Configration make from file. */ +int +config_from_file (struct vty *vty, FILE *fp, unsigned int *line_num) +{ + int ret; + *line_num = 0; + + while (fgets (vty->buf, vty->max, fp)) + { + ++(*line_num); + + ret = command_config_read_one_line (vty, NULL, 0); + + if (ret != CMD_SUCCESS && ret != CMD_WARNING + && ret != CMD_ERR_NOTHING_TODO) + return ret; + } + return CMD_SUCCESS; +} + +/* Configration from terminal */ +DEFUN (config_terminal, + config_terminal_cmd, + "configure terminal", + "Configuration from vty interface\n" + "Configuration terminal\n") +{ + if (vty_config_lock (vty)) + vty->node = CONFIG_NODE; + else + { + vty_out (vty, "VTY configuration is locked by other VTY%s", VTY_NEWLINE); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +/* Enable command */ +DEFUN (enable, + config_enable_cmd, + "enable", + "Turn on privileged mode command\n") +{ + /* If enable password is NULL, change to ENABLE_NODE */ + if ((host.enable == NULL && host.enable_encrypt == NULL) || + vty->type == VTY_SHELL_SERV) + vty->node = ENABLE_NODE; + else + vty->node = AUTH_ENABLE_NODE; + + return CMD_SUCCESS; +} + +/* Disable command */ +DEFUN (disable, + config_disable_cmd, + "disable", + "Turn off privileged mode command\n") +{ + if (vty->node == ENABLE_NODE) + vty->node = VIEW_NODE; + return CMD_SUCCESS; +} + +/* Down vty node level. */ +DEFUN (config_exit, + config_exit_cmd, + "exit", + "Exit current mode and down to previous mode\n") +{ + switch (vty->node) + { + case VIEW_NODE: + case ENABLE_NODE: + case RESTRICTED_NODE: + if (vty_shell (vty)) + exit (0); + else + vty->status = VTY_CLOSE; + break; + case CONFIG_NODE: + vty->node = ENABLE_NODE; + vty_config_unlock (vty); + break; + case INTERFACE_NODE: + case ZEBRA_NODE: + case BGP_NODE: + case RIP_NODE: + case RIPNG_NODE: + case BABEL_NODE: + case OSPF_NODE: + case OSPF6_NODE: + case ISIS_NODE: + case KEYCHAIN_NODE: + case MASC_NODE: + case RMAP_NODE: + case PIM_NODE: + case VTY_NODE: + vty->node = CONFIG_NODE; + break; + 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; + break; + case KEYCHAIN_KEY_NODE: + vty->node = KEYCHAIN_NODE; + break; + case LINK_PARAMS_NODE: + vty->node = INTERFACE_NODE; + break; + default: + break; + } + return CMD_SUCCESS; +} + +/* quit is alias of exit. */ +ALIAS (config_exit, + config_quit_cmd, + "quit", + "Exit current mode and down to previous mode\n") + +/* End of configuration. */ +DEFUN (config_end, + config_end_cmd, + "end", + "End current mode and change to enable mode.") +{ + switch (vty->node) + { + case VIEW_NODE: + case ENABLE_NODE: + case RESTRICTED_NODE: + /* Nothing to do. */ + break; + case CONFIG_NODE: + case INTERFACE_NODE: + case ZEBRA_NODE: + case RIP_NODE: + 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: + case BGP_IPV6M_NODE: + case RMAP_NODE: + case OSPF_NODE: + case OSPF6_NODE: + case ISIS_NODE: + 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; + default: + break; + } + return CMD_SUCCESS; +} + +/* Show version. */ +DEFUN (show_version, + show_version_cmd, + "show version", + SHOW_STR + "Displays zebra version\n") +{ + vty_out (vty, "Quagga %s (%s).%s", QUAGGA_VERSION, host.name?host.name:"", + 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; +} + +/* Help display function for all node. */ +DEFUN (config_help, + config_help_cmd, + "help", + "Description of the interactive help system\n") +{ + vty_out (vty, + "Quagga VTY provides advanced help feature. When you need help,%s\ +anytime at the command line please press '?'.%s\ +%s\ +If nothing matches, the help list will be empty and you must backup%s\ + until entering a '?' shows the available options.%s\ +Two styles of help are provided:%s\ +1. Full help is available when you are ready to enter a%s\ +command argument (e.g. 'show ?') and describes each possible%s\ +argument.%s\ +2. Partial help is provided when an abbreviated argument is entered%s\ + and you want to know what arguments match the input%s\ + (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, + VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, + VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + return CMD_SUCCESS; +} + +/* Help display function for all node. */ +DEFUN (config_list, + config_list_cmd, + "list", + "Print command list\n") +{ + unsigned int i; + struct cmd_node *cnode = vector_slot (cmdvec, vty->node); + struct cmd_element *cmd; + + for (i = 0; i < vector_active (cnode->cmd_vector); i++) + if ((cmd = vector_slot (cnode->cmd_vector, i)) != NULL + && !(cmd->attr == CMD_ATTR_DEPRECATED + || cmd->attr == CMD_ATTR_HIDDEN)) + vty_out (vty, " %s%s", cmd->string, + VTY_NEWLINE); + return CMD_SUCCESS; +} + +/* Write current configuration into file. */ +DEFUN (config_write_file, + config_write_file_cmd, + "write file", + "Write running configuration to memory, network, or terminal\n" + "Write to configuration file\n") +{ + unsigned int i; + int fd; + struct cmd_node *node; + char *config_file; + char *config_file_tmp = NULL; + char *config_file_sav = NULL; + int ret = CMD_WARNING; + struct vty *file_vty; + + /* Check and see if we are operating under vtysh configuration */ + if (host.config == NULL) + { + vty_out (vty, "Can't save to configuration file, using vtysh.%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Get filename. */ + config_file = host.config; + + config_file_sav = + XMALLOC (MTYPE_TMP, strlen (config_file) + strlen (CONF_BACKUP_EXT) + 1); + strcpy (config_file_sav, config_file); + strcat (config_file_sav, CONF_BACKUP_EXT); + + + config_file_tmp = XMALLOC (MTYPE_TMP, strlen (config_file) + 8); + sprintf (config_file_tmp, "%s.XXXXXX", config_file); + + /* Open file to configuration write. */ + fd = mkstemp (config_file_tmp); + if (fd < 0) + { + vty_out (vty, "Can't open configuration file %s.%s", config_file_tmp, + VTY_NEWLINE); + goto finished; + } + + /* Make vty for configuration file. */ + file_vty = vty_new (); + file_vty->wfd = fd; + file_vty->type = VTY_FILE; + + /* Config file header print. */ + vty_out (file_vty, "!\n! Zebra configuration saved from vty\n! "); + vty_time_print (file_vty, 1); + vty_out (file_vty, "!\n"); + + for (i = 0; i < vector_active (cmdvec); i++) + if ((node = vector_slot (cmdvec, i)) && node->func) + { + if ((*node->func) (file_vty)) + vty_out (file_vty, "!\n"); + } + vty_close (file_vty); + + if (unlink (config_file_sav) != 0) + if (errno != ENOENT) + { + vty_out (vty, "Can't unlink backup configuration file %s.%s", config_file_sav, + VTY_NEWLINE); + goto finished; + } + if (link (config_file, config_file_sav) != 0) + { + vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav, + VTY_NEWLINE); + goto finished; + } + sync (); + if (unlink (config_file) != 0) + { + vty_out (vty, "Can't unlink configuration file %s.%s", config_file, + VTY_NEWLINE); + goto finished; + } + if (link (config_file_tmp, config_file) != 0) + { + vty_out (vty, "Can't save configuration file %s.%s", config_file, + VTY_NEWLINE); + goto finished; + } + sync (); + + if (chmod (config_file, CONFIGFILE_MASK) != 0) + { + vty_out (vty, "Can't chmod configuration file %s: %s (%d).%s", + config_file, safe_strerror(errno), errno, VTY_NEWLINE); + goto finished; + } + + vty_out (vty, "Configuration saved to %s%s", config_file, + VTY_NEWLINE); + ret = CMD_SUCCESS; + +finished: + unlink (config_file_tmp); + XFREE (MTYPE_TMP, config_file_tmp); + XFREE (MTYPE_TMP, config_file_sav); + return ret; +} + +ALIAS (config_write_file, + config_write_cmd, + "write", + "Write running configuration to memory, network, or terminal\n") + +ALIAS (config_write_file, + config_write_memory_cmd, + "write memory", + "Write running configuration to memory, network, or terminal\n" + "Write configuration to the file (same as write file)\n") + +ALIAS (config_write_file, + copy_runningconfig_startupconfig_cmd, + "copy running-config startup-config", + "Copy configuration\n" + "Copy running config to... \n" + "Copy running config to startup config (same as write file)\n") + +/* Write current configuration into the terminal. */ +DEFUN (config_write_terminal, + config_write_terminal_cmd, + "write terminal", + "Write running configuration to memory, network, or terminal\n" + "Write to terminal\n") +{ + unsigned int i; + struct cmd_node *node; + + if (vty->type == VTY_SHELL_SERV) + { + for (i = 0; i < vector_active (cmdvec); i++) + if ((node = vector_slot (cmdvec, i)) && node->func && node->vtysh) + { + if ((*node->func) (vty)) + vty_out (vty, "!%s", VTY_NEWLINE); + } + } + else + { + vty_out (vty, "%sCurrent configuration:%s", VTY_NEWLINE, + VTY_NEWLINE); + vty_out (vty, "!%s", VTY_NEWLINE); + + for (i = 0; i < vector_active (cmdvec); i++) + if ((node = vector_slot (cmdvec, i)) && node->func) + { + if ((*node->func) (vty)) + vty_out (vty, "!%s", VTY_NEWLINE); + } + vty_out (vty, "end%s",VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +/* Write current configuration into the terminal. */ +ALIAS (config_write_terminal, + show_running_config_cmd, + "show running-config", + SHOW_STR + "running configuration\n") + +/* Write startup configuration into the terminal. */ +DEFUN (show_startup_config, + show_startup_config_cmd, + "show startup-config", + SHOW_STR + "Contentes of startup configuration\n") +{ + char buf[BUFSIZ]; + FILE *confp; + + confp = fopen (host.config, "r"); + if (confp == NULL) + { + vty_out (vty, "Can't open configuration file [%s]%s", + host.config, VTY_NEWLINE); + return CMD_WARNING; + } + + while (fgets (buf, BUFSIZ, confp)) + { + char *cp = buf; + + while (*cp != '\r' && *cp != '\n' && *cp != '\0') + cp++; + *cp = '\0'; + + vty_out (vty, "%s%s", buf, VTY_NEWLINE); + } + + fclose (confp); + + return CMD_SUCCESS; +} + +/* Hostname configuration */ +DEFUN (config_hostname, + hostname_cmd, + "hostname WORD", + "Set system's network name\n" + "This system's network name\n") +{ + if (!isalpha((int) *argv[0])) + { + vty_out (vty, "Please specify string starting with alphabet%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (host.name) + XFREE (MTYPE_HOST, host.name); + + host.name = XSTRDUP (MTYPE_HOST, argv[0]); + return CMD_SUCCESS; +} + +DEFUN (config_no_hostname, + no_hostname_cmd, + "no hostname [HOSTNAME]", + NO_STR + "Reset system's network name\n" + "Host name of this router\n") +{ + if (host.name) + XFREE (MTYPE_HOST, host.name); + host.name = NULL; + return CMD_SUCCESS; +} + +/* VTY interface password set. */ +DEFUN (config_password, password_cmd, + "password (8|) WORD", + "Assign the terminal connection password\n" + "Specifies a HIDDEN password will follow\n" + "dummy string \n" + "The HIDDEN line password string\n") +{ + /* Argument check. */ + if (argc == 0) + { + vty_out (vty, "Please specify password.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (argc == 2) + { + if (*argv[0] == '8') + { + if (host.password) + XFREE (MTYPE_HOST, host.password); + host.password = NULL; + if (host.password_encrypt) + XFREE (MTYPE_HOST, host.password_encrypt); + host.password_encrypt = XSTRDUP (MTYPE_HOST, argv[1]); + return CMD_SUCCESS; + } + else + { + vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + if (!isalnum ((int) *argv[0])) + { + vty_out (vty, + "Please specify string starting with alphanumeric%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (host.password) + XFREE (MTYPE_HOST, host.password); + host.password = NULL; + + if (host.encrypt) + { + if (host.password_encrypt) + XFREE (MTYPE_HOST, host.password_encrypt); + host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0])); + } + else + host.password = XSTRDUP (MTYPE_HOST, argv[0]); + + return CMD_SUCCESS; +} + +ALIAS (config_password, password_text_cmd, + "password LINE", + "Assign the terminal connection password\n" + "The UNENCRYPTED (cleartext) line password\n") + +/* VTY enable password set. */ +DEFUN (config_enable_password, enable_password_cmd, + "enable password (8|) WORD", + "Modify enable password parameters\n" + "Assign the privileged level password\n" + "Specifies a HIDDEN password will follow\n" + "dummy string \n" + "The HIDDEN 'enable' password string\n") +{ + /* Argument check. */ + if (argc == 0) + { + vty_out (vty, "Please specify password.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Crypt type is specified. */ + if (argc == 2) + { + if (*argv[0] == '8') + { + if (host.enable) + XFREE (MTYPE_HOST, host.enable); + host.enable = NULL; + + if (host.enable_encrypt) + XFREE (MTYPE_HOST, host.enable_encrypt); + host.enable_encrypt = XSTRDUP (MTYPE_HOST, argv[1]); + + return CMD_SUCCESS; + } + else + { + vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + if (!isalnum ((int) *argv[0])) + { + vty_out (vty, + "Please specify string starting with alphanumeric%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (host.enable) + XFREE (MTYPE_HOST, host.enable); + host.enable = NULL; + + /* Plain password input. */ + if (host.encrypt) + { + if (host.enable_encrypt) + XFREE (MTYPE_HOST, host.enable_encrypt); + host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0])); + } + else + host.enable = XSTRDUP (MTYPE_HOST, argv[0]); + + return CMD_SUCCESS; +} + +ALIAS (config_enable_password, + enable_password_text_cmd, + "enable password LINE", + "Modify enable password parameters\n" + "Assign the privileged level password\n" + "The UNENCRYPTED (cleartext) 'enable' password\n") + +/* VTY enable password delete. */ +DEFUN (no_config_enable_password, no_enable_password_cmd, + "no enable password", + NO_STR + "Modify enable password parameters\n" + "Assign the privileged level password\n") +{ + if (host.enable) + XFREE (MTYPE_HOST, host.enable); + host.enable = NULL; + + if (host.enable_encrypt) + XFREE (MTYPE_HOST, host.enable_encrypt); + host.enable_encrypt = NULL; + + return CMD_SUCCESS; +} + +DEFUN (service_password_encrypt, + service_password_encrypt_cmd, + "service password-encryption", + "Set up miscellaneous service\n" + "Enable encrypted passwords\n") +{ + if (host.encrypt) + return CMD_SUCCESS; + + host.encrypt = 1; + + if (host.password) + { + if (host.password_encrypt) + XFREE (MTYPE_HOST, host.password_encrypt); + host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.password)); + } + if (host.enable) + { + if (host.enable_encrypt) + XFREE (MTYPE_HOST, host.enable_encrypt); + host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.enable)); + } + + return CMD_SUCCESS; +} + +DEFUN (no_service_password_encrypt, + no_service_password_encrypt_cmd, + "no service password-encryption", + NO_STR + "Set up miscellaneous service\n" + "Enable encrypted passwords\n") +{ + if (! host.encrypt) + return CMD_SUCCESS; + + host.encrypt = 0; + + if (host.password_encrypt) + XFREE (MTYPE_HOST, host.password_encrypt); + host.password_encrypt = NULL; + + if (host.enable_encrypt) + XFREE (MTYPE_HOST, host.enable_encrypt); + host.enable_encrypt = NULL; + + return CMD_SUCCESS; +} + +DEFUN (config_terminal_length, config_terminal_length_cmd, + "terminal length <0-512>", + "Set terminal line parameters\n" + "Set number of lines on a screen\n" + "Number of lines on screen (0 for no pausing)\n") +{ + int lines; + char *endptr = NULL; + + lines = strtol (argv[0], &endptr, 10); + if (lines < 0 || lines > 512 || *endptr != '\0') + { + vty_out (vty, "length is malformed%s", VTY_NEWLINE); + return CMD_WARNING; + } + vty->lines = lines; + + return CMD_SUCCESS; +} + +DEFUN (config_terminal_no_length, config_terminal_no_length_cmd, + "terminal no length", + "Set terminal line parameters\n" + NO_STR + "Set number of lines on a screen\n") +{ + vty->lines = -1; + return CMD_SUCCESS; +} + +DEFUN (service_terminal_length, service_terminal_length_cmd, + "service terminal-length <0-512>", + "Set up miscellaneous service\n" + "System wide terminal length configuration\n" + "Number of lines of VTY (0 means no line control)\n") +{ + int lines; + char *endptr = NULL; + + lines = strtol (argv[0], &endptr, 10); + if (lines < 0 || lines > 512 || *endptr != '\0') + { + vty_out (vty, "length is malformed%s", VTY_NEWLINE); + return CMD_WARNING; + } + host.lines = lines; + + return CMD_SUCCESS; +} + +DEFUN (no_service_terminal_length, no_service_terminal_length_cmd, + "no service terminal-length [<0-512>]", + NO_STR + "Set up miscellaneous service\n" + "System wide terminal length configuration\n" + "Number of lines of VTY (0 means no line control)\n") +{ + host.lines = -1; + return CMD_SUCCESS; +} + +DEFUN_HIDDEN (do_echo, + echo_cmd, + "echo .MESSAGE", + "Echo a message back to the vty\n" + "The message to echo\n") +{ + char *message; + + vty_out (vty, "%s%s", ((message = argv_concat(argv, argc, 0)) ? message : ""), + VTY_NEWLINE); + if (message) + XFREE(MTYPE_TMP, message); + return CMD_SUCCESS; +} + +DEFUN (config_logmsg, + config_logmsg_cmd, + "logmsg "LOG_LEVELS" .MESSAGE", + "Send a message to enabled logging destinations\n" + LOG_LEVEL_DESC + "The message to send\n") +{ + int level; + char *message; + + if ((level = level_match(argv[0])) == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + + zlog(NULL, level, "%s", ((message = argv_concat(argv, argc, 1)) ? message : "")); + if (message) + XFREE(MTYPE_TMP, message); + return CMD_SUCCESS; +} + +DEFUN (show_logging, + show_logging_cmd, + "show logging", + SHOW_STR + "Show current logging configuration\n") +{ + struct zlog *zl = zlog_default; + + vty_out (vty, "Syslog logging: "); + if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED) + vty_out (vty, "disabled"); + else + vty_out (vty, "level %s, facility %s, ident %s", + zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]], + facility_name(zl->facility), zl->ident); + vty_out (vty, "%s", VTY_NEWLINE); + + vty_out (vty, "Stdout logging: "); + if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED) + vty_out (vty, "disabled"); + else + vty_out (vty, "level %s", + zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]); + vty_out (vty, "%s", VTY_NEWLINE); + + vty_out (vty, "Monitor logging: "); + if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED) + vty_out (vty, "disabled"); + else + vty_out (vty, "level %s", + zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]); + vty_out (vty, "%s", VTY_NEWLINE); + + vty_out (vty, "File logging: "); + if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || + !zl->fp) + vty_out (vty, "disabled"); + else + vty_out (vty, "level %s, filename %s", + zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]], + zl->filename); + vty_out (vty, "%s", VTY_NEWLINE); + + vty_out (vty, "Protocol name: %s%s", + zlog_proto_names[zl->protocol], VTY_NEWLINE); + vty_out (vty, "Record priority: %s%s", + (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE); + vty_out (vty, "Timestamp precision: %d%s", + zl->timestamp_precision, VTY_NEWLINE); + + return CMD_SUCCESS; +} + +DEFUN (config_log_stdout, + config_log_stdout_cmd, + "log stdout", + "Logging control\n" + "Set stdout logging level\n") +{ + zlog_set_level (NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl); + return CMD_SUCCESS; +} + +DEFUN (config_log_stdout_level, + config_log_stdout_level_cmd, + "log stdout "LOG_LEVELS, + "Logging control\n" + "Set stdout logging level\n" + LOG_LEVEL_DESC) +{ + int level; + + if ((level = level_match(argv[0])) == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + zlog_set_level (NULL, ZLOG_DEST_STDOUT, level); + return CMD_SUCCESS; +} + +DEFUN (no_config_log_stdout, + no_config_log_stdout_cmd, + "no log stdout [LEVEL]", + NO_STR + "Logging control\n" + "Cancel logging to stdout\n" + "Logging level\n") +{ + zlog_set_level (NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED); + return CMD_SUCCESS; +} + +DEFUN (config_log_monitor, + config_log_monitor_cmd, + "log monitor", + "Logging control\n" + "Set terminal line (monitor) logging level\n") +{ + zlog_set_level (NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl); + return CMD_SUCCESS; +} + +DEFUN (config_log_monitor_level, + config_log_monitor_level_cmd, + "log monitor "LOG_LEVELS, + "Logging control\n" + "Set terminal line (monitor) logging level\n" + LOG_LEVEL_DESC) +{ + int level; + + if ((level = level_match(argv[0])) == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + zlog_set_level (NULL, ZLOG_DEST_MONITOR, level); + return CMD_SUCCESS; +} + +DEFUN (no_config_log_monitor, + no_config_log_monitor_cmd, + "no log monitor [LEVEL]", + NO_STR + "Logging control\n" + "Disable terminal line (monitor) logging\n" + "Logging level\n") +{ + zlog_set_level (NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED); + return CMD_SUCCESS; +} + +static int +set_log_file(struct vty *vty, const char *fname, int loglevel) +{ + int ret; + char *p = NULL; + const char *fullpath; + + /* Path detection. */ + if (! IS_DIRECTORY_SEP (*fname)) + { + char cwd[MAXPATHLEN+1]; + cwd[MAXPATHLEN] = '\0'; + + if (getcwd (cwd, MAXPATHLEN) == NULL) + { + zlog_err ("config_log_file: Unable to alloc mem!"); + return CMD_WARNING; + } + + if ( (p = XMALLOC (MTYPE_TMP, strlen (cwd) + strlen (fname) + 2)) + == NULL) + { + zlog_err ("config_log_file: Unable to alloc mem!"); + return CMD_WARNING; + } + sprintf (p, "%s/%s", cwd, fname); + fullpath = p; + } + else + fullpath = fname; + + ret = zlog_set_file (NULL, fullpath, loglevel); + + if (p) + XFREE (MTYPE_TMP, p); + + if (!ret) + { + vty_out (vty, "can't open logfile %s\n", fname); + return CMD_WARNING; + } + + if (host.logfile) + XFREE (MTYPE_HOST, host.logfile); + + host.logfile = XSTRDUP (MTYPE_HOST, fname); + + return CMD_SUCCESS; +} + +DEFUN (config_log_file, + config_log_file_cmd, + "log file FILENAME", + "Logging control\n" + "Logging to file\n" + "Logging filename\n") +{ + return set_log_file(vty, argv[0], zlog_default->default_lvl); +} + +DEFUN (config_log_file_level, + config_log_file_level_cmd, + "log file FILENAME "LOG_LEVELS, + "Logging control\n" + "Logging to file\n" + "Logging filename\n" + LOG_LEVEL_DESC) +{ + int level; + + if ((level = level_match(argv[1])) == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + return set_log_file(vty, argv[0], level); +} + +DEFUN (no_config_log_file, + no_config_log_file_cmd, + "no log file [FILENAME]", + NO_STR + "Logging control\n" + "Cancel logging to file\n" + "Logging file name\n") +{ + zlog_reset_file (NULL); + + if (host.logfile) + XFREE (MTYPE_HOST, host.logfile); + + host.logfile = NULL; + + return CMD_SUCCESS; +} + +ALIAS (no_config_log_file, + no_config_log_file_level_cmd, + "no log file FILENAME LEVEL", + NO_STR + "Logging control\n" + "Cancel logging to file\n" + "Logging file name\n" + "Logging level\n") + +DEFUN (config_log_syslog, + config_log_syslog_cmd, + "log syslog", + "Logging control\n" + "Set syslog logging level\n") +{ + zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl); + return CMD_SUCCESS; +} + +DEFUN (config_log_syslog_level, + config_log_syslog_level_cmd, + "log syslog "LOG_LEVELS, + "Logging control\n" + "Set syslog logging level\n" + LOG_LEVEL_DESC) +{ + int level; + + if ((level = level_match(argv[0])) == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + zlog_set_level (NULL, ZLOG_DEST_SYSLOG, level); + return CMD_SUCCESS; +} + +DEFUN_DEPRECATED (config_log_syslog_facility, + config_log_syslog_facility_cmd, + "log syslog facility "LOG_FACILITIES, + "Logging control\n" + "Logging goes to syslog\n" + "(Deprecated) Facility parameter for syslog messages\n" + LOG_FACILITY_DESC) +{ + int facility; + + if ((facility = facility_match(argv[0])) < 0) + return CMD_ERR_NO_MATCH; + + zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl); + zlog_default->facility = facility; + return CMD_SUCCESS; +} + +DEFUN (no_config_log_syslog, + no_config_log_syslog_cmd, + "no log syslog [LEVEL]", + NO_STR + "Logging control\n" + "Cancel logging to syslog\n" + "Logging level\n") +{ + zlog_set_level (NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED); + return CMD_SUCCESS; +} + +ALIAS (no_config_log_syslog, + no_config_log_syslog_facility_cmd, + "no log syslog facility "LOG_FACILITIES, + NO_STR + "Logging control\n" + "Logging goes to syslog\n" + "Facility parameter for syslog messages\n" + LOG_FACILITY_DESC) + +DEFUN (config_log_facility, + config_log_facility_cmd, + "log facility "LOG_FACILITIES, + "Logging control\n" + "Facility parameter for syslog messages\n" + LOG_FACILITY_DESC) +{ + int facility; + + if ((facility = facility_match(argv[0])) < 0) + return CMD_ERR_NO_MATCH; + zlog_default->facility = facility; + return CMD_SUCCESS; +} + +DEFUN (no_config_log_facility, + no_config_log_facility_cmd, + "no log facility [FACILITY]", + NO_STR + "Logging control\n" + "Reset syslog facility to default (daemon)\n" + "Syslog facility\n") +{ + zlog_default->facility = LOG_DAEMON; + return CMD_SUCCESS; +} + +DEFUN_DEPRECATED (config_log_trap, + config_log_trap_cmd, + "log trap "LOG_LEVELS, + "Logging control\n" + "(Deprecated) Set logging level and default for all destinations\n" + LOG_LEVEL_DESC) +{ + int new_level ; + int i; + + if ((new_level = level_match(argv[0])) == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + + zlog_default->default_lvl = new_level; + for (i = 0; i < ZLOG_NUM_DESTS; i++) + if (zlog_default->maxlvl[i] != ZLOG_DISABLED) + zlog_default->maxlvl[i] = new_level; + return CMD_SUCCESS; +} + +DEFUN_DEPRECATED (no_config_log_trap, + no_config_log_trap_cmd, + "no log trap [LEVEL]", + NO_STR + "Logging control\n" + "Permit all logging information\n" + "Logging level\n") +{ + zlog_default->default_lvl = LOG_DEBUG; + return CMD_SUCCESS; +} + +DEFUN (config_log_record_priority, + config_log_record_priority_cmd, + "log record-priority", + "Logging control\n" + "Log the priority of the message within the message\n") +{ + zlog_default->record_priority = 1 ; + return CMD_SUCCESS; +} + +DEFUN (no_config_log_record_priority, + no_config_log_record_priority_cmd, + "no log record-priority", + NO_STR + "Logging control\n" + "Do not log the priority of the message within the message\n") +{ + zlog_default->record_priority = 0 ; + return CMD_SUCCESS; +} + +DEFUN (config_log_timestamp_precision, + config_log_timestamp_precision_cmd, + "log timestamp precision <0-6>", + "Logging control\n" + "Timestamp configuration\n" + "Set the timestamp precision\n" + "Number of subsecond digits\n") +{ + if (argc != 1) + { + vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE); + return CMD_WARNING; + } + + VTY_GET_INTEGER_RANGE("Timestamp Precision", + zlog_default->timestamp_precision, argv[0], 0, 6); + return CMD_SUCCESS; +} + +DEFUN (no_config_log_timestamp_precision, + no_config_log_timestamp_precision_cmd, + "no log timestamp precision", + NO_STR + "Logging control\n" + "Timestamp configuration\n" + "Reset the timestamp precision to the default value of 0\n") +{ + zlog_default->timestamp_precision = 0 ; + return CMD_SUCCESS; +} + +DEFUN (banner_motd_file, + banner_motd_file_cmd, + "banner motd file [FILE]", + "Set banner\n" + "Banner for motd\n" + "Banner from a file\n" + "Filename\n") +{ + if (host.motdfile) + XFREE (MTYPE_HOST, host.motdfile); + host.motdfile = XSTRDUP (MTYPE_HOST, argv[0]); + + return CMD_SUCCESS; +} + +DEFUN (banner_motd_default, + banner_motd_default_cmd, + "banner motd default", + "Set banner string\n" + "Strings for motd\n" + "Default string\n") +{ + host.motd = default_motd; + return CMD_SUCCESS; +} + +DEFUN (no_banner_motd, + no_banner_motd_cmd, + "no banner motd", + NO_STR + "Set banner string\n" + "Strings for motd\n") +{ + host.motd = NULL; + if (host.motdfile) + XFREE (MTYPE_HOST, host.motdfile); + host.motdfile = NULL; + 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) +{ + if (host.config) + XFREE (MTYPE_HOST, host.config); + host.config = XSTRDUP (MTYPE_HOST, filename); +} + +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_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); + install_element (node, &config_write_cmd); + install_element (node, &show_running_config_cmd); +} + +/* Initialize command interface. Install basic nodes and commands. */ +void +cmd_init (int terminal) +{ + 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; + host.enable = NULL; + host.logfile = NULL; + host.config = NULL; + host.lines = -1; + host.motd = default_motd; + host.motdfile = NULL; + + /* Install top nodes. */ + install_node (&view_node, NULL); + install_node (&enable_node, NULL); + install_node (&auth_node, NULL); + install_node (&auth_enable_node, NULL); + install_node (&restricted_node, NULL); + install_node (&config_node, config_write_host); + + /* Each node's basic commands. */ + install_element (VIEW_NODE, &show_version_cmd); + if (terminal) + { + 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_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); + } + + if (terminal) + { + install_default (ENABLE_NODE); + install_element (ENABLE_NODE, &config_disable_cmd); + install_element (ENABLE_NODE, &config_terminal_cmd); + install_element (ENABLE_NODE, ©_runningconfig_startupconfig_cmd); + } + install_element (ENABLE_NODE, &show_startup_config_cmd); + + if (terminal) + { + install_element (ENABLE_NODE, &config_logmsg_cmd); + + install_default (CONFIG_NODE); + } + + install_element (CONFIG_NODE, &hostname_cmd); + install_element (CONFIG_NODE, &no_hostname_cmd); + + if (terminal) + { + install_element (CONFIG_NODE, &password_cmd); + install_element (CONFIG_NODE, &password_text_cmd); + install_element (CONFIG_NODE, &enable_password_cmd); + install_element (CONFIG_NODE, &enable_password_text_cmd); + install_element (CONFIG_NODE, &no_enable_password_cmd); + + install_element (CONFIG_NODE, &config_log_stdout_cmd); + install_element (CONFIG_NODE, &config_log_stdout_level_cmd); + install_element (CONFIG_NODE, &no_config_log_stdout_cmd); + install_element (CONFIG_NODE, &config_log_monitor_cmd); + install_element (CONFIG_NODE, &config_log_monitor_level_cmd); + install_element (CONFIG_NODE, &no_config_log_monitor_cmd); + install_element (CONFIG_NODE, &config_log_file_cmd); + install_element (CONFIG_NODE, &config_log_file_level_cmd); + install_element (CONFIG_NODE, &no_config_log_file_cmd); + install_element (CONFIG_NODE, &no_config_log_file_level_cmd); + install_element (CONFIG_NODE, &config_log_syslog_cmd); + install_element (CONFIG_NODE, &config_log_syslog_level_cmd); + install_element (CONFIG_NODE, &config_log_syslog_facility_cmd); + install_element (CONFIG_NODE, &no_config_log_syslog_cmd); + install_element (CONFIG_NODE, &no_config_log_syslog_facility_cmd); + install_element (CONFIG_NODE, &config_log_facility_cmd); + install_element (CONFIG_NODE, &no_config_log_facility_cmd); + install_element (CONFIG_NODE, &config_log_trap_cmd); + install_element (CONFIG_NODE, &no_config_log_trap_cmd); + install_element (CONFIG_NODE, &config_log_record_priority_cmd); + install_element (CONFIG_NODE, &no_config_log_record_priority_cmd); + install_element (CONFIG_NODE, &config_log_timestamp_precision_cmd); + install_element (CONFIG_NODE, &no_config_log_timestamp_precision_cmd); + install_element (CONFIG_NODE, &service_password_encrypt_cmd); + install_element (CONFIG_NODE, &no_service_password_encrypt_cmd); + install_element (CONFIG_NODE, &banner_motd_default_cmd); + install_element (CONFIG_NODE, &banner_motd_file_cmd); + install_element (CONFIG_NODE, &no_banner_motd_cmd); + install_element (CONFIG_NODE, &service_terminal_length_cmd); + install_element (CONFIG_NODE, &no_service_terminal_length_cmd); + + install_element (VIEW_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 (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; + struct cmd_node *cmd_node; + struct cmd_element *cmd_element; + vector cmd_node_v; + + if (cmdvec) + { + for (i = 0; i < vector_active (cmdvec); i++) + if ((cmd_node = vector_slot (cmdvec, i)) != NULL) + { + 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_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); + cmdvec = NULL; + } + + if (command_cr) + 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) + XFREE (MTYPE_HOST, host.password); + if (host.password_encrypt) + XFREE (MTYPE_HOST, host.password_encrypt); + if (host.enable) + XFREE (MTYPE_HOST, host.enable); + if (host.enable_encrypt) + XFREE (MTYPE_HOST, host.enable_encrypt); + if (host.logfile) + XFREE (MTYPE_HOST, host.logfile); + if (host.motdfile) + XFREE (MTYPE_HOST, host.motdfile); + if (host.config) + XFREE (MTYPE_HOST, host.config); +} diff --git a/lib/command.h b/lib/command.h new file mode 100644 index 0000000..cc5dd08 --- /dev/null +++ b/lib/command.h @@ -0,0 +1,583 @@ +/* + * Zebra configuration command interface routine + * 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. + */ + +#ifndef _ZEBRA_COMMAND_H +#define _ZEBRA_COMMAND_H + +#include "vector.h" +#include "vty.h" +#include "lib/route_types.h" +#include "hash.h" + +/* Host configuration variable */ +struct host +{ + /* Host name of this router. */ + char *name; + + /* Password for vty interface. */ + char *password; + char *password_encrypt; + + /* Enable password */ + char *enable; + char *enable_encrypt; + + /* System wide terminal lines. */ + int lines; + + /* Log filename. */ + char *logfile; + + /* config file name of this host */ + char *config; + + /* Flags for services */ + int advanced; + int encrypt; + + /* Banner configuration. */ + const char *motd; + char *motdfile; +}; + +/* There are some command levels which called from command node. */ +enum node_type +{ + AUTH_NODE, /* Authentication mode of vty interface. */ + RESTRICTED_NODE, /* Restricted view mode */ + VIEW_NODE, /* View node. Default mode of vty interface. */ + 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. */ + KEYCHAIN_NODE, /* Key-chain node. */ + KEYCHAIN_KEY_NODE, /* Key-chain key node. */ + INTERFACE_NODE, /* Interface mode node. */ + ZEBRA_NODE, /* zebra connection node. */ + TABLE_NODE, /* rtm_table selection node. */ + RIP_NODE, /* RIP protocol mode node. */ + RIPNG_NODE, /* RIPng protocol mode node. */ + 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. */ + ACCESS_NODE, /* Access list node. */ + PREFIX_NODE, /* Prefix list node. */ + ACCESS_IPV6_NODE, /* Access list node. */ + PREFIX_IPV6_NODE, /* Prefix list node. */ + AS_LIST_NODE, /* AS list node. */ + COMMUNITY_LIST_NODE, /* Community list node. */ + RMAP_NODE, /* Route map node. */ + SMUX_NODE, /* SNMP configuration node. */ + DUMP_NODE, /* Packet dump node. */ + 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 + function pointer . */ +struct cmd_node +{ + /* Node index. */ + enum node_type node; + + /* Prompt character at vty interface. */ + const char *prompt; + + /* Is this node's configuration goes to vtysh ? */ + int vtysh; + + /* Node's configuration write function */ + int (*func) (struct vty *); + + /* Vector of this node's command list. */ + vector cmd_vector; + + /* Hashed index of command node list, for de-dupping primarily */ + struct hash *cmd_hash; +}; + +enum +{ + CMD_ATTR_DEPRECATED = 1, + CMD_ATTR_HIDDEN, +}; + +/* Structure of command element. */ +struct cmd_element +{ + const char *string; /* Command specification by string. */ + 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 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 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 *desc; /* Command's description. */ +}; + +/* Return value of the commands. */ +#define CMD_SUCCESS 0 +#define CMD_WARNING 1 +#define CMD_ERR_NO_MATCH 2 +#define CMD_ERR_AMBIGUOUS 3 +#define CMD_ERR_INCOMPLETE 4 +#define CMD_ERR_EXEED_ARGC_MAX 5 +#define CMD_ERR_NOTHING_TODO 6 +#define CMD_COMPLETE_FULL_MATCH 7 +#define CMD_COMPLETE_MATCH 8 +#define CMD_COMPLETE_LIST_MATCH 9 +#define CMD_SUCCESS_DAEMON 10 + +/* Argc max counts. */ +#define CMD_ARGC_MAX 25 + +/* Turn off these macros when uisng cpp with extract.pl */ +#ifndef VTYSH_EXTRACT_PL + +/* helper defines for end-user DEFUN* macros */ +#define DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \ + struct cmd_element cmdname = \ + { \ + .string = cmdstr, \ + .func = funcname, \ + .doc = helpstr, \ + .attr = attrs, \ + .daemon = dnum, \ + }; + +#define DEFUN_CMD_FUNC_DECL(funcname) \ + static int funcname (struct cmd_element *, struct vty *, int, const char *[]); + +#define DEFUN_CMD_FUNC_TEXT(funcname) \ + static int funcname \ + (struct cmd_element *self __attribute__ ((unused)), \ + struct vty *vty __attribute__ ((unused)), \ + int argc __attribute__ ((unused)), \ + const char *argv[] __attribute__ ((unused)) ) + +/* 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) \ + DEFUN_CMD_FUNC_TEXT(funcname) + +#define DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \ + DEFUN_CMD_FUNC_DECL(funcname) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) \ + DEFUN_CMD_FUNC_TEXT(funcname) + +#define DEFUN_HIDDEN(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN) + +#define DEFUN_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED) \ + +/* DEFUN_NOSH for commands that vtysh should ignore */ +#define DEFUN_NOSH(funcname, cmdname, cmdstr, helpstr) \ + DEFUN(funcname, cmdname, cmdstr, helpstr) + +/* DEFSH for vtysh. */ +#define DEFSH(daemon, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, 0, daemon) \ + +/* DEFUN + DEFSH */ +#define DEFUNSH(daemon, funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_FUNC_DECL(funcname) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon) \ + DEFUN_CMD_FUNC_TEXT(funcname) + +/* DEFUN + DEFSH with attributes */ +#define DEFUNSH_ATTR(daemon, funcname, cmdname, cmdstr, helpstr, attr) \ + DEFUN_CMD_FUNC_DECL(funcname) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, daemon) \ + DEFUN_CMD_FUNC_TEXT(funcname) + +#define DEFUNSH_HIDDEN(daemon, funcname, cmdname, cmdstr, helpstr) \ + DEFUNSH_ATTR (daemon, funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN) + +#define DEFUNSH_DEPRECATED(daemon, funcname, cmdname, cmdstr, helpstr) \ + DEFUNSH_ATTR (daemon, funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED) + +/* ALIAS macro which define existing command's alias. */ +#define ALIAS(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) + +#define ALIAS_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) + +#define ALIAS_HIDDEN(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, 0) + +#define ALIAS_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED, 0) + +#define ALIAS_SH(daemon, funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon) + +#define ALIAS_SH_HIDDEN(daemon, funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, daemon) + +#define ALIAS_SH_DEPRECATED(daemon, funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED, daemon) + +#endif /* VTYSH_EXTRACT_PL */ + +/* + * 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) ">" + + +/* Common descriptions. */ +#define SHOW_STR "Show running system information\n" +#define IP_STR "IP information\n" +#define IPV6_STR "IPv6 information\n" +#define NO_STR "Negate a command or set its defaults\n" +#define REDIST_STR "Redistribute information from another routing protocol\n" +#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" +#define UNDEBUG_STR "Disable debugging functions (see also 'debug')\n" +#define ROUTER_STR "Enable a routing process\n" +#define AS_STR "AS number\n" +#define MBGP_STR "MBGP information\n" +#define MATCH_STR "Match values from routing table\n" +#define SET_STR "Set values in destination routing protocol\n" +#define OUT_STR "Filter outgoing routing updates\n" +#define IN_STR "Filter incoming routing updates\n" +#define V4NOTATION_STR "specify by IPv4 address notation(e.g. 0.0.0.0)\n" +#define OSPF6_NUMBER_STR "Specify by number\n" +#define INTERFACE_STR "Interface infomation\n" +#define IFNAME_STR "Interface name(e.g. ep0)\n" +#define IP6_STR "IPv6 Information\n" +#define OSPF6_STR "Open Shortest Path First (OSPF) for IPv6\n" +#define OSPF6_ROUTER_STR "Enable a routing process\n" +#define OSPF6_INSTANCE_STR "<1-65535> Instance ID\n" +#define SECONDS_STR "<1-65535> Seconds\n" +#define ROUTE_STR "Routing Table\n" +#define PREFIX_LIST_STR "Build a prefix list\n" +#define OSPF6_DUMP_TYPE_LIST \ +"(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" + +/* IPv4 only machine should not accept IPv6 address for peer's IP + address. So we replace VTY command string like below. */ +#ifdef HAVE_IPV6 +#define NEIGHBOR_CMD "neighbor (A.B.C.D|X:X::X:X) " +#define NO_NEIGHBOR_CMD "no neighbor (A.B.C.D|X:X::X:X) " +#define NEIGHBOR_ADDR_STR "Neighbor address\nIPv6 address\n" +#define NEIGHBOR_CMD2 "neighbor (A.B.C.D|X:X::X:X|WORD) " +#define NO_NEIGHBOR_CMD2 "no neighbor (A.B.C.D|X:X::X:X|WORD) " +#define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor IPv6 address\nNeighbor tag\n" +#else +#define NEIGHBOR_CMD "neighbor A.B.C.D " +#define NO_NEIGHBOR_CMD "no neighbor A.B.C.D " +#define NEIGHBOR_ADDR_STR "Neighbor address\n" +#define NEIGHBOR_CMD2 "neighbor (A.B.C.D|WORD) " +#define NO_NEIGHBOR_CMD2 "no neighbor (A.B.C.D|WORD) " +#define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor tag\n" +#endif /* HAVE_IPV6 */ + +/* Prototypes. */ +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 *); + +/* Concatenates argv[shift] through argv[argc-1] into a single NUL-terminated + string with a space between each element (allocated using + XMALLOC(MTYPE_TMP)). Returns NULL if shift >= argc. */ +extern char *argv_concat (const char **argv, int argc, int shift); + +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 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 cmd_init (int); +extern void cmd_terminate (void); + +/* Export typical functions. */ +extern struct cmd_element config_end_cmd; +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 const char *host_config_get (void); +extern void host_config_set (char *); + +extern void print_version (const char *); + +/* struct host global, ick */ +extern struct host host; + +/* "" global */ +extern char *command_cr; +#endif /* _ZEBRA_COMMAND_H */ diff --git a/lib/daemon.c b/lib/daemon.c new file mode 100644 index 0000000..c473555 --- /dev/null +++ b/lib/daemon.c @@ -0,0 +1,81 @@ +/* + * Daemonize routine + * Copyright (C) 1997, 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 + +#ifndef HAVE_DAEMON + +/* Daemonize myself. */ +int +daemon (int nochdir, int noclose) +{ + pid_t pid; + + pid = fork (); + + /* In case of fork is error. */ + if (pid < 0) + { + zlog_err ("fork failed: %s", safe_strerror(errno)); + return -1; + } + + /* In case of this is parent process. */ + if (pid != 0) + exit (0); + + /* Become session leader and get pid. */ + pid = setsid(); + + if (pid == -1) + { + zlog_err ("setsid failed: %s", safe_strerror(errno)); + return -1; + } + + /* Change directory to root. */ + if (! nochdir) + chdir ("/"); + + /* File descriptor close. */ + if (! noclose) + { + int fd; + + fd = open ("/dev/null", O_RDWR, 0); + if (fd != -1) + { + dup2 (fd, STDIN_FILENO); + dup2 (fd, STDOUT_FILENO); + dup2 (fd, STDERR_FILENO); + if (fd > 2) + close (fd); + } + } + + umask (0027); + + return 0; +} + +#endif /* HAVE_DAEMON */ diff --git a/lib/distribute.c b/lib/distribute.c new file mode 100644 index 0000000..583befb --- /dev/null +++ b/lib/distribute.c @@ -0,0 +1,1020 @@ +/* Distribute list functions + * Copyright (C) 1998, 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 "hash.h" +#include "if.h" +#include "filter.h" +#include "command.h" +#include "distribute.h" +#include "memory.h" + +/* Hash of distribute list. */ +struct hash *disthash; + +/* Hook functions. */ +void (*distribute_add_hook) (struct distribute *); +void (*distribute_delete_hook) (struct distribute *); + +static struct distribute * +distribute_new (void) +{ + return XCALLOC (MTYPE_DISTRIBUTE, sizeof (struct distribute)); +} + +/* Free distribute object. */ +static void +distribute_free (struct distribute *dist) +{ + int i = 0; + if (dist->ifname) + XFREE (MTYPE_DISTRIBUTE_IFNAME, dist->ifname); + + for (i=0; i < DISTRIBUTE_MAX; i++) + if (dist->list[i]) + free(dist->list[i]); + + 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) +{ + struct distribute key; + struct distribute *dist; + + /* temporary reference */ + key.ifname = (char *)ifname; + + dist = hash_lookup (disthash, &key); + + return dist; +} + +void +distribute_list_add_hook (void (*func) (struct distribute *)) +{ + distribute_add_hook = func; +} + +void +distribute_list_delete_hook (void (*func) (struct distribute *)) +{ + distribute_delete_hook = func; +} + +static void * +distribute_hash_alloc (struct distribute *arg) +{ + struct distribute *dist; + + dist = distribute_new (); + if (arg->ifname) + dist->ifname = XSTRDUP (MTYPE_DISTRIBUTE_IFNAME, arg->ifname); + else + dist->ifname = NULL; + return dist; +} + +/* Make new distribute list and push into hash. */ +static struct distribute * +distribute_get (const char *ifname) +{ + struct distribute key; + + /* temporary reference */ + key.ifname = (char *)ifname; + + return hash_get (disthash, &key, (void * (*) (void *))distribute_hash_alloc); +} + +static unsigned int +distribute_hash_make (void *arg) +{ + const struct distribute *dist = arg; + + return dist->ifname ? string_hash_make (dist->ifname) : 0; +} + +/* If two distribute-list have same value then return 1 else return + 0. This function is used by hash package. */ +static int +distribute_cmp (const struct distribute *dist1, const struct distribute *dist2) +{ + if (dist1->ifname && dist2->ifname) + if (strcmp (dist1->ifname, dist2->ifname) == 0) + return 1; + if (! dist1->ifname && ! dist2->ifname) + 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, + const char *alist_name) +{ + struct distribute *dist; + + dist = distribute_get (ifname); + + 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, + const char *alist_name) +{ + struct distribute *dist; + + dist = distribute_lookup (ifname); + if (!dist) + return 0; + + if (!dist->list[type]) + return 0; + if (strcmp (dist->list[type], alist_name) != 0) + return 0; + + free (dist->list[type]); + dist->list[type] = NULL; + + /* Apply this distribute-list to the interface. */ + (*distribute_delete_hook) (dist); + + /* If all dist are NULL, then free distribute list. */ + distribute_free_if_empty(dist); + return 1; +} + +/* Set access-list name to the distribute list. */ +static struct distribute * +distribute_list_prefix_set (const char *ifname, enum distribute_type type, + const char *plist_name) +{ + struct distribute *dist; + + dist = distribute_get (ifname); + + 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); + + return dist; +} + +/* Unset distribute-list. If matched distribute-list exist then + return 1. */ +static int +distribute_list_prefix_unset (const char *ifname, enum distribute_type type, + const char *plist_name) +{ + struct distribute *dist; + + dist = distribute_lookup (ifname); + if (!dist) + return 0; + + if (!dist->prefix[type]) + return 0; + if (strcmp (dist->prefix[type], plist_name) != 0) + return 0; + + free (dist->prefix[type]); + dist->prefix[type] = NULL; + + /* Apply this distribute-list to the interface. */ + (*distribute_delete_hook) (dist); + + /* If all dist are NULL, then free distribute list. */ + distribute_free_if_empty(dist); + return 1; +} + +DEFUN (distribute_list_all, + 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") +{ + enum distribute_type type; + + /* Check of distribute list type. */ + if (strncmp (argv[1], "i", 1) == 0) + type = DISTRIBUTE_V4_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = DISTRIBUTE_V4_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; +} + +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" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n") + +DEFUN (no_distribute_list_all, + 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") +{ + int ret; + enum distribute_type type; + + /* Check of distribute list type. */ + if (strncmp (argv[1], "i", 1) == 0) + type = DISTRIBUTE_V4_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = DISTRIBUTE_V4_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; +} + +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" + "Access-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n") + +DEFUN (distribute_list, + 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") +{ + enum distribute_type type; + + /* Check of distribute list type. */ + if (strncmp (argv[1], "i", 1) == 0) + type = DISTRIBUTE_V4_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = DISTRIBUTE_V4_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; +} + +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" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") + +DEFUN (no_distribute_list, 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") +{ + int ret; + enum distribute_type type; + + /* Check of distribute list type. */ + if (strncmp (argv[1], "i", 1) == 0) + type = DISTRIBUTE_V4_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = DISTRIBUTE_V4_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; +} + +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_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" + "Access-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") + +DEFUN (distribute_list_prefix_all, + 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") +{ + enum distribute_type type; + + /* Check of distribute list type. */ + if (strncmp (argv[1], "i", 1) == 0) + type = DISTRIBUTE_V4_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = DISTRIBUTE_V4_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; +} + +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" + "Name of an IP prefix-list\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n") + +DEFUN (no_distribute_list_prefix_all, + 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") +{ + int ret; + enum distribute_type type; + + /* Check of distribute list type. */ + if (strncmp (argv[1], "i", 1) == 0) + type = DISTRIBUTE_V4_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = DISTRIBUTE_V4_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; +} + +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" + "Filter prefixes in routing updates\n" + "Name of an IP prefix-list\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n") + +DEFUN (distribute_list_prefix, 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") +{ + enum distribute_type type; + + /* Check of distribute list type. */ + if (strncmp (argv[1], "i", 1) == 0) + type = DISTRIBUTE_V4_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = DISTRIBUTE_V4_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; +} + +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 (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" + "Name of an IP prefix-list\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") + +DEFUN (no_distribute_list_prefix, 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") +{ + int ret; + enum distribute_type type; + + /* Check of distribute list type. */ + if (strncmp (argv[1], "i", 1) == 0) + type = DISTRIBUTE_V4_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = DISTRIBUTE_V4_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; +} + +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_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" + "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") + +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); + vty_out(vty, " Outgoing update filter list for all interface is"); + has_print = 0; + if (dist) + { + 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, " 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) + { + 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); + vty_out(vty, " Incoming update filter list for all interface is"); + has_print = 0; + if (dist) + { + 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, " 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) + { + 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; +} + +/* Configuration write function. */ +int +config_write_distribute (struct vty *vty) +{ + unsigned int i; + int j; + int output, v6; + struct hash_backet *mp; + int write = 0; + + for (i = 0; i < disthash->size; i++) + for (mp = disthash->index[i]; mp; mp = mp->next) + { + struct distribute *dist; + + dist = mp->data; + + 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++; + } + + 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++; + } + } + return write; +} + +/* Clear all distribute list. */ +void +distribute_list_reset () +{ + hash_clean (disthash, (void (*) (void *)) distribute_free); +} + +/* Initialize distribute list related hash. */ +void +distribute_list_init (int node) +{ + disthash = hash_create (distribute_hash_make, + (int (*) (const void *, const void *)) distribute_cmp); + /* 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); + install_element (node, &no_distribute_list_cmd); + install_element (node, &distribute_list_prefix_all_cmd); + install_element (node, &no_distribute_list_prefix_all_cmd); + install_element (node, &distribute_list_prefix_cmd); + install_element (node, &no_distribute_list_prefix_cmd); + } + + /* 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); + install_element (node, &no_ipv6_distribute_list_cmd); + install_element (node, &ipv6_distribute_list_prefix_all_cmd); + install_element (node, &no_ipv6_distribute_list_prefix_all_cmd); + 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 new file mode 100644 index 0000000..e9625a3 --- /dev/null +++ b/lib/distribute.h @@ -0,0 +1,63 @@ +/* Distribute list functions 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. + */ + +#ifndef _ZEBRA_DISTRIBUTE_H +#define _ZEBRA_DISTRIBUTE_H + +#include +#include "if.h" +#include "filter.h" + +/* Disctirubte list types. */ +enum distribute_type +{ + DISTRIBUTE_V4_IN, + DISTRIBUTE_V6_IN, + DISTRIBUTE_V4_OUT, + DISTRIBUTE_V6_OUT, + DISTRIBUTE_MAX +}; + +struct distribute +{ + /* Name of the interface. */ + char *ifname; + + /* Filter name of `in' and `out' */ + char *list[DISTRIBUTE_MAX]; + + /* prefix-list name of `in' and `out' */ + char *prefix[DISTRIBUTE_MAX]; +}; + +/* Prototypes for distribute-list. */ +extern void distribute_list_init (int); +extern void distribute_list_reset (void); +extern void distribute_list_add_hook (void (*) (struct distribute *)); +extern void distribute_list_delete_hook (void (*) (struct distribute *)); +extern struct distribute *distribute_lookup (const char *); +extern int config_write_distribute (struct vty *); +extern int config_show_distribute (struct vty *); + +extern enum filter_type distribute_apply_in (struct interface *, struct prefix *); +extern enum filter_type distribute_apply_out (struct interface *, struct prefix *); + +#endif /* _ZEBRA_DISTRIBUTE_H */ diff --git a/lib/event_counter.c b/lib/event_counter.c new file mode 100644 index 0000000..e94aa4c --- /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 0000000..f40c6cd --- /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 0000000..47a1e88 --- /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 new file mode 100644 index 0000000..a472941 --- /dev/null +++ b/lib/filter.c @@ -0,0 +1,2036 @@ +/* Route filtering function. + * Copyright (C) 1998, 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 "prefix.h" +#include "filter.h" +#include "memory.h" +#include "command.h" +#include "sockunion.h" +#include "buffer.h" +#include "log.h" + +struct filter_cisco +{ + /* Cisco access-list */ + int extended; + struct in_addr addr; + struct in_addr addr_mask; + struct in_addr mask; + struct in_addr mask_mask; +}; + +struct filter_zebra +{ + /* If this filter is "exact" match then this flag is set. */ + int exact; + + /* Prefix information. */ + struct prefix prefix; +}; + +/* Filter element of access list */ +struct filter +{ + /* For doubly linked list. */ + struct filter *next; + struct filter *prev; + + /* Filter type information. */ + enum filter_type type; + + /* Cisco access-list */ + int cisco; + + union + { + struct filter_cisco cfilter; + struct filter_zebra zfilter; + } u; +}; + +/* List of access_list. */ +struct access_list_list +{ + struct access_list *head; + struct access_list *tail; +}; + +/* Master structure of access_list. */ +struct access_master +{ + /* List of access_list which name is number. */ + struct access_list_list num; + + /* List of access_list which name is string. */ + struct access_list_list str; + + /* Hook function which is executed when new access_list is added. */ + void (*add_hook) (struct access_list *); + + /* Hook function which is executed when access_list is deleted. */ + void (*delete_hook) (struct access_list *); +}; + +/* Static structure for IPv4 access_list's master. */ +static struct access_master access_master_ipv4 = +{ + {NULL, NULL}, + {NULL, NULL}, + NULL, + NULL, +}; + +#ifdef HAVE_IPV6 +/* Static structure for IPv6 access_list's master. */ +static struct access_master access_master_ipv6 = +{ + {NULL, NULL}, + {NULL, NULL}, + NULL, + NULL, +}; +#endif /* HAVE_IPV6 */ + +static struct access_master * +access_master_get (afi_t afi) +{ + if (afi == AFI_IP) + return &access_master_ipv4; +#ifdef HAVE_IPV6 + else if (afi == AFI_IP6) + return &access_master_ipv6; +#endif /* HAVE_IPV6 */ + return NULL; +} + +/* Allocate new filter structure. */ +static struct filter * +filter_new (void) +{ + return (struct filter *) XCALLOC (MTYPE_ACCESS_FILTER, + sizeof (struct filter)); +} + +static void +filter_free (struct filter *filter) +{ + XFREE (MTYPE_ACCESS_FILTER, filter); +} + +/* Return string of filter_type. */ +static const char * +filter_type_str (struct filter *filter) +{ + switch (filter->type) + { + case FILTER_PERMIT: + return "permit"; + break; + case FILTER_DENY: + return "deny"; + break; + case FILTER_DYNAMIC: + return "dynamic"; + break; + default: + return ""; + break; + } +} + +/* If filter match to the prefix then return 1. */ +static int +filter_match_cisco (struct filter *mfilter, struct prefix *p) +{ + struct filter_cisco *filter; + struct in_addr mask; + u_int32_t check_addr; + u_int32_t check_mask; + + filter = &mfilter->u.cfilter; + check_addr = p->u.prefix4.s_addr & ~filter->addr_mask.s_addr; + + if (filter->extended) + { + masklen2ip (p->prefixlen, &mask); + check_mask = mask.s_addr & ~filter->mask_mask.s_addr; + + if (memcmp (&check_addr, &filter->addr.s_addr, 4) == 0 + && memcmp (&check_mask, &filter->mask.s_addr, 4) == 0) + return 1; + } + else if (memcmp (&check_addr, &filter->addr.s_addr, 4) == 0) + return 1; + + return 0; +} + +/* If filter match to the prefix then return 1. */ +static int +filter_match_zebra (struct filter *mfilter, struct prefix *p) +{ + struct filter_zebra *filter; + + filter = &mfilter->u.zfilter; + + if (filter->prefix.family == p->family) + { + if (filter->exact) + { + if (filter->prefix.prefixlen == p->prefixlen) + return prefix_match (&filter->prefix, p); + else + return 0; + } + else + return prefix_match (&filter->prefix, p); + } + else + return 0; +} + +/* Allocate new access list structure. */ +static struct access_list * +access_list_new (void) +{ + return (struct access_list *) XCALLOC (MTYPE_ACCESS_LIST, + sizeof (struct access_list)); +} + +/* Free allocated access_list. */ +static void +access_list_free (struct access_list *access) +{ + XFREE (MTYPE_ACCESS_LIST, access); +} + +/* Delete access_list from access_master and free it. */ +static void +access_list_delete (struct access_list *access) +{ + struct filter *filter; + struct filter *next; + struct access_list_list *list; + struct access_master *master; + + for (filter = access->head; filter; filter = next) + { + next = filter->next; + filter_free (filter); + } + + master = access->master; + + if (access->type == ACCESS_TYPE_NUMBER) + list = &master->num; + else + list = &master->str; + + if (access->next) + access->next->prev = access->prev; + else + list->tail = access->prev; + + if (access->prev) + access->prev->next = access->next; + else + list->head = access->next; + + if (access->name) + XFREE (MTYPE_ACCESS_LIST_STR, access->name); + + if (access->remark) + XFREE (MTYPE_TMP, access->remark); + + access_list_free (access); +} + +/* Insert new access list to list of access_list. Each acceess_list + is sorted by the name. */ +static struct access_list * +access_list_insert (afi_t afi, const char *name) +{ + unsigned int i; + long number; + struct access_list *access; + struct access_list *point; + struct access_list_list *alist; + struct access_master *master; + + master = access_master_get (afi); + if (master == NULL) + return NULL; + + /* Allocate new access_list and copy given name. */ + access = access_list_new (); + access->name = XSTRDUP (MTYPE_ACCESS_LIST_STR, name); + access->master = master; + + /* If name is made by all digit character. We treat it as + number. */ + for (number = 0, i = 0; i < strlen (name); i++) + { + if (isdigit ((int) name[i])) + number = (number * 10) + (name[i] - '0'); + else + break; + } + + /* In case of name is all digit character */ + if (i == strlen (name)) + { + access->type = ACCESS_TYPE_NUMBER; + + /* Set access_list to number list. */ + alist = &master->num; + + for (point = alist->head; point; point = point->next) + if (atol (point->name) >= number) + break; + } + else + { + access->type = ACCESS_TYPE_STRING; + + /* Set access_list to string list. */ + alist = &master->str; + + /* Set point to insertion point. */ + for (point = alist->head; point; point = point->next) + if (strcmp (point->name, name) >= 0) + break; + } + + /* In case of this is the first element of master. */ + if (alist->head == NULL) + { + alist->head = alist->tail = access; + return access; + } + + /* In case of insertion is made at the tail of access_list. */ + if (point == NULL) + { + access->prev = alist->tail; + alist->tail->next = access; + alist->tail = access; + return access; + } + + /* In case of insertion is made at the head of access_list. */ + if (point == alist->head) + { + access->next = alist->head; + alist->head->prev = access; + alist->head = access; + return access; + } + + /* Insertion is made at middle of the access_list. */ + access->next = point; + access->prev = point->prev; + + if (point->prev) + point->prev->next = access; + point->prev = access; + + return access; +} + +/* Lookup access_list from list of access_list by name. */ +struct access_list * +access_list_lookup (afi_t afi, const char *name) +{ + struct access_list *access; + struct access_master *master; + + if (name == NULL) + return NULL; + + master = access_master_get (afi); + if (master == NULL) + return NULL; + + for (access = master->num.head; access; access = access->next) + if (strcmp (access->name, name) == 0) + return access; + + for (access = master->str.head; access; access = access->next) + if (strcmp (access->name, name) == 0) + return access; + + return NULL; +} + +/* Get access list from list of access_list. If there isn't matched + access_list create new one and return it. */ +static struct access_list * +access_list_get (afi_t afi, const char *name) +{ + struct access_list *access; + + access = access_list_lookup (afi, name); + if (access == NULL) + access = access_list_insert (afi, name); + return access; +} + +/* Apply access list to object (which should be struct prefix *). */ +enum filter_type +access_list_apply (struct access_list *access, void *object) +{ + struct filter *filter; + struct prefix *p; + + p = (struct prefix *) object; + + if (access == NULL) + return FILTER_DENY; + + for (filter = access->head; filter; filter = filter->next) + { + if (filter->cisco) + { + if (filter_match_cisco (filter, p)) + return filter->type; + } + else + { + if (filter_match_zebra (filter, p)) + return filter->type; + } + } + + return FILTER_DENY; +} + +/* Add hook function. */ +void +access_list_add_hook (void (*func) (struct access_list *access)) +{ + access_master_ipv4.add_hook = func; +#ifdef HAVE_IPV6 + access_master_ipv6.add_hook = func; +#endif /* HAVE_IPV6 */ +} + +/* Delete hook function. */ +void +access_list_delete_hook (void (*func) (struct access_list *access)) +{ + access_master_ipv4.delete_hook = func; +#ifdef HAVE_IPV6 + access_master_ipv6.delete_hook = func; +#endif /* HAVE_IPV6 */ +} + +/* Add new filter to the end of specified access_list. */ +static void +access_list_filter_add (struct access_list *access, struct filter *filter) +{ + filter->next = NULL; + filter->prev = access->tail; + + if (access->tail) + access->tail->next = filter; + else + access->head = filter; + access->tail = filter; + + /* Run hook function. */ + if (access->master->add_hook) + (*access->master->add_hook) (access); +} + +/* If access_list has no filter then return 1. */ +static int +access_list_empty (struct access_list *access) +{ + if (access->head == NULL && access->tail == NULL) + return 1; + else + return 0; +} + +/* Delete filter from specified access_list. If there is hook + function execute it. */ +static void +access_list_filter_delete (struct access_list *access, struct filter *filter) +{ + struct access_master *master; + + master = access->master; + + if (filter->next) + filter->next->prev = filter->prev; + else + access->tail = filter->prev; + + if (filter->prev) + filter->prev->next = filter->next; + else + access->head = filter->next; + + filter_free (filter); + + /* 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 + dynamic ? +*/ + +/* + Hostname or A.B.C.D Address to match + any Any source host + host A single host address +*/ + +static struct filter * +filter_lookup_cisco (struct access_list *access, struct filter *mnew) +{ + struct filter *mfilter; + struct filter_cisco *filter; + struct filter_cisco *new; + + new = &mnew->u.cfilter; + + for (mfilter = access->head; mfilter; mfilter = mfilter->next) + { + filter = &mfilter->u.cfilter; + + if (filter->extended) + { + if (mfilter->type == mnew->type + && filter->addr.s_addr == new->addr.s_addr + && filter->addr_mask.s_addr == new->addr_mask.s_addr + && filter->mask.s_addr == new->mask.s_addr + && filter->mask_mask.s_addr == new->mask_mask.s_addr) + return mfilter; + } + else + { + if (mfilter->type == mnew->type + && filter->addr.s_addr == new->addr.s_addr + && filter->addr_mask.s_addr == new->addr_mask.s_addr) + return mfilter; + } + } + + return NULL; +} + +static struct filter * +filter_lookup_zebra (struct access_list *access, struct filter *mnew) +{ + struct filter *mfilter; + struct filter_zebra *filter; + struct filter_zebra *new; + + new = &mnew->u.zfilter; + + for (mfilter = access->head; mfilter; mfilter = mfilter->next) + { + filter = &mfilter->u.zfilter; + + if (filter->exact == new->exact + && mfilter->type == mnew->type + && prefix_same (&filter->prefix, &new->prefix)) + return mfilter; + } + return NULL; +} + +static int +vty_access_list_remark_unset (struct vty *vty, afi_t afi, const char *name) +{ + struct access_list *access; + + access = access_list_lookup (afi, name); + if (! access) + { + vty_out (vty, "%% access-list %s doesn't exist%s", name, + VTY_NEWLINE); + return CMD_WARNING; + } + + if (access->remark) + { + XFREE (MTYPE_TMP, access->remark); + access->remark = NULL; + } + + if (access->head == NULL && access->tail == NULL && access->remark == NULL) + access_list_delete (access); + + return CMD_SUCCESS; +} + +static int +filter_set_cisco (struct vty *vty, const char *name_str, const char *type_str, + const char *addr_str, const char *addr_mask_str, + const char *mask_str, const char *mask_mask_str, + int extended, int set) +{ + int ret; + enum filter_type type; + struct filter *mfilter; + struct filter_cisco *filter; + struct access_list *access; + struct in_addr addr; + struct in_addr addr_mask; + struct in_addr mask; + struct in_addr mask_mask; + + /* Check of filter type. */ + if (strncmp (type_str, "p", 1) == 0) + type = FILTER_PERMIT; + else if (strncmp (type_str, "d", 1) == 0) + type = FILTER_DENY; + else + { + vty_out (vty, "%% filter type must be permit or deny%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ret = inet_aton (addr_str, &addr); + if (ret <= 0) + { + vty_out (vty, "%%Inconsistent address and mask%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + ret = inet_aton (addr_mask_str, &addr_mask); + if (ret <= 0) + { + vty_out (vty, "%%Inconsistent address and mask%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + if (extended) + { + ret = inet_aton (mask_str, &mask); + if (ret <= 0) + { + vty_out (vty, "%%Inconsistent address and mask%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + ret = inet_aton (mask_mask_str, &mask_mask); + if (ret <= 0) + { + vty_out (vty, "%%Inconsistent address and mask%s", + VTY_NEWLINE); + return CMD_WARNING; + } + } + + mfilter = filter_new(); + mfilter->type = type; + mfilter->cisco = 1; + filter = &mfilter->u.cfilter; + filter->extended = extended; + filter->addr.s_addr = addr.s_addr & ~addr_mask.s_addr; + filter->addr_mask.s_addr = addr_mask.s_addr; + + if (extended) + { + filter->mask.s_addr = mask.s_addr & ~mask_mask.s_addr; + filter->mask_mask.s_addr = mask_mask.s_addr; + } + + /* Install new filter to the access_list. */ + access = access_list_get (AFI_IP, name_str); + + if (set) + { + if (filter_lookup_cisco (access, mfilter)) + filter_free (mfilter); + else + access_list_filter_add (access, mfilter); + } + else + { + struct filter *delete_filter; + + delete_filter = filter_lookup_cisco (access, mfilter); + if (delete_filter) + access_list_filter_delete (access, delete_filter); + + filter_free (mfilter); + } + + return CMD_SUCCESS; +} + +/* Standard access-list */ +DEFUN (access_list_standard, + access_list_standard_cmd, + "access-list (<1-99>|<1300-1999>) (deny|permit) A.B.C.D A.B.C.D", + "Add an access list entry\n" + "IP standard access list\n" + "IP standard access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Address to match\n" + "Wildcard bits\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], argv[3], + NULL, NULL, 0, 1); +} + +DEFUN (access_list_standard_nomask, + access_list_standard_nomask_cmd, + "access-list (<1-99>|<1300-1999>) (deny|permit) A.B.C.D", + "Add an access list entry\n" + "IP standard access list\n" + "IP standard access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Address to match\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], "0.0.0.0", + NULL, NULL, 0, 1); +} + +DEFUN (access_list_standard_host, + access_list_standard_host_cmd, + "access-list (<1-99>|<1300-1999>) (deny|permit) host A.B.C.D", + "Add an access list entry\n" + "IP standard access list\n" + "IP standard access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "A single host address\n" + "Address to match\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], "0.0.0.0", + NULL, NULL, 0, 1); +} + +DEFUN (access_list_standard_any, + access_list_standard_any_cmd, + "access-list (<1-99>|<1300-1999>) (deny|permit) any", + "Add an access list entry\n" + "IP standard access list\n" + "IP standard access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any source host\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], "0.0.0.0", + "255.255.255.255", NULL, NULL, 0, 1); +} + +DEFUN (no_access_list_standard, + no_access_list_standard_cmd, + "no access-list (<1-99>|<1300-1999>) (deny|permit) A.B.C.D A.B.C.D", + NO_STR + "Add an access list entry\n" + "IP standard access list\n" + "IP standard access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Address to match\n" + "Wildcard bits\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], argv[3], + NULL, NULL, 0, 0); +} + +DEFUN (no_access_list_standard_nomask, + no_access_list_standard_nomask_cmd, + "no access-list (<1-99>|<1300-1999>) (deny|permit) A.B.C.D", + NO_STR + "Add an access list entry\n" + "IP standard access list\n" + "IP standard access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Address to match\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], "0.0.0.0", + NULL, NULL, 0, 0); +} + +DEFUN (no_access_list_standard_host, + no_access_list_standard_host_cmd, + "no access-list (<1-99>|<1300-1999>) (deny|permit) host A.B.C.D", + NO_STR + "Add an access list entry\n" + "IP standard access list\n" + "IP standard access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "A single host address\n" + "Address to match\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], "0.0.0.0", + NULL, NULL, 0, 0); +} + +DEFUN (no_access_list_standard_any, + no_access_list_standard_any_cmd, + "no access-list (<1-99>|<1300-1999>) (deny|permit) any", + NO_STR + "Add an access list entry\n" + "IP standard access list\n" + "IP standard access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any source host\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], "0.0.0.0", + "255.255.255.255", NULL, NULL, 0, 0); +} + +/* Extended access-list */ +DEFUN (access_list_extended, + access_list_extended_cmd, + "access-list (<100-199>|<2000-2699>) (deny|permit) ip A.B.C.D A.B.C.D A.B.C.D A.B.C.D", + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "Source address\n" + "Source wildcard bits\n" + "Destination address\n" + "Destination Wildcard bits\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], + argv[3], argv[4], argv[5], 1 ,1); +} + +DEFUN (access_list_extended_mask_any, + access_list_extended_mask_any_cmd, + "access-list (<100-199>|<2000-2699>) (deny|permit) ip A.B.C.D A.B.C.D any", + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "Source address\n" + "Source wildcard bits\n" + "Any destination host\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], + argv[3], "0.0.0.0", + "255.255.255.255", 1, 1); +} + +DEFUN (access_list_extended_any_mask, + access_list_extended_any_mask_cmd, + "access-list (<100-199>|<2000-2699>) (deny|permit) ip any A.B.C.D A.B.C.D", + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "Any source host\n" + "Destination address\n" + "Destination Wildcard bits\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], "0.0.0.0", + "255.255.255.255", argv[2], + argv[3], 1, 1); +} + +DEFUN (access_list_extended_any_any, + access_list_extended_any_any_cmd, + "access-list (<100-199>|<2000-2699>) (deny|permit) ip any any", + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "Any source host\n" + "Any destination host\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], "0.0.0.0", + "255.255.255.255", "0.0.0.0", + "255.255.255.255", 1, 1); +} + +DEFUN (access_list_extended_mask_host, + access_list_extended_mask_host_cmd, + "access-list (<100-199>|<2000-2699>) (deny|permit) ip A.B.C.D A.B.C.D host A.B.C.D", + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "Source address\n" + "Source wildcard bits\n" + "A single destination host\n" + "Destination address\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], + argv[3], argv[4], + "0.0.0.0", 1, 1); +} + +DEFUN (access_list_extended_host_mask, + access_list_extended_host_mask_cmd, + "access-list (<100-199>|<2000-2699>) (deny|permit) ip host A.B.C.D A.B.C.D A.B.C.D", + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "A single source host\n" + "Source address\n" + "Destination address\n" + "Destination Wildcard bits\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], + "0.0.0.0", argv[3], + argv[4], 1, 1); +} + +DEFUN (access_list_extended_host_host, + access_list_extended_host_host_cmd, + "access-list (<100-199>|<2000-2699>) (deny|permit) ip host A.B.C.D host A.B.C.D", + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "A single source host\n" + "Source address\n" + "A single destination host\n" + "Destination address\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], + "0.0.0.0", argv[3], + "0.0.0.0", 1, 1); +} + +DEFUN (access_list_extended_any_host, + access_list_extended_any_host_cmd, + "access-list (<100-199>|<2000-2699>) (deny|permit) ip any host A.B.C.D", + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "Any source host\n" + "A single destination host\n" + "Destination address\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], "0.0.0.0", + "255.255.255.255", argv[2], + "0.0.0.0", 1, 1); +} + +DEFUN (access_list_extended_host_any, + access_list_extended_host_any_cmd, + "access-list (<100-199>|<2000-2699>) (deny|permit) ip host A.B.C.D any", + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "A single source host\n" + "Source address\n" + "Any destination host\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], + "0.0.0.0", "0.0.0.0", + "255.255.255.255", 1, 1); +} + +DEFUN (no_access_list_extended, + no_access_list_extended_cmd, + "no access-list (<100-199>|<2000-2699>) (deny|permit) ip A.B.C.D A.B.C.D A.B.C.D A.B.C.D", + NO_STR + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "Source address\n" + "Source wildcard bits\n" + "Destination address\n" + "Destination Wildcard bits\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], + argv[3], argv[4], argv[5], 1, 0); +} + +DEFUN (no_access_list_extended_mask_any, + no_access_list_extended_mask_any_cmd, + "no access-list (<100-199>|<2000-2699>) (deny|permit) ip A.B.C.D A.B.C.D any", + NO_STR + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "Source address\n" + "Source wildcard bits\n" + "Any destination host\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], + argv[3], "0.0.0.0", + "255.255.255.255", 1, 0); +} + +DEFUN (no_access_list_extended_any_mask, + no_access_list_extended_any_mask_cmd, + "no access-list (<100-199>|<2000-2699>) (deny|permit) ip any A.B.C.D A.B.C.D", + NO_STR + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "Any source host\n" + "Destination address\n" + "Destination Wildcard bits\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], "0.0.0.0", + "255.255.255.255", argv[2], + argv[3], 1, 0); +} + +DEFUN (no_access_list_extended_any_any, + no_access_list_extended_any_any_cmd, + "no access-list (<100-199>|<2000-2699>) (deny|permit) ip any any", + NO_STR + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "Any source host\n" + "Any destination host\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], "0.0.0.0", + "255.255.255.255", "0.0.0.0", + "255.255.255.255", 1, 0); +} + +DEFUN (no_access_list_extended_mask_host, + no_access_list_extended_mask_host_cmd, + "no access-list (<100-199>|<2000-2699>) (deny|permit) ip A.B.C.D A.B.C.D host A.B.C.D", + NO_STR + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "Source address\n" + "Source wildcard bits\n" + "A single destination host\n" + "Destination address\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], + argv[3], argv[4], + "0.0.0.0", 1, 0); +} + +DEFUN (no_access_list_extended_host_mask, + no_access_list_extended_host_mask_cmd, + "no access-list (<100-199>|<2000-2699>) (deny|permit) ip host A.B.C.D A.B.C.D A.B.C.D", + NO_STR + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "A single source host\n" + "Source address\n" + "Destination address\n" + "Destination Wildcard bits\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], + "0.0.0.0", argv[3], + argv[4], 1, 0); +} + +DEFUN (no_access_list_extended_host_host, + no_access_list_extended_host_host_cmd, + "no access-list (<100-199>|<2000-2699>) (deny|permit) ip host A.B.C.D host A.B.C.D", + NO_STR + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "A single source host\n" + "Source address\n" + "A single destination host\n" + "Destination address\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], + "0.0.0.0", argv[3], + "0.0.0.0", 1, 0); +} + +DEFUN (no_access_list_extended_any_host, + no_access_list_extended_any_host_cmd, + "no access-list (<100-199>|<2000-2699>) (deny|permit) ip any host A.B.C.D", + NO_STR + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "Any source host\n" + "A single destination host\n" + "Destination address\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], "0.0.0.0", + "255.255.255.255", argv[2], + "0.0.0.0", 1, 0); +} + +DEFUN (no_access_list_extended_host_any, + no_access_list_extended_host_any_cmd, + "no access-list (<100-199>|<2000-2699>) (deny|permit) ip host A.B.C.D any", + NO_STR + "Add an access list entry\n" + "IP extended access list\n" + "IP extended access list (expanded range)\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any Internet Protocol\n" + "A single source host\n" + "Source address\n" + "Any destination host\n") +{ + return filter_set_cisco (vty, argv[0], argv[1], argv[2], + "0.0.0.0", "0.0.0.0", + "255.255.255.255", 1, 0); +} + +static int +filter_set_zebra (struct vty *vty, const char *name_str, const char *type_str, + afi_t afi, const char *prefix_str, int exact, int set) +{ + int ret; + enum filter_type type; + struct filter *mfilter; + struct filter_zebra *filter; + struct access_list *access; + struct prefix p; + + /* Check of filter type. */ + if (strncmp (type_str, "p", 1) == 0) + type = FILTER_PERMIT; + else if (strncmp (type_str, "d", 1) == 0) + type = FILTER_DENY; + else + { + vty_out (vty, "filter type must be [permit|deny]%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Check string format of prefix and prefixlen. */ + if (afi == AFI_IP) + { + ret = str2prefix_ipv4 (prefix_str, (struct prefix_ipv4 *)&p); + if (ret <= 0) + { + vty_out (vty, "IP address prefix/prefixlen is malformed%s", + VTY_NEWLINE); + return CMD_WARNING; + } + } +#ifdef HAVE_IPV6 + else if (afi == AFI_IP6) + { + ret = str2prefix_ipv6 (prefix_str, (struct prefix_ipv6 *) &p); + if (ret <= 0) + { + vty_out (vty, "IPv6 address prefix/prefixlen is malformed%s", + VTY_NEWLINE); + return CMD_WARNING; + } + } +#endif /* HAVE_IPV6 */ + else + return CMD_WARNING; + + mfilter = filter_new (); + mfilter->type = type; + filter = &mfilter->u.zfilter; + prefix_copy (&filter->prefix, &p); + + /* "exact-match" */ + if (exact) + filter->exact = 1; + + /* Install new filter to the access_list. */ + access = access_list_get (afi, name_str); + + if (set) + { + if (filter_lookup_zebra (access, mfilter)) + filter_free (mfilter); + else + access_list_filter_add (access, mfilter); + } + else + { + struct filter *delete_filter; + + delete_filter = filter_lookup_zebra (access, mfilter); + if (delete_filter) + access_list_filter_delete (access, delete_filter); + + filter_free (mfilter); + } + + return CMD_SUCCESS; +} + +/* Zebra access-list */ +DEFUN (access_list, + access_list_cmd, + "access-list WORD (deny|permit) A.B.C.D/M", + "Add an access list entry\n" + "IP zebra access-list name\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Prefix to match. e.g. 10.0.0.0/8\n") +{ + return filter_set_zebra (vty, argv[0], argv[1], AFI_IP, argv[2], 0, 1); +} + +DEFUN (access_list_exact, + access_list_exact_cmd, + "access-list WORD (deny|permit) A.B.C.D/M exact-match", + "Add an access list entry\n" + "IP zebra access-list name\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Prefix to match. e.g. 10.0.0.0/8\n" + "Exact match of the prefixes\n") +{ + return filter_set_zebra (vty, argv[0], argv[1], AFI_IP, argv[2], 1, 1); +} + +DEFUN (access_list_any, + access_list_any_cmd, + "access-list WORD (deny|permit) any", + "Add an access list entry\n" + "IP zebra access-list name\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Prefix to match. e.g. 10.0.0.0/8\n") +{ + return filter_set_zebra (vty, argv[0], argv[1], AFI_IP, "0.0.0.0/0", 0, 1); +} + +DEFUN (no_access_list, + no_access_list_cmd, + "no access-list WORD (deny|permit) A.B.C.D/M", + NO_STR + "Add an access list entry\n" + "IP zebra access-list name\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Prefix to match. e.g. 10.0.0.0/8\n") +{ + return filter_set_zebra (vty, argv[0], argv[1], AFI_IP, argv[2], 0, 0); +} + +DEFUN (no_access_list_exact, + no_access_list_exact_cmd, + "no access-list WORD (deny|permit) A.B.C.D/M exact-match", + NO_STR + "Add an access list entry\n" + "IP zebra access-list name\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Prefix to match. e.g. 10.0.0.0/8\n" + "Exact match of the prefixes\n") +{ + return filter_set_zebra (vty, argv[0], argv[1], AFI_IP, argv[2], 1, 0); +} + +DEFUN (no_access_list_any, + no_access_list_any_cmd, + "no access-list WORD (deny|permit) any", + NO_STR + "Add an access list entry\n" + "IP zebra access-list name\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Prefix to match. e.g. 10.0.0.0/8\n") +{ + return filter_set_zebra (vty, argv[0], argv[1], AFI_IP, "0.0.0.0/0", 0, 0); +} + +DEFUN (no_access_list_all, + no_access_list_all_cmd, + "no access-list (<1-99>|<100-199>|<1300-1999>|<2000-2699>|WORD)", + NO_STR + "Add an access list entry\n" + "IP standard access list\n" + "IP extended access list\n" + "IP standard access list (expanded range)\n" + "IP extended access list (expanded range)\n" + "IP zebra access-list name\n") +{ + struct access_list *access; + struct access_master *master; + + /* Looking up access_list. */ + access = access_list_lookup (AFI_IP, argv[0]); + if (access == NULL) + { + vty_out (vty, "%% access-list %s doesn't exist%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + + master = access->master; + + /* Run hook function. */ + if (master->delete_hook) + (*master->delete_hook) (access); + + /* Delete all filter from access-list. */ + access_list_delete (access); + + return CMD_SUCCESS; +} + +DEFUN (access_list_remark, + access_list_remark_cmd, + "access-list (<1-99>|<100-199>|<1300-1999>|<2000-2699>|WORD) remark .LINE", + "Add an access list entry\n" + "IP standard access list\n" + "IP extended access list\n" + "IP standard access list (expanded range)\n" + "IP extended access list (expanded range)\n" + "IP zebra access-list\n" + "Access list entry comment\n" + "Comment up to 100 characters\n") +{ + struct access_list *access; + + access = access_list_get (AFI_IP, argv[0]); + + if (access->remark) + { + XFREE (MTYPE_TMP, access->remark); + access->remark = NULL; + } + access->remark = argv_concat(argv, argc, 1); + + return CMD_SUCCESS; +} + +DEFUN (no_access_list_remark, + no_access_list_remark_cmd, + "no access-list (<1-99>|<100-199>|<1300-1999>|<2000-2699>|WORD) remark", + NO_STR + "Add an access list entry\n" + "IP standard access list\n" + "IP extended access list\n" + "IP standard access list (expanded range)\n" + "IP extended access list (expanded range)\n" + "IP zebra access-list\n" + "Access list entry comment\n") +{ + return vty_access_list_remark_unset (vty, AFI_IP, argv[0]); +} + +ALIAS (no_access_list_remark, + no_access_list_remark_arg_cmd, + "no access-list (<1-99>|<100-199>|<1300-1999>|<2000-2699>|WORD) remark .LINE", + NO_STR + "Add an access list entry\n" + "IP standard access list\n" + "IP extended access list\n" + "IP standard access list (expanded range)\n" + "IP extended access list (expanded range)\n" + "IP zebra access-list\n" + "Access list entry comment\n" + "Comment up to 100 characters\n") + +#ifdef HAVE_IPV6 +DEFUN (ipv6_access_list, + ipv6_access_list_cmd, + "ipv6 access-list WORD (deny|permit) X:X::X:X/M", + IPV6_STR + "Add an access list entry\n" + "IPv6 zebra access-list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Prefix to match. e.g. 3ffe:506::/32\n") +{ + return filter_set_zebra (vty, argv[0], argv[1], AFI_IP6, argv[2], 0, 1); +} + +DEFUN (ipv6_access_list_exact, + ipv6_access_list_exact_cmd, + "ipv6 access-list WORD (deny|permit) X:X::X:X/M exact-match", + IPV6_STR + "Add an access list entry\n" + "IPv6 zebra access-list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Prefix to match. e.g. 3ffe:506::/32\n" + "Exact match of the prefixes\n") +{ + return filter_set_zebra (vty, argv[0], argv[1], AFI_IP6, argv[2], 1, 1); +} + +DEFUN (ipv6_access_list_any, + ipv6_access_list_any_cmd, + "ipv6 access-list WORD (deny|permit) any", + IPV6_STR + "Add an access list entry\n" + "IPv6 zebra access-list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any prefixi to match\n") +{ + return filter_set_zebra (vty, argv[0], argv[1], AFI_IP6, "::/0", 0, 1); +} + +DEFUN (no_ipv6_access_list, + no_ipv6_access_list_cmd, + "no ipv6 access-list WORD (deny|permit) X:X::X:X/M", + NO_STR + IPV6_STR + "Add an access list entry\n" + "IPv6 zebra access-list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Prefix to match. e.g. 3ffe:506::/32\n") +{ + return filter_set_zebra (vty, argv[0], argv[1], AFI_IP6, argv[2], 0, 0); +} + +DEFUN (no_ipv6_access_list_exact, + no_ipv6_access_list_exact_cmd, + "no ipv6 access-list WORD (deny|permit) X:X::X:X/M exact-match", + NO_STR + IPV6_STR + "Add an access list entry\n" + "IPv6 zebra access-list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Prefix to match. e.g. 3ffe:506::/32\n" + "Exact match of the prefixes\n") +{ + return filter_set_zebra (vty, argv[0], argv[1], AFI_IP6, argv[2], 1, 0); +} + +DEFUN (no_ipv6_access_list_any, + no_ipv6_access_list_any_cmd, + "no ipv6 access-list WORD (deny|permit) any", + NO_STR + IPV6_STR + "Add an access list entry\n" + "IPv6 zebra access-list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "Any prefixi to match\n") +{ + return filter_set_zebra (vty, argv[0], argv[1], AFI_IP6, "::/0", 0, 0); +} + + +DEFUN (no_ipv6_access_list_all, + no_ipv6_access_list_all_cmd, + "no ipv6 access-list WORD", + NO_STR + IPV6_STR + "Add an access list entry\n" + "IPv6 zebra access-list\n") +{ + struct access_list *access; + struct access_master *master; + + /* Looking up access_list. */ + access = access_list_lookup (AFI_IP6, argv[0]); + if (access == NULL) + { + vty_out (vty, "%% access-list %s doesn't exist%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + + master = access->master; + + /* Run hook function. */ + if (master->delete_hook) + (*master->delete_hook) (access); + + /* Delete all filter from access-list. */ + access_list_delete (access); + + return CMD_SUCCESS; +} + +DEFUN (ipv6_access_list_remark, + ipv6_access_list_remark_cmd, + "ipv6 access-list WORD remark .LINE", + IPV6_STR + "Add an access list entry\n" + "IPv6 zebra access-list\n" + "Access list entry comment\n" + "Comment up to 100 characters\n") +{ + struct access_list *access; + + access = access_list_get (AFI_IP6, argv[0]); + + if (access->remark) + { + XFREE (MTYPE_TMP, access->remark); + access->remark = NULL; + } + access->remark = argv_concat(argv, argc, 1); + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_access_list_remark, + no_ipv6_access_list_remark_cmd, + "no ipv6 access-list WORD remark", + NO_STR + IPV6_STR + "Add an access list entry\n" + "IPv6 zebra access-list\n" + "Access list entry comment\n") +{ + return vty_access_list_remark_unset (vty, AFI_IP6, argv[0]); +} + +ALIAS (no_ipv6_access_list_remark, + no_ipv6_access_list_remark_arg_cmd, + "no ipv6 access-list WORD remark .LINE", + NO_STR + IPV6_STR + "Add an access list entry\n" + "IPv6 zebra access-list\n" + "Access list entry comment\n" + "Comment up to 100 characters\n") +#endif /* HAVE_IPV6 */ + +void config_write_access_zebra (struct vty *, struct filter *); +void config_write_access_cisco (struct vty *, struct filter *); + +/* show access-list command. */ +static int +filter_show (struct vty *vty, const char *name, afi_t afi) +{ + struct access_list *access; + struct access_master *master; + struct filter *mfilter; + struct filter_cisco *filter; + int write = 0; + + master = access_master_get (afi); + if (master == NULL) + return 0; + + /* Print the name of the protocol */ + if (zlog_default) + vty_out (vty, "%s:%s", + zlog_proto_names[zlog_default->protocol], VTY_NEWLINE); + + for (access = master->num.head; access; access = access->next) + { + if (name && strcmp (access->name, name) != 0) + continue; + + write = 1; + + for (mfilter = access->head; mfilter; mfilter = mfilter->next) + { + filter = &mfilter->u.cfilter; + + if (write) + { + vty_out (vty, "%s IP%s access list %s%s", + mfilter->cisco ? + (filter->extended ? "Extended" : "Standard") : "Zebra", + afi == AFI_IP6 ? "v6" : "", + access->name, VTY_NEWLINE); + write = 0; + } + + vty_out (vty, " %s%s", filter_type_str (mfilter), + mfilter->type == FILTER_DENY ? " " : ""); + + if (! mfilter->cisco) + config_write_access_zebra (vty, mfilter); + else if (filter->extended) + config_write_access_cisco (vty, mfilter); + else + { + if (filter->addr_mask.s_addr == 0xffffffff) + vty_out (vty, " any%s", VTY_NEWLINE); + else + { + vty_out (vty, " %s", inet_ntoa (filter->addr)); + if (filter->addr_mask.s_addr != 0) + vty_out (vty, ", wildcard bits %s", inet_ntoa (filter->addr_mask)); + vty_out (vty, "%s", VTY_NEWLINE); + } + } + } + } + + for (access = master->str.head; access; access = access->next) + { + if (name && strcmp (access->name, name) != 0) + continue; + + write = 1; + + for (mfilter = access->head; mfilter; mfilter = mfilter->next) + { + filter = &mfilter->u.cfilter; + + if (write) + { + vty_out (vty, "%s IP%s access list %s%s", + mfilter->cisco ? + (filter->extended ? "Extended" : "Standard") : "Zebra", + afi == AFI_IP6 ? "v6" : "", + access->name, VTY_NEWLINE); + write = 0; + } + + vty_out (vty, " %s%s", filter_type_str (mfilter), + mfilter->type == FILTER_DENY ? " " : ""); + + if (! mfilter->cisco) + config_write_access_zebra (vty, mfilter); + else if (filter->extended) + config_write_access_cisco (vty, mfilter); + else + { + if (filter->addr_mask.s_addr == 0xffffffff) + vty_out (vty, " any%s", VTY_NEWLINE); + else + { + vty_out (vty, " %s", inet_ntoa (filter->addr)); + if (filter->addr_mask.s_addr != 0) + vty_out (vty, ", wildcard bits %s", inet_ntoa (filter->addr_mask)); + vty_out (vty, "%s", VTY_NEWLINE); + } + } + } + } + return CMD_SUCCESS; +} + +DEFUN (show_ip_access_list, + show_ip_access_list_cmd, + "show ip access-list", + SHOW_STR + IP_STR + "List IP access lists\n") +{ + return filter_show (vty, NULL, AFI_IP); +} + +DEFUN (show_ip_access_list_name, + show_ip_access_list_name_cmd, + "show ip access-list (<1-99>|<100-199>|<1300-1999>|<2000-2699>|WORD)", + SHOW_STR + IP_STR + "List IP access lists\n" + "IP standard access list\n" + "IP extended access list\n" + "IP standard access list (expanded range)\n" + "IP extended access list (expanded range)\n" + "IP zebra access-list\n") +{ + return filter_show (vty, argv[0], AFI_IP); +} + +#ifdef HAVE_IPV6 +DEFUN (show_ipv6_access_list, + show_ipv6_access_list_cmd, + "show ipv6 access-list", + SHOW_STR + IPV6_STR + "List IPv6 access lists\n") +{ + return filter_show (vty, NULL, AFI_IP6); +} + +DEFUN (show_ipv6_access_list_name, + show_ipv6_access_list_name_cmd, + "show ipv6 access-list WORD", + SHOW_STR + IPV6_STR + "List IPv6 access lists\n" + "IPv6 zebra access-list\n") +{ + return filter_show (vty, argv[0], AFI_IP6); +} +#endif /* HAVE_IPV6 */ + +void +config_write_access_cisco (struct vty *vty, struct filter *mfilter) +{ + struct filter_cisco *filter; + + filter = &mfilter->u.cfilter; + + if (filter->extended) + { + vty_out (vty, " ip"); + if (filter->addr_mask.s_addr == 0xffffffff) + vty_out (vty, " any"); + else if (filter->addr_mask.s_addr == 0) + vty_out (vty, " host %s", inet_ntoa (filter->addr)); + else + { + vty_out (vty, " %s", inet_ntoa (filter->addr)); + vty_out (vty, " %s", inet_ntoa (filter->addr_mask)); + } + + if (filter->mask_mask.s_addr == 0xffffffff) + vty_out (vty, " any"); + else if (filter->mask_mask.s_addr == 0) + vty_out (vty, " host %s", inet_ntoa (filter->mask)); + else + { + vty_out (vty, " %s", inet_ntoa (filter->mask)); + vty_out (vty, " %s", inet_ntoa (filter->mask_mask)); + } + vty_out (vty, "%s", VTY_NEWLINE); + } + else + { + if (filter->addr_mask.s_addr == 0xffffffff) + vty_out (vty, " any%s", VTY_NEWLINE); + else + { + vty_out (vty, " %s", inet_ntoa (filter->addr)); + if (filter->addr_mask.s_addr != 0) + vty_out (vty, " %s", inet_ntoa (filter->addr_mask)); + vty_out (vty, "%s", VTY_NEWLINE); + } + } +} + +void +config_write_access_zebra (struct vty *vty, struct filter *mfilter) +{ + struct filter_zebra *filter; + struct prefix *p; + char buf[BUFSIZ]; + + filter = &mfilter->u.zfilter; + p = &filter->prefix; + + if (p->prefixlen == 0 && ! filter->exact) + vty_out (vty, " any"); + else + vty_out (vty, " %s/%d%s", + inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ), + p->prefixlen, + filter->exact ? " exact-match" : ""); + + vty_out (vty, "%s", VTY_NEWLINE); +} + +static int +config_write_access (struct vty *vty, afi_t afi) +{ + struct access_list *access; + struct access_master *master; + struct filter *mfilter; + int write = 0; + + master = access_master_get (afi); + if (master == NULL) + return 0; + + for (access = master->num.head; access; access = access->next) + { + if (access->remark) + { + vty_out (vty, "%saccess-list %s remark %s%s", + afi == AFI_IP ? "" : "ipv6 ", + access->name, access->remark, + VTY_NEWLINE); + write++; + } + + for (mfilter = access->head; mfilter; mfilter = mfilter->next) + { + vty_out (vty, "%saccess-list %s %s", + afi == AFI_IP ? "" : "ipv6 ", + access->name, + filter_type_str (mfilter)); + + if (mfilter->cisco) + config_write_access_cisco (vty, mfilter); + else + config_write_access_zebra (vty, mfilter); + + write++; + } + } + + for (access = master->str.head; access; access = access->next) + { + if (access->remark) + { + vty_out (vty, "%saccess-list %s remark %s%s", + afi == AFI_IP ? "" : "ipv6 ", + access->name, access->remark, + VTY_NEWLINE); + write++; + } + + for (mfilter = access->head; mfilter; mfilter = mfilter->next) + { + vty_out (vty, "%saccess-list %s %s", + afi == AFI_IP ? "" : "ipv6 ", + access->name, + filter_type_str (mfilter)); + + if (mfilter->cisco) + config_write_access_cisco (vty, mfilter); + else + config_write_access_zebra (vty, mfilter); + + write++; + } + } + return write; +} + +/* Access-list node. */ +static struct cmd_node access_node = +{ + ACCESS_NODE, + "", /* Access list has no interface. */ + 1 +}; + +static int +config_write_access_ipv4 (struct vty *vty) +{ + return config_write_access (vty, AFI_IP); +} + +static void +access_list_reset_ipv4 (void) +{ + struct access_list *access; + struct access_list *next; + struct access_master *master; + + master = access_master_get (AFI_IP); + if (master == NULL) + return; + + for (access = master->num.head; access; access = next) + { + next = access->next; + access_list_delete (access); + } + for (access = master->str.head; access; access = next) + { + next = access->next; + access_list_delete (access); + } + + assert (master->num.head == NULL); + assert (master->num.tail == NULL); + + assert (master->str.head == NULL); + assert (master->str.tail == NULL); +} + +/* Install vty related command. */ +static void +access_list_init_ipv4 (void) +{ + install_node (&access_node, config_write_access_ipv4); + + install_element (ENABLE_NODE, &show_ip_access_list_cmd); + install_element (ENABLE_NODE, &show_ip_access_list_name_cmd); + + /* Zebra access-list */ + install_element (CONFIG_NODE, &access_list_cmd); + install_element (CONFIG_NODE, &access_list_exact_cmd); + install_element (CONFIG_NODE, &access_list_any_cmd); + install_element (CONFIG_NODE, &no_access_list_cmd); + install_element (CONFIG_NODE, &no_access_list_exact_cmd); + install_element (CONFIG_NODE, &no_access_list_any_cmd); + + /* Standard access-list */ + install_element (CONFIG_NODE, &access_list_standard_cmd); + install_element (CONFIG_NODE, &access_list_standard_nomask_cmd); + install_element (CONFIG_NODE, &access_list_standard_host_cmd); + install_element (CONFIG_NODE, &access_list_standard_any_cmd); + install_element (CONFIG_NODE, &no_access_list_standard_cmd); + install_element (CONFIG_NODE, &no_access_list_standard_nomask_cmd); + install_element (CONFIG_NODE, &no_access_list_standard_host_cmd); + install_element (CONFIG_NODE, &no_access_list_standard_any_cmd); + + /* Extended access-list */ + install_element (CONFIG_NODE, &access_list_extended_cmd); + install_element (CONFIG_NODE, &access_list_extended_any_mask_cmd); + install_element (CONFIG_NODE, &access_list_extended_mask_any_cmd); + install_element (CONFIG_NODE, &access_list_extended_any_any_cmd); + install_element (CONFIG_NODE, &access_list_extended_host_mask_cmd); + install_element (CONFIG_NODE, &access_list_extended_mask_host_cmd); + install_element (CONFIG_NODE, &access_list_extended_host_host_cmd); + install_element (CONFIG_NODE, &access_list_extended_any_host_cmd); + install_element (CONFIG_NODE, &access_list_extended_host_any_cmd); + install_element (CONFIG_NODE, &no_access_list_extended_cmd); + install_element (CONFIG_NODE, &no_access_list_extended_any_mask_cmd); + install_element (CONFIG_NODE, &no_access_list_extended_mask_any_cmd); + install_element (CONFIG_NODE, &no_access_list_extended_any_any_cmd); + install_element (CONFIG_NODE, &no_access_list_extended_host_mask_cmd); + install_element (CONFIG_NODE, &no_access_list_extended_mask_host_cmd); + install_element (CONFIG_NODE, &no_access_list_extended_host_host_cmd); + install_element (CONFIG_NODE, &no_access_list_extended_any_host_cmd); + install_element (CONFIG_NODE, &no_access_list_extended_host_any_cmd); + + install_element (CONFIG_NODE, &access_list_remark_cmd); + install_element (CONFIG_NODE, &no_access_list_all_cmd); + install_element (CONFIG_NODE, &no_access_list_remark_cmd); + install_element (CONFIG_NODE, &no_access_list_remark_arg_cmd); +} + +#ifdef HAVE_IPV6 +static struct cmd_node access_ipv6_node = +{ + ACCESS_IPV6_NODE, + "", + 1 +}; + +static int +config_write_access_ipv6 (struct vty *vty) +{ + return config_write_access (vty, AFI_IP6); +} + +static void +access_list_reset_ipv6 (void) +{ + struct access_list *access; + struct access_list *next; + struct access_master *master; + + master = access_master_get (AFI_IP6); + if (master == NULL) + return; + + for (access = master->num.head; access; access = next) + { + next = access->next; + access_list_delete (access); + } + for (access = master->str.head; access; access = next) + { + next = access->next; + access_list_delete (access); + } + + assert (master->num.head == NULL); + assert (master->num.tail == NULL); + + assert (master->str.head == NULL); + assert (master->str.tail == NULL); +} + +static void +access_list_init_ipv6 (void) +{ + install_node (&access_ipv6_node, config_write_access_ipv6); + + install_element (ENABLE_NODE, &show_ipv6_access_list_cmd); + install_element (ENABLE_NODE, &show_ipv6_access_list_name_cmd); + + install_element (CONFIG_NODE, &ipv6_access_list_cmd); + install_element (CONFIG_NODE, &ipv6_access_list_exact_cmd); + install_element (CONFIG_NODE, &ipv6_access_list_any_cmd); + install_element (CONFIG_NODE, &no_ipv6_access_list_exact_cmd); + install_element (CONFIG_NODE, &no_ipv6_access_list_cmd); + install_element (CONFIG_NODE, &no_ipv6_access_list_any_cmd); + + install_element (CONFIG_NODE, &no_ipv6_access_list_all_cmd); + install_element (CONFIG_NODE, &ipv6_access_list_remark_cmd); + install_element (CONFIG_NODE, &no_ipv6_access_list_remark_cmd); + install_element (CONFIG_NODE, &no_ipv6_access_list_remark_arg_cmd); +} +#endif /* HAVE_IPV6 */ + +void +access_list_init () +{ + access_list_init_ipv4 (); +#ifdef HAVE_IPV6 + access_list_init_ipv6(); +#endif /* HAVE_IPV6 */ +} + +void +access_list_reset () +{ + access_list_reset_ipv4 (); +#ifdef HAVE_IPV6 + access_list_reset_ipv6(); +#endif /* HAVE_IPV6 */ +} diff --git a/lib/filter.h b/lib/filter.h new file mode 100644 index 0000000..e6ccd33 --- /dev/null +++ b/lib/filter.h @@ -0,0 +1,72 @@ +/* + * Route filtering function. + * Copyright (C) 1998 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_FILTER_H +#define _ZEBRA_FILTER_H + +#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 +{ + FILTER_DENY, + FILTER_PERMIT, + FILTER_DYNAMIC +}; + +enum access_type +{ + ACCESS_TYPE_STRING, + ACCESS_TYPE_NUMBER +}; + +/* Access list */ +struct access_list +{ + char *name; + char *remark; + + struct access_master *master; + + enum access_type type; + + struct access_list *next; + struct access_list *prev; + + struct filter *head; + struct filter *tail; +}; + +/* Prototypes for access-list. */ +extern void access_list_init (void); +extern void access_list_reset (void); +extern void access_list_add_hook (void (*func)(struct access_list *)); +extern void access_list_delete_hook (void (*func)(struct access_list *)); +extern struct access_list *access_list_lookup (afi_t, const char *); +extern enum filter_type access_list_apply (struct access_list *, void *); + +#endif /* _ZEBRA_FILTER_H */ diff --git a/lib/getopt.c b/lib/getopt.c new file mode 100644 index 0000000..7a58a8a --- /dev/null +++ b/lib/getopt.c @@ -0,0 +1,1056 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to drepper@gnu.org + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98 + Free Software Foundation, Inc. + + NOTE: The canonical source of this file is maintained with the GNU C Library. + Bugs can be reported to bug-glibc@gnu.org. + + 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, 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. */ + +/* 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 + +#include + +#if !defined __STDC__ || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +# ifndef const +# define const +# endif +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +# include +# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +# define ELIDE_CODE +# endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +# include +# include +#endif /* GNU C library. */ + +#ifdef VMS +# include +# if HAVE_STRING_H - 0 +# include +# endif +#endif + +#ifndef _ +/* This is for other GNU distributions with internationalized messages. + When compiling libc, the _ macro is predefined. */ +# ifdef HAVE_LIBINTL_H +# include +# define _(msgid) gettext (msgid) +# else +# define _(msgid) (msgid) +# endif +#endif + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = NULL; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* 1003.2 says this must be 1 before any call. */ +int optind = 1; + +/* Formerly, initialization of getopt depended on optind==0, which + causes problems with re-calling getopt as programs generally don't + know that. */ + +int __getopt_initialized = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return -1 with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* 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. + On some systems, it contains special magic macros that don't work + in GCC. */ +# include +# define my_index strchr +#else + +# if HAVE_STRING_H +# include +# else +# include +# endif + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +#ifndef getenv +extern char *getenv (); +#endif + +static char * +my_index (str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. + If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. + That was relevant to code that was here before. */ +# if (!defined __STDC__ || !__STDC__) && !defined strlen +/* gcc with -traditional declares the built-in strlen to return int, + and has done so at least since version 2.4.5. -- rms. */ +extern int strlen (const char *); +# endif /* not __STDC__ */ +#endif /* __GNUC__ */ + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +#ifdef _LIBC +/* Bash 2.0 gives us an environment variable containing flags + indicating ARGV elements that should not be considered arguments. */ + +/* Defined in getopt_init.c */ +extern char *__getopt_nonoption_flags; + +static int nonoption_flags_max_len; +static int nonoption_flags_len; + +static int original_argc; +static char *const *original_argv; + +/* Make sure the environment variable bash 2.0 puts in the environment + is valid for the getopt call we must make sure that the ARGV passed + to getopt is that one passed to the process. */ +static void +__attribute__ ((unused)) +store_args_and_env (int argc, char *const *argv) +{ + /* XXX This is no good solution. We should rather copy the args so + that we can compare them later. But we must not use malloc(3). */ + original_argc = argc; + original_argv = argv; +} +# ifdef text_set_element +text_set_element (__libc_subinit, store_args_and_env); +# endif /* text_set_element */ + +# define SWAP_FLAGS(ch1, ch2) \ + if (nonoption_flags_len > 0) \ + { \ + char __tmp = __getopt_nonoption_flags[ch1]; \ + __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ + __getopt_nonoption_flags[ch2] = __tmp; \ + } +#else /* !_LIBC */ +# define SWAP_FLAGS(ch1, ch2) +#endif /* _LIBC */ + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +#if defined __STDC__ && __STDC__ +static void exchange (char **); +#endif + +static void +exchange (argv) + char **argv; +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + +#ifdef _LIBC + /* First make sure the handling of the `__getopt_nonoption_flags' + string can work normally. Our top argument must be in the range + of the string. */ + if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) + { + /* We must extend the array. The user plays games with us and + presents new arguments. */ + char *new_str = malloc (top + 1); + if (new_str == NULL) + nonoption_flags_len = nonoption_flags_max_len = 0; + else + { + memset (__mempcpy (new_str, __getopt_nonoption_flags, + nonoption_flags_max_len), + '\0', top + 1 - nonoption_flags_max_len); + nonoption_flags_max_len = top + 1; + __getopt_nonoption_flags = new_str; + } + } +#endif + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + SWAP_FLAGS (bottom + i, middle + i); + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Initialize the internal data when the first call is made. */ + +#if defined __STDC__ && __STDC__ +static const char *_getopt_initialize (int, char *const *, const char *); +#endif +static const char * +_getopt_initialize (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = optind; + + nextchar = NULL; + + posixly_correct = getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + +#ifdef _LIBC + if (posixly_correct == NULL + && argc == original_argc && argv == original_argv) + { + if (nonoption_flags_max_len == 0) + { + if (__getopt_nonoption_flags == NULL + || __getopt_nonoption_flags[0] == '\0') + nonoption_flags_max_len = -1; + else + { + const char *orig_str = __getopt_nonoption_flags; + int len = nonoption_flags_max_len = strlen (orig_str); + if (nonoption_flags_max_len < argc) + nonoption_flags_max_len = argc; + __getopt_nonoption_flags = + (char *) malloc (nonoption_flags_max_len); + if (__getopt_nonoption_flags == NULL) + nonoption_flags_max_len = -1; + else + memset (__mempcpy (__getopt_nonoption_flags, orig_str, len), + '\0', nonoption_flags_max_len - len); + } + } + nonoption_flags_len = nonoption_flags_max_len; + } + else + nonoption_flags_len = 0; +#endif + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns -1. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + optarg = NULL; + + if (optind == 0 || !__getopt_initialized) + { + if (optind == 0) + optind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = _getopt_initialize (argc, argv, optstring); + __getopt_initialized = 1; + } + + /* Test whether ARGV[optind] points to a non-option argument. + Either it does not have option syntax, or there is an environment flag + from the shell indicating it is not an option. The later information + is only used when the used in the GNU libc. */ +#ifdef _LIBC +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ + || (optind < nonoption_flags_len \ + && __getopt_nonoption_flags[optind] == '1')) +#else +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') +#endif + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been + moved back by the user (who may also have changed the arguments). */ + if (last_nonopt > optind) + last_nonopt = optind; + if (first_nonopt > optind) + first_nonopt = optind; + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc && NONOPTION_P) + optind++; + last_nonopt = optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return -1; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if (NONOPTION_P) + { + if (ordering == REQUIRE_ORDER) + return -1; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[optind][1] == '-' + || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) + == (unsigned int) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + optopt = 0; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + _("%s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + _("%s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[optind - 1][0], pfound->name); + } + + nextchar += strlen (nextchar); + + optopt = pfound->val; + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, _("%s: unrecognized option `--%s'\n"), + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + optopt = 0; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { + if (posixly_correct) + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: illegal option -- %c\n"), + argv[0], c); + else + fprintf (stderr, _("%s: invalid option -- %c\n"), + argv[0], c); + } + optopt = c; + return '?'; + } + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == 'W' && temp[1] == ';') + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; + + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + + /* optarg is now the argument, see if it's in the + table of longopts. */ + + for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + if (pfound != NULL) + { + option_index = indfound; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + fprintf (stderr, _("\ +%s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name); + + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + nextchar = NULL; + return 'W'; /* Let the application handle it. */ + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, + _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +#ifdef REALLY_NEED_PLAIN_GETOPT + +int +getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +#endif /* REALLY_NEED_PLAIN_GETOPT */ + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == -1) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/lib/getopt.h b/lib/getopt.h new file mode 100644 index 0000000..b359a47 --- /dev/null +++ b/lib/getopt.h @@ -0,0 +1,159 @@ +/* Declarations for getopt. + Copyright (C) 1989,90,91,92,93,94,96,97 Free Software Foundation, Inc. + + NOTE: The canonical source of this file is maintained with the GNU C Library. + Bugs can be reported to bug-glibc@gnu.org. + + 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, 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 _GETOPT_H +#define _GETOPT_H 1 + +/* + * The operating system may or may not provide getopt_long(), and if + * so it may or may not be a version we are willing to use. Our + * strategy is to declare getopt here, and then provide code unless + * the supplied version is adequate. The difficult case is when a + * declaration for getopt is provided, as our declaration must match. + * + * XXX Arguably this version should be named differently, and the + * local names defined to refer to the system version when we choose + * to use the system version. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ +#if defined (__STDC__) && __STDC__ + const char *name; +#else + char *name; +#endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +#if defined (__STDC__) && __STDC__ + +#if REALLY_NEED_PLAIN_GETOPT + +/* + * getopt is defined in POSIX.2. Assume that if the system defines + * getopt that it complies with POSIX.2. If not, an autoconf test + * should be written to define NONPOSIX_GETOPT_DEFINITION. + */ +#ifndef NONPOSIX_GETOPT_DEFINITION +extern int getopt (int argc, char *const *argv, const char *shortopts); +#else /* NONPOSIX_GETOPT_DEFINITION */ +extern int getopt (void); +#endif /* NONPOSIX_GETOPT_DEFINITION */ + +#endif + + +extern int getopt_long (int argc, char *const *argv, const char *shortopts, + const struct option *longopts, int *longind); +extern int getopt_long_only (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind); + +/* Internal only. Users should not call this directly. */ +extern int _getopt_internal (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind, + int long_only); +#else /* not __STDC__ */ + +#ifdef REALLY_NEED_PLAIN_GETOPT +extern int getopt (); +#endif /* REALLY_NEED_PLAIN_GETOPT */ + +extern int getopt_long (); +extern int getopt_long_only (); + +extern int _getopt_internal (); + +#endif /* __STDC__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* getopt.h */ diff --git a/lib/getopt1.c b/lib/getopt1.c new file mode 100644 index 0000000..bd3099e --- /dev/null +++ b/lib/getopt1.c @@ -0,0 +1,187 @@ +/* getopt_long and getopt_long_only entry points for GNU getopt. + Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98 + Free Software Foundation, Inc. + + NOTE: The canonical source of this file is maintained with the GNU C Library. + Bugs can be reported to bug-glibc@gnu.org. + + 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, 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 "getopt.h" + +#if !defined __STDC__ || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +#include +#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +#define ELIDE_CODE +#endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +#include +#endif + +#ifndef NULL +#define NULL 0 +#endif + +int +getopt_long (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +getopt_long_only (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 1); +} + + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +#include + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = + { + {"add", 1, 0, 0}, + {"append", 0, 0, 0}, + {"delete", 1, 0, 0}, + {"verbose", 0, 0, 0}, + {"create", 0, 0, 0}, + {"file", 1, 0, 0}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, "abc:d:0123456789", + long_options, &option_index); + if (c == -1) + break; + + switch (c) + { + case 0: + printf ("option %s", long_options[option_index].name); + if (optarg) + printf (" with arg %s", optarg); + printf ("\n"); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case 'd': + printf ("option d with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/lib/gitversion.pl b/lib/gitversion.pl new file mode 100644 index 0000000..8ddd9ff --- /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 < + +#include "hash.h" +#include "memory.h" + +/* Allocate a new hash. */ +struct hash * +hash_create_size (unsigned int size, unsigned int (*hash_key) (void *), + int (*hash_cmp) (const void *, const void *)) +{ + struct hash *hash; + + assert ((size & (size-1)) == 0); + hash = XMALLOC (MTYPE_HASH, sizeof (struct hash)); + hash->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; + + return hash; +} + +/* Allocate a new hash with default hash size. */ +struct hash * +hash_create (unsigned int (*hash_key) (void *), + int (*hash_cmp) (const void *, const void *)) +{ + return hash_create_size (HASH_INITIAL_SIZE, hash_key, hash_cmp); +} + +/* Utility function for hash_get(). When this function is specified + as alloc_func, return arugment as it is. This function is used for + intern already allocated value. */ +void * +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. */ +void * +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 - 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; + ++len; + } + + if (alloc_func) + { + newdata = (*alloc_func) (data); + 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; + backet->next = hash->index[index]; + hash->index[index] = backet; + hash->count++; + return backet->data; + } + return NULL; +} + +/* Hash lookup. */ +void * +hash_lookup (struct hash *hash, void *data) +{ + return hash_get (hash, data, NULL); +} + +/* Simple Bernstein hash which is simple and fast for common case */ +unsigned int string_hash_make (const char *str) +{ + unsigned int hash = 0; + + while (*str) + hash = (hash * 33) ^ (unsigned int) *str++; + + return hash; +} + +/* This function release registered value from specified hash. When + release is successfully finished, return the data pointer in the + hash backet. */ +void * +hash_release (struct hash *hash, void *data) +{ + void *ret; + unsigned int key; + unsigned int index; + struct hash_backet *backet; + struct hash_backet *pp; + + key = (*hash->hash_key) (data); + index = key & (hash->size - 1); + + for (backet = pp = hash->index[index]; backet; backet = backet->next) + { + if (backet->key == key && (*hash->hash_cmp) (backet->data, data)) + { + if (backet == pp) + hash->index[index] = backet->next; + else + pp->next = backet->next; + + ret = backet->data; + XFREE (MTYPE_HASH_BACKET, backet); + hash->count--; + return ret; + } + pp = backet; + } + return NULL; +} + +/* Iterator function for hash. */ +void +hash_iterate (struct hash *hash, + void (*func) (struct hash_backet *, void *), void *arg) +{ + unsigned int i; + struct hash_backet *hb; + struct hash_backet *hbnext; + + for (i = 0; i < hash->size; i++) + for (hb = hash->index[i]; hb; hb = hbnext) + { + /* get pointer to next hash backet here, in case (*func) + * decides to delete hb by calling hash_release + */ + hbnext = hb->next; + (*func) (hb, arg); + } +} + +/* Clean up hash. */ +void +hash_clean (struct hash *hash, void (*free_func) (void *)) +{ + unsigned int i; + struct hash_backet *hb; + struct hash_backet *next; + + for (i = 0; i < hash->size; i++) + { + for (hb = hash->index[i]; hb; hb = next) + { + next = hb->next; + + if (free_func) + (*free_func) (hb->data); + + XFREE (MTYPE_HASH_BACKET, hb); + hash->count--; + } + hash->index[i] = NULL; + } +} + +/* Free hash memory. You may call hash_clean before call this + function. */ +void +hash_free (struct hash *hash) +{ + XFREE (MTYPE_HASH_INDEX, hash->index); + XFREE (MTYPE_HASH, hash); +} diff --git a/lib/hash.h b/lib/hash.h new file mode 100644 index 0000000..920c668 --- /dev/null +++ b/lib/hash.h @@ -0,0 +1,79 @@ +/* Hash routine. + Copyright (C) 1998 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_HASH_H +#define _ZEBRA_HASH_H + +/* Default hash table size. */ +#define HASH_INITIAL_SIZE 256 /* initial number of backets. */ +#define HASH_THRESHOLD 10 /* expand when backet. */ + +struct hash_backet +{ + /* Linked list. */ + struct hash_backet *next; + + /* Hash key. */ + unsigned int key; + + /* Data. */ + void *data; +}; + +struct hash +{ + /* Hash backet. */ + struct hash_backet **index; + + /* 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 *); + + /* Data compare function. */ + int (*hash_cmp) (const void *, const void *); + + /* Backet alloc. */ + unsigned long count; +}; + +extern struct hash *hash_create (unsigned int (*) (void *), + int (*) (const void *, const void *)); +extern struct hash *hash_create_size (unsigned int, unsigned int (*) (void *), + int (*) (const void *, const void *)); + +extern void *hash_get (struct hash *, void *, void * (*) (void *)); +extern void *hash_alloc_intern (void *); +extern void *hash_lookup (struct hash *, void *); +extern void *hash_release (struct hash *, void *); + +extern void hash_iterate (struct hash *, + void (*) (struct hash_backet *, void *), void *); + +extern void hash_clean (struct hash *, void (*) (void *)); +extern void hash_free (struct hash *); + +extern unsigned int string_hash_make (const char *); + +#endif /* _ZEBRA_HASH_H */ diff --git a/lib/if.c b/lib/if.c new file mode 100644 index 0000000..0fc4b60 --- /dev/null +++ b/lib/if.c @@ -0,0 +1,1171 @@ + +/* + * Interface functions. + * 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 "linklist.h" +#include "vector.h" +#include "vty.h" +#include "command.h" +#include "vrf.h" +#include "if.h" +#include "sockunion.h" +#include "prefix.h" +#include "memory.h" +#include "table.h" +#include "buffer.h" +#include "str.h" +#include "log.h" + +/* List of interfaces in only the default VRF */ +struct list *iflist; + +/* One for each program. This structure is needed to store hooks. */ +struct if_master +{ + int (*if_new_hook) (struct interface *); + int (*if_delete_hook) (struct interface *); +} 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 + * alphabetic prefix and a numeric suffix. The primary sort key is + * lexicographic by name, and then numeric by number. No number sorts + * before all numbers. Examples: de0 < de1, de100 < fxp0 < xl0, devpty < + * devpty0, de0 < del0 + */ +int +if_cmp_func (struct interface *ifp1, struct interface *ifp2) +{ + unsigned int l1, l2; + long int x1, x2; + char *p1, *p2; + int res; + + p1 = ifp1->name; + p2 = ifp2->name; + + while (*p1 && *p2) { + /* look up to any number */ + l1 = strcspn(p1, "0123456789"); + l2 = strcspn(p2, "0123456789"); + + /* name lengths are different -> compare names */ + if (l1 != l2) + return (strcmp(p1, p2)); + + /* Note that this relies on all numbers being less than all letters, so + * that de0 < del0. + */ + res = strncmp(p1, p2, l1); + + /* names are different -> compare them */ + if (res) + return res; + + /* with identical name part, go to numeric part */ + p1 += l1; + p2 += l1; + + if (!*p1) + return -1; + if (!*p2) + return 1; + + x1 = strtol(p1, &p1, 10); + x2 = strtol(p2, &p2, 10); + + /* let's compare numbers now */ + if (x1 < x2) + return -1; + if (x1 > x2) + return 1; + + /* numbers were equal, lets do it again.. + (it happens with name like "eth123.456:789") */ + } + if (*p1) + return 1; + if (*p2) + return -1; + return 0; +} + +/* Create new interface structure. */ +struct interface * +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; + + assert (name); + assert (namelen <= INTERFACE_NAMSIZ); /* Need space for '\0' at end. */ + strncpy (ifp->name, name, namelen); + ifp->name[namelen] = '\0'; + 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 in VRF %u!", ifp->name, vrf_id); + ifp->connected = list_new (); + ifp->connected->del = (void (*) (void *)) connected_free; + + if (if_master.if_new_hook) + (*if_master.if_new_hook) (ifp); + + 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) +{ + if (if_master.if_delete_hook) + (*if_master.if_delete_hook) (ifp); + + /* Free connected address list */ + list_delete_all_node (ifp->connected); +} + +/* Delete and free interface structure. */ +void +if_delete (struct interface *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); +} + +/* Add hook to interface master. */ +void +if_add_hook (int type, int (*func)(struct interface *ifp)) +{ + switch (type) { + case IF_NEW_HOOK: + if_master.if_new_hook = func; + break; + case IF_DELETE_HOOK: + if_master.if_delete_hook = func; + break; + default: + break; + } +} + +/* Interface existance check by index. */ +struct interface * +if_lookup_by_index_vrf (ifindex_t ifindex, vrf_id_t vrf_id) +{ + struct listnode *node; + struct interface *ifp; + + for (ALL_LIST_ELEMENTS_RO (vrf_iflist (vrf_id), node, ifp)) + { + 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_vrf (ifindex_t ifindex, vrf_id_t vrf_id) +{ + struct interface *ifp; + + return ((ifp = if_lookup_by_index_vrf (ifindex, vrf_id)) != NULL) ? + ifp->name : "unknown"; +} + +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_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_vrf (const char *name, vrf_id_t vrf_id) +{ + struct listnode *node; + struct interface *ifp; + + if (name) + for (ALL_LIST_ELEMENTS_RO (vrf_iflist (vrf_id), node, ifp)) + { + if (strcmp(name, ifp->name) == 0) + return ifp; + } + return NULL; +} + +struct interface * +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; + + if (namelen > INTERFACE_NAMSIZ) + return NULL; + + for (ALL_LIST_ELEMENTS_RO (vrf_iflist (vrf_id), node, ifp)) + { + if (!memcmp(name, ifp->name, namelen) && (ifp->name[namelen] == '\0')) + return ifp; + } + 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_vrf (struct in_addr src, vrf_id_t vrf_id) +{ + struct listnode *node; + struct listnode *cnode; + struct interface *ifp; + struct prefix *p; + struct connected *c; + + for (ALL_LIST_ELEMENTS_RO (vrf_iflist (vrf_id), node, ifp)) + { + for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, c)) + { + p = c->address; + + if (p && p->family == AF_INET) + { + if (IPV4_ADDR_SAME (&p->u.prefix4, &src)) + return ifp; + } + } + } + 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_vrf (struct in_addr src, vrf_id_t vrf_id) +{ + struct listnode *node; + struct prefix addr; + int bestlen = 0; + struct listnode *cnode; + struct interface *ifp; + struct connected *c; + struct interface *match; + + addr.family = AF_INET; + addr.u.prefix4 = src; + addr.prefixlen = IPV4_MAX_BITLEN; + + match = NULL; + + for (ALL_LIST_ELEMENTS_RO (vrf_iflist (vrf_id), node, ifp)) + { + for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, c)) + { + if (c->address && (c->address->family == AF_INET) && + prefix_match(CONNECTED_PREFIX(c), &addr) && + (c->address->prefixlen > bestlen)) + { + bestlen = c->address->prefixlen; + match = ifp; + } + } + } + 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_vrf (const char *name, vrf_id_t vrf_id) +{ + struct interface *ifp; + + 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_vrf (const char *name, size_t namelen, vrf_id_t vrf_id) +{ + struct interface *ifp; + + 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 ? */ +int +if_is_up (struct interface *ifp) +{ + return ifp->flags & IFF_UP; +} + +/* Is interface running? */ +int +if_is_running (struct interface *ifp) +{ + return ifp->flags & IFF_RUNNING; +} + +/* Is the interface operative, eg. either UP & RUNNING + or UP & !ZEBRA_INTERFACE_LINK_DETECTION */ +int +if_is_operative (struct interface *ifp) +{ + return ((ifp->flags & IFF_UP) && + (ifp->flags & IFF_RUNNING || !CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION))); +} + +/* Is this loopback interface ? */ +int +if_is_loopback (struct interface *ifp) +{ + /* XXX: Do this better, eg what if IFF_WHATEVER means X on platform M + * but Y on platform N? + */ + return (ifp->flags & (IFF_LOOPBACK|IFF_NOXMIT|IFF_VIRTUAL)); +} + +/* Does this interface support broadcast ? */ +int +if_is_broadcast (struct interface *ifp) +{ + return ifp->flags & IFF_BROADCAST; +} + +/* Does this interface support broadcast ? */ +int +if_is_pointopoint (struct interface *ifp) +{ + return ifp->flags & IFF_POINTOPOINT; +} + +/* Does this interface support multicast ? */ +int +if_is_multicast (struct interface *ifp) +{ + return ifp->flags & IFF_MULTICAST; +} + +/* Printout flag information into log */ +const char * +if_flag_dump (unsigned long flag) +{ + int separator = 0; + static char logbuf[BUFSIZ]; + +#define IFF_OUT_LOG(X,STR) \ + if (flag & (X)) \ + { \ + if (separator) \ + strlcat (logbuf, ",", BUFSIZ); \ + else \ + separator = 1; \ + strlcat (logbuf, STR, BUFSIZ); \ + } + + strlcpy (logbuf, "<", BUFSIZ); + IFF_OUT_LOG (IFF_UP, "UP"); + IFF_OUT_LOG (IFF_BROADCAST, "BROADCAST"); + IFF_OUT_LOG (IFF_DEBUG, "DEBUG"); + IFF_OUT_LOG (IFF_LOOPBACK, "LOOPBACK"); + IFF_OUT_LOG (IFF_POINTOPOINT, "POINTOPOINT"); + IFF_OUT_LOG (IFF_NOTRAILERS, "NOTRAILERS"); + IFF_OUT_LOG (IFF_RUNNING, "RUNNING"); + IFF_OUT_LOG (IFF_NOARP, "NOARP"); + IFF_OUT_LOG (IFF_PROMISC, "PROMISC"); + IFF_OUT_LOG (IFF_ALLMULTI, "ALLMULTI"); + IFF_OUT_LOG (IFF_OACTIVE, "OACTIVE"); + IFF_OUT_LOG (IFF_SIMPLEX, "SIMPLEX"); + IFF_OUT_LOG (IFF_LINK0, "LINK0"); + IFF_OUT_LOG (IFF_LINK1, "LINK1"); + IFF_OUT_LOG (IFF_LINK2, "LINK2"); + IFF_OUT_LOG (IFF_MULTICAST, "MULTICAST"); + IFF_OUT_LOG (IFF_NOXMIT, "NOXMIT"); + IFF_OUT_LOG (IFF_NORTEXCH, "NORTEXCH"); + IFF_OUT_LOG (IFF_VIRTUAL, "VIRTUAL"); + IFF_OUT_LOG (IFF_IPV4, "IPv4"); + IFF_OUT_LOG (IFF_IPV6, "IPv6"); + + strlcat (logbuf, ">", BUFSIZ); + + return logbuf; +#undef IFF_OUT_LOG +} + +/* For debugging */ +static void +if_dump (const struct interface *ifp) +{ + struct listnode *node; + struct connected *c __attribute__((unused)); + + for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, c)) + zlog_info ("Interface %s vrf %u index %d metric %d mtu %d " +#ifdef HAVE_IPV6 + "mtu6 %d " +#endif /* HAVE_IPV6 */ + "%s", + ifp->name, ifp->vrf_id, ifp->ifindex, ifp->metric, ifp->mtu, +#ifdef HAVE_IPV6 + ifp->mtu6, +#endif /* HAVE_IPV6 */ + if_flag_dump (ifp->flags)); +} + +/* Interface printing for all interface. */ +void +if_dump_all (void) +{ + struct list *intf_list; + struct listnode *node; + void *p; + vrf_iter_t iter; + + 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, + interface_desc_cmd, + "description .LINE", + "Interface specific description\n" + "Characters describing this interface\n") +{ + struct interface *ifp; + + if (argc == 0) + return CMD_SUCCESS; + + ifp = vty->index; + if (ifp->desc) + XFREE (MTYPE_TMP, ifp->desc); + ifp->desc = argv_concat(argv, argc, 0); + + return CMD_SUCCESS; +} + +DEFUN (no_interface_desc, + no_interface_desc_cmd, + "no description", + NO_STR + "Interface specific description\n") +{ + struct interface *ifp; + + ifp = vty->index; + if (ifp->desc) + XFREE (MTYPE_TMP, ifp->desc); + ifp->desc = NULL; + + 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 + * file may be full of 'interface fooX:Y'. Solaris however does not + * expose logical interfaces via PF_ROUTE, so trying to track logical + * interfaces can be fruitless, for that reason Quagga only tracks + * the primary IP interface. + * + * We try accomodate SUNWzebra by: + * - looking up the interface name, to see whether it exists, if so + * its useable + * - for protocol daemons, this could only because zebra told us of + * the interface + * - for zebra, only because it learnt from kernel + * - if not: + * - search the name to see if it contains a sub-ipif / logical interface + * seperator, the ':' char. If it does: + * - text up to that char must be the primary name - get that name. + * if not: + * - no idea, just get the name in its entirety. + */ +static struct interface * +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_vrf (name, nlen, vrf_id)) != NULL) + return ifp; + + /* hunt the primary interface name... */ + while (seppos < nlen && name[seppos] != ':') + seppos++; + + /* Wont catch seperator as last char, e.g. 'foo0:' but thats invalid */ + if (seppos < nlen) + return if_get_by_name_len_vrf (name, seppos, vrf_id); + else + return if_get_by_name_len_vrf (name, nlen, vrf_id); +} +#endif /* SUNOS_5 */ + +DEFUN (interface, + interface_cmd, + "interface IFNAME", + "Select an interface to configure\n" + "Interface's name\n") +{ + struct interface *ifp; + size_t sl; + vrf_id_t vrf_id = VRF_DEFAULT; + + if ((sl = strlen(argv[0])) > INTERFACE_NAMSIZ) + { + vty_out (vty, "%% Interface name %s is invalid: length exceeds " + "%d characters%s", + argv[0], INTERFACE_NAMSIZ, VTY_NEWLINE); + 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, vrf_id); +#else + ifp = if_get_by_name_len_vrf (argv[0], sl, vrf_id); +#endif /* SUNOS_5 */ + + vty->index = ifp; + vty->node = INTERFACE_NODE; + + 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", + NO_STR + "Delete a pseudo interface's configuration\n" + "Interface's name\n") +{ + // deleting interface + struct interface *ifp; + vrf_id_t vrf_id = VRF_DEFAULT; + + 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) + { + vty_out (vty, "%% Interface %s does not exist%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + if (CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE)) + { + vty_out (vty, "%% Only inactive interfaces can be deleted%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + if_delete(ifp); + + 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, + "show address", + SHOW_STR + "address\n") +{ + struct listnode *node; + struct listnode *node2; + 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 (vrf_iflist (vrf_id), 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; +} + +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) +{ + return XCALLOC (MTYPE_CONNECTED, sizeof (struct connected)); +} + +/* Free connected structure. */ +void +connected_free (struct connected *connected) +{ + if (connected->address) + prefix_free (connected->address); + + if (connected->destination) + prefix_free (connected->destination); + + if (connected->label) + XFREE (MTYPE_CONNECTED_LABEL, connected->label); + + XFREE (MTYPE_CONNECTED, connected); +} + +/* Print if_addr structure. */ +static void __attribute__ ((unused)) +connected_log (struct connected *connected, char *str) +{ + struct prefix *p; + struct interface *ifp; + char logbuf[BUFSIZ]; + char buf[BUFSIZ]; + + ifp = connected->ifp; + p = connected->address; + + 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); + + p = connected->destination; + if (p) + { + strncat (logbuf, inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ), + BUFSIZ - strlen(logbuf)); + } + zlog (NULL, LOG_INFO, "%s", logbuf); +} + +/* If two connected address has same prefix return 1. */ +static int +connected_same_prefix (struct prefix *p1, struct prefix *p2) +{ + if (p1->family == p2->family) + { + if (p1->family == AF_INET && + IPV4_ADDR_SAME (&p1->u.prefix4, &p2->u.prefix4)) + return 1; +#ifdef HAVE_IPV6 + if (p1->family == AF_INET6 && + IPV6_ADDR_SAME (&p1->u.prefix6, &p2->u.prefix6)) + return 1; +#endif /* HAVE_IPV6 */ + } + return 0; +} + +struct connected * +connected_delete_by_prefix (struct interface *ifp, struct prefix *p) +{ + struct listnode *node; + struct listnode *next; + struct connected *ifc; + + /* In case of same prefix come, replace it with new one. */ + for (node = listhead (ifp->connected); node; node = next) + { + ifc = listgetdata (node); + next = node->next; + + if (connected_same_prefix (ifc->address, p)) + { + listnode_delete (ifp->connected, ifc); + return ifc; + } + } + return NULL; +} + +/* Find the IPv4 address on our side that will be used when packets + are sent to dst. */ +struct connected * +connected_lookup_address (struct interface *ifp, struct in_addr dst) +{ + struct prefix addr; + struct listnode *cnode; + struct connected *c; + struct connected *match; + + addr.family = AF_INET; + addr.u.prefix4 = dst; + addr.prefixlen = IPV4_MAX_BITLEN; + + match = NULL; + + for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, c)) + { + if (c->address && (c->address->family == AF_INET) && + prefix_match(CONNECTED_PREFIX(c), &addr) && + (!match || (c->address->prefixlen > match->address->prefixlen))) + match = c; + } + return match; +} + +struct connected * +connected_add_by_prefix (struct interface *ifp, struct prefix *p, + struct prefix *destination) +{ + struct connected *ifc; + + /* Allocate new connected address. */ + ifc = connected_new (); + ifc->ifp = ifp; + + /* Fetch interface address */ + ifc->address = prefix_new(); + memcpy (ifc->address, p, sizeof(struct prefix)); + + /* Fetch dest address */ + if (destination) + { + ifc->destination = prefix_new(); + memcpy (ifc->destination, destination, sizeof(struct prefix)); + } + + /* Add connected address to the interface. */ + listnode_add (ifp->connected, ifc); + return ifc; +} + +#ifndef HAVE_IF_NAMETOINDEX +ifindex_t +if_nametoindex (const char *name) +{ + struct interface *ifp; + + return ((ifp = if_lookup_by_name_len(name, strnlen(name, IFNAMSIZ))) != NULL) + ? ifp->ifindex : 0; +} +#endif + +#ifndef HAVE_IF_INDEXTONAME +char * +if_indextoname (ifindex_t ifindex, char *name) +{ + struct interface *ifp; + + if (!(ifp = if_lookup_by_index(ifindex))) + return NULL; + strncpy (name, ifp->name, IFNAMSIZ); + 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.. + */ +/* Interface looking up by interface's address. */ +/* Interface's IPv4 address reverse lookup table. */ +struct route_table *ifaddr_ipv4_table; +/* struct route_table *ifaddr_ipv6_table; */ + +static void +ifaddr_ipv4_add (struct in_addr *ifaddr, struct interface *ifp) +{ + struct route_node *rn; + struct prefix_ipv4 p; + + p.family = AF_INET; + p.prefixlen = IPV4_MAX_PREFIXLEN; + p.prefix = *ifaddr; + + rn = route_node_get (ifaddr_ipv4_table, (struct prefix *) &p); + if (rn) + { + route_unlock_node (rn); + zlog_info ("ifaddr_ipv4_add(): address %s is already added", + inet_ntoa (*ifaddr)); + return; + } + rn->info = ifp; +} + +static void +ifaddr_ipv4_delete (struct in_addr *ifaddr, struct interface *ifp) +{ + struct route_node *rn; + struct prefix_ipv4 p; + + p.family = AF_INET; + p.prefixlen = IPV4_MAX_PREFIXLEN; + p.prefix = *ifaddr; + + rn = route_node_lookup (ifaddr_ipv4_table, (struct prefix *) &p); + if (! rn) + { + zlog_info ("ifaddr_ipv4_delete(): can't find address %s", + inet_ntoa (*ifaddr)); + return; + } + rn->info = NULL; + route_unlock_node (rn); + route_unlock_node (rn); +} + +/* Lookup interface by interface's IP address or interface index. */ +static struct interface * +ifaddr_ipv4_lookup (struct in_addr *addr, ifindex_t ifindex) +{ + struct prefix_ipv4 p; + struct route_node *rn; + struct interface *ifp; + + if (addr) + { + p.family = AF_INET; + p.prefixlen = IPV4_MAX_PREFIXLEN; + p.prefix = *addr; + + rn = route_node_lookup (ifaddr_ipv4_table, (struct prefix *) &p); + if (! rn) + return NULL; + + ifp = rn->info; + route_unlock_node (rn); + return ifp; + } + else + return if_lookup_by_index(ifindex); +} +#endif /* ifaddr_ipv4_table */ + +/* Initialize interface list. */ +void +if_init (vrf_id_t vrf_id, struct list **intf_list) +{ + *intf_list = list_new (); +#if 0 + ifaddr_ipv4_table = route_table_init (); +#endif /* ifaddr_ipv4_table */ + + (*intf_list)->cmp = (int (*)(void *, void *))if_cmp_func; + + if (vrf_id == VRF_DEFAULT) + iflist = *intf_list; +} + +void +if_terminate (vrf_id_t vrf_id, struct list **intf_list) +{ + for (;;) + { + struct interface *ifp; + + ifp = listnode_head (*intf_list); + if (ifp == NULL) + break; + + if_delete (ifp); + } + + 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 new file mode 100644 index 0000000..862f7d4 --- /dev/null +++ b/lib/if.h @@ -0,0 +1,471 @@ +/* Interface related header. + 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_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. + + Linux define value in /usr/include/linux/if.h. + #define IFNAMSIZ 16 + + FreeBSD define value in /usr/include/net/if.h. + #define IFNAMSIZ 16 +*/ + +#define INTERFACE_NAMSIZ 20 +#define INTERFACE_HWADDR_MAX 20 + +typedef signed int ifindex_t; + +#ifdef HAVE_PROC_NET_DEV +struct if_stats +{ + unsigned long rx_packets; /* total packets received */ + unsigned long tx_packets; /* total packets transmitted */ + unsigned long rx_bytes; /* total bytes received */ + unsigned long tx_bytes; /* total bytes transmitted */ + unsigned long rx_errors; /* bad packets received */ + unsigned long tx_errors; /* packet transmit problems */ + unsigned long rx_dropped; /* no space in linux buffers */ + unsigned long tx_dropped; /* no space available in linux */ + unsigned long rx_multicast; /* multicast packets received */ + unsigned long rx_compressed; + unsigned long tx_compressed; + unsigned long collisions; + + /* detailed rx_errors: */ + unsigned long rx_length_errors; + unsigned long rx_over_errors; /* receiver ring buff overflow */ + unsigned long rx_crc_errors; /* recved pkt with crc error */ + unsigned long rx_frame_errors; /* recv'd frame alignment error */ + unsigned long rx_fifo_errors; /* recv'r fifo overrun */ + unsigned long rx_missed_errors; /* receiver missed packet */ + /* detailed tx_errors */ + unsigned long tx_aborted_errors; + unsigned long tx_carrier_errors; + unsigned long tx_fifo_errors; + unsigned long tx_heartbeat_errors; + unsigned long tx_window_errors; +}; +#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 +{ + /* Interface name. This should probably never be changed after the + interface is created, because the configuration info for this interface + is associated with this structure. For that reason, the interface + should also never be deleted (to avoid losing configuration info). + To delete, just set ifindex to IFINDEX_INTERNAL to indicate that the + interface does not exist in the kernel. + */ + char name[INTERFACE_NAMSIZ + 1]; + + /* Interface index (should be IFINDEX_INTERNAL for non-kernel or + deleted interfaces). */ + ifindex_t ifindex; +#define IFINDEX_INTERNAL 0 + + /* Zebra internal interface status */ + u_char status; +#define ZEBRA_INTERFACE_ACTIVE (1 << 0) +#define ZEBRA_INTERFACE_SUB (1 << 1) +#define ZEBRA_INTERFACE_LINKDETECTION (1 << 2) + + /* Interface flags. */ + uint64_t flags; + + /* Interface metric */ + int metric; + + /* Interface MTU. */ + unsigned int mtu; /* IPv4 MTU */ + unsigned int mtu6; /* IPv6 MTU - probably, but not neccessarily same as mtu */ + + /* Link-layer information and hardware address */ + enum zebra_link_type ll_type; + u_char hw_addr[INTERFACE_HWADDR_MAX]; + int hw_addr_len; + + /* interface bandwidth, kbits */ + unsigned int bandwidth; + + /* Link parameters for Traffic Engineering */ + struct if_link_params *link_params; + + /* description of the interface. */ + char *desc; + + /* Distribute list. */ + void *distribute_in; + void *distribute_out; + + /* Connected address list. */ + struct list *connected; + + /* Daemon specific interface data pointer. */ + void *info; + + /* Statistics fileds. */ +#ifdef HAVE_PROC_NET_DEV + struct if_stats stats; +#endif /* HAVE_PROC_NET_DEV */ +#ifdef HAVE_NET_RT_IFLIST + struct if_data stats; +#endif /* HAVE_NET_RT_IFLIST */ + + vrf_id_t vrf_id; +}; + +/* Connected address structure. */ +struct connected +{ + /* Attached interface. */ + struct interface *ifp; + + /* Flags for configuration. */ + 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 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. + Otherwise, if this flag is not set, the destination address + will either contain a broadcast address or be NULL. + */ + + /* Address of connected network. */ + struct prefix *address; + + /* Peer or Broadcast address, depending on whether ZEBRA_IFA_PEER is set. + Note: destination may be NULL if ZEBRA_IFA_PEER is not set. */ + struct prefix *destination; + + /* Label for Linux 2.2.X and upper. */ + char *label; +}; + +/* Does the destination field contain a peer address? */ +#define CONNECTED_PEER(C) CHECK_FLAG((C)->flags, ZEBRA_IFA_PEER) + +/* Prefix to insert into the RIB */ +#define CONNECTED_PREFIX(C) \ + (CONNECTED_PEER(C) ? (C)->destination : (C)->address) + +/* Identifying address. We guess that if there's a peer address, but the + local address is in the same prefix, then the local address may be unique. */ +#define CONNECTED_ID(C) \ + ((CONNECTED_PEER(C) && !prefix_match((C)->destination, (C)->address)) ?\ + (C)->destination : (C)->address) + +/* Interface hook sort. */ +#define IF_NEW_HOOK 0 +#define IF_DELETE_HOOK 1 + +/* There are some interface flags which are only supported by some + operating system. */ + +#ifndef IFF_NOTRAILERS +#define IFF_NOTRAILERS 0x0 +#endif /* IFF_NOTRAILERS */ +#ifndef IFF_OACTIVE +#define IFF_OACTIVE 0x0 +#endif /* IFF_OACTIVE */ +#ifndef IFF_SIMPLEX +#define IFF_SIMPLEX 0x0 +#endif /* IFF_SIMPLEX */ +#ifndef IFF_LINK0 +#define IFF_LINK0 0x0 +#endif /* IFF_LINK0 */ +#ifndef IFF_LINK1 +#define IFF_LINK1 0x0 +#endif /* IFF_LINK1 */ +#ifndef IFF_LINK2 +#define IFF_LINK2 0x0 +#endif /* IFF_LINK2 */ +#ifndef IFF_NOXMIT +#define IFF_NOXMIT 0x0 +#endif /* IFF_NOXMIT */ +#ifndef IFF_NORTEXCH +#define IFF_NORTEXCH 0x0 +#endif /* IFF_NORTEXCH */ +#ifndef IFF_IPV4 +#define IFF_IPV4 0x0 +#endif /* IFF_IPV4 */ +#ifndef IFF_IPV6 +#define IFF_IPV6 0x0 +#endif /* IFF_IPV6 */ +#ifndef IFF_VIRTUAL +#define IFF_VIRTUAL 0x0 +#endif /* IFF_VIRTUAL */ + +/* 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 (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); + +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 + interface list. It is often advisable to leave the pseudo interface + structure because there may be configuration information attached. */ +extern void if_delete_retain (struct interface *); + +/* Delete and free the interface structure: calls if_delete_retain and then + deletes it from the interface list and frees the structure. */ +extern void if_delete (struct interface *); + +extern int if_is_up (struct interface *); +extern int if_is_running (struct interface *); +extern int if_is_operative (struct interface *); +extern int if_is_loopback (struct interface *); +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 (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 (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 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); +extern void connected_free (struct connected *); +extern void connected_add (struct interface *, struct connected *); +extern struct connected *connected_add_by_prefix (struct interface *, + struct prefix *, + struct prefix *); +extern struct connected *connected_delete_by_prefix (struct interface *, + struct prefix *); +extern struct connected *connected_lookup_address (struct interface *, + struct in_addr); + +#ifndef HAVE_IF_NAMETOINDEX +extern ifindex_t if_nametoindex (const char *); +#endif +#ifndef HAVE_IF_INDEXTONAME +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 new file mode 100644 index 0000000..e4a83de --- /dev/null +++ b/lib/if_rmap.c @@ -0,0 +1,330 @@ +/* route-map for 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 "hash.h" +#include "command.h" +#include "memory.h" +#include "if.h" +#include "if_rmap.h" + +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) +{ + struct if_rmap *new; + + new = XCALLOC (MTYPE_IF_RMAP, sizeof (struct if_rmap)); + + return new; +} + +static void +if_rmap_free (struct if_rmap *if_rmap) +{ + if (if_rmap->ifname) + XFREE (MTYPE_IF_RMAP_NAME, if_rmap->ifname); + + if (if_rmap->routemap[IF_RMAP_IN]) + XFREE (MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_IN]); + if (if_rmap->routemap[IF_RMAP_OUT]) + XFREE (MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_OUT]); + + XFREE (MTYPE_IF_RMAP, if_rmap); +} + +struct if_rmap * +if_rmap_lookup (const char *ifname) +{ + struct if_rmap key; + struct if_rmap *if_rmap; + + /* temporary copy */ + key.ifname = (char *)ifname; + + if_rmap = hash_lookup (ifrmaphash, &key); + + return if_rmap; +} + +void +if_rmap_hook_add (void (*func) (struct if_rmap *)) +{ + if_rmap_add_hook = func; +} + +void +if_rmap_hook_delete (void (*func) (struct if_rmap *)) +{ + if_rmap_delete_hook = func; +} + +static void * +if_rmap_hash_alloc (void *arg) +{ + struct if_rmap *ifarg = arg; + struct if_rmap *if_rmap; + + if_rmap = if_rmap_new (); + if_rmap->ifname = XSTRDUP (MTYPE_IF_RMAP_NAME, ifarg->ifname); + + return if_rmap; +} + +static struct if_rmap * +if_rmap_get (const char *ifname) +{ + struct if_rmap key; + + /* temporary copy */ + key.ifname = (char *)ifname; + + return (struct if_rmap *) hash_get (ifrmaphash, &key, if_rmap_hash_alloc); +} + +static unsigned int +if_rmap_hash_make (void *data) +{ + const struct if_rmap *if_rmap = data; + + return string_hash_make (if_rmap->ifname); +} + +static int +if_rmap_hash_cmp (const void *arg1, const void* arg2) +{ + const struct if_rmap *if_rmap1 = arg1; + const struct if_rmap *if_rmap2 = 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) +{ + struct if_rmap *if_rmap; + + if_rmap = if_rmap_get (ifname); + + if (type == IF_RMAP_IN) + { + if (if_rmap->routemap[IF_RMAP_IN]) + XFREE (MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_IN]); + if_rmap->routemap[IF_RMAP_IN] + = XSTRDUP (MTYPE_IF_RMAP_NAME, routemap_name); + } + if (type == IF_RMAP_OUT) + { + if (if_rmap->routemap[IF_RMAP_OUT]) + XFREE (MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_OUT]); + if_rmap->routemap[IF_RMAP_OUT] + = XSTRDUP (MTYPE_IF_RMAP_NAME, routemap_name); + } + + if (if_rmap_add_hook) + (*if_rmap_add_hook) (if_rmap); + + return if_rmap; +} + +static int +if_rmap_unset (const char *ifname, enum if_rmap_type type, + const char *routemap_name) +{ + struct if_rmap *if_rmap; + + if_rmap = if_rmap_lookup (ifname); + if (!if_rmap) + return 0; + + if (type == IF_RMAP_IN) + { + if (!if_rmap->routemap[IF_RMAP_IN]) + return 0; + if (strcmp (if_rmap->routemap[IF_RMAP_IN], routemap_name) != 0) + return 0; + + XFREE (MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_IN]); + if_rmap->routemap[IF_RMAP_IN] = NULL; + } + + if (type == IF_RMAP_OUT) + { + if (!if_rmap->routemap[IF_RMAP_OUT]) + return 0; + if (strcmp (if_rmap->routemap[IF_RMAP_OUT], routemap_name) != 0) + return 0; + + XFREE (MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_OUT]); + if_rmap->routemap[IF_RMAP_OUT] = NULL; + } + + if (if_rmap_delete_hook) + (*if_rmap_delete_hook) (if_rmap); + + if (if_rmap->routemap[IF_RMAP_IN] == NULL && + if_rmap->routemap[IF_RMAP_OUT] == NULL) + { + hash_release (ifrmaphash, if_rmap); + if_rmap_free (if_rmap); + } + + return 1; +} + +DEFUN (if_rmap, + if_rmap_cmd, + "route-map RMAP_NAME (in|out) IFNAME", + "Route map set\n" + "Route map name\n" + "Route map set for input filtering\n" + "Route map set for output filtering\n" + "Route map interface name\n") +{ + enum if_rmap_type type; + + if (strncmp (argv[1], "i", 1) == 0) + type = IF_RMAP_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = IF_RMAP_OUT; + else + { + vty_out (vty, "route-map direction must be [in|out]%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if_rmap_set (argv[2], type, argv[0]); + + return CMD_SUCCESS; +} + +ALIAS (if_rmap, + if_ipv6_rmap_cmd, + "route-map RMAP_NAME (in|out) IFNAME", + "Route map set\n" + "Route map name\n" + "Route map set for input filtering\n" + "Route map set for output filtering\n" + "Route map interface name\n") + +DEFUN (no_if_rmap, + no_if_rmap_cmd, + "no route-map ROUTEMAP_NAME (in|out) IFNAME", + NO_STR + "Route map unset\n" + "Route map name\n" + "Route map for input filtering\n" + "Route map for output filtering\n" + "Route map interface name\n") +{ + int ret; + enum if_rmap_type type; + + if (strncmp (argv[1], "i", 1) == 0) + type = IF_RMAP_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = IF_RMAP_OUT; + else + { + vty_out (vty, "route-map direction must be [in|out]%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ret = if_rmap_unset (argv[2], type, argv[0]); + if (! ret) + { + vty_out (vty, "route-map doesn't exist%s", VTY_NEWLINE); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +ALIAS (no_if_rmap, + no_if_ipv6_rmap_cmd, + "no route-map ROUTEMAP_NAME (in|out) IFNAME", + NO_STR + "Route map unset\n" + "Route map name\n" + "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) +{ + unsigned int i; + struct hash_backet *mp; + int write = 0; + + for (i = 0; i < ifrmaphash->size; i++) + for (mp = ifrmaphash->index[i]; mp; mp = mp->next) + { + struct if_rmap *if_rmap; + + if_rmap = mp->data; + + if (if_rmap->routemap[IF_RMAP_IN]) + { + vty_out (vty, " route-map %s in %s%s", + if_rmap->routemap[IF_RMAP_IN], + if_rmap->ifname, + VTY_NEWLINE); + write++; + } + + if (if_rmap->routemap[IF_RMAP_OUT]) + { + vty_out (vty, " route-map %s out %s%s", + if_rmap->routemap[IF_RMAP_OUT], + if_rmap->ifname, + VTY_NEWLINE); + write++; + } + } + return write; +} + +void +if_rmap_reset () +{ + hash_clean (ifrmaphash, (void (*) (void *)) if_rmap_free); +} + +void +if_rmap_init (int node) +{ + ifrmaphash = hash_create (if_rmap_hash_make, if_rmap_hash_cmp); + if (node == RIPNG_NODE) { + install_element (RIPNG_NODE, &if_ipv6_rmap_cmd); + install_element (RIPNG_NODE, &no_if_ipv6_rmap_cmd); + } else if (node == RIP_NODE) { + install_element (RIP_NODE, &if_rmap_cmd); + install_element (RIP_NODE, &no_if_rmap_cmd); + } +} diff --git a/lib/if_rmap.h b/lib/if_rmap.h new file mode 100644 index 0000000..e6c2966 --- /dev/null +++ b/lib/if_rmap.h @@ -0,0 +1,47 @@ +/* route-map for 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. + */ + +#ifndef _ZEBRA_IF_RMAP_H +#define _ZEBRA_IF_RMAP_H + +enum if_rmap_type +{ + IF_RMAP_IN, + IF_RMAP_OUT, + IF_RMAP_MAX +}; + +struct if_rmap +{ + /* Name of the interface. */ + char *ifname; + + char *routemap[IF_RMAP_MAX]; +}; + +extern void if_rmap_init (int); +extern void if_rmap_reset (void); +extern void if_rmap_hook_add (void (*) (struct if_rmap *)); +extern void if_rmap_hook_delete (void (*) (struct if_rmap *)); +extern struct if_rmap *if_rmap_lookup (const char *); +extern int config_write_if_rmap (struct vty *); + +#endif /* _ZEBRA_IF_RMAP_H */ diff --git a/lib/jhash.c b/lib/jhash.c new file mode 100644 index 0000000..6154c34 --- /dev/null +++ b/lib/jhash.c @@ -0,0 +1,170 @@ +/* jhash.h: Jenkins hash support. + * + * Copyright (C) 1996 Bob Jenkins (bob_jenkins@burtleburtle.net) + * + * http://burtleburtle.net/bob/hash/ + * + * These are the credits from Bob's sources: + * + * lookup2.c, by Bob Jenkins, December 1996, Public Domain. + * hash(), hash2(), hash3, and mix() are externally useful functions. + * Routines to test the hash are included if SELF_TEST is defined. + * You can use this free for any purpose. It has no warranty. + * + * Copyright (C) 2003 David S. Miller (davem@redhat.com) + * + * I've modified Bob's hash to be useful in the Linux kernel, and + * any bugs present are surely my fault. -DaveM + */ + +#include "zebra.h" +#include "jhash.h" + +/* The golden ration: an arbitrary value */ +#define JHASH_GOLDEN_RATIO 0x9e3779b9 + +/* NOTE: Arguments are modified. */ +#define __jhash_mix(a, b, c) \ +{ \ + a -= b; a -= c; a ^= (c>>13); \ + b -= c; b -= a; b ^= (a<<8); \ + c -= a; c -= b; c ^= (b>>13); \ + a -= b; a -= c; a ^= (c>>12); \ + b -= c; b -= a; b ^= (a<<16); \ + c -= a; c -= b; c ^= (b>>5); \ + a -= b; a -= c; a ^= (c>>3); \ + b -= c; b -= a; b ^= (a<<10); \ + c -= a; c -= b; c ^= (b>>15); \ +} + +/* The most generic version, hashes an arbitrary sequence + * of bytes. No alignment or length assumptions are made about + * the input key. + */ +u_int32_t +jhash (const void *key, u_int32_t length, u_int32_t initval) +{ + u_int32_t a, b, c, len; + const u_int8_t *k = key; + + len = length; + a = b = JHASH_GOLDEN_RATIO; + c = initval; + + while (len >= 12) + { + a += + (k[0] + ((u_int32_t) k[1] << 8) + ((u_int32_t) k[2] << 16) + + ((u_int32_t) k[3] << 24)); + b += + (k[4] + ((u_int32_t) k[5] << 8) + ((u_int32_t) k[6] << 16) + + ((u_int32_t) k[7] << 24)); + c += + (k[8] + ((u_int32_t) k[9] << 8) + ((u_int32_t) k[10] << 16) + + ((u_int32_t) k[11] << 24)); + + __jhash_mix (a, b, c); + + k += 12; + len -= 12; + } + + c += length; + switch (len) + { + case 11: + c += ((u_int32_t) k[10] << 24); + case 10: + c += ((u_int32_t) k[9] << 16); + case 9: + c += ((u_int32_t) k[8] << 8); + case 8: + b += ((u_int32_t) k[7] << 24); + case 7: + b += ((u_int32_t) k[6] << 16); + case 6: + b += ((u_int32_t) k[5] << 8); + case 5: + b += k[4]; + case 4: + a += ((u_int32_t) k[3] << 24); + case 3: + a += ((u_int32_t) k[2] << 16); + case 2: + a += ((u_int32_t) k[1] << 8); + case 1: + a += k[0]; + }; + + __jhash_mix (a, b, c); + + return c; +} + +/* 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. + */ +u_int32_t +jhash2 (const u_int32_t *k, u_int32_t length, u_int32_t initval) +{ + u_int32_t a, b, c, len; + + a = b = JHASH_GOLDEN_RATIO; + c = initval; + len = length; + + while (len >= 3) + { + a += k[0]; + b += k[1]; + c += k[2]; + __jhash_mix (a, b, c); + k += 3; + len -= 3; + } + + c += length * 4; + + switch (len) + { + case 2: + b += k[1]; + case 1: + a += k[0]; + }; + + __jhash_mix (a, b, c); + + return c; +} + + +/* A special ultra-optimized versions that knows they are hashing exactly + * 3, 2 or 1 word(s). + * + * NOTE: In partilar the "c += length; __jhash_mix(a,b,c);" normally + * done at the end is not done here. + */ +u_int32_t +jhash_3words (u_int32_t a, u_int32_t b, u_int32_t c, u_int32_t initval) +{ + a += JHASH_GOLDEN_RATIO; + b += JHASH_GOLDEN_RATIO; + c += initval; + + __jhash_mix (a, b, c); + + return c; +} + +u_int32_t +jhash_2words (u_int32_t a, u_int32_t b, u_int32_t initval) +{ + return jhash_3words (a, b, 0, initval); +} + +u_int32_t +jhash_1word (u_int32_t a, u_int32_t initval) +{ + return jhash_3words (a, 0, 0, initval); +} diff --git a/lib/jhash.h b/lib/jhash.h new file mode 100644 index 0000000..985ac94 --- /dev/null +++ b/lib/jhash.h @@ -0,0 +1,44 @@ +/* jhash.h: Jenkins hash support. + * + * Copyright (C) 1996 Bob Jenkins (bob_jenkins@burtleburtle.net) + * + * http://burtleburtle.net/bob/hash/ + * + * These are the credits from Bob's sources: + * + * lookup2.c, by Bob Jenkins, December 1996, Public Domain. + * hash(), hash2(), hash3, and mix() are externally useful functions. + * Routines to test the hash are included if SELF_TEST is defined. + * You can use this free for any purpose. It has no warranty. + * + * Copyright (C) 2003 David S. Miller (davem@redhat.com) + * + * I've modified Bob's hash to be useful in the Linux kernel, and + * any bugs present are surely my fault. -DaveM + */ + +#ifndef _QUAGGA_JHASH_H +#define _QUAGGA_JHASH_H + +/* The most generic version, hashes an arbitrary sequence + * of bytes. No alignment or length assumptions are made about + * the input key. + */ +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(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). + * + * NOTE: In partilar the "c += length; __jhash_mix(a,b,c);" normally + * done at the end is not done here. + */ +extern u_int32_t jhash_3words(u_int32_t a, u_int32_t b, u_int32_t c, u_int32_t initval); +extern u_int32_t jhash_2words(u_int32_t a, u_int32_t b, u_int32_t initval); +extern u_int32_t jhash_1word(u_int32_t a, u_int32_t initval); + +#endif /* _QUAGGA_JHASH_H */ diff --git a/lib/keychain.c b/lib/keychain.c new file mode 100644 index 0000000..8a2fdd2 --- /dev/null +++ b/lib/keychain.c @@ -0,0 +1,988 @@ +/* key-chain for authentication. + 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 "memory.h" +#include "linklist.h" +#include "keychain.h" + +/* Master list of key chain. */ +struct list *keychain_list; + +static struct keychain * +keychain_new (void) +{ + return XCALLOC (MTYPE_KEYCHAIN, sizeof (struct keychain)); +} + +static void +keychain_free (struct keychain *keychain) +{ + XFREE (MTYPE_KEYCHAIN, keychain); +} + +static struct key * +key_new (void) +{ + return XCALLOC (MTYPE_KEY, sizeof (struct key)); +} + +static void +key_free (struct key *key) +{ + XFREE (MTYPE_KEY, key); +} + +struct keychain * +keychain_lookup (const char *name) +{ + struct listnode *node; + struct keychain *keychain; + + if (name == NULL) + return NULL; + + for (ALL_LIST_ELEMENTS_RO (keychain_list, node, keychain)) + { + if (strcmp (keychain->name, name) == 0) + return keychain; + } + return NULL; +} + +static int +key_cmp_func (void *arg1, void *arg2) +{ + const struct key *k1 = arg1; + const struct key *k2 = arg2; + + if (k1->index > k2->index) + return 1; + if (k1->index < k2->index) + return -1; + return 0; +} + +static void +key_delete_func (struct key *key) +{ + if (key->string) + free (key->string); + key_free (key); +} + +static struct keychain * +keychain_get (const char *name) +{ + struct keychain *keychain; + + keychain = keychain_lookup (name); + + if (keychain) + return keychain; + + keychain = keychain_new (); + keychain->name = strdup (name); + keychain->key = list_new (); + keychain->key->cmp = (int (*)(void *, void *)) key_cmp_func; + keychain->key->del = (void (*)(void *)) key_delete_func; + listnode_add (keychain_list, keychain); + + return keychain; +} + +static void +keychain_delete (struct keychain *keychain) +{ + if (keychain->name) + free (keychain->name); + + list_delete (keychain->key); + listnode_delete (keychain_list, keychain); + keychain_free (keychain); +} + +static struct key * +key_lookup (const struct keychain *keychain, u_int32_t index) +{ + struct listnode *node; + struct key *key; + + for (ALL_LIST_ELEMENTS_RO (keychain->key, node, key)) + { + if (key->index == index) + return key; + } + return NULL; +} + +struct key * +key_lookup_for_accept (const struct keychain *keychain, u_int32_t index) +{ + struct listnode *node; + struct key *key; + time_t now; + + now = time (NULL); + + for (ALL_LIST_ELEMENTS_RO (keychain->key, node, key)) + { + if (key->index >= index) + { + if (key->accept.start == 0) + return key; + + if (key->accept.start <= now) + if (key->accept.end >= now || key->accept.end == -1) + return key; + } + } + return NULL; +} + +struct key * +key_match_for_accept (const struct keychain *keychain, const char *auth_str) +{ + struct listnode *node; + struct key *key; + time_t now; + + now = time (NULL); + + for (ALL_LIST_ELEMENTS_RO (keychain->key, node, key)) + { + if (key->accept.start == 0 || + (key->accept.start <= now && + (key->accept.end >= now || key->accept.end == -1))) + if (strncmp (key->string, auth_str, 16) == 0) + return key; + } + return NULL; +} + +struct key * +key_lookup_for_send (const struct keychain *keychain) +{ + struct listnode *node; + struct key *key; + time_t now; + + now = time (NULL); + + for (ALL_LIST_ELEMENTS_RO (keychain->key, node, key)) + { + if (key->send.start == 0) + return key; + + if (key->send.start <= now) + if (key->send.end >= now || key->send.end == -1) + return key; + } + return NULL; +} + +static struct key * +key_get (const struct keychain *keychain, u_int32_t index) +{ + struct key *key; + + key = key_lookup (keychain, index); + + if (key) + return key; + + key = key_new (); + key->index = index; + listnode_add_sort (keychain->key, key); + + return key; +} + +static void +key_delete (struct keychain *keychain, struct key *key) +{ + listnode_delete (keychain->key, key); + + if (key->string) + free (key->string); + key_free (key); +} + +DEFUN (key_chain, + key_chain_cmd, + "key chain WORD", + "Authentication key management\n" + "Key-chain management\n" + "Key-chain name\n") +{ + struct keychain *keychain; + + keychain = keychain_get (argv[0]); + vty->index = keychain; + vty->node = KEYCHAIN_NODE; + + return CMD_SUCCESS; +} + +DEFUN (no_key_chain, + no_key_chain_cmd, + "no key chain WORD", + NO_STR + "Authentication key management\n" + "Key-chain management\n" + "Key-chain name\n") +{ + struct keychain *keychain; + + keychain = keychain_lookup (argv[0]); + + if (! keychain) + { + vty_out (vty, "Can't find keychain %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + keychain_delete (keychain); + + return CMD_SUCCESS; +} + +DEFUN (key, + key_cmd, + "key <0-2147483647>", + "Configure a key\n" + "Key identifier number\n") +{ + struct keychain *keychain; + struct key *key; + u_int32_t index; + + keychain = vty->index; + + VTY_GET_INTEGER ("key identifier", index, argv[0]); + key = key_get (keychain, index); + vty->index_sub = key; + vty->node = KEYCHAIN_KEY_NODE; + + return CMD_SUCCESS; +} + +DEFUN (no_key, + no_key_cmd, + "no key <0-2147483647>", + NO_STR + "Delete a key\n" + "Key identifier number\n") +{ + struct keychain *keychain; + struct key *key; + u_int32_t index; + + keychain = vty->index; + + VTY_GET_INTEGER ("key identifier", index, argv[0]); + key = key_lookup (keychain, index); + if (! key) + { + vty_out (vty, "Can't find key %d%s", index, VTY_NEWLINE); + return CMD_WARNING; + } + + key_delete (keychain, key); + + vty->node = KEYCHAIN_NODE; + + return CMD_SUCCESS; +} + +DEFUN (key_string, + key_string_cmd, + "key-string LINE", + "Set key string\n" + "The key\n") +{ + struct key *key; + + key = vty->index_sub; + + if (key->string) + free (key->string); + key->string = strdup (argv[0]); + + return CMD_SUCCESS; +} + +DEFUN (no_key_string, + no_key_string_cmd, + "no key-string [LINE]", + NO_STR + "Unset key string\n" + "The key\n") +{ + struct key *key; + + key = vty->index_sub; + + if (key->string) + { + free (key->string); + key->string = NULL; + } + + return CMD_SUCCESS; +} + +/* Convert HH:MM:SS MON DAY YEAR to time_t value. -1 is returned when + given string is malformed. */ +static time_t +key_str2time (const char *time_str, const char *day_str, const char *month_str, + const char *year_str) +{ + int i = 0; + char *colon; + struct tm tm; + time_t time; + unsigned int sec, min, hour; + unsigned int day, month, year; + + const char *month_name[] = + { + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", + NULL + }; + +#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 (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) + return -1; + *colon = '\0'; + + /* Hour must be between 0 and 23. */ + GET_LONG_RANGE0 (hour, time_str, 23); + + /* Check min field of time_str. */ + time_str = colon + 1; + colon = strchr (time_str, ':'); + if (*time_str == '\0' || colon == NULL) + return -1; + *colon = '\0'; + + /* Min must be between 0 and 59. */ + GET_LONG_RANGE0 (min, time_str, 59); + + /* Check sec field of time_str. */ + time_str = colon + 1; + if (*time_str == '\0') + return -1; + + /* Sec must be between 0 and 59. */ + GET_LONG_RANGE0 (sec, time_str, 59); + + /* Check day_str. Day must be <1-31>. */ + GET_LONG_RANGE (day, day_str, 1, 31); + + /* Check month_str. Month must match month_name. */ + month = 0; + if (strlen (month_str) >= 3) + for (i = 0; month_name[i]; i++) + if (strncmp (month_str, month_name[i], strlen (month_str)) == 0) + { + month = i; + break; + } + if (! month_name[i]) + return -1; + + /* Check year_str. Year must be <1993-2035>. */ + GET_LONG_RANGE (year, year_str, 1993, 2035); + + memset (&tm, 0, sizeof (struct tm)); + tm.tm_sec = sec; + tm.tm_min = min; + tm.tm_hour = hour; + tm.tm_mon = month; + tm.tm_mday = day; + tm.tm_year = year - 1900; + + time = mktime (&tm); + + return time; +#undef GET_LONG_RANGE +} + +static int +key_lifetime_set (struct vty *vty, struct key_range *krange, + const char *stime_str, const char *sday_str, + const char *smonth_str, const char *syear_str, + const char *etime_str, const char *eday_str, + const char *emonth_str, const char *eyear_str) +{ + time_t time_start; + time_t time_end; + + time_start = key_str2time (stime_str, sday_str, smonth_str, syear_str); + if (time_start < 0) + { + vty_out (vty, "Malformed time value%s", VTY_NEWLINE); + return CMD_WARNING; + } + time_end = key_str2time (etime_str, eday_str, emonth_str, eyear_str); + + if (time_end < 0) + { + vty_out (vty, "Malformed time value%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (time_end <= time_start) + { + vty_out (vty, "Expire time is not later than start time%s", VTY_NEWLINE); + return CMD_WARNING; + } + + krange->start = time_start; + krange->end = time_end; + + return CMD_SUCCESS; +} + +static int +key_lifetime_duration_set (struct vty *vty, struct key_range *krange, + const char *stime_str, const char *sday_str, + const char *smonth_str, const char *syear_str, + const char *duration_str) +{ + time_t time_start; + u_int32_t duration; + + time_start = key_str2time (stime_str, sday_str, smonth_str, syear_str); + if (time_start < 0) + { + vty_out (vty, "Malformed time value%s", VTY_NEWLINE); + return CMD_WARNING; + } + krange->start = time_start; + + VTY_GET_INTEGER ("duration", duration, duration_str); + krange->duration = 1; + krange->end = time_start + duration; + + return CMD_SUCCESS; +} + +static int +key_lifetime_infinite_set (struct vty *vty, struct key_range *krange, + const char *stime_str, const char *sday_str, + const char *smonth_str, const char *syear_str) +{ + time_t time_start; + + time_start = key_str2time (stime_str, sday_str, smonth_str, syear_str); + if (time_start < 0) + { + vty_out (vty, "Malformed time value%s", VTY_NEWLINE); + return CMD_WARNING; + } + krange->start = time_start; + + krange->end = -1; + + 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>", + "Set accept lifetime of the key\n" + "Time to start\n" + "Day of th month to start\n" + "Month of the year to start\n" + "Year to start\n" + "Time to expire\n" + "Day of th month to expire\n" + "Month of the year to expire\n" + "Year to expire\n") +{ + struct key *key; + + key = vty->index_sub; + + return key_lifetime_set (vty, &key->accept, argv[0], argv[1], argv[2], + argv[3], argv[4], argv[5], argv[6], argv[7]); +} + +DEFUN (accept_lifetime_day_month_month_day, + accept_lifetime_day_month_month_day_cmd, + "accept-lifetime HH:MM:SS <1-31> MONTH <1993-2035> HH:MM:SS MONTH <1-31> <1993-2035>", + "Set accept lifetime of the key\n" + "Time to start\n" + "Day of th month to start\n" + "Month of the year to start\n" + "Year to start\n" + "Time to expire\n" + "Month of the year to expire\n" + "Day of th month to expire\n" + "Year to expire\n") +{ + struct key *key; + + key = vty->index_sub; + + return key_lifetime_set (vty, &key->accept, argv[0], argv[1], argv[2], + argv[3], argv[4], argv[6], argv[5], argv[7]); +} + +DEFUN (accept_lifetime_month_day_day_month, + accept_lifetime_month_day_day_month_cmd, + "accept-lifetime HH:MM:SS MONTH <1-31> <1993-2035> HH:MM:SS <1-31> MONTH <1993-2035>", + "Set accept lifetime of the key\n" + "Time to start\n" + "Month of the year to start\n" + "Day of th month to start\n" + "Year to start\n" + "Time to expire\n" + "Day of th month to expire\n" + "Month of the year to expire\n" + "Year to expire\n") +{ + struct key *key; + + key = vty->index_sub; + + return key_lifetime_set (vty, &key->accept, argv[0], argv[2], argv[1], + argv[3], argv[4], argv[5], argv[6], argv[7]); +} + +DEFUN (accept_lifetime_month_day_month_day, + accept_lifetime_month_day_month_day_cmd, + "accept-lifetime HH:MM:SS MONTH <1-31> <1993-2035> HH:MM:SS MONTH <1-31> <1993-2035>", + "Set accept lifetime of the key\n" + "Time to start\n" + "Month of the year to start\n" + "Day of th month to start\n" + "Year to start\n" + "Time to expire\n" + "Month of the year to expire\n" + "Day of th month to expire\n" + "Year to expire\n") +{ + struct key *key; + + key = vty->index_sub; + + return key_lifetime_set (vty, &key->accept, argv[0], argv[2], argv[1], + argv[3], argv[4], argv[6], argv[5], argv[7]); +} + +DEFUN (accept_lifetime_infinite_day_month, + accept_lifetime_infinite_day_month_cmd, + "accept-lifetime HH:MM:SS <1-31> MONTH <1993-2035> infinite", + "Set accept lifetime of the key\n" + "Time to start\n" + "Day of th month to start\n" + "Month of the year to start\n" + "Year to start\n" + "Never expires") +{ + struct key *key; + + key = vty->index_sub; + + return key_lifetime_infinite_set (vty, &key->accept, argv[0], argv[1], + argv[2], argv[3]); +} + +DEFUN (accept_lifetime_infinite_month_day, + accept_lifetime_infinite_month_day_cmd, + "accept-lifetime HH:MM:SS MONTH <1-31> <1993-2035> infinite", + "Set accept lifetime of the key\n" + "Time to start\n" + "Month of the year to start\n" + "Day of th month to start\n" + "Year to start\n" + "Never expires") +{ + struct key *key; + + key = vty->index_sub; + + return key_lifetime_infinite_set (vty, &key->accept, argv[0], argv[2], + argv[1], argv[3]); +} + +DEFUN (accept_lifetime_duration_day_month, + accept_lifetime_duration_day_month_cmd, + "accept-lifetime HH:MM:SS <1-31> MONTH <1993-2035> duration <1-2147483646>", + "Set accept lifetime of the key\n" + "Time to start\n" + "Day of th month to start\n" + "Month of the year to start\n" + "Year to start\n" + "Duration of the key\n" + "Duration seconds\n") +{ + struct key *key; + + key = vty->index_sub; + + return key_lifetime_duration_set (vty, &key->accept, argv[0], argv[1], + argv[2], argv[3], argv[4]); +} + +DEFUN (accept_lifetime_duration_month_day, + accept_lifetime_duration_month_day_cmd, + "accept-lifetime HH:MM:SS MONTH <1-31> <1993-2035> duration <1-2147483646>", + "Set accept lifetime of the key\n" + "Time to start\n" + "Month of the year to start\n" + "Day of th month to start\n" + "Year to start\n" + "Duration of the key\n" + "Duration seconds\n") +{ + struct key *key; + + key = vty->index_sub; + + 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>", + "Set send lifetime of the key\n" + "Time to start\n" + "Day of th month to start\n" + "Month of the year to start\n" + "Year to start\n" + "Time to expire\n" + "Day of th month to expire\n" + "Month of the year to expire\n" + "Year to expire\n") +{ + struct key *key; + + key = vty->index_sub; + + return key_lifetime_set (vty, &key->send, argv[0], argv[1], argv[2], argv[3], + argv[4], argv[5], argv[6], argv[7]); +} + +DEFUN (send_lifetime_day_month_month_day, + send_lifetime_day_month_month_day_cmd, + "send-lifetime HH:MM:SS <1-31> MONTH <1993-2035> HH:MM:SS MONTH <1-31> <1993-2035>", + "Set send lifetime of the key\n" + "Time to start\n" + "Day of th month to start\n" + "Month of the year to start\n" + "Year to start\n" + "Time to expire\n" + "Month of the year to expire\n" + "Day of th month to expire\n" + "Year to expire\n") +{ + struct key *key; + + key = vty->index_sub; + + return key_lifetime_set (vty, &key->send, argv[0], argv[1], argv[2], argv[3], + argv[4], argv[6], argv[5], argv[7]); +} + +DEFUN (send_lifetime_month_day_day_month, + send_lifetime_month_day_day_month_cmd, + "send-lifetime HH:MM:SS MONTH <1-31> <1993-2035> HH:MM:SS <1-31> MONTH <1993-2035>", + "Set send lifetime of the key\n" + "Time to start\n" + "Month of the year to start\n" + "Day of th month to start\n" + "Year to start\n" + "Time to expire\n" + "Day of th month to expire\n" + "Month of the year to expire\n" + "Year to expire\n") +{ + struct key *key; + + key = vty->index_sub; + + return key_lifetime_set (vty, &key->send, argv[0], argv[2], argv[1], argv[3], + argv[4], argv[5], argv[6], argv[7]); +} + +DEFUN (send_lifetime_month_day_month_day, + send_lifetime_month_day_month_day_cmd, + "send-lifetime HH:MM:SS MONTH <1-31> <1993-2035> HH:MM:SS MONTH <1-31> <1993-2035>", + "Set send lifetime of the key\n" + "Time to start\n" + "Month of the year to start\n" + "Day of th month to start\n" + "Year to start\n" + "Time to expire\n" + "Month of the year to expire\n" + "Day of th month to expire\n" + "Year to expire\n") +{ + struct key *key; + + key = vty->index_sub; + + return key_lifetime_set (vty, &key->send, argv[0], argv[2], argv[1], argv[3], + argv[4], argv[6], argv[5], argv[7]); +} + +DEFUN (send_lifetime_infinite_day_month, + send_lifetime_infinite_day_month_cmd, + "send-lifetime HH:MM:SS <1-31> MONTH <1993-2035> infinite", + "Set send lifetime of the key\n" + "Time to start\n" + "Day of th month to start\n" + "Month of the year to start\n" + "Year to start\n" + "Never expires") +{ + struct key *key; + + key = vty->index_sub; + + return key_lifetime_infinite_set (vty, &key->send, argv[0], argv[1], argv[2], + argv[3]); +} + +DEFUN (send_lifetime_infinite_month_day, + send_lifetime_infinite_month_day_cmd, + "send-lifetime HH:MM:SS MONTH <1-31> <1993-2035> infinite", + "Set send lifetime of the key\n" + "Time to start\n" + "Month of the year to start\n" + "Day of th month to start\n" + "Year to start\n" + "Never expires") +{ + struct key *key; + + key = vty->index_sub; + + return key_lifetime_infinite_set (vty, &key->send, argv[0], argv[2], argv[1], + argv[3]); +} + +DEFUN (send_lifetime_duration_day_month, + send_lifetime_duration_day_month_cmd, + "send-lifetime HH:MM:SS <1-31> MONTH <1993-2035> duration <1-2147483646>", + "Set send lifetime of the key\n" + "Time to start\n" + "Day of th month to start\n" + "Month of the year to start\n" + "Year to start\n" + "Duration of the key\n" + "Duration seconds\n") +{ + struct key *key; + + key = vty->index_sub; + + return key_lifetime_duration_set (vty, &key->send, argv[0], argv[1], argv[2], + argv[3], argv[4]); +} + +DEFUN (send_lifetime_duration_month_day, + send_lifetime_duration_month_day_cmd, + "send-lifetime HH:MM:SS MONTH <1-31> <1993-2035> duration <1-2147483646>", + "Set send lifetime of the key\n" + "Time to start\n" + "Month of the year to start\n" + "Day of th month to start\n" + "Year to start\n" + "Duration of the key\n" + "Duration seconds\n") +{ + struct key *key; + + key = vty->index_sub; + + 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, + "%s(config-keychain)# ", + 1 +}; + +static struct cmd_node keychain_key_node = +{ + KEYCHAIN_KEY_NODE, + "%s(config-keychain-key)# ", + 1 +}; + +static int +keychain_strftime (char *buf, int bufsiz, time_t *time) +{ + struct tm *tm; + size_t len; + + tm = localtime (time); + + len = strftime (buf, bufsiz, "%T %b %d %Y", tm); + + return len; +} + +static int +keychain_config_write (struct vty *vty) +{ + struct keychain *keychain; + struct key *key; + struct listnode *node; + struct listnode *knode; + char buf[BUFSIZ]; + + for (ALL_LIST_ELEMENTS_RO (keychain_list, node, keychain)) + { + vty_out (vty, "key chain %s%s", keychain->name, VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO (keychain->key, knode, key)) + { + vty_out (vty, " key %d%s", key->index, VTY_NEWLINE); + + if (key->string) + vty_out (vty, " key-string %s%s", key->string, VTY_NEWLINE); + + if (key->accept.start) + { + keychain_strftime (buf, BUFSIZ, &key->accept.start); + vty_out (vty, " accept-lifetime %s", buf); + + if (key->accept.end == -1) + vty_out (vty, " infinite"); + else if (key->accept.duration) + vty_out (vty, " duration %ld", + (long)(key->accept.end - key->accept.start)); + else + { + keychain_strftime (buf, BUFSIZ, &key->accept.end); + vty_out (vty, " %s", buf); + } + vty_out (vty, "%s", VTY_NEWLINE); + } + + if (key->send.start) + { + keychain_strftime (buf, BUFSIZ, &key->send.start); + vty_out (vty, " send-lifetime %s", buf); + + if (key->send.end == -1) + vty_out (vty, " infinite"); + else if (key->send.duration) + vty_out (vty, " duration %ld", (long)(key->send.end - key->send.start)); + else + { + keychain_strftime (buf, BUFSIZ, &key->send.end); + vty_out (vty, " %s", buf); + } + vty_out (vty, "%s", VTY_NEWLINE); + } + } + vty_out (vty, "!%s", VTY_NEWLINE); + } + + return 0; +} + +void +keychain_init () +{ + keychain_list = list_new (); + + install_node (&keychain_node, keychain_config_write); + install_node (&keychain_key_node, NULL); + + install_default (KEYCHAIN_NODE); + install_default (KEYCHAIN_KEY_NODE); + + install_element (CONFIG_NODE, &key_chain_cmd); + install_element (CONFIG_NODE, &no_key_chain_cmd); + install_element (KEYCHAIN_NODE, &key_cmd); + install_element (KEYCHAIN_NODE, &no_key_cmd); + + install_element (KEYCHAIN_NODE, &key_chain_cmd); + install_element (KEYCHAIN_NODE, &no_key_chain_cmd); + + install_element (KEYCHAIN_KEY_NODE, &key_string_cmd); + install_element (KEYCHAIN_KEY_NODE, &no_key_string_cmd); + + install_element (KEYCHAIN_KEY_NODE, &key_chain_cmd); + install_element (KEYCHAIN_KEY_NODE, &no_key_chain_cmd); + + install_element (KEYCHAIN_KEY_NODE, &key_cmd); + install_element (KEYCHAIN_KEY_NODE, &no_key_cmd); + + install_element (KEYCHAIN_KEY_NODE, &accept_lifetime_day_month_day_month_cmd); + install_element (KEYCHAIN_KEY_NODE, &accept_lifetime_day_month_month_day_cmd); + install_element (KEYCHAIN_KEY_NODE, &accept_lifetime_month_day_day_month_cmd); + install_element (KEYCHAIN_KEY_NODE, &accept_lifetime_month_day_month_day_cmd); + install_element (KEYCHAIN_KEY_NODE, &accept_lifetime_infinite_day_month_cmd); + install_element (KEYCHAIN_KEY_NODE, &accept_lifetime_infinite_month_day_cmd); + install_element (KEYCHAIN_KEY_NODE, &accept_lifetime_duration_day_month_cmd); + install_element (KEYCHAIN_KEY_NODE, &accept_lifetime_duration_month_day_cmd); + + install_element (KEYCHAIN_KEY_NODE, &send_lifetime_day_month_day_month_cmd); + install_element (KEYCHAIN_KEY_NODE, &send_lifetime_day_month_month_day_cmd); + install_element (KEYCHAIN_KEY_NODE, &send_lifetime_month_day_day_month_cmd); + install_element (KEYCHAIN_KEY_NODE, &send_lifetime_month_day_month_day_cmd); + install_element (KEYCHAIN_KEY_NODE, &send_lifetime_infinite_day_month_cmd); + install_element (KEYCHAIN_KEY_NODE, &send_lifetime_infinite_month_day_cmd); + install_element (KEYCHAIN_KEY_NODE, &send_lifetime_duration_day_month_cmd); + install_element (KEYCHAIN_KEY_NODE, &send_lifetime_duration_month_day_cmd); +} diff --git a/lib/keychain.h b/lib/keychain.h new file mode 100644 index 0000000..f962864 --- /dev/null +++ b/lib/keychain.h @@ -0,0 +1,56 @@ +/* key-chain for authentication. + * 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. + */ + +#ifndef _ZEBRA_KEYCHAIN_H +#define _ZEBRA_KEYCHAIN_H + +struct keychain +{ + char *name; + + struct list *key; +}; + +struct key_range +{ + time_t start; + time_t end; + + u_char duration; +}; + +struct key +{ + u_int32_t index; + + char *string; + + struct key_range send; + struct key_range accept; +}; + +extern void keychain_init (void); +extern struct keychain *keychain_lookup (const char *); +extern struct key *key_lookup_for_accept (const struct keychain *, u_int32_t); +extern struct key *key_match_for_accept (const struct keychain *, const char *); +extern struct key *key_lookup_for_send (const struct keychain *); + +#endif /* _ZEBRA_KEYCHAIN_H */ diff --git a/lib/libospf.h b/lib/libospf.h new file mode 100644 index 0000000..9265ca5 --- /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 new file mode 100644 index 0000000..8b6a852 --- /dev/null +++ b/lib/linklist.c @@ -0,0 +1,361 @@ +/* Generic linked list routine. + * Copyright (C) 1997, 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 "linklist.h" +#include "memory.h" + +/* Allocate new list. */ +struct list * +list_new (void) +{ + return XCALLOC (MTYPE_LINK_LIST, sizeof (struct list)); +} + +/* Free list. */ +void +list_free (struct list *l) +{ + XFREE (MTYPE_LINK_LIST, l); +} + +/* Allocate new listnode. Internal use only. */ +static struct listnode * +listnode_new (void) +{ + return XCALLOC (MTYPE_LINK_NODE, sizeof (struct listnode)); +} + +/* Free listnode. */ +static void +listnode_free (struct listnode *node) +{ + XFREE (MTYPE_LINK_NODE, node); +} + +/* Add new data to the list. */ +void +listnode_add (struct list *list, void *val) +{ + struct listnode *node; + + assert (val != NULL); + + node = listnode_new (); + + node->prev = list->tail; + node->data = val; + + if (list->head == NULL) + list->head = node; + else + list->tail->next = node; + list->tail = node; + + list->count++; +} + +/* + * Add a node to the list. If the list was sorted according to the + * cmp function, insert a new node with the given val such that the + * list remains sorted. The new node is always inserted; there is no + * notion of omitting duplicates. + */ +void +listnode_add_sort (struct list *list, void *val) +{ + struct listnode *n; + struct listnode *new; + + assert (val != NULL); + + new = listnode_new (); + new->data = val; + + if (list->cmp) + { + for (n = list->head; n; n = n->next) + { + if ((*list->cmp) (val, n->data) < 0) + { + new->next = n; + new->prev = n->prev; + + if (n->prev) + n->prev->next = new; + else + list->head = new; + n->prev = new; + list->count++; + return; + } + } + } + + new->prev = list->tail; + + if (list->tail) + list->tail->next = new; + else + list->head = new; + + list->tail = new; + list->count++; +} + +void +listnode_add_after (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->head) + list->head->prev = nn; + else + list->tail = nn; + + nn->next = list->head; + nn->prev = pp; + + list->head = nn; + } + else + { + if (pp->next) + pp->next->prev = nn; + else + list->tail = nn; + + nn->next = pp->next; + nn->prev = pp; + + pp->next = nn; + } + 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 +listnode_delete (struct list *list, void *val) +{ + struct listnode *node; + + assert(list); + for (node = list->head; node; node = node->next) + { + if (node->data == val) + { + if (node->prev) + node->prev->next = node->next; + else + list->head = node->next; + + if (node->next) + node->next->prev = node->prev; + else + list->tail = node->prev; + + list->count--; + listnode_free (node); + return; + } + } +} + +/* Return first node's data if it is there. */ +void * +listnode_head (struct list *list) +{ + struct listnode *node; + + assert(list); + node = list->head; + + if (node) + return node->data; + return NULL; +} + +/* Delete all listnode from the list. */ +void +list_delete_all_node (struct list *list) +{ + struct listnode *node; + struct listnode *next; + + assert(list); + for (node = list->head; node; node = next) + { + next = node->next; + if (list->del) + (*list->del) (node->data); + listnode_free (node); + } + list->head = list->tail = NULL; + list->count = 0; +} + +/* Delete all listnode then free list itself. */ +void +list_delete (struct list *list) +{ + assert(list); + list_delete_all_node (list); + list_free (list); +} + +/* Lookup the node which has given data. */ +struct listnode * +listnode_lookup (struct list *list, void *data) +{ + struct listnode *node; + + assert(list); + for (node = listhead(list); node; node = listnextnode (node)) + if (data == listgetdata (node)) + return node; + return NULL; +} + +/* Delete the node from list. For ospfd and ospf6d. */ +void +list_delete_node (struct list *list, struct listnode *node) +{ + if (node->prev) + node->prev->next = node->next; + else + list->head = node->next; + if (node->next) + node->next->prev = node->prev; + else + list->tail = node->prev; + list->count--; + listnode_free (node); +} + +/* ospf_spf.c */ +void +list_add_node_prev (struct list *list, struct listnode *current, void *val) +{ + struct listnode *node; + + assert (val != NULL); + + node = listnode_new (); + node->next = current; + node->data = val; + + if (current->prev == NULL) + list->head = node; + else + current->prev->next = node; + + node->prev = current->prev; + current->prev = node; + + list->count++; +} + +/* ospf_spf.c */ +void +list_add_node_next (struct list *list, struct listnode *current, void *val) +{ + struct listnode *node; + + assert (val != NULL); + + node = listnode_new (); + node->prev = current; + node->data = val; + + if (current->next == NULL) + list->tail = node; + else + current->next->prev = node; + + node->next = current->next; + current->next = node; + + list->count++; +} + +/* ospf_spf.c */ +void +list_add_list (struct list *l, struct list *m) +{ + struct listnode *n; + + for (n = listhead (m); n; n = listnextnode (n)) + listnode_add (l, n->data); +} diff --git a/lib/linklist.h b/lib/linklist.h new file mode 100644 index 0000000..96aaf43 --- /dev/null +++ b/lib/linklist.h @@ -0,0 +1,151 @@ +/* Generic linked list + * Copyright (C) 1997, 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. + */ + +#ifndef _ZEBRA_LINKLIST_H +#define _ZEBRA_LINKLIST_H + +/* listnodes must always contain data to be valid. Adding an empty node + * to a list is invalid + */ +struct listnode +{ + struct listnode *next; + struct listnode *prev; + + /* private member, use getdata() to retrieve, do not access directly */ + void *data; +}; + +struct list +{ + struct listnode *head; + struct listnode *tail; + + /* invariant: count is the number of listnodes in the list */ + unsigned int count; + + /* + * Returns -1 if val1 < val2, 0 if equal?, 1 if val1 > val2. + * Used as definition of sorted for listnode_add_sort + */ + int (*cmp) (void *val1, void *val2); + + /* callback to free user-owned data when listnode is deleted. supplying + * this callback is very much encouraged! + */ + void (*del) (void *val); +}; + +#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) + +/* Prototypes. */ +extern struct list *list_new(void); /* encouraged: set list.del callback on new lists */ +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 *); + +extern void list_delete (struct list *); +extern void list_delete_all_node (struct list *); + +/* For ospfd and ospf6d. */ +extern void list_delete_node (struct list *, struct listnode *); + +/* For ospf_spf.c */ +extern void list_add_node_prev (struct list *, struct listnode *, void *); +extern void list_add_node_next (struct list *, struct listnode *, void *); +extern void list_add_list (struct list *, struct list *); + +/* List iteration macro. + * Usage: for (ALL_LIST_ELEMENTS (...) { ... } + * It is safe to delete the listnode using this macro. + */ +#define ALL_LIST_ELEMENTS(list,node,nextnode,data) \ + (node) = listhead(list), ((data) = NULL); \ + (node) != NULL && \ + ((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 + * use this macro when it is *immediately obvious* the listnode is not + * deleted in the body of the loop. Does not have forward-reference overhead + * of previous macro. + */ +#define ALL_LIST_ELEMENTS_RO(list,node,data) \ + (node) = listhead(list), ((data) = NULL);\ + (node) != NULL && ((data) = listgetdata(node), 1); \ + (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. + */ + +/* List node attach macro. */ +#define LISTNODE_ATTACH(L,N) \ + do { \ + (N)->prev = (L)->tail; \ + (N)->next = NULL; \ + if ((L)->head == NULL) \ + (L)->head = (N); \ + else \ + (L)->tail->next = (N); \ + (L)->tail = (N); \ + (L)->count++; \ + } while (0) + +/* List node detach macro. */ +#define LISTNODE_DETACH(L,N) \ + do { \ + if ((N)->prev) \ + (N)->prev->next = (N)->next; \ + else \ + (L)->head = (N)->next; \ + if ((N)->next) \ + (N)->next->prev = (N)->prev; \ + else \ + (L)->tail = (N)->prev; \ + (L)->count--; \ + } while (0) + +/* Deprecated: 20050406 */ +#if !defined(QUAGGA_NO_DEPRECATED_INTERFACES) +#warning "Using deprecated libzebra interfaces" +#define LISTNODE_ADD(L,N) LISTNODE_ATTACH(L,N) +#define LISTNODE_DELETE(L,N) LISTNODE_DETACH(L,N) +#define nextnode(X) ((X) = (X)->next) +#define getdata(X) listgetdata(X) +#define LIST_LOOP(L,V,N) \ + for (ALL_LIST_ELEMENTS_RO (L,N,V)) +#endif /* QUAGGA_NO_DEPRECATED_INTERFACES */ + +#endif /* _ZEBRA_LINKLIST_H */ diff --git a/lib/log.c b/lib/log.c new file mode 100644 index 0000000..d437066 --- /dev/null +++ b/lib/log.c @@ -0,0 +1,1057 @@ +/* + * Logging of zebra + * Copyright (C) 1997, 1998, 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. + */ + +#define QUAGGA_DEFINE_DESC_TABLE + +#include + +#include "log.h" +#include "memory.h" +#include "command.h" +#ifndef SUNOS_5 +#include +#endif +/* for printstack on solaris */ +#ifdef HAVE_UCONTEXT_H +#include +#endif + +static int logfile_fd = -1; /* Used in signal handler. */ + +struct zlog *zlog_default = NULL; + +const char *zlog_proto_names[] = +{ + "NONE", + "DEFAULT", + "ZEBRA", + "RIP", + "BGP", + "OSPF", + "RIPNG", + "BABEL", + "OSPF6", + "ISIS", + "PIM", + "MASC", + "NHRP", + NULL, +}; + +const char *zlog_priority[] = +{ + "emergencies", + "alerts", + "critical", + "errors", + "warnings", + "notifications", + "informational", + "debugging", + NULL, +}; + + + +/* For time string format. */ + +size_t +quagga_timestamp(int timestamp_precision, char *buf, size_t buflen) +{ + static struct { + time_t last; + size_t len; + char buf[28]; + } cache; + struct timeval clock; + + /* would it be sufficient to use global 'recent_time' here? I fear not... */ + gettimeofday(&clock, NULL); + + /* first, we update the cache if the time has changed */ + if (cache.last != clock.tv_sec) + { + struct tm *tm; + cache.last = clock.tv_sec; + tm = localtime(&cache.last); + cache.len = strftime(cache.buf, sizeof(cache.buf), + "%Y/%m/%d %H:%M:%S", tm); + } + /* note: it's not worth caching the subsecond part, because + chances are that back-to-back calls are not sufficiently close together + for the clock not to have ticked forward */ + + if (buflen > cache.len) + { + memcpy(buf, cache.buf, cache.len); + if ((timestamp_precision > 0) && + (buflen > cache.len+1+timestamp_precision)) + { + /* should we worry about locale issues? */ + static const int divisor[] = {0, 100000, 10000, 1000, 100, 10, 1}; + int prec; + char *p = buf+cache.len+1+(prec = timestamp_precision); + *p-- = '\0'; + while (prec > 6) + /* this is unlikely to happen, but protect anyway */ + { + *p-- = '0'; + prec--; + } + clock.tv_usec /= divisor[prec]; + do + { + *p-- = '0'+(clock.tv_usec % 10); + clock.tv_usec /= 10; + } + while (--prec > 0); + *p = '.'; + return cache.len+1+timestamp_precision; + } + buf[cache.len] = '\0'; + return cache.len; + } + if (buflen > 0) + buf[0] = '\0'; + return 0; +} + +/* Utility routine for current time printing. */ +static void +time_print(FILE *fp, struct timestamp_control *ctl) +{ + if (!ctl->already_rendered) + { + ctl->len = quagga_timestamp(ctl->precision, ctl->buf, sizeof(ctl->buf)); + ctl->already_rendered = 1; + } + 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; + + /* If zlog is not specified, use default one. */ + if (zl == NULL) + zl = zlog_default; + + /* When zlog_default is also NULL, use stderr for logging. */ + if (zl == NULL) + { + tsctl.precision = 0; + time_print(stderr, &tsctl); + fprintf (stderr, "%s: ", "unknown"); + vfprintf (stderr, format, args); + fprintf (stderr, "\n"); + fflush (stderr); + + /* In this case we return at here. */ + errno = original_errno; + return; + } + tsctl.precision = zl->timestamp_precision; + + /* Syslog output */ + if (priority <= zl->maxlvl[ZLOG_DEST_SYSLOG]) + { + va_list ac; + va_copy(ac, args); + vsyslog (priority|zlog_default->facility, format, ac); + va_end(ac); + } + + /* File output. */ + if ((priority <= zl->maxlvl[ZLOG_DEST_FILE]) && zl->fp) + { + va_list ac; + time_print (zl->fp, &tsctl); + if (zl->record_priority) + fprintf (zl->fp, "%s: ", zlog_priority[priority]); + fprintf (zl->fp, "%s: ", zlog_proto_names[zl->protocol]); + va_copy(ac, args); + vfprintf (zl->fp, format, ac); + va_end(ac); + fprintf (zl->fp, "\n"); + fflush (zl->fp); + } + + /* stdout output. */ + if (priority <= zl->maxlvl[ZLOG_DEST_STDOUT]) + { + va_list ac; + time_print (stdout, &tsctl); + if (zl->record_priority) + fprintf (stdout, "%s: ", zlog_priority[priority]); + fprintf (stdout, "%s: ", zlog_proto_names[zl->protocol]); + va_copy(ac, args); + vfprintf (stdout, format, ac); + va_end(ac); + fprintf (stdout, "\n"); + fflush (stdout); + } + + /* Terminal monitor. */ + 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 * +str_append(char *dst, int len, const char *src) +{ + while ((len-- > 0) && *src) + *dst++ = *src++; + return dst; +} + +static char * +num_append(char *s, int len, u_long x) +{ + char buf[30]; + char *t; + + if (!x) + return str_append(s,len,"0"); + *(t = &buf[sizeof(buf)-1]) = '\0'; + while (x && (t > buf)) + { + *--t = '0'+(x % 10); + x /= 10; + } + return str_append(s,len,t); +} + +#if defined(SA_SIGINFO) || defined(HAVE_STACK_TRACE) +static char * +hex_append(char *s, int len, u_long x) +{ + char buf[30]; + char *t; + + if (!x) + return str_append(s,len,"0"); + *(t = &buf[sizeof(buf)-1]) = '\0'; + while (x && (t > buf)) + { + u_int cc = (x % 16); + *--t = ((cc < 10) ? ('0'+cc) : ('a'+cc-10)); + x /= 16; + } + return str_append(s,len,t); +} +#endif + +/* Needs to be enhanced to support Solaris. */ +static int +syslog_connect(void) +{ +#ifdef SUNOS_5 + return -1; +#else + int fd; + char *s; + struct sockaddr_un addr; + + if ((fd = socket(AF_UNIX,SOCK_DGRAM,0)) < 0) + return -1; + addr.sun_family = AF_UNIX; +#ifdef _PATH_LOG +#define SYSLOG_SOCKET_PATH _PATH_LOG +#else +#define SYSLOG_SOCKET_PATH "/dev/log" +#endif + s = str_append(addr.sun_path,sizeof(addr.sun_path),SYSLOG_SOCKET_PATH); +#undef SYSLOG_SOCKET_PATH + *s = '\0'; + if (connect(fd,(struct sockaddr *)&addr,sizeof(addr)) < 0) + { + close(fd); + return -1; + } + return fd; +#endif +} + +static void +syslog_sigsafe(int priority, const char *msg, size_t msglen) +{ + static int syslog_fd = -1; + char buf[sizeof("<1234567890>ripngd[1234567890]: ")+msglen+50]; + char *s; + + if ((syslog_fd < 0) && ((syslog_fd = syslog_connect()) < 0)) + return; + +#define LOC s,buf+sizeof(buf)-s + s = buf; + s = str_append(LOC,"<"); + s = num_append(LOC,priority); + s = str_append(LOC,">"); + /* forget about the timestamp, too difficult in a signal handler */ + s = str_append(LOC,zlog_default->ident); + if (zlog_default->syslog_options & LOG_PID) + { + s = str_append(LOC,"["); + s = num_append(LOC,getpid()); + s = str_append(LOC,"]"); + } + s = str_append(LOC,": "); + s = str_append(LOC,msg); + write(syslog_fd,buf,s-buf); +#undef LOC +} + +static int +open_crashlog(void) +{ +#define CRASHLOG_PREFIX "/var/tmp/quagga." +#define CRASHLOG_SUFFIX "crashlog" + if (zlog_default && zlog_default->ident) + { + /* Avoid strlen since it is not async-signal-safe. */ + const char *p; + size_t ilen; + + for (p = zlog_default->ident, ilen = 0; *p; p++) + ilen++; + { + char buf[sizeof(CRASHLOG_PREFIX)+ilen+sizeof(CRASHLOG_SUFFIX)+3]; + char *s = buf; +#define LOC s,buf+sizeof(buf)-s + s = str_append(LOC, CRASHLOG_PREFIX); + s = str_append(LOC, zlog_default->ident); + s = str_append(LOC, "."); + s = str_append(LOC, CRASHLOG_SUFFIX); +#undef LOC + *s = '\0'; + return open(buf, O_WRONLY|O_CREAT|O_EXCL, LOGFILE_MASK); + } + } + return open(CRASHLOG_PREFIX CRASHLOG_SUFFIX, O_WRONLY|O_CREAT|O_EXCL, + LOGFILE_MASK); +#undef CRASHLOG_SUFFIX +#undef CRASHLOG_PREFIX +} + +/* Note: the goal here is to use only async-signal-safe functions. */ +void +zlog_signal(int signo, const char *action +#ifdef SA_SIGINFO + , siginfo_t *siginfo, void *program_counter +#endif + ) +{ + time_t now; + char buf[sizeof("DEFAULT: Received signal S at T (si_addr 0xP, PC 0xP); aborting...")+100]; + char *s = buf; + char *msgstart = buf; +#define LOC s,buf+sizeof(buf)-s + + time(&now); + if (zlog_default) + { + s = str_append(LOC,zlog_proto_names[zlog_default->protocol]); + *s++ = ':'; + *s++ = ' '; + msgstart = s; + } + s = str_append(LOC,"Received signal "); + s = num_append(LOC,signo); + s = str_append(LOC," at "); + s = num_append(LOC,now); +#ifdef SA_SIGINFO + s = str_append(LOC," (si_addr 0x"); + s = hex_append(LOC,(u_long)(siginfo->si_addr)); + if (program_counter) + { + s = str_append(LOC,", PC 0x"); + s = hex_append(LOC,(u_long)program_counter); + } + s = str_append(LOC,"); "); +#else /* SA_SIGINFO */ + s = str_append(LOC,"; "); +#endif /* SA_SIGINFO */ + s = str_append(LOC,action); + if (s < buf+sizeof(buf)) + *s++ = '\n'; + + /* N.B. implicit priority is most severe */ +#define PRI LOG_CRIT + +#define DUMP(FD) write(FD, buf, s-buf); + /* If no file logging configured, try to write to fallback log file. */ + if ((logfile_fd >= 0) || ((logfile_fd = open_crashlog()) >= 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 + + zlog_backtrace_sigsafe(PRI, +#ifdef SA_SIGINFO + program_counter +#else + 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 +} + +/* Log a backtrace using only async-signal-safe functions. + Needs to be enhanced to support syslog logging. */ +void +zlog_backtrace_sigsafe(int priority, void *program_counter) +{ +#ifdef HAVE_STACK_TRACE + static const char pclabel[] = "Program counter: "; + void *array[64]; + int size; + char buf[100]; + char *s, **bt = NULL; +#define LOC s,buf+sizeof(buf)-s + +#ifdef HAVE_GLIBC_BACKTRACE + size = backtrace(array, array_size(array)); + if (size <= 0 || (size_t)size > array_size(array)) + return; + +#define DUMP(FD) { \ + if (program_counter) \ + { \ + write(FD, pclabel, sizeof(pclabel)-1); \ + backtrace_symbols_fd(&program_counter, 1, FD); \ + } \ + write(FD, buf, s-buf); \ + backtrace_symbols_fd(array, size, FD); \ +} +#elif defined(HAVE_PRINTSTACK) +#define DUMP(FD) { \ + if (program_counter) \ + write((FD), pclabel, sizeof(pclabel)-1); \ + write((FD), buf, s-buf); \ + printstack((FD)); \ +} +#endif /* HAVE_GLIBC_BACKTRACE, HAVE_PRINTSTACK */ + + s = buf; + s = str_append(LOC,"Backtrace for "); + s = num_append(LOC,size); + s = str_append(LOC," stack frames:\n"); + + if ((logfile_fd >= 0) || ((logfile_fd = open_crashlog()) >= 0)) + DUMP(logfile_fd) + if (!zlog_default) + DUMP(STDERR_FILENO) + else + { + if (priority <= zlog_default->maxlvl[ZLOG_DEST_STDOUT]) + DUMP(STDOUT_FILENO) + /* Remove trailing '\n' for monitor and syslog */ + *--s = '\0'; + if (priority <= zlog_default->maxlvl[ZLOG_DEST_MONITOR]) + vty_log_fixed(buf,s-buf); + if (priority <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG]) + syslog_sigsafe(priority|zlog_default->facility,buf,s-buf); + { + int i; +#ifdef HAVE_GLIBC_BACKTRACE + bt = backtrace_symbols(array, size); +#endif + /* Just print the function addresses. */ + for (i = 0; i < size; i++) + { + s = buf; + if (bt) + s = str_append(LOC, bt[i]); + else { + s = str_append(LOC,"[bt "); + s = num_append(LOC,i); + s = str_append(LOC,"] 0x"); + s = hex_append(LOC,(u_long)(array[i])); + } + *s = '\0'; + if (priority <= zlog_default->maxlvl[ZLOG_DEST_MONITOR]) + vty_log_fixed(buf,s-buf); + if (priority <= zlog_default->maxlvl[ZLOG_DEST_SYSLOG]) + syslog_sigsafe(priority|zlog_default->facility,buf,s-buf); + } + if (bt) + free(bt); + } + } +#undef DUMP +#undef LOC +#endif /* HAVE_STRACK_TRACE */ +} + +void +zlog_backtrace(int priority) +{ +#ifndef HAVE_GLIBC_BACKTRACE + zlog(NULL, priority, "No backtrace available on this platform."); +#else + void *array[20]; + int size, i; + char **strings; + + 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)(array_size(array))); + return; + } + zlog(NULL, priority, "Backtrace for %d stack frames:", size); + if (!(strings = backtrace_symbols(array, size))) + { + zlog_err("Cannot get backtrace symbols (out of memory?)"); + for (i = 0; i < size; i++) + zlog(NULL, priority, "[bt %d] %p",i,array[i]); + } + else + { + for (i = 0; i < size; i++) + zlog(NULL, priority, "[bt %d] %s",i,strings[i]); + free(strings); + } +#endif /* HAVE_GLIBC_BACKTRACE */ +} + +void +zlog (struct zlog *zl, int priority, const char *format, ...) +{ + va_list args; + + va_start(args, format); + vzlog (zl, priority, format, args); + va_end (args); +} + +#define ZLOG_FUNC(FUNCNAME,PRIORITY) \ +void \ +FUNCNAME(const char *format, ...) \ +{ \ + va_list args; \ + va_start(args, format); \ + vzlog (NULL, PRIORITY, format, args); \ + va_end(args); \ +} + +ZLOG_FUNC(zlog_err, LOG_ERR) + +ZLOG_FUNC(zlog_warn, LOG_WARNING) + +ZLOG_FUNC(zlog_info, LOG_INFO) + +ZLOG_FUNC(zlog_notice, LOG_NOTICE) + +ZLOG_FUNC(zlog_debug, LOG_DEBUG) + +#undef ZLOG_FUNC + +#define PLOG_FUNC(FUNCNAME,PRIORITY) \ +void \ +FUNCNAME(struct zlog *zl, const char *format, ...) \ +{ \ + va_list args; \ + va_start(args, format); \ + vzlog (zl, PRIORITY, format, args); \ + va_end(args); \ +} + +PLOG_FUNC(plog_err, LOG_ERR) + +PLOG_FUNC(plog_warn, LOG_WARNING) + +PLOG_FUNC(plog_info, LOG_INFO) + +PLOG_FUNC(plog_notice, LOG_NOTICE) + +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) +{ + /* Force fallback file logging? */ + if (zlog_default && !zlog_default->fp && + ((logfile_fd = open_crashlog()) >= 0) && + ((zlog_default->fp = fdopen(logfile_fd, "w")) != NULL)) + zlog_default->maxlvl[ZLOG_DEST_FILE] = LOG_ERR; + 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, + int syslog_flags, int syslog_facility) +{ + struct zlog *zl; + u_int i; + + zl = XCALLOC(MTYPE_ZLOG, sizeof (struct zlog)); + + zl->ident = progname; + zl->protocol = protocol; + zl->facility = syslog_facility; + zl->syslog_options = syslog_flags; + + /* Set default logging levels. */ + 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; + + openlog (progname, syslog_flags, zl->facility); + + return zl; +} + +void +closezlog (struct zlog *zl) +{ + closelog(); + + if (zl->fp != NULL) + fclose (zl->fp); + + if (zl->filename != NULL) + free (zl->filename); + + XFREE (MTYPE_ZLOG, zl); +} + +/* Called from command.c. */ +void +zlog_set_level (struct zlog *zl, zlog_dest_t dest, int log_level) +{ + if (zl == NULL) + zl = zlog_default; + + zl->maxlvl[dest] = log_level; +} + +int +zlog_set_file (struct zlog *zl, const char *filename, int log_level) +{ + FILE *fp; + mode_t oldumask; + + /* There is opend file. */ + zlog_reset_file (zl); + + /* Set default zl. */ + if (zl == NULL) + zl = zlog_default; + + /* Open file. */ + oldumask = umask (0777 & ~LOGFILE_MASK); + fp = fopen (filename, "a"); + umask(oldumask); + if (fp == NULL) + return 0; + + /* Set flags. */ + zl->filename = strdup (filename); + zl->maxlvl[ZLOG_DEST_FILE] = log_level; + zl->fp = fp; + logfile_fd = fileno(fp); + + return 1; +} + +/* Reset opend file. */ +int +zlog_reset_file (struct zlog *zl) +{ + if (zl == NULL) + zl = zlog_default; + + if (zl->fp) + fclose (zl->fp); + zl->fp = NULL; + logfile_fd = -1; + zl->maxlvl[ZLOG_DEST_FILE] = ZLOG_DISABLED; + + if (zl->filename) + free (zl->filename); + zl->filename = NULL; + + return 1; +} + +/* Reopen log file. */ +int +zlog_rotate (struct zlog *zl) +{ + int level; + + if (zl == NULL) + zl = zlog_default; + + if (zl->fp) + fclose (zl->fp); + zl->fp = NULL; + logfile_fd = -1; + level = zl->maxlvl[ZLOG_DEST_FILE]; + zl->maxlvl[ZLOG_DEST_FILE] = ZLOG_DISABLED; + + if (zl->filename) + { + mode_t oldumask; + int save_errno; + + oldumask = umask (0777 & ~LOGFILE_MASK); + zl->fp = fopen (zl->filename, "a"); + save_errno = errno; + umask(oldumask); + if (zl->fp == NULL) + { + zlog_err("Log rotate failed: cannot open file %s for append: %s", + zl->filename, safe_strerror(save_errno)); + return -1; + } + logfile_fd = fileno(zl->fp); + zl->maxlvl[ZLOG_DEST_FILE] = level; + } + + return 1; +} + +/* Message lookup function. */ +const char * +lookup (const struct message *mes, int key) +{ + const struct message *pnt; + + for (pnt = mes; pnt->key != 0; pnt++) + if (pnt->key == key) + return pnt->str; + + return ""; +} + +/* Older/faster version of message lookup function, but requires caller to pass + * in the array size (instead of relying on a 0 key to terminate the search). + * + * The return value is the message string if found, or the 'none' pointer + * provided otherwise. + */ +const char * +mes_lookup (const struct message *meslist, int max, int index, + const char *none, const char *mesname) +{ + int pos = index - meslist[0].key; + + /* first check for best case: index is in range and matches the key + * value in that slot. + * NB: key numbering might be offset from 0. E.g. protocol constants + * often start at 1. + */ + if ((pos >= 0) && (pos < max) + && (meslist[pos].key == index)) + return meslist[pos].str; + + /* fall back to linear search */ + { + int i; + + for (i = 0; i < max; i++, meslist++) + { + if (meslist->key == index) + { + const char *str = (meslist->str ? meslist->str : none); + + zlog_debug ("message index %d [%s] found in %s at position %d (max is %d)", + index, str, mesname, i, max); + return str; + } + } + } + zlog_err("message index %d not found in %s (max is %d)", index, mesname, max); + assert (none); + return none; +} + +/* Wrapper around strerror to handle case where it returns NULL. */ +const char * +safe_strerror(int errnum) +{ + const char *s = strerror(errnum); + return (s != NULL) ? s : "Unknown error"; +} + +#define DESC_ENTRY(T) [(T)] = { (T), (#T), '\0' } +static const struct zebra_desc_table command_types[] = { + DESC_ENTRY (ZEBRA_INTERFACE_ADD), + DESC_ENTRY (ZEBRA_INTERFACE_DELETE), + DESC_ENTRY (ZEBRA_INTERFACE_ADDRESS_ADD), + DESC_ENTRY (ZEBRA_INTERFACE_ADDRESS_DELETE), + DESC_ENTRY (ZEBRA_INTERFACE_UP), + DESC_ENTRY (ZEBRA_INTERFACE_DOWN), + DESC_ENTRY (ZEBRA_IPV4_ROUTE_ADD), + DESC_ENTRY (ZEBRA_IPV4_ROUTE_DELETE), + DESC_ENTRY (ZEBRA_IPV6_ROUTE_ADD), + DESC_ENTRY (ZEBRA_IPV6_ROUTE_DELETE), + DESC_ENTRY (ZEBRA_REDISTRIBUTE_ADD), + DESC_ENTRY (ZEBRA_REDISTRIBUTE_DELETE), + DESC_ENTRY (ZEBRA_REDISTRIBUTE_DEFAULT_ADD), + DESC_ENTRY (ZEBRA_REDISTRIBUTE_DEFAULT_DELETE), + DESC_ENTRY (ZEBRA_IPV4_NEXTHOP_LOOKUP), + DESC_ENTRY (ZEBRA_IPV6_NEXTHOP_LOOKUP), + DESC_ENTRY (ZEBRA_IPV4_IMPORT_LOOKUP), + DESC_ENTRY (ZEBRA_IPV6_IMPORT_LOOKUP), + DESC_ENTRY (ZEBRA_INTERFACE_RENAME), + DESC_ENTRY (ZEBRA_ROUTER_ID_ADD), + 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 + +static const struct zebra_desc_table unknown = { 0, "unknown", '?' }; + +static const struct zebra_desc_table * +zroute_lookup(u_int zroute) +{ + u_int i; + + 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 < array_size(route_types); i++) + { + if (zroute == route_types[i].type) + { + zlog_warn("internal error: route type table out of order " + "while searching for %u, please notify developers", zroute); + return &route_types[i]; + } + } + zlog_err("internal error: cannot find route type %u in table!", zroute); + return &unknown; +} + +const char * +zebra_route_string(u_int zroute) +{ + return zroute_lookup(zroute)->string; +} + +char +zebra_route_char(u_int zroute) +{ + return zroute_lookup(zroute)->chr; +} + +const char * +zserv_command_string (unsigned int command) +{ + if (command >= array_size(command_types)) + { + zlog_err ("unknown zserv command type: %u", command); + return unknown.string; + } + return command_types[command].string; +} + +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 new file mode 100644 index 0000000..59968ae --- /dev/null +++ b/lib/log.h @@ -0,0 +1,241 @@ +/* + * Zebra logging funcions. + * Copyright (C) 1997, 1998, 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 _ZEBRA_LOG_H +#define _ZEBRA_LOG_H + +#include +#include + +/* Here is some guidance on logging levels to use: + * + * LOG_DEBUG - For all messages that are enabled by optional debugging + * features, typically preceded by "if (IS...DEBUG...)" + * LOG_INFO - Information that may be of interest, but everything seems + * to be working properly. + * LOG_NOTICE - Only for message pertaining to daemon startup or shutdown. + * LOG_WARNING - Warning conditions: unexpected events, but the daemon believes + * it can continue to operate correctly. + * LOG_ERR - Error situations indicating malfunctions. Probably require + * attention. + * + * Note: LOG_CRIT, LOG_ALERT, and LOG_EMERG are currently not used anywhere, + * please use LOG_ERR instead. + */ + +typedef enum +{ + ZLOG_NONE, + ZLOG_DEFAULT, + ZLOG_ZEBRA, + ZLOG_RIP, + ZLOG_BGP, + ZLOG_OSPF, + ZLOG_RIPNG, + ZLOG_BABEL, + ZLOG_OSPF6, + ZLOG_ISIS, + ZLOG_PIM, + ZLOG_MASC, + ZLOG_NHRP, +} zlog_proto_t; + +/* If maxlvl is set to ZLOG_DISABLED, then no messages will be sent + to that logging destination. */ +#define ZLOG_DISABLED (LOG_EMERG-1) + +typedef enum +{ + ZLOG_DEST_SYSLOG = 0, + ZLOG_DEST_STDOUT, + ZLOG_DEST_MONITOR, + ZLOG_DEST_FILE +} zlog_dest_t; +#define ZLOG_NUM_DESTS (ZLOG_DEST_FILE+1) + +struct zlog +{ + const char *ident; /* daemon name (first arg to openlog) */ + zlog_proto_t protocol; + int maxlvl[ZLOG_NUM_DESTS]; /* maximum priority to send to associated + logging destination */ + int default_lvl; /* maxlvl to use if none is specified */ + FILE *fp; + char *filename; + int facility; /* as per syslog facility */ + int record_priority; /* should messages logged through stdio include the + priority of the message? */ + int syslog_options; /* 2nd arg to openlog */ + int timestamp_precision; /* # of digits of subsecond precision */ +}; + +/* Message structure. */ +struct message +{ + int key; + const char *str; +}; + +/* Default logging strucutre. */ +extern struct zlog *zlog_default; + +/* Open zlog function */ +extern struct zlog *openzlog (const char *progname, zlog_proto_t protocol, + int syslog_options, int syslog_facility); + +/* Close zlog function. */ +extern void closezlog (struct zlog *zl); + +/* GCC have printf type attribute check. */ +#ifdef __GNUC__ +#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b))) +#else +#define PRINTF_ATTRIBUTE(a,b) +#endif /* __GNUC__ */ + +/* Generic function for zlog. */ +extern void zlog (struct zlog *zl, int priority, const char *format, ...) + PRINTF_ATTRIBUTE(3, 4); + +/* Handy zlog functions. */ +extern void zlog_err (const char *format, ...) PRINTF_ATTRIBUTE(1, 2); +extern void zlog_warn (const char *format, ...) PRINTF_ATTRIBUTE(1, 2); +extern void zlog_info (const char *format, ...) PRINTF_ATTRIBUTE(1, 2); +extern void zlog_notice (const char *format, ...) PRINTF_ATTRIBUTE(1, 2); +extern void zlog_debug (const char *format, ...) PRINTF_ATTRIBUTE(1, 2); + +/* For bgpd's peer oriented log. */ +extern void plog_err (struct zlog *, const char *format, ...) + PRINTF_ATTRIBUTE(2, 3); +extern void plog_warn (struct zlog *, const char *format, ...) + PRINTF_ATTRIBUTE(2, 3); +extern void plog_info (struct zlog *, const char *format, ...) + PRINTF_ATTRIBUTE(2, 3); +extern void plog_notice (struct zlog *, const char *format, ...) + PRINTF_ATTRIBUTE(2, 3); +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 + or zlog_reset_file instead). */ +extern void zlog_set_level (struct zlog *zl, zlog_dest_t, int log_level); + +/* Set logging to the given filename at the specified level. */ +extern int zlog_set_file (struct zlog *zl, const char *filename, int log_level); +/* Disable file logging. */ +extern int zlog_reset_file (struct zlog *zl); + +/* Rotate log. */ +extern int zlog_rotate (struct zlog *); + +/* 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, + int max, int index, + const char *no_item, const char *mesname); + +extern const char *zlog_priority[]; +extern const char *zlog_proto_names[]; + +/* Safe version of strerror -- never returns NULL. */ +extern const char *safe_strerror(int errnum); + +/* To be called when a fatal signal is caught. */ +extern void zlog_signal(int signo, const char *action +#ifdef SA_SIGINFO + , siginfo_t *siginfo, void *program_counter +#endif + ); + +/* Log a backtrace. */ +extern void zlog_backtrace(int priority); + +/* Log a backtrace, but in an async-signal-safe way. Should not be + called unless the program is about to exit or abort, since it messes + up the state of zlog file pointers. If program_counter is non-NULL, + that is logged in addition to the current backtrace. */ +extern void zlog_backtrace_sigsafe(int priority, void *program_counter); + +/* Puts a current timestamp in buf and returns the number of characters + written (not including the terminating NUL). The purpose of + this function is to avoid calls to localtime appearing all over the code. + 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[QUAGGA_TIMESTAMP_LEN]; /* will contain the rendered timestamp */ +}; + +/* Defines for use in command construction: */ + +#define LOG_LEVELS "(emergencies|alerts|critical|errors|warnings|notifications|informational|debugging)" + +#define LOG_LEVEL_DESC \ + "System is unusable\n" \ + "Immediate action needed\n" \ + "Critical conditions\n" \ + "Error conditions\n" \ + "Warning conditions\n" \ + "Normal but significant conditions\n" \ + "Informational messages\n" \ + "Debugging messages\n" + +#define LOG_FACILITIES "(kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7)" + +#define LOG_FACILITY_DESC \ + "Kernel\n" \ + "User process\n" \ + "Mail system\n" \ + "System daemons\n" \ + "Authorization system\n" \ + "Syslog itself\n" \ + "Line printer system\n" \ + "USENET news\n" \ + "Unix-to-Unix copy system\n" \ + "Cron/at facility\n" \ + "Local use\n" \ + "Local use\n" \ + "Local use\n" \ + "Local use\n" \ + "Local use\n" \ + "Local use\n" \ + "Local use\n" \ + "Local use\n" + +#endif /* _ZEBRA_LOG_H */ diff --git a/lib/md5.c b/lib/md5.c new file mode 100644 index 0000000..ce459bb --- /dev/null +++ b/lib/md5.c @@ -0,0 +1,373 @@ +/* $USAGI: md5.c,v 1.2 2000/11/02 11:59:24 yoshfuji Exp $ */ +/* $KAME: md5.c,v 1.2 2000/05/27 07:07:48 jinmei Exp $ */ +/* $Id: md5.c,v 1.6 2006/01/17 23:39:04 vincent Exp $ */ + +/* + * Copyright (C) 2004 6WIND + * + * All rights reserved. + * + * This MD5 code is Big endian and Little Endian compatible. + */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include "md5.h" + +#define SHIFT(X, s) (((X) << (s)) | ((X) >> (32 - (s)))) + +#define F(X, Y, Z) (((X) & (Y)) | ((~X) & (Z))) +#define G(X, Y, Z) (((X) & (Z)) | ((Y) & (~Z))) +#define H(X, Y, Z) ((X) ^ (Y) ^ (Z)) +#define I(X, Y, Z) ((Y) ^ ((X) | (~Z))) + +#define ROUND1(a, b, c, d, k, s, i) { \ + (a) = (a) + F((b), (c), (d)) + X[(k)] + T[(i)]; \ + (a) = SHIFT((a), (s)); \ + (a) = (b) + (a); \ +} + +#define ROUND2(a, b, c, d, k, s, i) { \ + (a) = (a) + G((b), (c), (d)) + X[(k)] + T[(i)]; \ + (a) = SHIFT((a), (s)); \ + (a) = (b) + (a); \ +} + +#define ROUND3(a, b, c, d, k, s, i) { \ + (a) = (a) + H((b), (c), (d)) + X[(k)] + T[(i)]; \ + (a) = SHIFT((a), (s)); \ + (a) = (b) + (a); \ +} + +#define ROUND4(a, b, c, d, k, s, i) { \ + (a) = (a) + I((b), (c), (d)) + X[(k)] + T[(i)]; \ + (a) = SHIFT((a), (s)); \ + (a) = (b) + (a); \ +} + +#define Sa 7 +#define Sb 12 +#define Sc 17 +#define Sd 22 + +#define Se 5 +#define Sf 9 +#define Sg 14 +#define Sh 20 + +#define Si 4 +#define Sj 11 +#define Sk 16 +#define Sl 23 + +#define Sm 6 +#define Sn 10 +#define So 15 +#define Sp 21 + +#define MD5_A0 0x67452301 +#define MD5_B0 0xefcdab89 +#define MD5_C0 0x98badcfe +#define MD5_D0 0x10325476 + +/* Integer part of 4294967296 times abs(sin(i)), where i is in radians. */ +static const uint32_t T[65] = { + 0, + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, + + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, + 0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, + + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, + + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391, +}; + +static const uint8_t md5_paddat[MD5_BUFLEN] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static void md5_calc (const uint8_t *, md5_ctxt *); + +void md5_init(md5_ctxt *ctxt) +{ + ctxt->md5_n = 0; + ctxt->md5_i = 0; + ctxt->md5_sta = MD5_A0; + ctxt->md5_stb = MD5_B0; + ctxt->md5_stc = MD5_C0; + ctxt->md5_std = MD5_D0; + memset (ctxt->md5_buf, 0, sizeof(ctxt->md5_buf)); +} + +void md5_loop(md5_ctxt *ctxt, const void *vinput, uint len) +{ + uint gap, i; + const uint8_t *input = vinput; + + ctxt->md5_n += len * 8; /* byte to bit */ + gap = MD5_BUFLEN - ctxt->md5_i; + + if (len >= gap) { + memcpy (ctxt->md5_buf + ctxt->md5_i, input, gap); + md5_calc(ctxt->md5_buf, ctxt); + + for (i = gap; i + MD5_BUFLEN <= len; i += MD5_BUFLEN) { + md5_calc((input + i), ctxt); + } + + ctxt->md5_i = len - i; + memcpy (ctxt->md5_buf, (input + i), ctxt->md5_i); + } else { + memcpy (ctxt->md5_buf + ctxt->md5_i, input, len); + ctxt->md5_i += len; + } +} + +void md5_pad(md5_ctxt *ctxt) +{ + uint gap; + + /* Don't count up padding. Keep md5_n. */ + gap = MD5_BUFLEN - ctxt->md5_i; + if (gap > 8) { + memcpy (ctxt->md5_buf + ctxt->md5_i, md5_paddat, + gap - sizeof(ctxt->md5_n)); + } else { + /* including gap == 8 */ + memcpy (ctxt->md5_buf + ctxt->md5_i, md5_paddat, gap); + md5_calc (ctxt->md5_buf, ctxt); + memcpy (ctxt->md5_buf, md5_paddat + gap, + MD5_BUFLEN - sizeof(ctxt->md5_n)); + } + + /* 8 byte word */ + if (BYTE_ORDER == LITTLE_ENDIAN) + memcpy (&ctxt->md5_buf[56], &ctxt->md5_n8[0], 8); + else + { + ctxt->md5_buf[56] = ctxt->md5_n8[7]; + ctxt->md5_buf[57] = ctxt->md5_n8[6]; + ctxt->md5_buf[58] = ctxt->md5_n8[5]; + ctxt->md5_buf[59] = ctxt->md5_n8[4]; + ctxt->md5_buf[60] = ctxt->md5_n8[3]; + ctxt->md5_buf[61] = ctxt->md5_n8[2]; + ctxt->md5_buf[62] = ctxt->md5_n8[1]; + ctxt->md5_buf[63] = ctxt->md5_n8[0]; + } + md5_calc(ctxt->md5_buf, ctxt); +} + +void md5_result(uint8_t *digest, md5_ctxt *ctxt) +{ + /* 4 byte words */ + if (BYTE_ORDER == LITTLE_ENDIAN) + memcpy (digest, &ctxt->md5_st8[0], 16); + else if (BYTE_ORDER == BIG_ENDIAN) + { + digest[ 0] = ctxt->md5_st8[ 3]; digest[ 1] = ctxt->md5_st8[ 2]; + digest[ 2] = ctxt->md5_st8[ 1]; digest[ 3] = ctxt->md5_st8[ 0]; + digest[ 4] = ctxt->md5_st8[ 7]; digest[ 5] = ctxt->md5_st8[ 6]; + digest[ 6] = ctxt->md5_st8[ 5]; digest[ 7] = ctxt->md5_st8[ 4]; + digest[ 8] = ctxt->md5_st8[11]; digest[ 9] = ctxt->md5_st8[10]; + digest[10] = ctxt->md5_st8[ 9]; digest[11] = ctxt->md5_st8[ 8]; + digest[12] = ctxt->md5_st8[15]; digest[13] = ctxt->md5_st8[14]; + digest[14] = ctxt->md5_st8[13]; digest[15] = ctxt->md5_st8[12]; + } +} + +static void md5_calc(const uint8_t *b64, md5_ctxt * ctxt) +{ + uint32_t A = ctxt->md5_sta; + uint32_t B = ctxt->md5_stb; + uint32_t C = ctxt->md5_stc; + uint32_t D = ctxt->md5_std; +#if (BYTE_ORDER == LITTLE_ENDIAN) + const uint32_t *X = (const uint32_t *)b64; +#elif (BYTE_ORDER == BIG_ENDIAN) + uint32_t X[16]; + + if (BYTE_ORDER == BIG_ENDIAN) + { + /* 4 byte words */ + /* what a brute force but fast! */ + uint8_t *y = (uint8_t *)X; + y[ 0] = b64[ 3]; y[ 1] = b64[ 2]; y[ 2] = b64[ 1]; y[ 3] = b64[ 0]; + y[ 4] = b64[ 7]; y[ 5] = b64[ 6]; y[ 6] = b64[ 5]; y[ 7] = b64[ 4]; + y[ 8] = b64[11]; y[ 9] = b64[10]; y[10] = b64[ 9]; y[11] = b64[ 8]; + y[12] = b64[15]; y[13] = b64[14]; y[14] = b64[13]; y[15] = b64[12]; + y[16] = b64[19]; y[17] = b64[18]; y[18] = b64[17]; y[19] = b64[16]; + y[20] = b64[23]; y[21] = b64[22]; y[22] = b64[21]; y[23] = b64[20]; + y[24] = b64[27]; y[25] = b64[26]; y[26] = b64[25]; y[27] = b64[24]; + y[28] = b64[31]; y[29] = b64[30]; y[30] = b64[29]; y[31] = b64[28]; + y[32] = b64[35]; y[33] = b64[34]; y[34] = b64[33]; y[35] = b64[32]; + y[36] = b64[39]; y[37] = b64[38]; y[38] = b64[37]; y[39] = b64[36]; + y[40] = b64[43]; y[41] = b64[42]; y[42] = b64[41]; y[43] = b64[40]; + y[44] = b64[47]; y[45] = b64[46]; y[46] = b64[45]; y[47] = b64[44]; + y[48] = b64[51]; y[49] = b64[50]; y[50] = b64[49]; y[51] = b64[48]; + y[52] = b64[55]; y[53] = b64[54]; y[54] = b64[53]; y[55] = b64[52]; + y[56] = b64[59]; y[57] = b64[58]; y[58] = b64[57]; y[59] = b64[56]; + y[60] = b64[63]; y[61] = b64[62]; y[62] = b64[61]; y[63] = b64[60]; + } +#endif + + ROUND1(A, B, C, D, 0, Sa, 1); ROUND1(D, A, B, C, 1, Sb, 2); + ROUND1(C, D, A, B, 2, Sc, 3); ROUND1(B, C, D, A, 3, Sd, 4); + ROUND1(A, B, C, D, 4, Sa, 5); ROUND1(D, A, B, C, 5, Sb, 6); + ROUND1(C, D, A, B, 6, Sc, 7); ROUND1(B, C, D, A, 7, Sd, 8); + ROUND1(A, B, C, D, 8, Sa, 9); ROUND1(D, A, B, C, 9, Sb, 10); + ROUND1(C, D, A, B, 10, Sc, 11); ROUND1(B, C, D, A, 11, Sd, 12); + ROUND1(A, B, C, D, 12, Sa, 13); ROUND1(D, A, B, C, 13, Sb, 14); + ROUND1(C, D, A, B, 14, Sc, 15); ROUND1(B, C, D, A, 15, Sd, 16); + + ROUND2(A, B, C, D, 1, Se, 17); ROUND2(D, A, B, C, 6, Sf, 18); + ROUND2(C, D, A, B, 11, Sg, 19); ROUND2(B, C, D, A, 0, Sh, 20); + ROUND2(A, B, C, D, 5, Se, 21); ROUND2(D, A, B, C, 10, Sf, 22); + ROUND2(C, D, A, B, 15, Sg, 23); ROUND2(B, C, D, A, 4, Sh, 24); + ROUND2(A, B, C, D, 9, Se, 25); ROUND2(D, A, B, C, 14, Sf, 26); + ROUND2(C, D, A, B, 3, Sg, 27); ROUND2(B, C, D, A, 8, Sh, 28); + ROUND2(A, B, C, D, 13, Se, 29); ROUND2(D, A, B, C, 2, Sf, 30); + ROUND2(C, D, A, B, 7, Sg, 31); ROUND2(B, C, D, A, 12, Sh, 32); + + ROUND3(A, B, C, D, 5, Si, 33); ROUND3(D, A, B, C, 8, Sj, 34); + ROUND3(C, D, A, B, 11, Sk, 35); ROUND3(B, C, D, A, 14, Sl, 36); + ROUND3(A, B, C, D, 1, Si, 37); ROUND3(D, A, B, C, 4, Sj, 38); + ROUND3(C, D, A, B, 7, Sk, 39); ROUND3(B, C, D, A, 10, Sl, 40); + ROUND3(A, B, C, D, 13, Si, 41); ROUND3(D, A, B, C, 0, Sj, 42); + ROUND3(C, D, A, B, 3, Sk, 43); ROUND3(B, C, D, A, 6, Sl, 44); + ROUND3(A, B, C, D, 9, Si, 45); ROUND3(D, A, B, C, 12, Sj, 46); + ROUND3(C, D, A, B, 15, Sk, 47); ROUND3(B, C, D, A, 2, Sl, 48); + + ROUND4(A, B, C, D, 0, Sm, 49); ROUND4(D, A, B, C, 7, Sn, 50); + ROUND4(C, D, A, B, 14, So, 51); ROUND4(B, C, D, A, 5, Sp, 52); + ROUND4(A, B, C, D, 12, Sm, 53); ROUND4(D, A, B, C, 3, Sn, 54); + ROUND4(C, D, A, B, 10, So, 55); ROUND4(B, C, D, A, 1, Sp, 56); + ROUND4(A, B, C, D, 8, Sm, 57); ROUND4(D, A, B, C, 15, Sn, 58); + ROUND4(C, D, A, B, 6, So, 59); ROUND4(B, C, D, A, 13, Sp, 60); + ROUND4(A, B, C, D, 4, Sm, 61); ROUND4(D, A, B, C, 11, Sn, 62); + ROUND4(C, D, A, B, 2, So, 63); ROUND4(B, C, D, A, 9, Sp, 64); + + ctxt->md5_sta += A; + ctxt->md5_stb += B; + ctxt->md5_stc += C; + ctxt->md5_std += D; +} + +/* From RFC 2104 */ +void +hmac_md5(text, text_len, key, key_len, digest) +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 */ +uint8_t * digest; /* caller digest to be filled in */ + +{ + MD5_CTX context; + unsigned char k_ipad[65]; /* inner padding - + * key XORd with ipad + */ + unsigned char k_opad[65]; /* outer padding - + * key XORd with opad + */ + unsigned char tk[16]; + int i; + /* if key is longer than 64 bytes reset it to key=MD5(key) */ + if (key_len > 64) { + + MD5_CTX tctx; + + MD5Init(&tctx); + MD5Update(&tctx, key, key_len); + MD5Final(tk, &tctx); + + key = tk; + key_len = 16; + } + + /* + * the HMAC_MD5 transform looks like: + * + * MD5(K XOR opad, MD5(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected + */ + + /* start out by storing key in pads */ + bzero( k_ipad, sizeof k_ipad); + bzero( k_opad, sizeof k_opad); + bcopy( key, k_ipad, key_len); + bcopy( key, k_opad, key_len); + + /* XOR key with ipad and opad values */ + for (i=0; i<64; i++) { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + /* + * perform inner MD5 + */ + MD5Init(&context); /* init context for 1st + * pass */ + MD5Update(&context, k_ipad, 64); /* start with inner pad */ + MD5Update(&context, text, text_len); /* then text of datagram */ + MD5Final(digest, &context); /* finish up 1st pass */ + /* + * perform outer MD5 + */ + MD5Init(&context); /* init context for 2nd + * pass */ + MD5Update(&context, k_opad, 64); /* start with outer pad */ + MD5Update(&context, digest, 16); /* then results of 1st + * hash */ + MD5Final(digest, &context); /* finish up 2nd pass */ +} diff --git a/lib/md5.h b/lib/md5.h new file mode 100644 index 0000000..4e5ffbd --- /dev/null +++ b/lib/md5.h @@ -0,0 +1,89 @@ +/* $USAGI: md5.h,v 1.2 2000/11/02 11:59:25 yoshfuji Exp $ */ +/* $KAME: md5.h,v 1.4 2000/03/27 04:36:22 sumikawa Exp $ */ +/* $Id: md5.h,v 1.3 2006/01/17 17:40:45 paul Exp $ */ + +/* + * Copyright (C) 2004 6WIND + * + * All rights reserved. + * + * This MD5 code is Big endian and Little Endian compatible. + */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _LIBZEBRA_MD5_H_ +#define _LIBZEBRA_MD5_H_ + +#define MD5_BUFLEN 64 + +typedef struct { + union { + uint32_t md5_state32[4]; + uint8_t md5_state8[16]; + } md5_st; + +#define md5_sta md5_st.md5_state32[0] +#define md5_stb md5_st.md5_state32[1] +#define md5_stc md5_st.md5_state32[2] +#define md5_std md5_st.md5_state32[3] +#define md5_st8 md5_st.md5_state8 + + union { + uint64_t md5_count64; + uint8_t md5_count8[8]; + } md5_count; +#define md5_n md5_count.md5_count64 +#define md5_n8 md5_count.md5_count8 + + uint md5_i; + uint8_t md5_buf[MD5_BUFLEN]; +} md5_ctxt; + +extern void md5_init (md5_ctxt *); +extern void md5_loop (md5_ctxt *, const void *, u_int); +extern void md5_pad (md5_ctxt *); +extern void md5_result (uint8_t *, md5_ctxt *); + +/* compatibility */ +#define MD5_CTX md5_ctxt +#define MD5Init(x) md5_init((x)) +#define MD5Update(x, y, z) md5_loop((x), (y), (z)) +#define MD5Final(x, y) \ +do { \ + md5_pad((y)); \ + md5_result((x), (y)); \ +} while (0) + +/* From RFC 2104 */ +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 new file mode 100644 index 0000000..b8305dd --- /dev/null +++ b/lib/memory.c @@ -0,0 +1,486 @@ +/* + * Memory management routine + * Copyright (C) 1998 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 +/* malloc.h is generally obsolete, however GNU Libc mallinfo wants it. */ +#if !defined(HAVE_STDLIB_H) || (defined(GNU_LINUX) && defined(HAVE_MALLINFO)) +#include +#endif /* !HAVE_STDLIB_H || HAVE_MALLINFO */ + +#include "log.h" +#include "memory.h" + +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" }, + { MTYPE_THREAD_MASTER, "thread_master" }, + { MTYPE_VECTOR, "vector" }, + { MTYPE_VECTOR_INDEX, "vector_index" }, + { MTYPE_IF, "interface" }, + { 0, NULL }, +}; + +/* Fatal memory allocation error occured. */ +static void __attribute__ ((noreturn)) +zerror (const char *fname, int type, size_t size) +{ + zlog_err ("%s : can't allocate memory for `%s' size %d: %s\n", + fname, lookup (mstr, type), (int) size, safe_strerror(errno)); + log_memstats(LOG_WARNING); + /* N.B. It might be preferable to call zlog_backtrace_sigsafe here, since + that function should definitely be safe in an OOM condition. But + unfortunately zlog_backtrace_sigsafe does not support syslog logging at + this time... */ + zlog_backtrace(LOG_WARNING); + abort(); +} + +/* + * Allocate memory of a given size, to be tracked by a given type. + * Effects: Returns a pointer to usable memory. If memory cannot + * be allocated, aborts execution. + */ +void * +zmalloc (int type, size_t size) +{ + void *memory; + + memory = malloc (size); + + if (memory == NULL) + zerror ("malloc", type, size); + + alloc_inc (type); + + return memory; +} + +/* + * 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 * +zzcalloc (int type, size_t size) +{ + void *memory; + + memory = calloc (1, size); + + if (memory == NULL) + zerror ("calloc", type, size); + + alloc_inc (type); + + return memory; +} + +/* + * 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, zzcalloc, or zrealloc with the + * same type. + * Effects: Returns a pointer to the new memory, or aborts. + */ +void * +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); + if (ptr == NULL) + alloc_inc (type); + + return memory; +} + +/* + * Free memory allocated by z*alloc or zstrdup. + * Requires: ptr was returned by zmalloc, zzcalloc, or zrealloc with the + * same type. + * Effects: The memory is freed and may no longer be referenced. + */ +void +zfree (int type, void *ptr) +{ + if (ptr != NULL) + { + alloc_dec (type); + free (ptr); + } +} + +/* + * Duplicate a string, counting memory usage by type. + * Effects: The string is duplicated, and the return value must + * eventually be passed to zfree with the same type. The function will + * succeed or abort. + */ +char * +zstrdup (int type, const char *str) +{ + void *dup; + + dup = strdup (str); + if (dup == NULL) + zerror ("strdup", type, strlen (str)); + alloc_inc (type); + return dup; +} + +#ifdef MEMORY_LOG +static struct +{ + const char *name; + long alloc; + unsigned long t_malloc; + unsigned long c_malloc; + unsigned long t_calloc; + unsigned long c_calloc; + unsigned long t_realloc; + unsigned long t_free; + unsigned long c_strdup; +} mstat [MTYPE_MAX]; + +static void +mtype_log (char *func, void *memory, const char *file, int line, int type) +{ + zlog_debug ("%s: %s %p %s %d", func, lookup (mstr, type), memory, file, line); +} + +void * +mtype_zmalloc (const char *file, int line, int type, size_t size) +{ + void *memory; + + mstat[type].c_malloc++; + mstat[type].t_malloc++; + + memory = zmalloc (type, size); + mtype_log ("zmalloc", memory, file, line, type); + + return memory; +} + +void * +mtype_zcalloc (const char *file, int line, int type, size_t size) +{ + void *memory; + + mstat[type].c_calloc++; + mstat[type].t_calloc++; + + memory = zzcalloc (type, size); + mtype_log ("xcalloc", memory, file, line, type); + + return memory; +} + +void * +mtype_zrealloc (const char *file, int line, int type, void *ptr, size_t size) +{ + void *memory; + + /* Realloc need before allocated pointer. */ + mstat[type].t_realloc++; + + memory = zrealloc (type, ptr, size); + + mtype_log ("xrealloc", memory, file, line, type); + + return memory; +} + +/* Important function. */ +void +mtype_zfree (const char *file, int line, int type, void *ptr) +{ + mstat[type].t_free++; + + mtype_log ("xfree", ptr, file, line, type); + + zfree (type, ptr); +} + +char * +mtype_zstrdup (const char *file, int line, int type, const char *str) +{ + char *memory; + + mstat[type].c_strdup++; + + memory = zstrdup (type, str); + + mtype_log ("xstrdup", memory, file, line, type); + + return memory; +} +#else +static struct +{ + char *name; + long alloc; +} mstat [MTYPE_MAX]; +#endif /* MEMORY_LOG */ + +/* Increment allocation counter. */ +static void +alloc_inc (int type) +{ + mstat[type].alloc++; +} + +/* Decrement allocation counter. */ +static void +alloc_dec (int type) +{ + mstat[type].alloc--; +} + +/* Looking up memory status from vty interface. */ +#include "vector.h" +#include "vty.h" +#include "command.h" + +static void +log_memstats(int pri) +{ + struct mlist *ml; + + for (ml = mlists; ml->list; ml++) + { + struct memory_list *m; + + zlog (NULL, pri, "Memory utilization in module %s:", ml->name); + for (m = ml->list; m->index >= 0; m++) + if (m->index && mstat[m->index].alloc) + zlog (NULL, pri, " %-30s: %10ld", m->format, mstat[m->index].alloc); + } +} + +void +log_memstats_stderr (const char *prefix) +{ + struct mlist *ml; + struct memory_list *m; + int i; + int j = 0; + + for (ml = mlists; ml->list; ml++) + { + i = 0; + + for (m = ml->list; m->index >= 0; m++) + if (m->index && mstat[m->index].alloc) + { + if (!i) + fprintf (stderr, + "%s: memstats: Current memory utilization in module %s:\n", + prefix, + ml->name); + fprintf (stderr, + "%s: memstats: %-30s: %10ld%s\n", + prefix, + m->format, + mstat[m->index].alloc, + mstat[m->index].alloc < 0 ? " (REPORT THIS BUG!)" : ""); + i = j = 1; + } + } + + if (j) + fprintf (stderr, + "%s: memstats: NOTE: If configuration exists, utilization may be " + "expected.\n", + prefix); + else + fprintf (stderr, + "%s: memstats: No remaining tracked memory utilization.\n", + prefix); +} + +static void +show_separator(struct vty *vty) +{ + vty_out (vty, "-----------------------------\r\n"); +} + +static int +show_memory_vty (struct vty *vty, struct memory_list *list) +{ + struct memory_list *m; + int needsep = 0; + + for (m = list; m->index >= 0; m++) + if (m->index == 0) + { + if (needsep) + { + show_separator (vty); + needsep = 0; + } + } + else if (mstat[m->index].alloc) + { + vty_out (vty, "%-30s: %10ld\r\n", m->format, mstat[m->index].alloc); + needsep = 1; + } + return needsep; +} + +#ifdef HAVE_MALLINFO +static int +show_memory_mallinfo (struct vty *vty) +{ + struct mallinfo minfo = mallinfo(); + char buf[MTYPE_MEMSTR_LEN]; + + vty_out (vty, "System allocator statistics:%s", VTY_NEWLINE); + vty_out (vty, " Total heap allocated: %s%s", + mtype_memstr (buf, MTYPE_MEMSTR_LEN, minfo.arena), + VTY_NEWLINE); + vty_out (vty, " Holding block headers: %s%s", + mtype_memstr (buf, MTYPE_MEMSTR_LEN, minfo.hblkhd), + VTY_NEWLINE); + vty_out (vty, " Used small blocks: %s%s", + mtype_memstr (buf, MTYPE_MEMSTR_LEN, minfo.usmblks), + VTY_NEWLINE); + vty_out (vty, " Used ordinary blocks: %s%s", + mtype_memstr (buf, MTYPE_MEMSTR_LEN, minfo.uordblks), + VTY_NEWLINE); + vty_out (vty, " Free small blocks: %s%s", + mtype_memstr (buf, MTYPE_MEMSTR_LEN, minfo.fsmblks), + VTY_NEWLINE); + vty_out (vty, " Free ordinary blocks: %s%s", + mtype_memstr (buf, MTYPE_MEMSTR_LEN, minfo.fordblks), + VTY_NEWLINE); + vty_out (vty, " Ordinary blocks: %ld%s", + (unsigned long)minfo.ordblks, + VTY_NEWLINE); + vty_out (vty, " Small blocks: %ld%s", + (unsigned long)minfo.smblks, + VTY_NEWLINE); + vty_out (vty, " Holding blocks: %ld%s", + (unsigned long)minfo.hblks, + VTY_NEWLINE); + vty_out (vty, "(see system documentation for 'mallinfo' for meaning)%s", + VTY_NEWLINE); + return 1; +} +#endif /* HAVE_MALLINFO */ + +DEFUN (show_memory, + show_memory_cmd, + "show memory", + "Show running system information\n" + "Memory statistics\n") +{ + struct mlist *ml; + int needsep = 0; + +#ifdef HAVE_MALLINFO + needsep = show_memory_mallinfo (vty); +#endif /* HAVE_MALLINFO */ + + for (ml = mlists; ml->list; ml++) + { + if (needsep) + show_separator (vty); + needsep = show_memory_vty (vty, ml->list); + } + + return CMD_SUCCESS; +} + + +void +memory_init (void) +{ + install_element (RESTRICTED_NODE, &show_memory_cmd); + + install_element (VIEW_NODE, &show_memory_cmd); +} + +/* Stats querying from users */ +/* Return a pointer to a human friendly string describing + * the byte count passed in. E.g: + * "0 bytes", "2048 bytes", "110kB", "500MiB", "11GiB", etc. + * Up to 4 significant figures will be given. + * The pointer returned may be NULL (indicating an error) + * or point to the given buffer, or point to static storage. + */ +const char * +mtype_memstr (char *buf, size_t len, unsigned long bytes) +{ + unsigned int m, k; + + /* easy cases */ + if (!bytes) + return "0 bytes"; + if (bytes == 1) + return "1 byte"; + + /* + * 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 (m > 10) + { + if (bytes & (1 << 19)) + m++; + snprintf (buf, len, "%d MiB", m); + } + else if (k > 10) + { + if (bytes & (1 << 9)) + k++; + snprintf (buf, len, "%d KiB", k); + } + else + snprintf (buf, len, "%ld bytes", bytes); + + return buf; +} + +unsigned long +mtype_stats_alloc (int type) +{ + return mstat[type].alloc; +} diff --git a/lib/memory.h b/lib/memory.h new file mode 100644 index 0000000..5013529 --- /dev/null +++ b/lib/memory.h @@ -0,0 +1,96 @@ +/* Memory management routine + Copyright (C) 1998 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_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 +{ + int index; + const char *format; +}; + +struct mlist { + struct memory_list *list; + const char *name; +}; + +#include "lib/memtypes.h" + +extern struct mlist mlists[]; + +/* #define MEMORY_LOG */ +#ifdef MEMORY_LOG +#define XMALLOC(mtype, size) \ + mtype_zmalloc (__FILE__, __LINE__, (mtype), (size)) +#define XCALLOC(mtype, size) \ + mtype_zcalloc (__FILE__, __LINE__, (mtype), (size)) +#define XREALLOC(mtype, ptr, size) \ + mtype_zrealloc (__FILE__, __LINE__, (mtype), (ptr), (size)) +#define XFREE(mtype, ptr) \ + do { \ + mtype_zfree (__FILE__, __LINE__, (mtype), (ptr)); \ + ptr = NULL; } \ + while (0) +#define XSTRDUP(mtype, str) \ + mtype_zstrdup (__FILE__, __LINE__, (mtype), (str)) +#else +#define XMALLOC(mtype, size) zmalloc ((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)); \ + ptr = NULL; } \ + while (0) +#define XSTRDUP(mtype, str) zstrdup ((mtype), (str)) +#endif /* MEMORY_LOG */ + +/* Prototypes of memory function. */ +extern void *zmalloc (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); + +extern void *mtype_zmalloc (const char *file, int line, int type, size_t size); + +extern void *mtype_zcalloc (const char *file, int line, int type, size_t size); + +extern void *mtype_zrealloc (const char *file, int line, int type, void *ptr, + size_t size); + +extern void mtype_zfree (const char *file, int line, int type, + void *ptr); + +extern char *mtype_zstrdup (const char *file, int line, int type, + const char *str); +extern void memory_init (void); +extern void log_memstats_stderr (const char *); + +/* return number of allocations outstanding for the type */ +extern unsigned long mtype_stats_alloc (int); + +/* Human friendly string for given byte count */ +#define MTYPE_MEMSTR_LEN 20 +extern const char *mtype_memstr (char *, size_t, unsigned long); +#endif /* _ZEBRA_MEMORY_H */ diff --git a/lib/memtypes.awk b/lib/memtypes.awk new file mode 100644 index 0000000..bd13327 --- /dev/null +++ b/lib/memtypes.awk @@ -0,0 +1,87 @@ +### +# 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 +# for the struct memory_list arrays +# +# struct memory_list's must be declared as: +# '\nstruct memory_list memory_list_[] .....' +# +# Each MTYPE_ within the definition must the second token on the line, +# tokens being delineated by whitespace. It may only consist of the set of +# characters [[:upper:]_[:digit:]]. Eg: +# +# '\n { MTYPE_AWESOME_IPV8 , "Amazing new protocol, says genius" {}..boo' +# +# We try to ignore lines whose first token is /* or *, ie C comment lines. +# So the following should work fine: +# +# '/* This is the best memory_list ever! +# ' * It's got all my MTYPE's */ +# ' +# 'struct memory_list memory_list_my_amazing_mlist[] = = +# '{ +# ' { MTYPE_DONGLE, "Dongle widget" } +# ' { MTYPE_FROB, "Frobulator" }, +# '{ MTYPE_WIPPLE, "Wipple combombulator"} +# '}}} +# +# Even if it isn't quite a valid C declaration. +# + +BEGIN { + mlistregex = "memory_list_(.*)\\[\\]"; + mtyperegex = "^(MTYPE_[[:upper:]_[:digit:]]+).*"; + header = "/* Auto-generated from memtypes.c by " ARGV[0] ". */\n"; + header = header "/* Do not edit! */\n"; + header = header "\n#ifndef _QUAGGA_MEMTYPES_H\n"; + header = header "#define _QUAGGA_MEMTYPES_H\n"; + footer = "\n#endif /* _QUAGGA_MEMTYPES_H */\n\n"; + mlistformat = "extern struct memory_list memory_list_%s[];"; + printf ("%s\n", header); +} + +# catch lines beginning with 'struct memory list ' and try snag the +# memory_list name. Has to be 3rd field. +($0 ~ /^struct memory_list /) && (NF >= 3) { + mlists[lcount++] = gensub(mlistregex, "\\1", "g",$3); +} + +# snag the MTYPE, it must self-standing and the second field, +# though we do manage to tolerate the , C seperator being appended +($1 !~ /^\/?\*/) && ($2 ~ /^MTYPE_/) { + mtype[tcount++] = gensub(mtyperegex, "\\1", "g", $2); +} + +END { + printf("enum\n{\n MTYPE_TMP = 1,\n"); + for (i = 0; i < tcount; i++) { + if (mtype[i] != "" && mtype[i] != "MTYPE_TMP") + printf (" %s,\n", mtype[i]); + } + printf (" MTYPE_MAX,\n};\n\n"); + for (i = 0; i < lcount; i++) { + if (mlists[i] != "") + printf (mlistformat "\n", mlists[i]); + } + printf (footer); +} diff --git a/lib/memtypes.c b/lib/memtypes.c new file mode 100644 index 0000000..e5b3546 --- /dev/null +++ b/lib/memtypes.c @@ -0,0 +1,319 @@ +/* + * Memory type definitions. This file is parsed by memtypes.awk to extract + * MTYPE_ and memory_list_.. information in order to autogenerate + * memtypes.h. + * + * The script is sensitive to the format (though not whitespace), see + * the top of memtypes.awk for more details. + */ + +#include "zebra.h" +#include "memory.h" + +struct memory_list memory_list_lib[] = +{ + { MTYPE_TMP, "Temporary memory" }, + { MTYPE_STRVEC, "String vector" }, + { MTYPE_VECTOR, "Vector" }, + { MTYPE_VECTOR_INDEX, "Vector index" }, + { MTYPE_LINK_LIST, "Link List" }, + { MTYPE_LINK_NODE, "Link Node" }, + { MTYPE_THREAD, "Thread" }, + { MTYPE_THREAD_MASTER, "Thread master" }, + { MTYPE_THREAD_STATS, "Thread stats" }, + { MTYPE_VTY, "VTY" }, + { MTYPE_VTY_OUT_BUF, "VTY output buffer" }, + { MTYPE_VTY_HIST, "VTY history" }, + { MTYPE_IF, "Interface" }, + { MTYPE_CONNECTED, "Connected" }, + { MTYPE_CONNECTED_LABEL, "Connected interface label" }, + { MTYPE_BUFFER, "Buffer" }, + { MTYPE_BUFFER_DATA, "Buffer data" }, + { MTYPE_STREAM, "Stream" }, + { MTYPE_STREAM_DATA, "Stream data" }, + { MTYPE_STREAM_FIFO, "Stream FIFO" }, + { MTYPE_PREFIX, "Prefix" }, + { MTYPE_PREFIX_IPV4, "Prefix IPv4" }, + { MTYPE_PREFIX_IPV6, "Prefix IPv6" }, + { MTYPE_HASH, "Hash" }, + { MTYPE_HASH_BACKET, "Hash Bucket" }, + { MTYPE_HASH_INDEX, "Hash Index" }, + { MTYPE_ROUTE_TABLE, "Route table" }, + { MTYPE_ROUTE_NODE, "Route node" }, + { MTYPE_DISTRIBUTE, "Distribute list" }, + { MTYPE_DISTRIBUTE_IFNAME, "Dist-list ifname" }, + { MTYPE_ACCESS_LIST, "Access List" }, + { MTYPE_ACCESS_LIST_STR, "Access List Str" }, + { MTYPE_ACCESS_FILTER, "Access Filter" }, + { MTYPE_PREFIX_LIST, "Prefix List" }, + { MTYPE_PREFIX_LIST_ENTRY, "Prefix List Entry" }, + { MTYPE_PREFIX_LIST_STR, "Prefix List Str" }, + { MTYPE_ROUTE_MAP, "Route map" }, + { MTYPE_ROUTE_MAP_NAME, "Route map name" }, + { MTYPE_ROUTE_MAP_INDEX, "Route map index" }, + { MTYPE_ROUTE_MAP_RULE, "Route map rule" }, + { MTYPE_ROUTE_MAP_RULE_STR, "Route map rule str" }, + { MTYPE_ROUTE_MAP_COMPILED, "Route map compiled" }, + { MTYPE_CMD_TOKENS, "Command desc" }, + { MTYPE_KEY, "Key" }, + { MTYPE_KEYCHAIN, "Key chain" }, + { MTYPE_IF_RMAP, "Interface route map" }, + { MTYPE_IF_RMAP_NAME, "I.f. route map name", }, + { MTYPE_SOCKUNION, "Socket union" }, + { MTYPE_PRIVS, "Privilege information" }, + { MTYPE_ZLOG, "Logging" }, + { MTYPE_ZCLIENT, "Zclient" }, + { MTYPE_WORK_QUEUE, "Work queue" }, + { MTYPE_WORK_QUEUE_ITEM, "Work queue item" }, + { MTYPE_WORK_QUEUE_NAME, "Work queue name string" }, + { 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_ZEBRA_VRF, "ZEBRA VRF" }, + { MTYPE_NEXTHOP, "Nexthop" }, + { MTYPE_RIB, "RIB" }, + { MTYPE_RIB_QUEUE, "RIB process work queue" }, + { 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 }, +}; + +struct memory_list memory_list_bgp[] = +{ + { MTYPE_BGP, "BGP instance" }, + { MTYPE_BGP_LISTENER, "BGP listen socket details" }, + { MTYPE_BGP_PEER, "BGP peer" }, + { MTYPE_BGP_PEER_HOST, "BGP peer hostname" }, + { MTYPE_PEER_GROUP, "Peer group" }, + { MTYPE_PEER_DESC, "Peer description" }, + { MTYPE_PEER_PASSWORD, "Peer password string" }, + { MTYPE_ATTR, "BGP attribute" }, + { MTYPE_ATTR_EXTRA, "BGP extra attributes" }, + { MTYPE_AS_PATH, "BGP aspath" }, + { MTYPE_AS_SEG, "BGP aspath seg" }, + { MTYPE_AS_SEG_DATA, "BGP aspath segment data" }, + { MTYPE_AS_STR, "BGP aspath str" }, + { 0, NULL }, + { MTYPE_BGP_TABLE, "BGP table" }, + { MTYPE_BGP_NODE, "BGP node" }, + { MTYPE_BGP_ROUTE, "BGP route" }, + { MTYPE_BGP_ROUTE_EXTRA, "BGP ancillary route info" }, + { MTYPE_BGP_CONN, "BGP connected" }, + { MTYPE_BGP_STATIC, "BGP static" }, + { MTYPE_BGP_ADVERTISE_ATTR, "BGP adv attr" }, + { MTYPE_BGP_ADVERTISE, "BGP adv" }, + { 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" }, + { MTYPE_AS_FILTER_STR, "BGP AS filter str" }, + { 0, NULL }, + { MTYPE_COMMUNITY, "community" }, + { MTYPE_COMMUNITY_VAL, "community val" }, + { MTYPE_COMMUNITY_STR, "community str" }, + { 0, NULL }, + { MTYPE_ECOMMUNITY, "extcommunity" }, + { MTYPE_ECOMMUNITY_VAL, "extcommunity val" }, + { MTYPE_ECOMMUNITY_STR, "extcommunity str" }, + { 0, NULL }, + { MTYPE_COMMUNITY_LIST, "community-list" }, + { MTYPE_COMMUNITY_LIST_NAME, "community-list name" }, + { MTYPE_COMMUNITY_LIST_ENTRY, "community-list entry" }, + { MTYPE_COMMUNITY_LIST_CONFIG, "community-list config" }, + { MTYPE_COMMUNITY_LIST_HANDLER, "community-list handler" }, + { 0, NULL }, + { MTYPE_CLUSTER, "Cluster list" }, + { MTYPE_CLUSTER_VAL, "Cluster list val" }, + { 0, NULL }, + { MTYPE_BGP_PROCESS_QUEUE, "BGP Process queue" }, + { MTYPE_BGP_CLEAR_NODE_QUEUE, "BGP node clear queue" }, + { 0, NULL }, + { MTYPE_TRANSIT, "BGP transit attr" }, + { MTYPE_TRANSIT_VAL, "BGP transit val" }, + { 0, NULL }, + { MTYPE_BGP_DISTANCE, "BGP distance" }, + { MTYPE_BGP_NEXTHOP_CACHE, "BGP nexthop" }, + { MTYPE_BGP_CONFED_LIST, "BGP confed list" }, + { MTYPE_PEER_UPDATE_SOURCE, "BGP peer update interface" }, + { MTYPE_BGP_DAMP_INFO, "Dampening info" }, + { 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 } +}; + +struct memory_list memory_list_rip[] = +{ + { MTYPE_RIP, "RIP structure" }, + { MTYPE_RIP_INFO, "RIP route info" }, + { MTYPE_RIP_INTERFACE, "RIP interface" }, + { MTYPE_RIP_PEER, "RIP peer" }, + { MTYPE_RIP_OFFSET_LIST, "RIP offset list" }, + { MTYPE_RIP_DISTANCE, "RIP distance" }, + { -1, NULL } +}; + +struct memory_list memory_list_ripng[] = +{ + { MTYPE_RIPNG, "RIPng structure" }, + { MTYPE_RIPNG_ROUTE, "RIPng route info" }, + { MTYPE_RIPNG_AGGREGATE, "RIPng aggregate" }, + { MTYPE_RIPNG_PEER, "RIPng peer" }, + { MTYPE_RIPNG_OFFSET_LIST, "RIPng offset lst" }, + { MTYPE_RIPNG_RTE_DATA, "RIPng rte data" }, + { -1, NULL } +}; + +struct memory_list memory_list_babel[] = +{ + { MTYPE_BABEL, "Babel structure" }, + { MTYPE_BABEL_IF, "Babel interface" }, + { -1, NULL } +}; + +struct memory_list memory_list_ospf[] = +{ + { MTYPE_OSPF_TOP, "OSPF top" }, + { MTYPE_OSPF_AREA, "OSPF area" }, + { MTYPE_OSPF_AREA_RANGE, "OSPF area range" }, + { MTYPE_OSPF_NETWORK, "OSPF network" }, + { MTYPE_OSPF_NEIGHBOR_STATIC,"OSPF static nbr" }, + { MTYPE_OSPF_IF, "OSPF interface" }, + { MTYPE_OSPF_NEIGHBOR, "OSPF neighbor" }, + { MTYPE_OSPF_ROUTE, "OSPF route" }, + { MTYPE_OSPF_TMP, "OSPF tmp mem" }, + { MTYPE_OSPF_LSA, "OSPF LSA" }, + { MTYPE_OSPF_LSA_DATA, "OSPF LSA data" }, + { MTYPE_OSPF_LSDB, "OSPF LSDB" }, + { MTYPE_OSPF_PACKET, "OSPF packet" }, + { MTYPE_OSPF_FIFO, "OSPF FIFO queue" }, + { MTYPE_OSPF_VERTEX, "OSPF vertex" }, + { MTYPE_OSPF_VERTEX_PARENT, "OSPF vertex parent", }, + { MTYPE_OSPF_NEXTHOP, "OSPF nexthop" }, + { MTYPE_OSPF_PATH, "OSPF path" }, + { MTYPE_OSPF_VL_DATA, "OSPF VL data" }, + { MTYPE_OSPF_CRYPT_KEY, "OSPF crypt key" }, + { MTYPE_OSPF_EXTERNAL_INFO, "OSPF ext. info" }, + { MTYPE_OSPF_DISTANCE, "OSPF distance" }, + { 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 }, +}; + +struct memory_list memory_list_ospf6[] = +{ + { MTYPE_OSPF6_TOP, "OSPF6 top" }, + { MTYPE_OSPF6_AREA, "OSPF6 area" }, + { MTYPE_OSPF6_IF, "OSPF6 interface" }, + { MTYPE_OSPF6_NEIGHBOR, "OSPF6 neighbor" }, + { MTYPE_OSPF6_ROUTE, "OSPF6 route" }, + { MTYPE_OSPF6_PREFIX, "OSPF6 prefix" }, + { MTYPE_OSPF6_MESSAGE, "OSPF6 message" }, + { MTYPE_OSPF6_LSA, "OSPF6 LSA" }, + { MTYPE_OSPF6_LSA_SUMMARY, "OSPF6 LSA summary" }, + { MTYPE_OSPF6_LSDB, "OSPF6 LSA database" }, + { MTYPE_OSPF6_VERTEX, "OSPF6 vertex" }, + { MTYPE_OSPF6_SPFTREE, "OSPF6 SPF tree" }, + { MTYPE_OSPF6_NEXTHOP, "OSPF6 nexthop" }, + { MTYPE_OSPF6_EXTERNAL_INFO,"OSPF6 ext. info" }, + { MTYPE_OSPF6_OTHER, "OSPF6 other" }, + { MTYPE_OSPF6_DISTANCE, "OSPF6 distance" }, + { -1, NULL }, +}; + +struct memory_list memory_list_isis[] = +{ + { MTYPE_ISIS, "ISIS" }, + { MTYPE_ISIS_TMP, "ISIS TMP" }, + { MTYPE_ISIS_CIRCUIT, "ISIS circuit" }, + { MTYPE_ISIS_LSP, "ISIS LSP" }, + { MTYPE_ISIS_ADJACENCY, "ISIS adjacency" }, + { MTYPE_ISIS_AREA, "ISIS area" }, + { MTYPE_ISIS_AREA_ADDR, "ISIS area address" }, + { MTYPE_ISIS_TLV, "ISIS TLV" }, + { MTYPE_ISIS_DYNHN, "ISIS dyn hostname" }, + { MTYPE_ISIS_SPFTREE, "ISIS SPFtree" }, + { MTYPE_ISIS_VERTEX, "ISIS vertex" }, + { 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", }, + { MTYPE_VTYSH_CONFIG_LINE, "Vtysh configuration line" }, + { -1, NULL }, +}; + +struct mlist mlists[] __attribute__ ((unused)) = { + { memory_list_lib, "LIB" }, + { memory_list_zebra, "ZEBRA" }, + { memory_list_rip, "RIP" }, + { memory_list_ripng, "RIPNG" }, + { memory_list_ospf, "OSPF" }, + { 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 new file mode 100644 index 0000000..b982640 --- /dev/null +++ b/lib/network.c @@ -0,0 +1,116 @@ +/* + * Network library. + * 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 "log.h" +#include "network.h" + +/* Read nbytes from fd and store into ptr. */ +int +readn (int fd, u_char *ptr, int nbytes) +{ + int nleft; + int nread; + + nleft = nbytes; + + while (nleft > 0) + { + nread = read (fd, ptr, nleft); + + if (nread < 0) + return (nread); + else + if (nread == 0) + break; + + nleft -= nread; + ptr += nread; + } + + return nbytes - nleft; +} + +/* Write nbytes from ptr to fd. */ +int +writen(int fd, const u_char *ptr, int nbytes) +{ + int nleft; + int nwritten; + + nleft = nbytes; + + while (nleft > 0) + { + nwritten = write(fd, ptr, nleft); + + if (nwritten <= 0) + return (nwritten); + + nleft -= nwritten; + ptr += nwritten; + } + return nbytes - nleft; +} + +int +set_nonblocking(int fd) +{ + int flags; + + /* According to the Single UNIX Spec, the return value for F_GETFL should + never be negative. */ + if ((flags = fcntl(fd, F_GETFL)) < 0) + { + zlog_warn("fcntl(F_GETFL) failed for fd %d: %s", + fd, safe_strerror(errno)); + return -1; + } + if (fcntl(fd, F_SETFL, (flags | O_NONBLOCK)) < 0) + { + zlog_warn("fcntl failed setting fd %d non-blocking: %s", + fd, safe_strerror(errno)); + return -1; + } + 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 new file mode 100644 index 0000000..0fcb575 --- /dev/null +++ b/lib/network.h @@ -0,0 +1,43 @@ +/* + * Network library header. + * Copyright (C) 1998 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_NETWORK_H +#define _ZEBRA_NETWORK_H + +/* Both readn and writen are deprecated and will be removed. They are not + suitable for use with non-blocking file descriptors. + */ +extern int readn (int, u_char *, int); +extern int writen (int, const u_char *, int); + +/* Set the file descriptor to use non-blocking I/O. Returns 0 for success, + -1 on error. */ +extern int set_nonblocking(int fd); + +/* Does the I/O error indicate that the operation should be retried later? */ +#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 0000000..5eb2182 --- /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 0000000..0c0dfca --- /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/pid_output.c b/lib/pid_output.c new file mode 100644 index 0000000..5261bab --- /dev/null +++ b/lib/pid_output.c @@ -0,0 +1,108 @@ +/* + * Process id output. + * Copyright (C) 1998, 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 "version.h" + +#define PIDFILE_MASK 0644 +#ifndef HAVE_FCNTL + +pid_t +pid_output (const char *path) +{ + FILE *fp; + pid_t pid; + mode_t oldumask; + + pid = getpid(); + + oldumask = umask(0777 & ~PIDFILE_MASK); + fp = fopen (path, "w"); + if (fp != NULL) + { + fprintf (fp, "%d\n", (int) pid); + fclose (fp); + umask(oldumask); + return pid; + } + /* XXX Why do we continue instead of exiting? This seems incompatible + with the behavior of the fcntl version below. */ + zlog_warn("Can't fopen pid lock file %s (%s), continuing", + path, safe_strerror(errno)); + umask(oldumask); + return -1; +} + +#else /* HAVE_FCNTL */ + +pid_t +pid_output (const char *path) +{ + int tmp; + int fd; + pid_t pid; + char buf[16]; + struct flock lock; + mode_t oldumask; + + pid = getpid (); + + oldumask = umask(0777 & ~PIDFILE_MASK); + fd = open (path, O_RDWR | O_CREAT, PIDFILE_MASK); + if (fd < 0) + { + zlog_err("Can't create pid lock file %s (%s), exiting", + path, safe_strerror(errno)); + umask(oldumask); + exit(1); + } + else + { + size_t pidsize; + + umask(oldumask); + memset (&lock, 0, sizeof(lock)); + + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + + if (fcntl(fd, F_SETLK, &lock) < 0) + { + zlog_err("Could not lock pid_file %s, exiting", path); + exit(1); + } + + sprintf (buf, "%d\n", (int) pid); + pidsize = strlen(buf); + if ((tmp = write (fd, buf, pidsize)) != (int)pidsize) + zlog_err("Could not write pid %d to pid_file %s, rc was %d: %s", + (int)pid,path,tmp,safe_strerror(errno)); + else if (ftruncate(fd, pidsize) < 0) + zlog_err("Could not truncate pid_file %s to %u bytes: %s", + path,(u_int)pidsize,safe_strerror(errno)); + } + return pid; +} + +#endif /* HAVE_FCNTL */ diff --git a/lib/plist.c b/lib/plist.c new file mode 100644 index 0000000..644506b --- /dev/null +++ b/lib/plist.c @@ -0,0 +1,2789 @@ +/* Prefix list functions. + * 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 "prefix.h" +#include "command.h" +#include "memory.h" +#include "plist.h" +#include "sockunion.h" +#include "buffer.h" +#include "stream.h" +#include "log.h" + +#include "plist_int.h" + +/* List of struct prefix_list. */ +struct prefix_list_list +{ + struct prefix_list *head; + struct prefix_list *tail; +}; + +/* Master structure of prefix_list. */ +struct prefix_master +{ + /* List of prefix_list which name is number. */ + struct prefix_list_list num; + + /* List of prefix_list which name is string. */ + struct prefix_list_list str; + + /* Whether sequential number is used. */ + int seqnum; + + /* The latest update. */ + struct prefix_list *recent; + + /* Hook function which is executed when new prefix_list is added. */ + void (*add_hook) (struct prefix_list *); + + /* Hook function which is executed when prefix_list is deleted. */ + void (*delete_hook) (struct prefix_list *); +}; + +/* Static structure of IPv4 prefix_list's master. */ +static struct prefix_master prefix_master_ipv4 = +{ + {NULL, NULL}, + {NULL, NULL}, + 1, + NULL, + NULL, +}; + +#ifdef HAVE_IPV6 +/* Static structure of IPv6 prefix-list's master. */ +static struct prefix_master prefix_master_ipv6 = +{ + {NULL, NULL}, + {NULL, NULL}, + 1, + NULL, + NULL, +}; +#endif /* HAVE_IPV6*/ + +/* Static structure of BGP ORF prefix_list's master. */ +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, int orf) +{ + if (afi == AFI_IP) + 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. */ +static struct prefix_list * +prefix_list_lookup_do (afi_t afi, int orf, const char *name) +{ + struct prefix_list *plist; + struct prefix_master *master; + + if (name == NULL) + return NULL; + + master = prefix_master_get (afi, orf); + if (master == NULL) + return NULL; + + for (plist = master->num.head; plist; plist = plist->next) + if (strcmp (plist->name, name) == 0) + return plist; + + for (plist = master->str.head; plist; plist = plist->next) + if (strcmp (plist->name, name) == 0) + return plist; + + 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) +{ + struct prefix_list *new; + + new = XCALLOC (MTYPE_PREFIX_LIST, sizeof (struct prefix_list)); + return new; +} + +static void +prefix_list_free (struct prefix_list *plist) +{ + XFREE (MTYPE_PREFIX_LIST, plist); +} + +static struct prefix_list_entry * +prefix_list_entry_new (void) +{ + struct prefix_list_entry *new; + + new = XCALLOC (MTYPE_PREFIX_LIST_ENTRY, sizeof (struct prefix_list_entry)); + return new; +} + +static void +prefix_list_entry_free (struct prefix_list_entry *pentry) +{ + XFREE (MTYPE_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, int orf, const char *name) +{ + unsigned int i; + long number; + struct prefix_list *plist; + struct prefix_list *point; + struct prefix_list_list *list; + struct prefix_master *master; + + master = prefix_master_get (afi, orf); + if (master == NULL) + return NULL; + + /* Allocate new prefix_list and copy given name. */ + plist = prefix_list_new (); + plist->name = XSTRDUP (MTYPE_PREFIX_LIST_STR, name); + plist->master = master; + + /* If name is made by all digit character. We treat it as + number. */ + for (number = 0, i = 0; i < strlen (name); i++) + { + if (isdigit ((int) name[i])) + number = (number * 10) + (name[i] - '0'); + else + break; + } + + /* In case of name is all digit character */ + if (i == strlen (name)) + { + plist->type = PREFIX_TYPE_NUMBER; + + /* Set prefix_list to number list. */ + list = &master->num; + + for (point = list->head; point; point = point->next) + if (atol (point->name) >= number) + break; + } + else + { + plist->type = PREFIX_TYPE_STRING; + + /* Set prefix_list to string list. */ + list = &master->str; + + /* Set point to insertion point. */ + for (point = list->head; point; point = point->next) + if (strcmp (point->name, name) >= 0) + break; + } + + /* In case of this is the first element of master. */ + if (list->head == NULL) + { + list->head = list->tail = plist; + return plist; + } + + /* In case of insertion is made at the tail of access_list. */ + if (point == NULL) + { + plist->prev = list->tail; + list->tail->next = plist; + list->tail = plist; + return plist; + } + + /* In case of insertion is made at the head of access_list. */ + if (point == list->head) + { + plist->next = list->head; + list->head->prev = plist; + list->head = plist; + return plist; + } + + /* Insertion is made at middle of the access_list. */ + plist->next = point; + plist->prev = point->prev; + + if (point->prev) + point->prev->next = plist; + point->prev = plist; + + return plist; +} + +static struct prefix_list * +prefix_list_get (afi_t afi, int orf, const char *name) +{ + struct prefix_list *plist; + + plist = prefix_list_lookup_do (afi, orf, name); + + if (plist == NULL) + plist = prefix_list_insert (afi, orf, name); + return plist; +} + +/* Delete prefix-list from prefix_list_master and free it. */ +static void +prefix_list_delete (struct prefix_list *plist) +{ + struct prefix_list_list *list; + struct prefix_master *master; + struct prefix_list_entry *pentry; + struct prefix_list_entry *next; + + /* If prefix-list contain prefix_list_entry free all of it. */ + for (pentry = plist->head; pentry; pentry = next) + { + next = pentry->next; + prefix_list_entry_free (pentry); + plist->count--; + } + + master = plist->master; + + if (plist->type == PREFIX_TYPE_NUMBER) + list = &master->num; + else + list = &master->str; + + if (plist->next) + plist->next->prev = plist->prev; + else + list->tail = plist->prev; + + if (plist->prev) + plist->prev->next = plist->next; + else + list->head = plist->next; + + if (plist->desc) + XFREE (MTYPE_TMP, plist->desc); + + /* Make sure master's recent changed prefix-list information is + cleared. */ + master->recent = NULL; + + if (plist->name) + XFREE (MTYPE_PREFIX_LIST_STR, plist->name); + + prefix_list_free (plist); + + if (master->delete_hook) + (*master->delete_hook) (NULL); +} + +static struct prefix_list_entry * +prefix_list_entry_make (struct prefix *prefix, enum prefix_list_type type, + int seq, int le, int ge, int any) +{ + struct prefix_list_entry *pentry; + + pentry = prefix_list_entry_new (); + + if (any) + pentry->any = 1; + + prefix_copy (&pentry->prefix, prefix); + pentry->type = type; + pentry->seq = seq; + pentry->le = le; + pentry->ge = ge; + + return pentry; +} + +/* Add hook function. */ +void +prefix_list_add_hook (void (*func) (struct prefix_list *plist)) +{ + prefix_master_ipv4.add_hook = func; +#ifdef HAVE_IPV6 + prefix_master_ipv6.add_hook = func; +#endif /* HAVE_IPV6 */ +} + +/* Delete hook function. */ +void +prefix_list_delete_hook (void (*func) (struct prefix_list *plist)) +{ + prefix_master_ipv4.delete_hook = func; +#ifdef HAVE_IPV6 + prefix_master_ipv6.delete_hook = func; +#endif /* HAVE_IPVt6 */ +} + +/* Calculate new sequential number. */ +static int +prefix_new_seq_get (struct prefix_list *plist) +{ + int maxseq; + int newseq; + struct prefix_list_entry *pentry; + + maxseq = newseq = 0; + + for (pentry = plist->head; pentry; pentry = pentry->next) + { + if (maxseq < pentry->seq) + maxseq = pentry->seq; + } + + newseq = ((maxseq / 5) * 5) + 5; + + return newseq; +} + +/* Return prefix list entry which has same seq number. */ +static struct prefix_list_entry * +prefix_seq_check (struct prefix_list *plist, int seq) +{ + struct prefix_list_entry *pentry; + + for (pentry = plist->head; pentry; pentry = pentry->next) + if (pentry->seq == seq) + return pentry; + return NULL; +} + +static struct prefix_list_entry * +prefix_list_entry_lookup (struct prefix_list *plist, struct prefix *prefix, + enum prefix_list_type type, int seq, int le, int ge) +{ + struct prefix_list_entry *pentry; + + for (pentry = plist->head; pentry; pentry = pentry->next) + if (prefix_same (&pentry->prefix, prefix) && pentry->type == type) + { + if (seq >= 0 && pentry->seq != seq) + continue; + + if (pentry->le != le) + continue; + if (pentry->ge != ge) + continue; + + return pentry; + } + + return NULL; +} + +static void +prefix_list_entry_delete (struct prefix_list *plist, + struct prefix_list_entry *pentry, + int update_list) +{ + if (plist == NULL || pentry == NULL) + return; + if (pentry->prev) + pentry->prev->next = pentry->next; + else + plist->head = pentry->next; + if (pentry->next) + pentry->next->prev = pentry->prev; + else + plist->tail = pentry->prev; + + prefix_list_entry_free (pentry); + + plist->count--; + + if (update_list) + { + if (plist->master->delete_hook) + (*plist->master->delete_hook) (plist); + + if (plist->head == NULL && plist->tail == NULL && plist->desc == NULL) + prefix_list_delete (plist); + else + plist->master->recent = plist; + } +} + +static void +prefix_list_entry_add (struct prefix_list *plist, + struct prefix_list_entry *pentry) +{ + struct prefix_list_entry *replace; + struct prefix_list_entry *point; + + /* Automatic asignment of seq no. */ + if (pentry->seq == -1) + pentry->seq = prefix_new_seq_get (plist); + + /* Is there any same seq prefix list entry? */ + replace = prefix_seq_check (plist, pentry->seq); + if (replace) + prefix_list_entry_delete (plist, replace, 0); + + /* Check insert point. */ + for (point = plist->head; point; point = point->next) + if (point->seq >= pentry->seq) + break; + + /* In case of this is the first element of the list. */ + pentry->next = point; + + if (point) + { + if (point->prev) + point->prev->next = pentry; + else + plist->head = pentry; + + pentry->prev = point->prev; + point->prev = pentry; + } + else + { + if (plist->tail) + plist->tail->next = pentry; + else + plist->head = pentry; + + pentry->prev = plist->tail; + plist->tail = pentry; + } + + /* Increment count. */ + plist->count++; + + /* Run hook function. */ + if (plist->master->add_hook) + (*plist->master->add_hook) (plist); + + plist->master->recent = plist; +} + +/* Return string of prefix_list_type. */ +static const char * +prefix_list_type_str (struct prefix_list_entry *pentry) +{ + switch (pentry->type) + { + case PREFIX_PERMIT: + return "permit"; + case PREFIX_DENY: + return "deny"; + default: + return ""; + } +} + +static int +prefix_list_entry_match (struct prefix_list_entry *pentry, struct prefix *p) +{ + int ret; + + ret = prefix_match (&pentry->prefix, p); + if (! ret) + return 0; + + /* In case of le nor ge is specified, exact match is performed. */ + if (! pentry->le && ! pentry->ge) + { + if (pentry->prefix.prefixlen != p->prefixlen) + return 0; + } + else + { + if (pentry->le) + if (p->prefixlen > pentry->le) + return 0; + + if (pentry->ge) + if (p->prefixlen < pentry->ge) + return 0; + } + return 1; +} + +enum prefix_list_type +prefix_list_apply (struct prefix_list *plist, void *object) +{ + struct prefix_list_entry *pentry; + struct prefix *p; + + p = (struct prefix *) object; + + if (plist == NULL) + return PREFIX_DENY; + + if (plist->count == 0) + return PREFIX_PERMIT; + + for (pentry = plist->head; pentry; pentry = pentry->next) + { + pentry->refcnt++; + if (prefix_list_entry_match (pentry, p)) + { + pentry->hitcnt++; + return pentry->type; + } + } + + return PREFIX_DENY; +} + +static void __attribute__ ((unused)) +prefix_list_print (struct prefix_list *plist) +{ + struct prefix_list_entry *pentry; + + if (plist == NULL) + return; + + printf ("ip prefix-list %s: %d entries\n", plist->name, plist->count); + + for (pentry = plist->head; pentry; pentry = pentry->next) + { + if (pentry->any) + printf ("any %s\n", prefix_list_type_str (pentry)); + else + { + struct prefix *p; + char buf[BUFSIZ]; + + p = &pentry->prefix; + + printf (" seq %d %s %s/%d", + pentry->seq, + prefix_list_type_str (pentry), + inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ), + p->prefixlen); + if (pentry->ge) + printf (" ge %d", pentry->ge); + if (pentry->le) + printf (" le %d", pentry->le); + printf ("\n"); + } + } +} + +/* Retrun 1 when plist already include pentry policy. */ +static struct prefix_list_entry * +prefix_entry_dup_check (struct prefix_list *plist, + struct prefix_list_entry *new) +{ + struct prefix_list_entry *pentry; + int seq = 0; + + if (new->seq == -1) + seq = prefix_new_seq_get (plist); + else + seq = new->seq; + + for (pentry = plist->head; pentry; pentry = pentry->next) + { + if (prefix_same (&pentry->prefix, &new->prefix) + && pentry->type == new->type + && pentry->le == new->le + && pentry->ge == new->ge + && pentry->seq != seq) + return pentry; + } + return NULL; +} + +static int +vty_invalid_prefix_range (struct vty *vty, const char *prefix) +{ + vty_out (vty, "%% Invalid prefix range for %s, make sure: len < ge-value <= le-value%s", + prefix, VTY_NEWLINE); + return CMD_WARNING; +} + +static int +vty_prefix_list_install (struct vty *vty, afi_t afi, const char *name, + const char *seq, const char *typestr, + const char *prefix, const char *ge, const char *le) +{ + int ret; + enum prefix_list_type type; + struct prefix_list *plist; + struct prefix_list_entry *pentry; + struct prefix_list_entry *dup; + struct prefix p; + int any = 0; + 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); + + /* ge and le number */ + if (ge) + genum = atoi (ge); + if (le) + lenum = atoi (le); + + /* Check filter type. */ + if (strncmp ("permit", typestr, 1) == 0) + type = PREFIX_PERMIT; + else if (strncmp ("deny", typestr, 1) == 0) + type = PREFIX_DENY; + else + { + vty_out (vty, "%% prefix type must be permit or deny%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* "any" is special token for matching any IPv4 addresses. */ + switch (afi) + { + case AFI_IP: + if (strncmp ("any", prefix, strlen (prefix)) == 0) + { + ret = str2prefix_ipv4 ("0.0.0.0/0", (struct prefix_ipv4 *) &p); + genum = 0; + lenum = IPV4_MAX_BITLEN; + any = 1; + } + else + ret = str2prefix_ipv4 (prefix, (struct prefix_ipv4 *) &p); + + if (ret <= 0) + { + vty_out (vty, "%% Malformed IPv4 prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + break; + case AFI_IP6: + if (strncmp ("any", prefix, strlen (prefix)) == 0) + { + ret = str2prefix_ipv6 ("::/0", (struct prefix_ipv6 *) &p); + genum = 0; + lenum = IPV6_MAX_BITLEN; + any = 1; + } + else + ret = str2prefix_ipv6 (prefix, (struct prefix_ipv6 *) &p); + + if (ret <= 0) + { + 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; + } + + /* ge and le check. */ + if (genum && (genum <= p.prefixlen)) + return vty_invalid_prefix_range (vty, prefix); + + if (lenum && (lenum <= p.prefixlen)) + return vty_invalid_prefix_range (vty, prefix); + + if (lenum && (genum > lenum)) + return vty_invalid_prefix_range (vty, prefix); + + if (genum && (lenum == (afi == AFI_IP ? 32 : 128))) + lenum = 0; + + /* Get prefix_list with name. */ + plist = prefix_list_get (afi, 0, name); + + /* Make prefix entry. */ + pentry = prefix_list_entry_make (&p, type, seqnum, lenum, genum, any); + + /* Check same policy. */ + dup = prefix_entry_dup_check (plist, pentry); + + if (dup) + { + prefix_list_entry_free (pentry); + vty_out (vty, "%% Insertion failed - prefix-list entry exists:%s", + VTY_NEWLINE); + vty_out (vty, " seq %d %s %s", dup->seq, typestr, prefix); + if (! any && genum) + vty_out (vty, " ge %d", genum); + if (! any && lenum) + vty_out (vty, " le %d", lenum); + vty_out (vty, "%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Install new filter to the access_list. */ + prefix_list_entry_add (plist, pentry); + + return CMD_SUCCESS; +} + +static int +vty_prefix_list_uninstall (struct vty *vty, afi_t afi, const char *name, + const char *seq, const char *typestr, + const char *prefix, const char *ge, const char *le) +{ + int ret; + enum prefix_list_type type; + struct prefix_list *plist; + struct prefix_list_entry *pentry; + struct prefix p; + int seqnum = -1; + int lenum = 0; + int genum = 0; + + /* Check prefix list name. */ + plist = prefix_list_lookup (afi, name); + if (! plist) + { + vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Only prefix-list name specified, delete the entire prefix-list. */ + if (seq == NULL && typestr == NULL && prefix == NULL && + ge == NULL && le == NULL) + { + prefix_list_delete (plist); + return CMD_SUCCESS; + } + + /* We must have, at a minimum, both the type and prefix here */ + if ((typestr == NULL) || (prefix == NULL)) + { + vty_out (vty, "%% Both prefix and type required%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Check sequence number. */ + if (seq) + seqnum = atoi (seq); + + /* ge and le number */ + if (ge) + genum = atoi (ge); + if (le) + lenum = atoi (le); + + /* Check of filter type. */ + if (strncmp ("permit", typestr, 1) == 0) + type = PREFIX_PERMIT; + else if (strncmp ("deny", typestr, 1) == 0) + type = PREFIX_DENY; + else + { + vty_out (vty, "%% prefix type must be permit or deny%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* "any" is special token for matching any IPv4 addresses. */ + if (afi == AFI_IP) + { + if (strncmp ("any", prefix, strlen (prefix)) == 0) + { + ret = str2prefix_ipv4 ("0.0.0.0/0", (struct prefix_ipv4 *) &p); + genum = 0; + lenum = IPV4_MAX_BITLEN; + } + else + ret = str2prefix_ipv4 (prefix, (struct prefix_ipv4 *) &p); + + if (ret <= 0) + { + vty_out (vty, "%% Malformed IPv4 prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + } +#ifdef HAVE_IPV6 + else if (afi == AFI_IP6) + { + if (strncmp ("any", prefix, strlen (prefix)) == 0) + { + ret = str2prefix_ipv6 ("::/0", (struct prefix_ipv6 *) &p); + genum = 0; + lenum = IPV6_MAX_BITLEN; + } + else + ret = str2prefix_ipv6 (prefix, (struct prefix_ipv6 *) &p); + + if (ret <= 0) + { + vty_out (vty, "%% Malformed IPv6 prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + } +#endif /* HAVE_IPV6 */ + + /* Lookup prefix entry. */ + pentry = prefix_list_entry_lookup(plist, &p, type, seqnum, lenum, genum); + + if (pentry == NULL) + { + vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Install new filter to the access_list. */ + prefix_list_entry_delete (plist, pentry, 1); + + return CMD_SUCCESS; +} + +static int +vty_prefix_list_desc_unset (struct vty *vty, afi_t afi, const char *name) +{ + struct prefix_list *plist; + + plist = prefix_list_lookup (afi, name); + if (! plist) + { + vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (plist->desc) + { + XFREE (MTYPE_TMP, plist->desc); + plist->desc = NULL; + } + + if (plist->head == NULL && plist->tail == NULL && plist->desc == NULL) + prefix_list_delete (plist); + + return CMD_SUCCESS; +} + +enum display_type +{ + normal_display, + summary_display, + detail_display, + sequential_display, + longer_display, + first_match_display +}; + +static void +vty_show_prefix_entry (struct vty *vty, afi_t afi, struct prefix_list *plist, + struct prefix_master *master, enum display_type dtype, + int seqnum) +{ + struct prefix_list_entry *pentry; + + /* Print the name of the protocol */ + if (zlog_default) + vty_out (vty, "%s: ", zlog_proto_names[zlog_default->protocol]); + + if (dtype == normal_display) + { + vty_out (vty, "ip%s prefix-list %s: %d entries%s", + afi == AFI_IP ? "" : "v6", + plist->name, plist->count, VTY_NEWLINE); + if (plist->desc) + vty_out (vty, " Description: %s%s", plist->desc, VTY_NEWLINE); + } + else if (dtype == summary_display || dtype == detail_display) + { + vty_out (vty, "ip%s prefix-list %s:%s", + afi == AFI_IP ? "" : "v6", plist->name, VTY_NEWLINE); + + if (plist->desc) + vty_out (vty, " Description: %s%s", plist->desc, VTY_NEWLINE); + + vty_out (vty, " count: %d, range entries: %d, sequences: %d - %d%s", + plist->count, plist->rangecount, + plist->head ? plist->head->seq : 0, + plist->tail ? plist->tail->seq : 0, + VTY_NEWLINE); + } + + if (dtype != summary_display) + { + for (pentry = plist->head; pentry; pentry = pentry->next) + { + if (dtype == sequential_display && pentry->seq != seqnum) + continue; + + vty_out (vty, " "); + + if (master->seqnum) + vty_out (vty, "seq %d ", pentry->seq); + + vty_out (vty, "%s ", prefix_list_type_str (pentry)); + + if (pentry->any) + vty_out (vty, "any"); + else + { + struct prefix *p = &pentry->prefix; + char buf[BUFSIZ]; + + vty_out (vty, "%s/%d", + inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ), + p->prefixlen); + + if (pentry->ge) + vty_out (vty, " ge %d", pentry->ge); + if (pentry->le) + vty_out (vty, " le %d", pentry->le); + } + + if (dtype == detail_display || dtype == sequential_display) + vty_out (vty, " (hit count: %ld, refcount: %ld)", + pentry->hitcnt, pentry->refcnt); + + vty_out (vty, "%s", VTY_NEWLINE); + } + } +} + +static int +vty_show_prefix_list (struct vty *vty, afi_t afi, const char *name, + const char *seq, enum display_type dtype) +{ + struct prefix_list *plist; + struct prefix_master *master; + int seqnum = 0; + + master = prefix_master_get (afi, 0); + if (master == NULL) + return CMD_WARNING; + + if (seq) + seqnum = atoi (seq); + + if (name) + { + plist = prefix_list_lookup (afi, name); + if (! plist) + { + vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE); + return CMD_WARNING; + } + vty_show_prefix_entry (vty, afi, plist, master, dtype, seqnum); + } + else + { + if (dtype == detail_display || dtype == summary_display) + { + if (master->recent) + vty_out (vty, "Prefix-list with the last deletion/insertion: %s%s", + master->recent->name, VTY_NEWLINE); + } + + for (plist = master->num.head; plist; plist = plist->next) + vty_show_prefix_entry (vty, afi, plist, master, dtype, seqnum); + + for (plist = master->str.head; plist; plist = plist->next) + vty_show_prefix_entry (vty, afi, plist, master, dtype, seqnum); + } + + return CMD_SUCCESS; +} + +static int +vty_show_prefix_list_prefix (struct vty *vty, afi_t afi, const char *name, + const char *prefix, enum display_type type) +{ + struct prefix_list *plist; + struct prefix_list_entry *pentry; + struct prefix p; + int ret; + int match; + + plist = prefix_list_lookup (afi, name); + if (! plist) + { + vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ret = str2prefix (prefix, &p); + if (ret <= 0) + { + vty_out (vty, "%% prefix is malformed%s", VTY_NEWLINE); + return CMD_WARNING; + } + + for (pentry = plist->head; pentry; pentry = pentry->next) + { + match = 0; + + if (type == normal_display || type == first_match_display) + if (prefix_same (&p, &pentry->prefix)) + match = 1; + + if (type == longer_display) + if (prefix_match (&p, &pentry->prefix)) + match = 1; + + if (match) + { + vty_out (vty, " seq %d %s ", + pentry->seq, + prefix_list_type_str (pentry)); + + if (pentry->any) + vty_out (vty, "any"); + else + { + struct prefix *p = &pentry->prefix; + char buf[BUFSIZ]; + + vty_out (vty, "%s/%d", + inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ), + p->prefixlen); + + if (pentry->ge) + vty_out (vty, " ge %d", pentry->ge); + if (pentry->le) + vty_out (vty, " le %d", pentry->le); + } + + if (type == normal_display || type == first_match_display) + vty_out (vty, " (hit count: %ld, refcount: %ld)", + pentry->hitcnt, pentry->refcnt); + + vty_out (vty, "%s", VTY_NEWLINE); + + if (type == first_match_display) + return CMD_SUCCESS; + } + } + return CMD_SUCCESS; +} + +static int +vty_clear_prefix_list (struct vty *vty, afi_t afi, const char *name, + const char *prefix) +{ + struct prefix_master *master; + struct prefix_list *plist; + struct prefix_list_entry *pentry; + int ret; + struct prefix p; + + master = prefix_master_get (afi, 0); + if (master == NULL) + return CMD_WARNING; + + if (name == NULL && prefix == NULL) + { + for (plist = master->num.head; plist; plist = plist->next) + for (pentry = plist->head; pentry; pentry = pentry->next) + pentry->hitcnt = 0; + + for (plist = master->str.head; plist; plist = plist->next) + for (pentry = plist->head; pentry; pentry = pentry->next) + pentry->hitcnt = 0; + } + else + { + plist = prefix_list_lookup (afi, name); + if (! plist) + { + vty_out (vty, "%% Can't find specified prefix-list%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (prefix) + { + ret = str2prefix (prefix, &p); + if (ret <= 0) + { + vty_out (vty, "%% prefix is malformed%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + for (pentry = plist->head; pentry; pentry = pentry->next) + { + if (prefix) + { + if (prefix_match (&pentry->prefix, &p)) + pentry->hitcnt = 0; + } + else + pentry->hitcnt = 0; + } + } + return CMD_SUCCESS; +} + +DEFUN (ip_prefix_list, + ip_prefix_list_cmd, + "ip prefix-list WORD (deny|permit) (A.B.C.D/M|any)", + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Any prefix match. Same as \"0.0.0.0/0 le 32\"\n") +{ + return vty_prefix_list_install (vty, AFI_IP, argv[0], NULL, + argv[1], argv[2], NULL, NULL); +} + +DEFUN (ip_prefix_list_ge, + ip_prefix_list_ge_cmd, + "ip prefix-list WORD (deny|permit) A.B.C.D/M ge <0-32>", + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return vty_prefix_list_install (vty, AFI_IP, argv[0], NULL, argv[1], + argv[2], argv[3], NULL); +} + +DEFUN (ip_prefix_list_ge_le, + ip_prefix_list_ge_le_cmd, + "ip prefix-list WORD (deny|permit) A.B.C.D/M ge <0-32> le <0-32>", + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return vty_prefix_list_install (vty, AFI_IP, argv[0], NULL, argv[1], + argv[2], argv[3], argv[4]); +} + +DEFUN (ip_prefix_list_le, + ip_prefix_list_le_cmd, + "ip prefix-list WORD (deny|permit) A.B.C.D/M le <0-32>", + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return vty_prefix_list_install (vty, AFI_IP, argv[0], NULL, argv[1], + argv[2], NULL, argv[3]); +} + +DEFUN (ip_prefix_list_le_ge, + ip_prefix_list_le_ge_cmd, + "ip prefix-list WORD (deny|permit) A.B.C.D/M le <0-32> ge <0-32>", + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return vty_prefix_list_install (vty, AFI_IP, argv[0], NULL, argv[1], + argv[2], argv[4], argv[3]); +} + +DEFUN (ip_prefix_list_seq, + ip_prefix_list_seq_cmd, + "ip prefix-list WORD seq <1-4294967295> (deny|permit) (A.B.C.D/M|any)", + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Any prefix match. Same as \"0.0.0.0/0 le 32\"\n") +{ + return vty_prefix_list_install (vty, AFI_IP, argv[0], argv[1], argv[2], + argv[3], NULL, NULL); +} + +DEFUN (ip_prefix_list_seq_ge, + ip_prefix_list_seq_ge_cmd, + "ip prefix-list WORD seq <1-4294967295> (deny|permit) A.B.C.D/M ge <0-32>", + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return vty_prefix_list_install (vty, AFI_IP, argv[0], argv[1], argv[2], + argv[3], argv[4], NULL); +} + +DEFUN (ip_prefix_list_seq_ge_le, + ip_prefix_list_seq_ge_le_cmd, + "ip prefix-list WORD seq <1-4294967295> (deny|permit) A.B.C.D/M ge <0-32> le <0-32>", + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return vty_prefix_list_install (vty, AFI_IP, argv[0], argv[1], argv[2], + argv[3], argv[4], argv[5]); +} + +DEFUN (ip_prefix_list_seq_le, + ip_prefix_list_seq_le_cmd, + "ip prefix-list WORD seq <1-4294967295> (deny|permit) A.B.C.D/M le <0-32>", + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return vty_prefix_list_install (vty, AFI_IP, argv[0], argv[1], argv[2], + argv[3], NULL, argv[4]); +} + +DEFUN (ip_prefix_list_seq_le_ge, + ip_prefix_list_seq_le_ge_cmd, + "ip prefix-list WORD seq <1-4294967295> (deny|permit) A.B.C.D/M le <0-32> ge <0-32>", + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return vty_prefix_list_install (vty, AFI_IP, argv[0], argv[1], argv[2], + argv[3], argv[5], argv[4]); +} + +DEFUN (no_ip_prefix_list, + no_ip_prefix_list_cmd, + "no ip prefix-list WORD", + NO_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], NULL, NULL, + NULL, NULL, NULL); +} + +DEFUN (no_ip_prefix_list_prefix, + no_ip_prefix_list_prefix_cmd, + "no ip prefix-list WORD (deny|permit) (A.B.C.D/M|any)", + NO_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Any prefix match. Same as \"0.0.0.0/0 le 32\"\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], NULL, argv[1], + argv[2], NULL, NULL); +} + +DEFUN (no_ip_prefix_list_ge, + no_ip_prefix_list_ge_cmd, + "no ip prefix-list WORD (deny|permit) A.B.C.D/M ge <0-32>", + NO_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], NULL, argv[1], + argv[2], argv[3], NULL); +} + +DEFUN (no_ip_prefix_list_ge_le, + no_ip_prefix_list_ge_le_cmd, + "no ip prefix-list WORD (deny|permit) A.B.C.D/M ge <0-32> le <0-32>", + NO_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], NULL, argv[1], + argv[2], argv[3], argv[4]); +} + +DEFUN (no_ip_prefix_list_le, + no_ip_prefix_list_le_cmd, + "no ip prefix-list WORD (deny|permit) A.B.C.D/M le <0-32>", + NO_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], NULL, argv[1], + argv[2], NULL, argv[3]); +} + +DEFUN (no_ip_prefix_list_le_ge, + no_ip_prefix_list_le_ge_cmd, + "no ip prefix-list WORD (deny|permit) A.B.C.D/M le <0-32> ge <0-32>", + NO_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], NULL, argv[1], + argv[2], argv[4], argv[3]); +} + +DEFUN (no_ip_prefix_list_seq, + no_ip_prefix_list_seq_cmd, + "no ip prefix-list WORD seq <1-4294967295> (deny|permit) (A.B.C.D/M|any)", + NO_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Any prefix match. Same as \"0.0.0.0/0 le 32\"\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], argv[1], argv[2], + argv[3], NULL, NULL); +} + +DEFUN (no_ip_prefix_list_seq_ge, + no_ip_prefix_list_seq_ge_cmd, + "no ip prefix-list WORD seq <1-4294967295> (deny|permit) A.B.C.D/M ge <0-32>", + NO_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], argv[1], argv[2], + argv[3], argv[4], NULL); +} + +DEFUN (no_ip_prefix_list_seq_ge_le, + no_ip_prefix_list_seq_ge_le_cmd, + "no ip prefix-list WORD seq <1-4294967295> (deny|permit) A.B.C.D/M ge <0-32> le <0-32>", + NO_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], argv[1], argv[2], + argv[3], argv[4], argv[5]); +} + +DEFUN (no_ip_prefix_list_seq_le, + no_ip_prefix_list_seq_le_cmd, + "no ip prefix-list WORD seq <1-4294967295> (deny|permit) A.B.C.D/M le <0-32>", + NO_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], argv[1], argv[2], + argv[3], NULL, argv[4]); +} + +DEFUN (no_ip_prefix_list_seq_le_ge, + no_ip_prefix_list_seq_le_ge_cmd, + "no ip prefix-list WORD seq <1-4294967295> (deny|permit) A.B.C.D/M le <0-32> ge <0-32>", + NO_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], argv[1], argv[2], + argv[3], argv[5], argv[4]); +} + +DEFUN (ip_prefix_list_sequence_number, + ip_prefix_list_sequence_number_cmd, + "ip prefix-list sequence-number", + IP_STR + PREFIX_LIST_STR + "Include/exclude sequence numbers in NVGEN\n") +{ + prefix_master_ipv4.seqnum = 1; + return CMD_SUCCESS; +} + +DEFUN (no_ip_prefix_list_sequence_number, + no_ip_prefix_list_sequence_number_cmd, + "no ip prefix-list sequence-number", + NO_STR + IP_STR + PREFIX_LIST_STR + "Include/exclude sequence numbers in NVGEN\n") +{ + prefix_master_ipv4.seqnum = 0; + return CMD_SUCCESS; +} + +DEFUN (ip_prefix_list_description, + ip_prefix_list_description_cmd, + "ip prefix-list WORD description .LINE", + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Prefix-list specific description\n" + "Up to 80 characters describing this prefix-list\n") +{ + struct prefix_list *plist; + + plist = prefix_list_get (AFI_IP, 0, argv[0]); + + if (plist->desc) + { + XFREE (MTYPE_TMP, plist->desc); + plist->desc = NULL; + } + plist->desc = argv_concat(argv, argc, 1); + + return CMD_SUCCESS; +} + +DEFUN (no_ip_prefix_list_description, + no_ip_prefix_list_description_cmd, + "no ip prefix-list WORD description", + NO_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Prefix-list specific description\n") +{ + return vty_prefix_list_desc_unset (vty, AFI_IP, argv[0]); +} + +ALIAS (no_ip_prefix_list_description, + no_ip_prefix_list_description_arg_cmd, + "no ip prefix-list WORD description .LINE", + NO_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Prefix-list specific description\n" + "Up to 80 characters describing this prefix-list\n") + +DEFUN (show_ip_prefix_list, + show_ip_prefix_list_cmd, + "show ip prefix-list", + SHOW_STR + IP_STR + PREFIX_LIST_STR) +{ + return vty_show_prefix_list (vty, AFI_IP, NULL, NULL, normal_display); +} + +DEFUN (show_ip_prefix_list_name, + show_ip_prefix_list_name_cmd, + "show ip prefix-list WORD", + SHOW_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n") +{ + return vty_show_prefix_list (vty, AFI_IP, argv[0], NULL, normal_display); +} + +DEFUN (show_ip_prefix_list_name_seq, + show_ip_prefix_list_name_seq_cmd, + "show ip prefix-list WORD seq <1-4294967295>", + SHOW_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n") +{ + return vty_show_prefix_list (vty, AFI_IP, argv[0], argv[1], sequential_display); +} + +DEFUN (show_ip_prefix_list_prefix, + show_ip_prefix_list_prefix_cmd, + "show ip prefix-list WORD A.B.C.D/M", + SHOW_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "IP prefix /, e.g., 35.0.0.0/8\n") +{ + return vty_show_prefix_list_prefix (vty, AFI_IP, argv[0], argv[1], normal_display); +} + +DEFUN (show_ip_prefix_list_prefix_longer, + show_ip_prefix_list_prefix_longer_cmd, + "show ip prefix-list WORD A.B.C.D/M longer", + SHOW_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Lookup longer prefix\n") +{ + return vty_show_prefix_list_prefix (vty, AFI_IP, argv[0], argv[1], longer_display); +} + +DEFUN (show_ip_prefix_list_prefix_first_match, + show_ip_prefix_list_prefix_first_match_cmd, + "show ip prefix-list WORD A.B.C.D/M first-match", + SHOW_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "First matched prefix\n") +{ + return vty_show_prefix_list_prefix (vty, AFI_IP, argv[0], argv[1], first_match_display); +} + +DEFUN (show_ip_prefix_list_summary, + show_ip_prefix_list_summary_cmd, + "show ip prefix-list summary", + SHOW_STR + IP_STR + PREFIX_LIST_STR + "Summary of prefix lists\n") +{ + return vty_show_prefix_list (vty, AFI_IP, NULL, NULL, summary_display); +} + +DEFUN (show_ip_prefix_list_summary_name, + show_ip_prefix_list_summary_name_cmd, + "show ip prefix-list summary WORD", + SHOW_STR + IP_STR + PREFIX_LIST_STR + "Summary of prefix lists\n" + "Name of a prefix list\n") +{ + return vty_show_prefix_list (vty, AFI_IP, argv[0], NULL, summary_display); +} + + +DEFUN (show_ip_prefix_list_detail, + show_ip_prefix_list_detail_cmd, + "show ip prefix-list detail", + SHOW_STR + IP_STR + PREFIX_LIST_STR + "Detail of prefix lists\n") +{ + return vty_show_prefix_list (vty, AFI_IP, NULL, NULL, detail_display); +} + +DEFUN (show_ip_prefix_list_detail_name, + show_ip_prefix_list_detail_name_cmd, + "show ip prefix-list detail WORD", + SHOW_STR + IP_STR + PREFIX_LIST_STR + "Detail of prefix lists\n" + "Name of a prefix list\n") +{ + return vty_show_prefix_list (vty, AFI_IP, argv[0], NULL, detail_display); +} + +DEFUN (clear_ip_prefix_list, + clear_ip_prefix_list_cmd, + "clear ip prefix-list", + CLEAR_STR + IP_STR + PREFIX_LIST_STR) +{ + return vty_clear_prefix_list (vty, AFI_IP, NULL, NULL); +} + +DEFUN (clear_ip_prefix_list_name, + clear_ip_prefix_list_name_cmd, + "clear ip prefix-list WORD", + CLEAR_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n") +{ + return vty_clear_prefix_list (vty, AFI_IP, argv[0], NULL); +} + +DEFUN (clear_ip_prefix_list_name_prefix, + clear_ip_prefix_list_name_prefix_cmd, + "clear ip prefix-list WORD A.B.C.D/M", + CLEAR_STR + IP_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "IP prefix /, e.g., 35.0.0.0/8\n") +{ + return vty_clear_prefix_list (vty, AFI_IP, argv[0], argv[1]); +} + +#ifdef HAVE_IPV6 +DEFUN (ipv6_prefix_list, + ipv6_prefix_list_cmd, + "ipv6 prefix-list WORD (deny|permit) (X:X::X:X/M|any)", + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix /, e.g., 3ffe::/16\n" + "Any prefix match. Same as \"::0/0 le 128\"\n") +{ + return vty_prefix_list_install (vty, AFI_IP6, argv[0], NULL, + argv[1], argv[2], NULL, NULL); +} + +DEFUN (ipv6_prefix_list_ge, + ipv6_prefix_list_ge_cmd, + "ipv6 prefix-list WORD (deny|permit) X:X::X:X/M ge <0-128>", + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix /, e.g., 3ffe::/16\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return vty_prefix_list_install (vty, AFI_IP6, argv[0], NULL, argv[1], + argv[2], argv[3], NULL); +} + +DEFUN (ipv6_prefix_list_ge_le, + ipv6_prefix_list_ge_le_cmd, + "ipv6 prefix-list WORD (deny|permit) X:X::X:X/M ge <0-128> le <0-128>", + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix /, e.g., 3ffe::/16\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") + +{ + return vty_prefix_list_install (vty, AFI_IP6, argv[0], NULL, argv[1], + argv[2], argv[3], argv[4]); +} + +DEFUN (ipv6_prefix_list_le, + ipv6_prefix_list_le_cmd, + "ipv6 prefix-list WORD (deny|permit) X:X::X:X/M le <0-128>", + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix /, e.g., 3ffe::/16\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return vty_prefix_list_install (vty, AFI_IP6, argv[0], NULL, argv[1], + argv[2], NULL, argv[3]); +} + +DEFUN (ipv6_prefix_list_le_ge, + ipv6_prefix_list_le_ge_cmd, + "ipv6 prefix-list WORD (deny|permit) X:X::X:X/M le <0-128> ge <0-128>", + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix /, e.g., 3ffe::/16\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return vty_prefix_list_install (vty, AFI_IP6, argv[0], NULL, argv[1], + argv[2], argv[4], argv[3]); +} + +DEFUN (ipv6_prefix_list_seq, + ipv6_prefix_list_seq_cmd, + "ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) (X:X::X:X/M|any)", + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix /, e.g., 3ffe::/16\n" + "Any prefix match. Same as \"::0/0 le 128\"\n") +{ + return vty_prefix_list_install (vty, AFI_IP6, argv[0], argv[1], argv[2], + argv[3], NULL, NULL); +} + +DEFUN (ipv6_prefix_list_seq_ge, + ipv6_prefix_list_seq_ge_cmd, + "ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) X:X::X:X/M ge <0-128>", + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix /, e.g., 3ffe::/16\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return vty_prefix_list_install (vty, AFI_IP6, argv[0], argv[1], argv[2], + argv[3], argv[4], NULL); +} + +DEFUN (ipv6_prefix_list_seq_ge_le, + ipv6_prefix_list_seq_ge_le_cmd, + "ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) X:X::X:X/M ge <0-128> le <0-128>", + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix /, e.g., 3ffe::/16\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return vty_prefix_list_install (vty, AFI_IP6, argv[0], argv[1], argv[2], + argv[3], argv[4], argv[5]); +} + +DEFUN (ipv6_prefix_list_seq_le, + ipv6_prefix_list_seq_le_cmd, + "ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) X:X::X:X/M le <0-128>", + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix /, e.g., 3ffe::/16\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return vty_prefix_list_install (vty, AFI_IP6, argv[0], argv[1], argv[2], + argv[3], NULL, argv[4]); +} + +DEFUN (ipv6_prefix_list_seq_le_ge, + ipv6_prefix_list_seq_le_ge_cmd, + "ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) X:X::X:X/M le <0-128> ge <0-128>", + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix /, e.g., 3ffe::/16\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return vty_prefix_list_install (vty, AFI_IP6, argv[0], argv[1], argv[2], + argv[3], argv[5], argv[4]); +} + +DEFUN (no_ipv6_prefix_list, + no_ipv6_prefix_list_cmd, + "no ipv6 prefix-list WORD", + NO_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], NULL, NULL, + NULL, NULL, NULL); +} + +DEFUN (no_ipv6_prefix_list_prefix, + no_ipv6_prefix_list_prefix_cmd, + "no ipv6 prefix-list WORD (deny|permit) (X:X::X:X/M|any)", + NO_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix /, e.g., 3ffe::/16\n" + "Any prefix match. Same as \"::0/0 le 128\"\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], NULL, argv[1], + argv[2], NULL, NULL); +} + +DEFUN (no_ipv6_prefix_list_ge, + no_ipv6_prefix_list_ge_cmd, + "no ipv6 prefix-list WORD (deny|permit) X:X::X:X/M ge <0-128>", + NO_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix /, e.g., 3ffe::/16\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], NULL, argv[1], + argv[2], argv[3], NULL); +} + +DEFUN (no_ipv6_prefix_list_ge_le, + no_ipv6_prefix_list_ge_le_cmd, + "no ipv6 prefix-list WORD (deny|permit) X:X::X:X/M ge <0-128> le <0-128>", + NO_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix /, e.g., 3ffe::/16\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], NULL, argv[1], + argv[2], argv[3], argv[4]); +} + +DEFUN (no_ipv6_prefix_list_le, + no_ipv6_prefix_list_le_cmd, + "no ipv6 prefix-list WORD (deny|permit) X:X::X:X/M le <0-128>", + NO_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix /, e.g., 3ffe::/16\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], NULL, argv[1], + argv[2], NULL, argv[3]); +} + +DEFUN (no_ipv6_prefix_list_le_ge, + no_ipv6_prefix_list_le_ge_cmd, + "no ipv6 prefix-list WORD (deny|permit) X:X::X:X/M le <0-128> ge <0-128>", + NO_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix /, e.g., 3ffe::/16\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], NULL, argv[1], + argv[2], argv[4], argv[3]); +} + +DEFUN (no_ipv6_prefix_list_seq, + no_ipv6_prefix_list_seq_cmd, + "no ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) (X:X::X:X/M|any)", + NO_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix /, e.g., 3ffe::/16\n" + "Any prefix match. Same as \"::0/0 le 128\"\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], argv[1], argv[2], + argv[3], NULL, NULL); +} + +DEFUN (no_ipv6_prefix_list_seq_ge, + no_ipv6_prefix_list_seq_ge_cmd, + "no ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) X:X::X:X/M ge <0-128>", + NO_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix /, e.g., 3ffe::/16\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], argv[1], argv[2], + argv[3], argv[4], NULL); +} + +DEFUN (no_ipv6_prefix_list_seq_ge_le, + no_ipv6_prefix_list_seq_ge_le_cmd, + "no ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) X:X::X:X/M ge <0-128> le <0-128>", + NO_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix /, e.g., 3ffe::/16\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], argv[1], argv[2], + argv[3], argv[4], argv[5]); +} + +DEFUN (no_ipv6_prefix_list_seq_le, + no_ipv6_prefix_list_seq_le_cmd, + "no ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) X:X::X:X/M le <0-128>", + NO_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix /, e.g., 3ffe::/16\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], argv[1], argv[2], + argv[3], NULL, argv[4]); +} + +DEFUN (no_ipv6_prefix_list_seq_le_ge, + no_ipv6_prefix_list_seq_le_ge_cmd, + "no ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) X:X::X:X/M le <0-128> ge <0-128>", + NO_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n" + "Specify packets to reject\n" + "Specify packets to forward\n" + "IPv6 prefix /, e.g., 3ffe::/16\n" + "Maximum prefix length to be matched\n" + "Maximum prefix length\n" + "Minimum prefix length to be matched\n" + "Minimum prefix length\n") +{ + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], argv[1], argv[2], + argv[3], argv[5], argv[4]); +} + +DEFUN (ipv6_prefix_list_sequence_number, + ipv6_prefix_list_sequence_number_cmd, + "ipv6 prefix-list sequence-number", + IPV6_STR + PREFIX_LIST_STR + "Include/exclude sequence numbers in NVGEN\n") +{ + prefix_master_ipv6.seqnum = 1; + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_prefix_list_sequence_number, + no_ipv6_prefix_list_sequence_number_cmd, + "no ipv6 prefix-list sequence-number", + NO_STR + IPV6_STR + PREFIX_LIST_STR + "Include/exclude sequence numbers in NVGEN\n") +{ + prefix_master_ipv6.seqnum = 0; + return CMD_SUCCESS; +} + +DEFUN (ipv6_prefix_list_description, + ipv6_prefix_list_description_cmd, + "ipv6 prefix-list WORD description .LINE", + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Prefix-list specific description\n" + "Up to 80 characters describing this prefix-list\n") +{ + struct prefix_list *plist; + + plist = prefix_list_get (AFI_IP6, 0, argv[0]); + + if (plist->desc) + { + XFREE (MTYPE_TMP, plist->desc); + plist->desc = NULL; + } + plist->desc = argv_concat(argv, argc, 1); + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_prefix_list_description, + no_ipv6_prefix_list_description_cmd, + "no ipv6 prefix-list WORD description", + NO_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Prefix-list specific description\n") +{ + return vty_prefix_list_desc_unset (vty, AFI_IP6, argv[0]); +} + +ALIAS (no_ipv6_prefix_list_description, + no_ipv6_prefix_list_description_arg_cmd, + "no ipv6 prefix-list WORD description .LINE", + NO_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "Prefix-list specific description\n" + "Up to 80 characters describing this prefix-list\n") + +DEFUN (show_ipv6_prefix_list, + show_ipv6_prefix_list_cmd, + "show ipv6 prefix-list", + SHOW_STR + IPV6_STR + PREFIX_LIST_STR) +{ + return vty_show_prefix_list (vty, AFI_IP6, NULL, NULL, normal_display); +} + +DEFUN (show_ipv6_prefix_list_name, + show_ipv6_prefix_list_name_cmd, + "show ipv6 prefix-list WORD", + SHOW_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n") +{ + return vty_show_prefix_list (vty, AFI_IP6, argv[0], NULL, normal_display); +} + +DEFUN (show_ipv6_prefix_list_name_seq, + show_ipv6_prefix_list_name_seq_cmd, + "show ipv6 prefix-list WORD seq <1-4294967295>", + SHOW_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "sequence number of an entry\n" + "Sequence number\n") +{ + return vty_show_prefix_list (vty, AFI_IP6, argv[0], argv[1], sequential_display); +} + +DEFUN (show_ipv6_prefix_list_prefix, + show_ipv6_prefix_list_prefix_cmd, + "show ipv6 prefix-list WORD X:X::X:X/M", + SHOW_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "IPv6 prefix /, e.g., 3ffe::/16\n") +{ + return vty_show_prefix_list_prefix (vty, AFI_IP6, argv[0], argv[1], normal_display); +} + +DEFUN (show_ipv6_prefix_list_prefix_longer, + show_ipv6_prefix_list_prefix_longer_cmd, + "show ipv6 prefix-list WORD X:X::X:X/M longer", + SHOW_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "IPv6 prefix /, e.g., 3ffe::/16\n" + "Lookup longer prefix\n") +{ + return vty_show_prefix_list_prefix (vty, AFI_IP6, argv[0], argv[1], longer_display); +} + +DEFUN (show_ipv6_prefix_list_prefix_first_match, + show_ipv6_prefix_list_prefix_first_match_cmd, + "show ipv6 prefix-list WORD X:X::X:X/M first-match", + SHOW_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "IPv6 prefix /, e.g., 3ffe::/16\n" + "First matched prefix\n") +{ + return vty_show_prefix_list_prefix (vty, AFI_IP6, argv[0], argv[1], first_match_display); +} + +DEFUN (show_ipv6_prefix_list_summary, + show_ipv6_prefix_list_summary_cmd, + "show ipv6 prefix-list summary", + SHOW_STR + IPV6_STR + PREFIX_LIST_STR + "Summary of prefix lists\n") +{ + return vty_show_prefix_list (vty, AFI_IP6, NULL, NULL, summary_display); +} + +DEFUN (show_ipv6_prefix_list_summary_name, + show_ipv6_prefix_list_summary_name_cmd, + "show ipv6 prefix-list summary WORD", + SHOW_STR + IPV6_STR + PREFIX_LIST_STR + "Summary of prefix lists\n" + "Name of a prefix list\n") +{ + return vty_show_prefix_list (vty, AFI_IP6, argv[0], NULL, summary_display); +} + +DEFUN (show_ipv6_prefix_list_detail, + show_ipv6_prefix_list_detail_cmd, + "show ipv6 prefix-list detail", + SHOW_STR + IPV6_STR + PREFIX_LIST_STR + "Detail of prefix lists\n") +{ + return vty_show_prefix_list (vty, AFI_IP6, NULL, NULL, detail_display); +} + +DEFUN (show_ipv6_prefix_list_detail_name, + show_ipv6_prefix_list_detail_name_cmd, + "show ipv6 prefix-list detail WORD", + SHOW_STR + IPV6_STR + PREFIX_LIST_STR + "Detail of prefix lists\n" + "Name of a prefix list\n") +{ + return vty_show_prefix_list (vty, AFI_IP6, argv[0], NULL, detail_display); +} + +DEFUN (clear_ipv6_prefix_list, + clear_ipv6_prefix_list_cmd, + "clear ipv6 prefix-list", + CLEAR_STR + IPV6_STR + PREFIX_LIST_STR) +{ + return vty_clear_prefix_list (vty, AFI_IP6, NULL, NULL); +} + +DEFUN (clear_ipv6_prefix_list_name, + clear_ipv6_prefix_list_name_cmd, + "clear ipv6 prefix-list WORD", + CLEAR_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n") +{ + return vty_clear_prefix_list (vty, AFI_IP6, argv[0], NULL); +} + +DEFUN (clear_ipv6_prefix_list_name_prefix, + clear_ipv6_prefix_list_name_prefix_cmd, + "clear ipv6 prefix-list WORD X:X::X:X/M", + CLEAR_STR + IPV6_STR + PREFIX_LIST_STR + "Name of a prefix list\n" + "IPv6 prefix /, e.g., 3ffe::/16\n") +{ + 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) +{ + struct prefix_list *plist; + struct prefix_list_entry *pentry; + struct prefix_master *master; + int write = 0; + + master = prefix_master_get (afi, 0); + if (master == NULL) + return 0; + + if (! master->seqnum) + { + vty_out (vty, "no ip%s prefix-list sequence-number%s", + afi == AFI_IP ? "" : "v6", VTY_NEWLINE); + vty_out (vty, "!%s", VTY_NEWLINE); + } + + for (plist = master->num.head; plist; plist = plist->next) + { + if (plist->desc) + { + vty_out (vty, "ip%s prefix-list %s description %s%s", + afi == AFI_IP ? "" : "v6", + plist->name, plist->desc, VTY_NEWLINE); + write++; + } + + for (pentry = plist->head; pentry; pentry = pentry->next) + { + vty_out (vty, "ip%s prefix-list %s ", + afi == AFI_IP ? "" : "v6", + plist->name); + + if (master->seqnum) + vty_out (vty, "seq %d ", pentry->seq); + + vty_out (vty, "%s ", prefix_list_type_str (pentry)); + + if (pentry->any) + vty_out (vty, "any"); + else + { + struct prefix *p = &pentry->prefix; + char buf[BUFSIZ]; + + vty_out (vty, "%s/%d", + inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ), + p->prefixlen); + + if (pentry->ge) + vty_out (vty, " ge %d", pentry->ge); + if (pentry->le) + vty_out (vty, " le %d", pentry->le); + } + vty_out (vty, "%s", VTY_NEWLINE); + write++; + } + /* vty_out (vty, "!%s", VTY_NEWLINE); */ + } + + for (plist = master->str.head; plist; plist = plist->next) + { + if (plist->desc) + { + vty_out (vty, "ip%s prefix-list %s description %s%s", + afi == AFI_IP ? "" : "v6", + plist->name, plist->desc, VTY_NEWLINE); + write++; + } + + for (pentry = plist->head; pentry; pentry = pentry->next) + { + vty_out (vty, "ip%s prefix-list %s ", + afi == AFI_IP ? "" : "v6", + plist->name); + + if (master->seqnum) + vty_out (vty, "seq %d ", pentry->seq); + + vty_out (vty, "%s", prefix_list_type_str (pentry)); + + if (pentry->any) + vty_out (vty, " any"); + else + { + struct prefix *p = &pentry->prefix; + char buf[BUFSIZ]; + + vty_out (vty, " %s/%d", + inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ), + p->prefixlen); + + if (pentry->ge) + vty_out (vty, " ge %d", pentry->ge); + if (pentry->le) + vty_out (vty, " le %d", pentry->le); + } + vty_out (vty, "%s", VTY_NEWLINE); + write++; + } + } + + return write; +} + +struct stream * +prefix_bgp_orf_entry (struct stream *s, struct prefix_list *plist, + u_char init_flag, u_char permit_flag, u_char deny_flag) +{ + struct prefix_list_entry *pentry; + + if (! plist) + return s; + + for (pentry = plist->head; pentry; pentry = pentry->next) + { + u_char flag = init_flag; + struct prefix *p = &pentry->prefix; + + flag |= (pentry->type == PREFIX_PERMIT ? + permit_flag : deny_flag); + stream_putc (s, flag); + stream_putl (s, (u_int32_t)pentry->seq); + stream_putc (s, (u_char)pentry->ge); + stream_putc (s, (u_char)pentry->le); + stream_put_prefix (s, p); + } + + return s; +} + +int +prefix_bgp_orf_set (char *name, afi_t afi, struct orf_prefix *orfp, + int permit, int set) +{ + struct prefix_list *plist; + struct prefix_list_entry *pentry; + + /* ge and le value check */ + if (orfp->ge && orfp->ge <= orfp->p.prefixlen) + return CMD_WARNING; + if (orfp->le && orfp->le <= orfp->p.prefixlen) + return CMD_WARNING; + if (orfp->le && orfp->ge > orfp->le) + return CMD_WARNING; + + if (orfp->ge && orfp->le == (afi == AFI_IP ? 32 : 128)) + orfp->le = 0; + + plist = prefix_list_get (afi, 1, name); + if (! plist) + return CMD_WARNING; + + if (set) + { + pentry = prefix_list_entry_make (&orfp->p, + (permit ? PREFIX_PERMIT : PREFIX_DENY), + orfp->seq, orfp->le, orfp->ge, 0); + + if (prefix_entry_dup_check (plist, pentry)) + { + prefix_list_entry_free (pentry); + return CMD_WARNING; + } + + prefix_list_entry_add (plist, pentry); + } + else + { + pentry = prefix_list_entry_lookup (plist, &orfp->p, + (permit ? PREFIX_PERMIT : PREFIX_DENY), + orfp->seq, orfp->le, orfp->ge); + + if (! pentry) + return CMD_WARNING; + + prefix_list_entry_delete (plist, pentry, 1); + } + + return CMD_SUCCESS; +} + +void +prefix_bgp_orf_remove_all (afi_t afi, char *name) +{ + struct prefix_list *plist; + + plist = prefix_bgp_orf_lookup (afi, name); + if (plist) + prefix_list_delete (plist); +} + +/* return prefix count */ +int +prefix_bgp_show_prefix_list (struct vty *vty, afi_t afi, char *name) +{ + struct prefix_list *plist; + struct prefix_list_entry *pentry; + + plist = prefix_bgp_orf_lookup (afi, name); + if (! plist) + return 0; + + if (! vty) + return plist->count; + + vty_out (vty, "ip%s prefix-list %s: %d entries%s", + afi == AFI_IP ? "" : "v6", + plist->name, plist->count, VTY_NEWLINE); + + for (pentry = plist->head; pentry; pentry = pentry->next) + { + struct prefix *p = &pentry->prefix; + char buf[BUFSIZ]; + + vty_out (vty, " seq %d %s %s/%d", pentry->seq, + prefix_list_type_str (pentry), + inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ), + p->prefixlen); + + if (pentry->ge) + vty_out (vty, " ge %d", pentry->ge); + if (pentry->le) + vty_out (vty, " le %d", pentry->le); + + vty_out (vty, "%s", VTY_NEWLINE); + } + return plist->count; +} + +static 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); + 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; +} + + +/* Prefix-list node. */ +static struct cmd_node prefix_node = +{ + PREFIX_NODE, + "", /* Prefix list has no interface. */ + 1 +}; + +static int +config_write_prefix_ipv4 (struct vty *vty) +{ + return config_write_prefix_afi (AFI_IP, vty); +} + +static void +prefix_list_init_ipv4 (void) +{ + install_node (&prefix_node, config_write_prefix_ipv4); + + install_element (CONFIG_NODE, &ip_prefix_list_cmd); + install_element (CONFIG_NODE, &ip_prefix_list_ge_cmd); + install_element (CONFIG_NODE, &ip_prefix_list_ge_le_cmd); + install_element (CONFIG_NODE, &ip_prefix_list_le_cmd); + install_element (CONFIG_NODE, &ip_prefix_list_le_ge_cmd); + install_element (CONFIG_NODE, &ip_prefix_list_seq_cmd); + install_element (CONFIG_NODE, &ip_prefix_list_seq_ge_cmd); + install_element (CONFIG_NODE, &ip_prefix_list_seq_ge_le_cmd); + install_element (CONFIG_NODE, &ip_prefix_list_seq_le_cmd); + install_element (CONFIG_NODE, &ip_prefix_list_seq_le_ge_cmd); + + install_element (CONFIG_NODE, &no_ip_prefix_list_cmd); + install_element (CONFIG_NODE, &no_ip_prefix_list_prefix_cmd); + install_element (CONFIG_NODE, &no_ip_prefix_list_ge_cmd); + install_element (CONFIG_NODE, &no_ip_prefix_list_ge_le_cmd); + install_element (CONFIG_NODE, &no_ip_prefix_list_le_cmd); + install_element (CONFIG_NODE, &no_ip_prefix_list_le_ge_cmd); + install_element (CONFIG_NODE, &no_ip_prefix_list_seq_cmd); + install_element (CONFIG_NODE, &no_ip_prefix_list_seq_ge_cmd); + install_element (CONFIG_NODE, &no_ip_prefix_list_seq_ge_le_cmd); + install_element (CONFIG_NODE, &no_ip_prefix_list_seq_le_cmd); + install_element (CONFIG_NODE, &no_ip_prefix_list_seq_le_ge_cmd); + + install_element (CONFIG_NODE, &ip_prefix_list_description_cmd); + install_element (CONFIG_NODE, &no_ip_prefix_list_description_cmd); + install_element (CONFIG_NODE, &no_ip_prefix_list_description_arg_cmd); + + install_element (CONFIG_NODE, &ip_prefix_list_sequence_number_cmd); + install_element (CONFIG_NODE, &no_ip_prefix_list_sequence_number_cmd); + + install_element (VIEW_NODE, &show_ip_prefix_list_cmd); + install_element (VIEW_NODE, &show_ip_prefix_list_name_cmd); + install_element (VIEW_NODE, &show_ip_prefix_list_name_seq_cmd); + install_element (VIEW_NODE, &show_ip_prefix_list_prefix_cmd); + install_element (VIEW_NODE, &show_ip_prefix_list_prefix_longer_cmd); + install_element (VIEW_NODE, &show_ip_prefix_list_prefix_first_match_cmd); + install_element (VIEW_NODE, &show_ip_prefix_list_summary_cmd); + install_element (VIEW_NODE, &show_ip_prefix_list_summary_name_cmd); + 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, &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); +} + +/* Prefix-list node. */ +static struct cmd_node prefix_ipv6_node = +{ + PREFIX_IPV6_NODE, + "", /* Prefix list has no interface. */ + 1 +}; + +static int +config_write_prefix_ipv6 (struct vty *vty) +{ + return config_write_prefix_afi (AFI_IP6, vty); +} + +static void +prefix_list_init_ipv6 (void) +{ + install_node (&prefix_ipv6_node, config_write_prefix_ipv6); + + install_element (CONFIG_NODE, &ipv6_prefix_list_cmd); + install_element (CONFIG_NODE, &ipv6_prefix_list_ge_cmd); + install_element (CONFIG_NODE, &ipv6_prefix_list_ge_le_cmd); + install_element (CONFIG_NODE, &ipv6_prefix_list_le_cmd); + install_element (CONFIG_NODE, &ipv6_prefix_list_le_ge_cmd); + install_element (CONFIG_NODE, &ipv6_prefix_list_seq_cmd); + install_element (CONFIG_NODE, &ipv6_prefix_list_seq_ge_cmd); + install_element (CONFIG_NODE, &ipv6_prefix_list_seq_ge_le_cmd); + install_element (CONFIG_NODE, &ipv6_prefix_list_seq_le_cmd); + install_element (CONFIG_NODE, &ipv6_prefix_list_seq_le_ge_cmd); + + install_element (CONFIG_NODE, &no_ipv6_prefix_list_cmd); + install_element (CONFIG_NODE, &no_ipv6_prefix_list_prefix_cmd); + install_element (CONFIG_NODE, &no_ipv6_prefix_list_ge_cmd); + install_element (CONFIG_NODE, &no_ipv6_prefix_list_ge_le_cmd); + install_element (CONFIG_NODE, &no_ipv6_prefix_list_le_cmd); + install_element (CONFIG_NODE, &no_ipv6_prefix_list_le_ge_cmd); + install_element (CONFIG_NODE, &no_ipv6_prefix_list_seq_cmd); + install_element (CONFIG_NODE, &no_ipv6_prefix_list_seq_ge_cmd); + install_element (CONFIG_NODE, &no_ipv6_prefix_list_seq_ge_le_cmd); + install_element (CONFIG_NODE, &no_ipv6_prefix_list_seq_le_cmd); + install_element (CONFIG_NODE, &no_ipv6_prefix_list_seq_le_ge_cmd); + + install_element (CONFIG_NODE, &ipv6_prefix_list_description_cmd); + install_element (CONFIG_NODE, &no_ipv6_prefix_list_description_cmd); + install_element (CONFIG_NODE, &no_ipv6_prefix_list_description_arg_cmd); + + install_element (CONFIG_NODE, &ipv6_prefix_list_sequence_number_cmd); + install_element (CONFIG_NODE, &no_ipv6_prefix_list_sequence_number_cmd); + + install_element (VIEW_NODE, &show_ipv6_prefix_list_cmd); + install_element (VIEW_NODE, &show_ipv6_prefix_list_name_cmd); + install_element (VIEW_NODE, &show_ipv6_prefix_list_name_seq_cmd); + install_element (VIEW_NODE, &show_ipv6_prefix_list_prefix_cmd); + install_element (VIEW_NODE, &show_ipv6_prefix_list_prefix_longer_cmd); + install_element (VIEW_NODE, &show_ipv6_prefix_list_prefix_first_match_cmd); + install_element (VIEW_NODE, &show_ipv6_prefix_list_summary_cmd); + install_element (VIEW_NODE, &show_ipv6_prefix_list_summary_name_cmd); + 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, &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); +} + +void +prefix_list_init () +{ + prefix_list_init_ipv4 (); +#ifdef HAVE_IPV6 + prefix_list_init_ipv6 (); +#endif /* HAVE_IPV6 */ +} + +void +prefix_list_reset () +{ + 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 new file mode 100644 index 0000000..aa14e74 --- /dev/null +++ b/lib/plist.h @@ -0,0 +1,60 @@ +/* + * Prefix list functions. + * 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_H +#define _QUAGGA_PLIST_H + +enum prefix_list_type +{ + PREFIX_DENY, + PREFIX_PERMIT, +}; + +struct prefix_list; + +struct orf_prefix +{ + u_int32_t seq; + u_char ge; + u_char le; + struct prefix p; +}; + +/* Prototypes. */ +extern void prefix_list_init (void); +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 (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 0000000..6459579 --- /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 new file mode 100644 index 0000000..69ab8e6 --- /dev/null +++ b/lib/pqueue.c @@ -0,0 +1,187 @@ +/* Priority queue functions. + Copyright (C) 2003 Yasuhiro Ohara + +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 "memory.h" +#include "pqueue.h" + +/* priority queue using heap sort */ + +/* pqueue->cmp() controls the order of sorting (i.e, ascending or + descending). If you want the left node to move upper of the heap + binary tree, make cmp() to return less than 0. for example, if cmp + (10, 20) returns -1, the sorting is ascending order. if cmp (10, + 20) returns 1, the sorting is descending order. if cmp (10, 20) + returns 0, this library does not do sorting (which will not be what + you want). To be brief, if the contents of cmp_func (left, right) + is left - right, dequeue () returns the smallest node. Otherwise + (if the contents is right - left), dequeue () returns the largest + node. */ + +#define DATA_SIZE (sizeof (void *)) +#define PARENT_OF(x) ((x - 1) / 2) +#define LEFT_OF(x) (2 * x + 1) +#define RIGHT_OF(x) (2 * x + 2) +#define HAVE_CHILD(x,q) (x < (q)->size / 2) + +void +trickle_up (int index, struct pqueue *queue) +{ + void *tmp; + + /* Save current node as tmp node. */ + tmp = queue->array[index]; + + /* Continue until the node reaches top or the place where the parent + node should be upper than the tmp node. */ + while (index > 0 && + (*queue->cmp) (tmp, queue->array[PARENT_OF (index)]) < 0) + { + /* actually trickle up */ + queue->array[index] = queue->array[PARENT_OF (index)]; + if (queue->update != NULL) + (*queue->update) (queue->array[index], index); + index = PARENT_OF (index); + } + + /* Restore the tmp node to appropriate place. */ + queue->array[index] = tmp; + if (queue->update != NULL) + (*queue->update) (tmp, index); +} + +void +trickle_down (int index, struct pqueue *queue) +{ + void *tmp; + int which; + + /* Save current node as tmp node. */ + tmp = queue->array[index]; + + /* Continue until the node have at least one (left) child. */ + while (HAVE_CHILD (index, queue)) + { + /* If right child exists, and if the right child is more proper + to be moved upper. */ + if (RIGHT_OF (index) < queue->size && + (*queue->cmp) (queue->array[LEFT_OF (index)], + queue->array[RIGHT_OF (index)]) > 0) + which = RIGHT_OF (index); + else + which = LEFT_OF (index); + + /* If the tmp node should be upper than the child, break. */ + if ((*queue->cmp) (queue->array[which], tmp) > 0) + break; + + /* Actually trickle down the tmp node. */ + queue->array[index] = queue->array[which]; + if (queue->update != NULL) + (*queue->update) (queue->array[index], index); + index = which; + } + + /* Restore the tmp node to appropriate place. */ + queue->array[index] = tmp; + if (queue->update != NULL) + (*queue->update) (tmp, index); +} + +struct pqueue * +pqueue_create (void) +{ + struct pqueue *queue; + + queue = XCALLOC (MTYPE_PQUEUE, sizeof (struct pqueue)); + + queue->array = XCALLOC (MTYPE_PQUEUE_DATA, + DATA_SIZE * PQUEUE_INIT_ARRAYSIZE); + queue->array_size = PQUEUE_INIT_ARRAYSIZE; + + /* By default we want nothing to happen when a node changes. */ + queue->update = NULL; + return queue; +} + +void +pqueue_delete (struct pqueue *queue) +{ + XFREE (MTYPE_PQUEUE_DATA, queue->array); + XFREE (MTYPE_PQUEUE, queue); +} + +static int +pqueue_expand (struct pqueue *queue) +{ + void **newarray; + + newarray = XCALLOC (MTYPE_PQUEUE_DATA, queue->array_size * DATA_SIZE * 2); + if (newarray == NULL) + return 0; + + memcpy (newarray, queue->array, queue->array_size * DATA_SIZE); + + XFREE (MTYPE_PQUEUE_DATA, queue->array); + queue->array = newarray; + queue->array_size *= 2; + + return 1; +} + +void +pqueue_enqueue (void *data, struct pqueue *queue) +{ + if (queue->size + 2 >= queue->array_size && ! pqueue_expand (queue)) + return; + + queue->array[queue->size] = data; + if (queue->update != NULL) + (*queue->update) (data, queue->size); + trickle_up (queue->size, queue); + queue->size ++; +} + +void * +pqueue_dequeue (struct pqueue *queue) +{ + void *data = queue->array[0]; + queue->array[0] = queue->array[--queue->size]; + 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 new file mode 100644 index 0000000..8bb6961 --- /dev/null +++ b/lib/pqueue.h @@ -0,0 +1,46 @@ +/* Priority queue functions. + Copyright (C) 2003 Yasuhiro Ohara + +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_PQUEUE_H +#define _ZEBRA_PQUEUE_H + +struct pqueue +{ + void **array; + int array_size; + int size; + + int (*cmp) (void *, void *); + void (*update) (void * node, int actual_position); +}; + +#define PQUEUE_INIT_ARRAYSIZE 32 + +extern struct pqueue *pqueue_create (void); +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); + +#endif /* _ZEBRA_PQUEUE_H */ diff --git a/lib/prefix.c b/lib/prefix.c new file mode 100644 index 0000000..43fc317 --- /dev/null +++ b/lib/prefix.c @@ -0,0 +1,1042 @@ +/* + * Prefix related functions. + * 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. + */ + +#include + +#include "prefix.h" +#include "vty.h" +#include "sockunion.h" +#include "memory.h" +#include "log.h" + +/* Maskbit. */ +static const u_char maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0, + 0xf8, 0xfc, 0xfe, 0xff}; + +static const struct in6_addr maskbytes6[] = +{ + /* /0 */ { { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /1 */ { { { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /2 */ { { { 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /3 */ { { { 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /4 */ { { { 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /5 */ { { { 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /6 */ { { { 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /7 */ { { { 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /8 */ { { { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /9 */ { { { 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /10 */ { { { 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /11 */ { { { 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /12 */ { { { 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /13 */ { { { 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /14 */ { { { 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /15 */ { { { 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /16 */ { { { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /17 */ { { { 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /18 */ { { { 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /19 */ { { { 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /20 */ { { { 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /21 */ { { { 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /22 */ { { { 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /23 */ { { { 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /24 */ { { { 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /25 */ { { { 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /26 */ { { { 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /27 */ { { { 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /28 */ { { { 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /29 */ { { { 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /30 */ { { { 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /31 */ { { { 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /32 */ { { { 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /33 */ { { { 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /34 */ { { { 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /35 */ { { { 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /36 */ { { { 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /37 */ { { { 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /38 */ { { { 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /39 */ { { { 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /40 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /41 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /42 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /43 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /44 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /45 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /46 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /47 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /48 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /49 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /50 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /51 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /52 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /53 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /54 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /55 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /56 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /57 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /58 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /59 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /60 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /61 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /62 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /63 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /64 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /65 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /66 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /67 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /68 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /69 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /70 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /71 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /72 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /73 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /74 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /75 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /76 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /77 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /78 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /79 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /80 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /81 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /82 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /83 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /84 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /85 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /86 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /87 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /88 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + /* /89 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00 } } }, + /* /90 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00 } } }, + /* /91 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00 } } }, + /* /92 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00 } } }, + /* /93 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00 } } }, + /* /94 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00 } } }, + /* /95 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00 } } }, + /* /96 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } } }, + /* /97 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00 } } }, + /* /98 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00 } } }, + /* /99 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00 } } }, + /* /100 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00 } } }, + /* /101 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00 } } }, + /* /102 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00 } } }, + /* /103 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00 } } }, + /* /104 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 } } }, + /* /105 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00 } } }, + /* /106 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00 } } }, + /* /107 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00 } } }, + /* /108 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00 } } }, + /* /109 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00 } } }, + /* /110 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00 } } }, + /* /111 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00 } } }, + /* /112 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00 } } }, + /* /113 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00 } } }, + /* /114 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00 } } }, + /* /115 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00 } } }, + /* /116 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00 } } }, + /* /117 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00 } } }, + /* /118 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00 } } }, + /* /119 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00 } } }, + /* /120 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 } } }, + /* /121 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80 } } }, + /* /122 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0 } } }, + /* /123 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0 } } }, + /* /124 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0 } } }, + /* /125 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8 } } }, + /* /126 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc } } }, + /* /127 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe } } }, + /* /128 */ { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } } } +}; + +/* Number of bits in prefix type. */ +#ifndef PNBBY +#define PNBBY 8 +#endif /* PNBBY */ + +#define MASKBIT(offset) ((0xff << (PNBBY - (offset))) & 0xff) + +unsigned int +prefix_bit (const u_char *prefix, const u_char prefixlen) +{ + unsigned int offset = prefixlen / 8; + unsigned int shift = 7 - (prefixlen % 8); + + return (prefix[offset] >> shift) & 1; +} + +unsigned int +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) +{ + if (afi == AFI_IP) + return AF_INET; +#ifdef HAVE_IPV6 + else if (afi == AFI_IP6) + return AF_INET6; +#endif /* HAVE_IPV6 */ + else if (afi == AFI_ETHER) + return AF_ETHERNET; + return 0; +} + +afi_t +family2afi (int family) +{ + if (family == AF_INET) + return AFI_IP; +#ifdef HAVE_IPV6 + 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) +{ + int offset; + int shift; + const u_char *np, *pp; + + /* If n's prefix is longer than p's one return 0. */ + if (n->prefixlen > p->prefixlen) + return 0; + + /* Set both prefix's head pointer. */ + np = (const u_char *)&n->u.prefix; + pp = (const u_char *)&p->u.prefix; + + offset = n->prefixlen / PNBBY; + shift = n->prefixlen % PNBBY; + + if (shift) + if (maskbit[shift] & (np[offset] ^ pp[offset])) + return 0; + + while (offset--) + if (np[offset] != pp[offset]) + return 0; + return 1; +} + +/* Copy prefix from src to dest. */ +void +prefix_copy (struct prefix *dest, const struct prefix *src) +{ + dest->family = src->family; + dest->prefixlen = src->prefixlen; + + if (src->family == AF_INET) + dest->u.prefix4 = src->u.prefix4; +#ifdef HAVE_IPV6 + else if (src->family == AF_INET6) + dest->u.prefix6 = src->u.prefix6; +#endif /* HAVE_IPV6 */ + else if (src->family == AF_UNSPEC) + { + 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", + src->family); + assert (0); + } +} + +/* + * Return 1 if the address/netmask contained in the prefix structure + * is the same, and else return 0. For this routine, 'same' requires + * that not only the prefix length and the network part be the same, + * but also the host part. Thus, 10.0.0.1/8 and 10.0.0.2/8 are not + * the same. Note that this routine has the same return value sense + * as '==' (which is different from prefix_cmp). + */ +int +prefix_same (const struct prefix *p1, const struct prefix *p2) +{ + if (p1->family == p2->family && p1->prefixlen == p2->prefixlen) + { + if (p1->family == AF_INET) + if (IPV4_ADDR_SAME (&p1->u.prefix4.s_addr, &p2->u.prefix4.s_addr)) + return 1; +#ifdef HAVE_IPV6 + if (p1->family == AF_INET6 ) + 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; +} + +/* + * Return 0 if the network prefixes represented by the struct prefix + * arguments are the same prefix, and 1 otherwise. Network prefixes + * are considered the same if the prefix lengths are equal and the + * network parts are the same. Host bits (which are considered masked + * by the prefix length) are not significant. Thus, 10.0.0.1/8 and + * 10.0.0.2/8 are considered equivalent by this routine. Note that + * this routine has the same return sense as strcmp (which is different + * from prefix_same). + */ +int +prefix_cmp (const struct prefix *p1, const struct prefix *p2) +{ + int offset; + int shift; + + /* Set both prefix's head pointer. */ + const u_char *pp1 = (const u_char *)&p1->u.prefix; + const u_char *pp2 = (const u_char *)&p2->u.prefix; + + if (p1->family != p2->family || p1->prefixlen != p2->prefixlen) + return 1; + + offset = p1->prefixlen / PNBBY; + shift = p1->prefixlen % PNBBY; + + if (shift) + if (maskbit[shift] & (pp1[offset] ^ pp2[offset])) + return 1; + + while (offset--) + if (pp1[offset] != pp2[offset]) + return 1; + + return 0; +} + +/* + * Count the number of common bits in 2 prefixes. The prefix length is + * ignored for this function; the whole prefix is compared. If the prefix + * address families don't match, return -1; otherwise the return value is + * in range 0 ... maximum prefix length for the address family. + */ +int +prefix_common_bits (const struct prefix *p1, const struct prefix *p2) +{ + int pos, bit; + int length = 0; + u_char xor; + + /* Set both prefix's head pointer. */ + const u_char *pp1 = (const u_char *)&p1->u.prefix; + const u_char *pp2 = (const u_char *)&p2->u.prefix; + + if (p1->family == AF_INET) + length = IPV4_MAX_BYTELEN; +#ifdef HAVE_IPV6 + if (p1->family == AF_INET6) + length = IPV6_MAX_BYTELEN; +#endif + if (p1->family != p2->family || !length) + return -1; + + for (pos = 0; pos < length; pos++) + if (pp1[pos] != pp2[pos]) + break; + if (pos == length) + return pos * 8; + + xor = pp1[pos] ^ pp2[pos]; + for (bit = 0; bit < 8; bit++) + if (xor & (1 << (7 - bit))) + break; + + return pos * 8 + bit; +} + +/* Return prefix family type string. */ +const char * +prefix_family_str (const struct prefix *p) +{ + if (p->family == AF_INET) + return "inet"; +#ifdef HAVE_IPV6 + if (p->family == AF_INET6) + return "inet6"; +#endif /* HAVE_IPV6 */ + if (p->family == AF_ETHERNET) + return "ether"; + return "unspec"; +} + +/* Allocate new prefix_ipv4 structure. */ +struct prefix_ipv4 * +prefix_ipv4_new () +{ + struct prefix_ipv4 *p; + + /* Call prefix_new to allocate a full-size struct prefix to avoid problems + where the struct prefix_ipv4 is cast to struct prefix and unallocated + bytes were being referenced (e.g. in structure assignments). */ + p = (struct prefix_ipv4 *)prefix_new(); + p->family = AF_INET; + return p; +} + +/* Free prefix_ipv4 structure. */ +void +prefix_ipv4_free (struct prefix_ipv4 *p) +{ + prefix_free((struct prefix *)p); +} + +/* When string format is invalid return 0. */ +int +str2prefix_ipv4 (const char *str, struct prefix_ipv4 *p) +{ + int ret; + int plen; + char *pnt; + char *cp; + + /* Find slash inside string. */ + pnt = strchr (str, '/'); + + /* String doesn't contail slash. */ + if (pnt == NULL) + { + /* Convert string to prefix. */ + ret = inet_aton (str, &p->prefix); + if (ret == 0) + return 0; + + /* If address doesn't contain slash we assume it host address. */ + p->family = AF_INET; + p->prefixlen = IPV4_MAX_BITLEN; + + return ret; + } + else + { + cp = XMALLOC (MTYPE_TMP, (pnt - str) + 1); + strncpy (cp, str, pnt - str); + *(cp + (pnt - str)) = '\0'; + ret = inet_aton (cp, &p->prefix); + XFREE (MTYPE_TMP, cp); + + /* Get prefix length. */ + plen = (u_char) atoi (++pnt); + if (plen > IPV4_MAX_PREFIXLEN) + return 0; + + p->family = AF_INET; + p->prefixlen = plen; + } + + 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) +{ + assert (masklen >= 0 && masklen <= IPV4_MAX_BITLEN); + + /* left shift is only defined for less than the size of the type. + * we unconditionally use long long in case the target platform + * has defined behaviour for << 32 (or has a 64-bit left shift) */ + + if (sizeof(unsigned long long) > 4) + netmask->s_addr = htonl(0xffffffffULL << (32 - masklen)); + else + netmask->s_addr = htonl(masklen ? 0xffffffffU << (32 - masklen) : 0); +} + +/* Convert IP address's netmask into integer. We assume netmask is + sequential one. Argument netmask should be network byte order. */ +u_char +ip_masklen (struct in_addr netmask) +{ + uint32_t tmp = ~ntohl(netmask.s_addr); + if (tmp) + /* clz: count leading zeroes. sadly, the behaviour of this builtin + * is undefined for a 0 argument, even though most CPUs give 32 */ + return __builtin_clz(tmp); + else + return 32; +} + +/* Apply mask to IPv4 prefix (network byte order). */ +void +apply_mask_ipv4 (struct prefix_ipv4 *p) +{ + struct in_addr mask; + masklen2ip(p->prefixlen, &mask); + p->prefix.s_addr &= mask.s_addr; +} + +/* If prefix is 0.0.0.0/0 then return 1 else return 0. */ +int +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 */ +struct prefix_ipv6 * +prefix_ipv6_new (void) +{ + struct prefix_ipv6 *p; + + /* Allocate a full-size struct prefix to avoid problems with structure + size mismatches. */ + p = (struct prefix_ipv6 *)prefix_new(); + p->family = AF_INET6; + return p; +} + +/* Free prefix for IPv6. */ +void +prefix_ipv6_free (struct prefix_ipv6 *p) +{ + prefix_free((struct prefix *)p); +} + +/* If given string is valid return pin6 else return NULL */ +int +str2prefix_ipv6 (const char *str, struct prefix_ipv6 *p) +{ + char *pnt; + char *cp; + int ret; + + pnt = strchr (str, '/'); + + /* If string doesn't contain `/' treat it as host route. */ + if (pnt == NULL) + { + ret = inet_pton (AF_INET6, str, &p->prefix); + if (ret == 0) + return 0; + p->prefixlen = IPV6_MAX_BITLEN; + } + else + { + int plen; + + cp = XMALLOC (MTYPE_TMP, (pnt - str) + 1); + strncpy (cp, str, pnt - str); + *(cp + (pnt - str)) = '\0'; + ret = inet_pton (AF_INET6, cp, &p->prefix); + free (cp); + if (ret == 0) + return 0; + plen = (u_char) atoi (++pnt); + if (plen > IPV6_MAX_BITLEN) + return 0; + p->prefixlen = plen; + } + p->family = AF_INET6; + + return ret; +} + +/* Convert struct in6_addr netmask into integer. + * FIXME return u_char as ip_maskleni() does. */ +int +ip6_masklen (struct in6_addr netmask) +{ + int len = 0; + unsigned char val; + unsigned char *pnt; + + pnt = (unsigned char *) & netmask; + + while ((*pnt == 0xff) && len < IPV6_MAX_BITLEN) + { + len += 8; + pnt++; + } + + if (len < IPV6_MAX_BITLEN) + { + val = *pnt; + while (val) + { + len++; + val <<= 1; + } + } + return len; +} + +void +masklen2ip6 (const int masklen, struct in6_addr *netmask) +{ + assert (masklen >= 0 && masklen <= IPV6_MAX_BITLEN); + memcpy (netmask, maskbytes6 + masklen, sizeof (struct in6_addr)); +} + +void +apply_mask_ipv6 (struct prefix_ipv6 *p) +{ + u_char *pnt; + int index; + int offset; + + index = p->prefixlen / 8; + + if (index < 16) + { + pnt = (u_char *) &p->prefix; + offset = p->prefixlen % 8; + + pnt[index] &= maskbit[offset]; + index++; + + while (index < 16) + pnt[index++] = 0; + } +} + +void +str2in6_addr (const char *str, struct in6_addr *addr) +{ + int i; + unsigned int x; + + /* %x must point to unsinged int */ + for (i = 0; i < 16; i++) + { + sscanf (str + (i * 2), "%02x", &x); + addr->s6_addr[i] = x & 0xff; + } +} +#endif /* HAVE_IPV6 */ + +void +apply_mask (struct prefix *p) +{ + switch (p->family) + { + case AF_INET: + apply_mask_ipv4 ((struct prefix_ipv4 *)p); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + apply_mask_ipv6 ((struct prefix_ipv6 *)p); + break; +#endif /* HAVE_IPV6 */ + default: + break; + } + return; +} + +/* Utility function of convert between struct prefix <=> union sockunion. + * FIXME This function isn't used anywhere. */ +struct prefix * +sockunion2prefix (const union sockunion *dest, + const union sockunion *mask) +{ + if (dest->sa.sa_family == AF_INET) + { + struct prefix_ipv4 *p; + + p = prefix_ipv4_new (); + p->family = AF_INET; + p->prefix = dest->sin.sin_addr; + p->prefixlen = ip_masklen (mask->sin.sin_addr); + return (struct prefix *) p; + } +#ifdef HAVE_IPV6 + if (dest->sa.sa_family == AF_INET6) + { + struct prefix_ipv6 *p; + + p = prefix_ipv6_new (); + p->family = AF_INET6; + p->prefixlen = ip6_masklen (mask->sin6.sin6_addr); + memcpy (&p->prefix, &dest->sin6.sin6_addr, sizeof (struct in6_addr)); + return (struct prefix *) p; + } +#endif /* HAVE_IPV6 */ + return NULL; +} + +/* Utility function of convert between struct prefix <=> union sockunion. */ +struct prefix * +sockunion2hostprefix (const union sockunion *su, struct prefix *prefix) +{ + if (su->sa.sa_family == AF_INET) + { + struct prefix_ipv4 *p; + + p = prefix ? (struct prefix_ipv4 *) prefix : prefix_ipv4_new (); + p->family = AF_INET; + p->prefix = su->sin.sin_addr; + p->prefixlen = IPV4_MAX_BITLEN; + return (struct prefix *) p; + } +#ifdef HAVE_IPV6 + if (su->sa.sa_family == AF_INET6) + { + struct prefix_ipv6 *p; + + 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)); + return (struct prefix *) p; + } +#endif /* HAVE_IPV6 */ + return NULL; +} + +void +prefix2sockunion (const struct prefix *p, union sockunion *su) +{ + memset (su, 0, sizeof (*su)); + + su->sa.sa_family = p->family; + if (p->family == AF_INET) + su->sin.sin_addr = p->u.prefix4; +#ifdef HAVE_IPV6 + if (p->family == AF_INET6) + memcpy (&su->sin6.sin6_addr, &p->u.prefix6, sizeof (struct in6_addr)); +#endif /* HAVE_IPV6 */ +} + +int +prefix_blen (const struct prefix *p) +{ + switch (p->family) + { + case AF_INET: + return IPV4_MAX_BYTELEN; + break; +#ifdef HAVE_IPV6 + case AF_INET6: + return IPV6_MAX_BYTELEN; + break; +#endif /* HAVE_IPV6 */ + case AF_ETHERNET: + return ETHER_ADDR_LEN; + } + return 0; +} + +/* Generic function for conversion string to struct prefix. */ +int +str2prefix (const char *str, struct prefix *p) +{ + int ret; + + /* First we try to convert string to struct prefix_ipv4. */ + ret = str2prefix_ipv4 (str, (struct prefix_ipv4 *) p); + if (ret) + return ret; + +#ifdef HAVE_IPV6 + /* Next we try to convert string to struct prefix_ipv6. */ + ret = str2prefix_ipv6 (str, (struct prefix_ipv6 *) p); + if (ret) + 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; +} + +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 str; +} + +struct prefix * +prefix_new () +{ + struct prefix *p; + + p = XCALLOC (MTYPE_PREFIX, sizeof *p); + return p; +} + +/* Free prefix structure. */ +void +prefix_free (struct prefix *p) +{ + XFREE (MTYPE_PREFIX, p); +} + +/* Utility function. Check the string only contains digit + * character. + * FIXME str.[c|h] would be better place for this function. */ +int +all_digit (const char *str) +{ + for (; *str != '\0'; str++) + if (!isdigit ((int) *str)) + return 0; + return 1; +} + +/* Utility function to convert ipv4 prefixes to Classful prefixes */ +void apply_classful_mask_ipv4 (struct prefix_ipv4 *p) +{ + + u_int32_t destination; + + destination = ntohl (p->prefix.s_addr); + + if (p->prefixlen == IPV4_MAX_PREFIXLEN); + /* do nothing for host routes */ + else if (IN_CLASSC (destination)) + { + p->prefixlen=24; + apply_mask_ipv4(p); + } + else if (IN_CLASSB(destination)) + { + p->prefixlen=16; + apply_mask_ipv4(p); + } + else + { + p->prefixlen=8; + apply_mask_ipv4(p); + } +} + +in_addr_t +ipv4_network_addr (in_addr_t hostaddr, int masklen) +{ + struct in_addr mask; + + masklen2ip (masklen, &mask); + return hostaddr & mask.s_addr; +} + +in_addr_t +ipv4_broadcast_addr (in_addr_t hostaddr, int masklen) +{ + struct in_addr mask; + + masklen2ip (masklen, &mask); + return (masklen != IPV4_MAX_PREFIXLEN-1) ? + /* normal case */ + (hostaddr | ~mask.s_addr) : + /* special case for /31 */ + (hostaddr ^ ~mask.s_addr); +} + +/* Utility function to convert ipv4 netmask to prefixes + ex.) "1.1.0.0" "255.255.0.0" => "1.1.0.0/16" + ex.) "1.0.0.0" NULL => "1.0.0.0/8" */ +int +netmask_str2prefix_str (const char *net_str, const char *mask_str, + char *prefix_str) +{ + struct in_addr network; + struct in_addr mask; + u_char prefixlen; + u_int32_t destination; + int ret; + + ret = inet_aton (net_str, &network); + if (! ret) + return 0; + + if (mask_str) + { + ret = inet_aton (mask_str, &mask); + if (! ret) + return 0; + + prefixlen = ip_masklen (mask); + } + else + { + destination = ntohl (network.s_addr); + + if (network.s_addr == 0) + prefixlen = 0; + else if (IN_CLASSC (destination)) + prefixlen = 24; + else if (IN_CLASSB (destination)) + prefixlen = 16; + else if (IN_CLASSA (destination)) + prefixlen = 8; + else + return 0; + } + + sprintf (prefix_str, "%s/%d", net_str, prefixlen); + + return 1; +} + +#ifdef HAVE_IPV6 +/* Utility function for making IPv6 address string. */ +const char * +inet6_ntoa (struct in6_addr addr) +{ + static char buf[INET6_ADDRSTRLEN]; + + inet_ntop (AF_INET6, &addr, buf, INET6_ADDRSTRLEN); + return buf; +} +#endif /* HAVE_IPV6 */ diff --git a/lib/prefix.h b/lib/prefix.h new file mode 100644 index 0000000..2cf0b20 --- /dev/null +++ b/lib/prefix.h @@ -0,0 +1,295 @@ +/* + * Prefix structure. + * Copyright (C) 1998 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_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 + * by CIDR, where the 'host bits' of the prefix are 0 + * (e.g. AF_INET:10.0.0.0/8), or an address and netmask + * (e.g. AF_INET:10.0.0.9/8), such as might be configured on an + * 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 +{ + u_char family; + u_char prefixlen; + union + { + u_char prefix; + struct in_addr prefix4; +#ifdef HAVE_IPV6 + struct in6_addr prefix6; +#endif /* HAVE_IPV6 */ + struct + { + 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))); +}; + +/* IPv4 prefix structure. */ +struct prefix_ipv4 +{ + u_char family; + u_char prefixlen; + struct in_addr prefix __attribute__ ((aligned (8))); +}; + +/* IPv6 prefix structure. */ +#ifdef HAVE_IPV6 +struct prefix_ipv6 +{ + u_char family; + u_char prefixlen; + struct in6_addr prefix __attribute__ ((aligned (8))); +}; +#endif /* HAVE_IPV6 */ + +struct prefix_ls +{ + u_char family; + u_char prefixlen; + struct in_addr id __attribute__ ((aligned (8))); + struct in_addr adv_router; +}; + +/* Prefix for routing distinguisher. */ +struct prefix_rd +{ + u_char family; + u_char prefixlen; + 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 */ + +#ifndef INET6_ADDRSTRLEN +#define INET6_ADDRSTRLEN 46 +#endif /* INET6_ADDRSTRLEN */ + +#ifndef INET6_BUFSIZ +#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 +#define IPV4_MAX_PREFIXLEN 32 +#define IPV4_ADDR_CMP(D,S) memcmp ((D), (S), IPV4_MAX_BYTELEN) +#define IPV4_ADDR_SAME(D,S) (memcmp ((D), (S), IPV4_MAX_BYTELEN) == 0) +#define IPV4_ADDR_COPY(D,S) memcpy ((D), (S), IPV4_MAX_BYTELEN) + +#define IPV4_NET0(a) ((((u_int32_t) (a)) & 0xff000000) == 0x00000000) +#define IPV4_NET127(a) ((((u_int32_t) (a)) & 0xff000000) == 0x7f000000) +#define IPV4_LINKLOCAL(a) ((((u_int32_t) (a)) & 0xffff0000) == 0xa9fe0000) +#define IPV4_CLASS_DE(a) ((((u_int32_t) (a)) & 0xe0000000) == 0xe0000000) + +/* Max bit/byte length of IPv6 address. */ +#define IPV6_MAX_BYTELEN 16 +#define IPV6_MAX_BITLEN 128 +#define IPV6_MAX_PREFIXLEN 128 +#define IPV6_ADDR_CMP(D,S) memcmp ((D), (S), IPV6_MAX_BYTELEN) +#define IPV6_ADDR_SAME(D,S) (memcmp ((D), (S), IPV6_MAX_BYTELEN) == 0) +#define IPV6_ADDR_COPY(D,S) memcpy ((D), (S), IPV6_MAX_BYTELEN) + +/* Count prefix size from mask length */ +#define PSIZE(a) (((a) + 7) / (8)) + +/* 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); +extern unsigned int prefix6_bit (const struct in6_addr *prefix, const u_char prefixlen); + +extern struct prefix *prefix_new (void); +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 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 *); +extern int prefix_common_bits (const struct prefix *, const struct prefix *); +extern void prefix_copy (struct prefix *dest, const struct prefix *src); +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 *, 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 *); +extern void apply_mask_ipv4 (struct prefix_ipv4 *); + +#define PREFIX_COPY_IPV4(DST, SRC) \ + *((struct prefix_ipv4 *)(DST)) = *((const struct prefix_ipv4 *)(SRC)); + +extern int prefix_ipv4_any (const struct prefix_ipv4 *); +extern void apply_classful_mask_ipv4 (struct prefix_ipv4 *); + +extern u_char ip_masklen (struct in_addr); +extern void masklen2ip (const int, struct in_addr *); +/* returns the network portion of the host address */ +extern in_addr_t ipv4_network_addr (in_addr_t hostaddr, int masklen); +/* given the address of a host on a network and the network mask length, + * calculate the broadcast address for that network; + * special treatment for /31: returns the address of the other host + * on the network by flipping the host bit */ +extern in_addr_t ipv4_broadcast_addr (in_addr_t hostaddr, int masklen); + +extern int netmask_str2prefix_str (const char *, const char *, char *); + +#ifdef HAVE_IPV6 +extern struct prefix_ipv6 *prefix_ipv6_new (void); +extern void prefix_ipv6_free (struct prefix_ipv6 *); +extern int str2prefix_ipv6 (const char *, struct prefix_ipv6 *); +extern void apply_mask_ipv6 (struct prefix_ipv6 *); + +#define PREFIX_COPY_IPV6(DST, SRC) \ + *((struct prefix_ipv6 *)(DST)) = *((const struct prefix_ipv6 *)(SRC)); + +extern int ip6_masklen (struct in6_addr); +extern void masklen2ip6 (const int, struct in6_addr *); + +extern void str2in6_addr (const char *, struct in6_addr *); +extern const char *inet6_ntoa (struct in6_addr); + +#endif /* HAVE_IPV6 */ + +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 new file mode 100644 index 0000000..3fb96ae --- /dev/null +++ b/lib/privs.c @@ -0,0 +1,852 @@ +/* + * Zebra privileges. + * + * Copyright (C) 2003 Paul Jakma. + * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. + * + * 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 "privs.h" +#include "memory.h" + +#ifdef HAVE_CAPABILITIES +/* sort out some generic internal types for: + * + * privilege values (cap_value_t, priv_t) -> pvalue_t + * privilege set (..., priv_set_t) -> pset_t + * privilege working storage (cap_t, ...) -> pstorage_t + * + * values we think of as numeric (they're ints really, but we dont know) + * sets are mostly opaque, to hold a set of privileges, related in some way. + * storage binds together a set of sets we're interested in. + * (in reality: cap_value_t and priv_t are ints) + */ +#ifdef HAVE_LCAPS +/* Linux doesn't have a 'set' type: a set of related privileges */ +struct _pset { + int num; + cap_value_t *caps; +}; +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; +typedef priv_set_t *pstorage_t; +#else /* neither LCAPS nor SOLARIS_CAPABILITIES */ +#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. + */ +static zebra_privs_current_t zprivs_null_state = ZPRIVS_RAISED; + +/* internal privileges state */ +static struct _zprivs_t +{ +#ifdef HAVE_CAPABILITIES + pstorage_t caps; /* working storage */ + pset_t *syscaps_p; /* system-type requested permitted caps */ + pset_t *syscaps_i; /* system-type requested inheritable caps */ +#endif /* HAVE_CAPABILITIES */ + uid_t zuid, /* uid to run as */ + zsuid; /* saved uid */ + gid_t zgid; /* gid to run as */ + gid_t vtygrp; /* gid for vty sockets */ +} zprivs_state; + +/* externally exported but not directly accessed functions */ +#ifdef HAVE_CAPABILITIES +int zprivs_change_caps (zebra_privs_ops_t); +zebra_privs_current_t zprivs_state_caps (void); +#endif /* HAVE_CAPABILITIES */ +int zprivs_change_uid (zebra_privs_ops_t); +zebra_privs_current_t zprivs_state_uid (void); +int zprivs_change_null (zebra_privs_ops_t); +zebra_privs_current_t zprivs_state_null (void); + +#ifdef HAVE_CAPABILITIES +/* internal capability API */ +static pset_t *zcaps2sys (zebra_capabilities_t *, int); +static void zprivs_caps_init (struct zebra_privs_t *); +static void zprivs_caps_terminate (void); + +/* Map of Quagga abstract capabilities to system capabilities */ +static struct +{ + int num; + pvalue_t *system_caps; +} cap_map [ZCAP_MAX] = +{ +#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 }, }, + [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, }, }, + [ZCAP_NICE] = { 1, (pvalue_t []) { CAP_SYS_NICE }, }, + [ZCAP_PTRACE] = { 1, (pvalue_t []) { CAP_SYS_PTRACE }, }, + [ZCAP_DAC_OVERRIDE] = { 1, (pvalue_t []) { CAP_DAC_OVERRIDE }, }, + [ZCAP_READ_SEARCH] = { 1, (pvalue_t []) { CAP_DAC_READ_SEARCH }, }, + [ZCAP_SYS_ADMIN] = { 1, (pvalue_t []) { CAP_SYS_ADMIN }, }, + [ZCAP_FOWNER] = { 1, (pvalue_t []) { CAP_FOWNER }, }, +#elif defined(HAVE_SOLARIS_CAPABILITIES) /* HAVE_LCAPS */ + /* Quagga -> Solaris privilege mappings */ + [ZCAP_SETID] = { 1, (pvalue_t []) { PRIV_PROC_SETID }, }, + [ZCAP_BIND] = { 1, (pvalue_t []) { PRIV_NET_PRIVADDR }, }, + /* IP_CONFIG is a subset of NET_CONFIG and is allowed in zones */ +#ifdef PRIV_SYS_IP_CONFIG + [ZCAP_NET_ADMIN] = { 1, (pvalue_t []) { PRIV_SYS_IP_CONFIG }, }, +#else + [ZCAP_NET_ADMIN] = { 1, (pvalue_t []) { PRIV_SYS_NET_CONFIG }, }, +#endif + [ZCAP_NET_RAW] = { 2, (pvalue_t []) { PRIV_NET_RAWACCESS, + PRIV_NET_ICMPACCESS }, }, + [ZCAP_CHROOT] = { 1, (pvalue_t []) { PRIV_PROC_CHROOT }, }, + [ZCAP_NICE] = { 1, (pvalue_t []) { PRIV_PROC_PRIOCNTL }, }, + [ZCAP_PTRACE] = { 1, (pvalue_t []) { PRIV_PROC_SESSION }, }, + [ZCAP_DAC_OVERRIDE] = { 2, (pvalue_t []) { PRIV_FILE_DAC_EXECUTE, + PRIV_FILE_DAC_READ, + PRIV_FILE_DAC_SEARCH, + PRIV_FILE_DAC_WRITE, + PRIV_FILE_DAC_SEARCH }, }, + [ZCAP_READ_SEARCH] = { 2, (pvalue_t []) { PRIV_FILE_DAC_SEARCH, + PRIV_FILE_DAC_READ }, }, + [ZCAP_SYS_ADMIN] = { 1, (pvalue_t []) { PRIV_SYS_ADMIN }, }, + [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 */ +static pset_t * +zcaps2sys (zebra_capabilities_t *zcaps, int num) +{ + pset_t *syscaps; + int i, j = 0, count = 0; + + if (!num) + return NULL; + + /* first count up how many system caps we have */ + for (i= 0; i < num; i++) + count += cap_map[zcaps[i]].num; + + if ( (syscaps = XCALLOC (MTYPE_PRIVS, (sizeof(pset_t) * num))) == NULL) + { + fprintf (stderr, "%s: could not allocate syscaps!", __func__); + return NULL; + } + + syscaps->caps = XCALLOC (MTYPE_PRIVS, (sizeof (pvalue_t) * count)); + + if (!syscaps->caps) + { + fprintf (stderr, "%s: could not XCALLOC caps!", __func__); + return NULL; + } + + /* copy the capabilities over */ + count = 0; + for (i=0; i < num; i++) + for (j = 0; j < cap_map[zcaps[i]].num; j++) + syscaps->caps[count++] = cap_map[zcaps[i]].system_caps[j]; + + /* iterations above should be exact same as previous count, obviously.. */ + syscaps->num = count; + + return syscaps; +} + +/* set or clear the effective capabilities to/from permitted */ +int +zprivs_change_caps (zebra_privs_ops_t op) +{ + cap_flag_value_t cflag; + + /* should be no possibility of being called without valid caps */ + assert (zprivs_state.syscaps_p && zprivs_state.caps); + if (! (zprivs_state.syscaps_p && zprivs_state.caps)) + exit (1); + + if (op == ZPRIVS_RAISE) + cflag = CAP_SET; + else if (op == ZPRIVS_LOWER) + cflag = CAP_CLEAR; + else + return -1; + + if ( !cap_set_flag (zprivs_state.caps, CAP_EFFECTIVE, + zprivs_state.syscaps_p->num, + zprivs_state.syscaps_p->caps, + cflag)) + return cap_set_proc (zprivs_state.caps); + return -1; +} + +zebra_privs_current_t +zprivs_state_caps (void) +{ + int i; + cap_flag_value_t val; + + /* should be no possibility of being called without valid caps */ + assert (zprivs_state.syscaps_p && zprivs_state.caps); + if (! (zprivs_state.syscaps_p && zprivs_state.caps)) + exit (1); + + for (i=0; i < zprivs_state.syscaps_p->num; i++) + { + if ( cap_get_flag (zprivs_state.caps, zprivs_state.syscaps_p->caps[i], + CAP_EFFECTIVE, &val) ) + { + zlog_warn ("zprivs_state_caps: could not cap_get_flag, %s", + safe_strerror (errno) ); + return ZPRIVS_UNKNOWN; + } + if (val == CAP_SET) + return ZPRIVS_RAISED; + } + return ZPRIVS_LOWERED; +} + +static void +zprivs_caps_init (struct zebra_privs_t *zprivs) +{ + zprivs_state.syscaps_p = zcaps2sys (zprivs->caps_p, zprivs->cap_num_p); + zprivs_state.syscaps_i = zcaps2sys (zprivs->caps_i, zprivs->cap_num_i); + + /* Tell kernel we want caps maintained across uid changes */ + if ( prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1 ) + { + fprintf (stderr, "privs_init: could not set PR_SET_KEEPCAPS, %s\n", + safe_strerror (errno) ); + exit(1); + } + + if ( !zprivs_state.syscaps_p ) + { + fprintf (stderr, "privs_init: capabilities enabled, " + "but no capabilities supplied\n"); + } + + /* we have caps, we have no need to ever change back the original user */ + if (zprivs_state.zuid) + { + if ( setreuid (zprivs_state.zuid, zprivs_state.zuid) ) + { + fprintf (stderr, "zprivs_init (cap): could not setreuid, %s\n", + safe_strerror (errno)); + exit (1); + } + } + + if ( !(zprivs_state.caps = cap_init()) ) + { + fprintf (stderr, "privs_init: failed to cap_init, %s\n", + safe_strerror (errno)); + exit (1); + } + + if ( cap_clear (zprivs_state.caps) ) + { + fprintf (stderr, "privs_init: failed to cap_clear, %s\n", + safe_strerror (errno)); + exit (1); + } + + /* set permitted caps */ + cap_set_flag(zprivs_state.caps, CAP_PERMITTED, + zprivs_state.syscaps_p->num, + zprivs_state.syscaps_p->caps, + CAP_SET); + + /* set inheritable caps, if any */ + if (zprivs_state.syscaps_i && zprivs_state.syscaps_i->num) + { + cap_set_flag(zprivs_state.caps, CAP_INHERITABLE, + zprivs_state.syscaps_i->num, + zprivs_state.syscaps_i->caps, + CAP_SET); + } + + /* apply caps. CAP_EFFECTIVE is cleared. we'll raise the caps as + * and when, and only when, they are needed. + */ + if ( cap_set_proc (zprivs_state.caps) ) + { + 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); + } + + /* set methods for the caller to use */ + zprivs->change = zprivs_change_caps; + zprivs->current_state = zprivs_state_caps; +} + +static void +zprivs_caps_terminate (void) +{ + /* clear all capabilities */ + if (zprivs_state.caps) + cap_clear (zprivs_state.caps); + + /* and boom, capabilities are gone forever */ + if ( cap_set_proc (zprivs_state.caps) ) + { + fprintf (stderr, "privs_terminate: cap_set_proc failed, %s", + safe_strerror (errno) ); + exit (1); + } + + /* free up private state */ + if (zprivs_state.syscaps_p->num) + { + XFREE (MTYPE_PRIVS, zprivs_state.syscaps_p->caps); + XFREE (MTYPE_PRIVS, zprivs_state.syscaps_p); + } + + if (zprivs_state.syscaps_i && zprivs_state.syscaps_i->num) + { + XFREE (MTYPE_PRIVS, zprivs_state.syscaps_i->caps); + XFREE (MTYPE_PRIVS, zprivs_state.syscaps_i); + } + + cap_free (zprivs_state.caps); +} +#elif defined (HAVE_SOLARIS_CAPABILITIES) /* !HAVE_LCAPS */ + +/* Solaris specific capability/privilege methods + * + * Resources: + * - the 'privileges' man page + * - http://cvs.opensolaris.org + * - 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) +{ + pset_t *syscaps; + int i, j = 0; + + if ((syscaps = priv_allocset()) == NULL) + { + fprintf (stderr, "%s: could not allocate syscaps!\n", __func__); + exit (1); + } + + priv_emptyset (syscaps); + + for (i=0; i < num; i++) + for (j = 0; j < cap_map[zcaps[i]].num; j++) + priv_addset (syscaps, cap_map[zcaps[i]].system_caps[j]); + + return syscaps; +} + +/* callback exported to users to RAISE and LOWER effective privileges + * from nothing to the given permitted set and back down + */ +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 as our working effective set + * to lower: copy regular effective set stored in zprivs_state.caps + */ + if (op == ZPRIVS_RAISE) + privset = zprivs_state.syscaps_p; + else if (op == ZPRIVS_LOWER) + privset = zprivs_state.caps; + else + return -1; + + if (setppriv (PRIV_SET, PRIV_EFFECTIVE, privset) != 0) + return -1; + + return 0; +} + +/* Retrieve current privilege state, is it RAISED or LOWERED? */ +zebra_privs_current_t +zprivs_state_caps (void) +{ + zebra_privs_current_t result; + pset_t *effective; + + if ( (effective = priv_allocset()) == NULL) + { + fprintf (stderr, "%s: failed to get priv_allocset! %s\n", __func__, + safe_strerror (errno)); + return ZPRIVS_UNKNOWN; + } + + if (getppriv (PRIV_EFFECTIVE, effective)) + { + fprintf (stderr, "%s: failed to get state! %s\n", __func__, + safe_strerror (errno)); + result = ZPRIVS_UNKNOWN; + } + else + { + 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_UNKNOWN; + } + + priv_freeset (effective); + return result; +} + +static void +zprivs_caps_init (struct zebra_privs_t *zprivs) +{ + pset_t *basic; + pset_t *minimal; + + /* the specified sets */ + zprivs_state.syscaps_p = zcaps2sys (zprivs->caps_p, zprivs->cap_num_p); + zprivs_state.syscaps_i = zcaps2sys (zprivs->caps_i, zprivs->cap_num_i); + + /* nonsensical to have gotten here but not have capabilities */ + if (!zprivs_state.syscaps_p) + { + fprintf (stderr, "%s: capabilities enabled, " + "but no valid capabilities supplied\n", + __func__); + } + + /* We retain the basic set in our permitted set, as Linux has no + * equivalent. The basic set on Linux hence is implicit, always + * there. + */ + if ((basic = priv_str_to_set("basic", ",", NULL)) == NULL) + { + fprintf (stderr, "%s: couldn't get basic set!\n", __func__); + exit (1); + } + + /* Add the basic set to the permitted set */ + priv_union (basic, zprivs_state.syscaps_p); + priv_freeset (basic); + + /* Hey kernel, we know about privileges! + * this isn't strictly required, use of setppriv should have same effect + */ + if (setpflags (PRIV_AWARE, 1)) + { + fprintf (stderr, "%s: error setting PRIV_AWARE!, %s\n", __func__, + safe_strerror (errno) ); + exit (1); + } + + /* need either valid or empty sets for both p and i.. */ + assert (zprivs_state.syscaps_i && zprivs_state.syscaps_p); + + /* we have caps, we have no need to ever change back the original user + * change real, effective and saved to the specified user. + */ + if (zprivs_state.zuid) + { + if ( setreuid (zprivs_state.zuid, zprivs_state.zuid) ) + { + fprintf (stderr, "%s: could not setreuid, %s\n", + __func__, safe_strerror (errno)); + exit (1); + } + } + + /* set the permitted set */ + if (setppriv (PRIV_SET, PRIV_PERMITTED, zprivs_state.syscaps_p)) + { + fprintf (stderr, "%s: error setting permitted set!, %s\n", __func__, + safe_strerror (errno) ); + exit (1); + } + + /* set the inheritable set */ + if (setppriv (PRIV_SET, PRIV_INHERITABLE, zprivs_state.syscaps_i)) + { + fprintf (stderr, "%s: error setting inheritable set!, %s\n", __func__, + safe_strerror (errno) ); + exit (1); + } + + /* 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 the minimal set as our working-storage privset */ + zprivs_state.caps = minimal; + + /* set methods for the caller to use */ + zprivs->change = zprivs_change_caps; + zprivs->current_state = zprivs_state_caps; +} + +static void +zprivs_caps_terminate (void) +{ + assert (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); + + /* free up private state */ + if (zprivs_state.syscaps_p) + priv_freeset (zprivs_state.syscaps_p); + if (zprivs_state.syscaps_i) + priv_freeset (zprivs_state.syscaps_i); + + priv_freeset (zprivs_state.caps); +} +#else /* !HAVE_LCAPS && ! HAVE_SOLARIS_CAPABILITIES */ +#error "Neither Solaris nor Linux capabilities, dazed and confused..." +#endif /* HAVE_LCAPS */ +#endif /* HAVE_CAPABILITIES */ + +int +zprivs_change_uid (zebra_privs_ops_t op) +{ + + if (op == ZPRIVS_RAISE) + return seteuid (zprivs_state.zsuid); + else if (op == ZPRIVS_LOWER) + return seteuid (zprivs_state.zuid); + else + return -1; +} + +zebra_privs_current_t +zprivs_state_uid (void) +{ + return ( (zprivs_state.zuid == geteuid()) ? ZPRIVS_LOWERED : ZPRIVS_RAISED); +} + +int +zprivs_change_null (zebra_privs_ops_t op) +{ + return 0; +} + +zebra_privs_current_t +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) + { + fprintf (stderr, "zprivs_init: called with NULL arg!\n"); + exit (1); + } + + /* NULL privs */ + if (! (zprivs->user || zprivs->group + || zprivs->cap_num_p || zprivs->cap_num_i) ) + { + zprivs->change = zprivs_change_null; + zprivs->current_state = zprivs_state_null; + return; + } + + if (zprivs->user) + { + 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; + + for ( i = 0; i < ngroups; i++ ) + if ( groups[i] == zprivs_state.vtygrp ) + { + found++; + break; + } + + if (!found) + { + 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 + { + fprintf (stderr, "privs_init: could not lookup vty group %s\n", + zprivs->vty_group); + exit (1); + } + } + + if (ngroups) + { + if ( setgroups (ngroups, groups) ) + { + 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) ) + { + fprintf (stderr, "zprivs_init: could not setregid, %s\n", + safe_strerror (errno) ); + exit (1); + } + } + +#ifdef HAVE_CAPABILITIES + zprivs_caps_init (zprivs); +#else /* !HAVE_CAPABILITIES */ + /* we dont have caps. we'll need to maintain rid and saved uid + * and change euid back to saved uid (who we presume has all neccessary + * privileges) whenever we are asked to raise our privileges. + * + * This is not worth that much security wise, but all we can do. + */ + zprivs_state.zsuid = geteuid(); + if ( zprivs_state.zuid ) + { + if ( setreuid (-1, zprivs_state.zuid) ) + { + fprintf (stderr, "privs_init (uid): could not setreuid, %s\n", + safe_strerror (errno)); + exit (1); + } + } + + zprivs->change = zprivs_change_uid; + zprivs->current_state = zprivs_state_uid; +#endif /* HAVE_CAPABILITIES */ +} + +void +zprivs_terminate (struct zebra_privs_t *zprivs) +{ + if (!zprivs) + { + fprintf (stderr, "%s: no privs struct given, terminating", __func__); + exit (0); + } + +#ifdef HAVE_CAPABILITIES + zprivs_caps_terminate(); +#else /* !HAVE_CAPABILITIES */ + if (zprivs_state.zuid) + { + if ( setreuid (zprivs_state.zuid, zprivs_state.zuid) ) + { + fprintf (stderr, "privs_terminate: could not setreuid, %s", + safe_strerror (errno) ); + exit (1); + } + } +#endif /* HAVE_LCAPS */ + + zprivs->change = zprivs_change_null; + zprivs->current_state = zprivs_state_null; + zprivs_null_state = ZPRIVS_LOWERED; + return; +} + +void +zprivs_get_ids(struct zprivs_ids_t *ids) +{ + + ids->uid_priv = getuid(); + (zprivs_state.zuid) ? (ids->uid_normal = zprivs_state.zuid) + : (ids->uid_normal = -1); + (zprivs_state.zgid) ? (ids->gid_normal = zprivs_state.zgid) + : (ids->gid_normal = -1); + (zprivs_state.vtygrp) ? (ids->gid_vty = zprivs_state.vtygrp) + : (ids->gid_vty = -1); + + return; +} diff --git a/lib/privs.h b/lib/privs.h new file mode 100644 index 0000000..46d614e --- /dev/null +++ b/lib/privs.h @@ -0,0 +1,90 @@ +/* + * Zebra privileges header. + * + * Copyright (C) 2003 Paul Jakma. + * + * 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_PRIVS_H +#define _ZEBRA_PRIVS_H + +/* list of zebra capabilities */ +typedef enum +{ + ZCAP_SETID, + ZCAP_BIND, + ZCAP_NET_ADMIN, + ZCAP_SYS_ADMIN, + ZCAP_NET_RAW, + ZCAP_CHROOT, + ZCAP_NICE, + ZCAP_PTRACE, + ZCAP_DAC_OVERRIDE, + ZCAP_READ_SEARCH, + ZCAP_FOWNER, + ZCAP_MAX +} zebra_capabilities_t; + +typedef enum +{ + ZPRIVS_LOWERED, + ZPRIVS_RAISED, + ZPRIVS_UNKNOWN, +} zebra_privs_current_t; + +typedef enum +{ + ZPRIVS_RAISE, + ZPRIVS_LOWER, +} zebra_privs_ops_t; + +struct zebra_privs_t +{ + zebra_capabilities_t *caps_p; /* caps required for operation */ + zebra_capabilities_t *caps_i; /* caps to allow inheritance of */ + int cap_num_p; /* number of caps in arrays */ + int cap_num_i; + const char *user; /* user and group to run as */ + const char *group; + const char *vty_group; /* group to chown vty socket to */ + /* methods */ + int + (*change) (zebra_privs_ops_t); /* change privileges, 0 on success */ + zebra_privs_current_t + (*current_state) (void); /* current privilege state */ +}; + +struct zprivs_ids_t +{ + /* -1 is undefined */ + uid_t uid_priv; /* privileged uid */ + uid_t uid_normal; /* normal uid */ + gid_t gid_priv; /* privileged uid */ + gid_t gid_normal; /* normal uid */ + gid_t gid_vty; /* vty gid */ +}; + + /* initialise zebra privileges */ +extern void zprivs_init (struct zebra_privs_t *zprivs); + /* drop all and terminate privileges */ +extern void zprivs_terminate (struct zebra_privs_t *); + /* query for runtime uid's and gid's, eg vty needs this */ +extern void zprivs_get_ids(struct zprivs_ids_t *); + +#endif /* _ZEBRA_PRIVS_H */ diff --git a/lib/queue.h b/lib/queue.h new file mode 100644 index 0000000..48b363e --- /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 new file mode 100644 index 0000000..4cee464 --- /dev/null +++ b/lib/regex-gnu.h @@ -0,0 +1,542 @@ +/* Definitions for data structures and routines for the regular + expression library, version 0.12. + Copyright (C) 1985,89,90,91,92,93,95,96,97,98 Free Software Foundation, Inc. + + This file is part of the GNU C Library. Its master source is NOT part of + the C library, however. The master source lives in /gd/gnu/lib. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifndef _REGEX_H +#define _REGEX_H 1 + +/* Allow the use in C++ code. */ +#ifdef __cplusplus +extern "C" { +#endif + +/* POSIX says that must be included (by the caller) before + . */ + +#if !defined _POSIX_C_SOURCE && !defined _POSIX_SOURCE && defined VMS +/* VMS doesn't have `size_t' in , even though POSIX says it + should be there. */ +# include +#endif + +/* The following two types have to be signed and unsigned integer type + wide enough to hold a value of a pointer. For most ANSI compilers + ptrdiff_t and size_t should be likely OK. Still size of these two + types is 2 for Microsoft C. Ugh... */ +typedef long int s_reg_t; +typedef unsigned long int active_reg_t; + +/* The following bits are used to determine the regexp syntax we + recognize. The set/not-set meanings are chosen so that Emacs syntax + remains the value 0. The bits are given in alphabetical order, and + the definitions shifted by one from the previous bit; thus, when we + add or remove a bit, only one other definition need change. */ +typedef unsigned long int reg_syntax_t; + +/* If this bit is not set, then \ inside a bracket expression is literal. + If set, then such a \ quotes the following character. */ +#define RE_BACKSLASH_ESCAPE_IN_LISTS ((unsigned long int) 1) + +/* If this bit is not set, then + and ? are operators, and \+ and \? are + literals. + If set, then \+ and \? are operators and + and ? are literals. */ +#define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1) + +/* If this bit is set, then character classes are supported. They are: + [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:], + [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:]. + If not set, then character classes are not supported. */ +#define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1) + +/* If this bit is set, then ^ and $ are always anchors (outside bracket + expressions, of course). + If this bit is not set, then it depends: + ^ is an anchor if it is at the beginning of a regular + expression or after an open-group or an alternation operator; + $ is an anchor if it is at the end of a regular expression, or + before a close-group or an alternation operator. + + This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because + POSIX draft 11.2 says that * etc. in leading positions is undefined. + We already implemented a previous draft which made those constructs + invalid, though, so we haven't changed the code back. */ +#define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1) + +/* If this bit is set, then special characters are always special + regardless of where they are in the pattern. + If this bit is not set, then special characters are special only in + some contexts; otherwise they are ordinary. Specifically, + * + ? and intervals are only special when not after the beginning, + open-group, or alternation operator. */ +#define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1) + +/* If this bit is set, then *, +, ?, and { cannot be first in an re or + immediately after an alternation or begin-group operator. */ +#define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1) + +/* If this bit is set, then . matches newline. + If not set, then it doesn't. */ +#define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1) + +/* If this bit is set, then . doesn't match NUL. + If not set, then it does. */ +#define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1) + +/* If this bit is set, nonmatching lists [^...] do not match newline. + If not set, they do. */ +#define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1) + +/* If this bit is set, either \{...\} or {...} defines an + interval, depending on RE_NO_BK_BRACES. + If not set, \{, \}, {, and } are literals. */ +#define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1) + +/* If this bit is set, +, ? and | aren't recognized as operators. + If not set, they are. */ +#define RE_LIMITED_OPS (RE_INTERVALS << 1) + +/* If this bit is set, newline is an alternation operator. + If not set, newline is literal. */ +#define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1) + +/* If this bit is set, then `{...}' defines an interval, and \{ and \} + are literals. + If not set, then `\{...\}' defines an interval. */ +#define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1) + +/* If this bit is set, (...) defines a group, and \( and \) are literals. + If not set, \(...\) defines a group, and ( and ) are literals. */ +#define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1) + +/* If this bit is set, then \ matches . + If not set, then \ is a back-reference. */ +#define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1) + +/* If this bit is set, then | is an alternation operator, and \| is literal. + If not set, then \| is an alternation operator, and | is literal. */ +#define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1) + +/* If this bit is set, then an ending range point collating higher + than the starting range point, as in [z-a], is invalid. + If not set, then when ending range point collates higher than the + starting range point, the range is ignored. */ +#define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1) + +/* If this bit is set, then an unmatched ) is ordinary. + If not set, then an unmatched ) is invalid. */ +#define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1) + +/* If this bit is set, succeed as soon as we match the whole pattern, + without further backtracking. */ +#define RE_NO_POSIX_BACKTRACKING (RE_UNMATCHED_RIGHT_PAREN_ORD << 1) + +/* If this bit is set, do not process the GNU regex operators. + If not set, then the GNU regex operators are recognized. */ +#define RE_NO_GNU_OPS (RE_NO_POSIX_BACKTRACKING << 1) + +/* If this bit is set, turn on internal regex debugging. + If not set, and debugging was on, turn it off. + This only works if regex.c is compiled -DDEBUG. + We define this bit always, so that all that's needed to turn on + debugging is to recompile regex.c; the calling code can always have + this bit set, and it won't affect anything in the normal case. */ +#define RE_DEBUG (RE_NO_GNU_OPS << 1) + +/* This global variable defines the particular regexp syntax to use (for + some interfaces). When a regexp is compiled, the syntax used is + 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!) */ +/* [[[begin syntaxes]]] */ +#define RE_SYNTAX_EMACS 0 + +#define RE_SYNTAX_AWK \ + (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL \ + | RE_NO_BK_PARENS | RE_NO_BK_REFS \ + | RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES \ + | RE_DOT_NEWLINE | RE_CONTEXT_INDEP_ANCHORS \ + | RE_UNMATCHED_RIGHT_PAREN_ORD | RE_NO_GNU_OPS) + +#define RE_SYNTAX_GNU_AWK \ + ((RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DEBUG) \ + & ~(RE_DOT_NOT_NULL | RE_INTERVALS | RE_CONTEXT_INDEP_OPS)) + +#define RE_SYNTAX_POSIX_AWK \ + (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS \ + | RE_INTERVALS | RE_NO_GNU_OPS) + +#define RE_SYNTAX_GREP \ + (RE_BK_PLUS_QM | RE_CHAR_CLASSES \ + | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS \ + | RE_NEWLINE_ALT) + +#define RE_SYNTAX_EGREP \ + (RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE \ + | RE_NEWLINE_ALT | RE_NO_BK_PARENS \ + | RE_NO_BK_VBAR) + +#define RE_SYNTAX_POSIX_EGREP \ + (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES) + +/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff. */ +#define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC + +#define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC + +/* Syntax bits common to both basic and extended POSIX regex syntax. */ +#define _RE_SYNTAX_POSIX_COMMON \ + (RE_CHAR_CLASSES | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \ + | RE_INTERVALS | RE_NO_EMPTY_RANGES) + +#define RE_SYNTAX_POSIX_BASIC \ + (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM) + +/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes + RE_LIMITED_OPS, i.e., \? \+ \| are not recognized. Actually, this + isn't minimal, since other operators, such as \`, aren't disabled. */ +#define RE_SYNTAX_POSIX_MINIMAL_BASIC \ + (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS) + +#define RE_SYNTAX_POSIX_EXTENDED \ + (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES \ + | RE_NO_BK_PARENS | RE_NO_BK_VBAR \ + | RE_UNMATCHED_RIGHT_PAREN_ORD) + +/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INVALID_OPS + replaces RE_CONTEXT_INDEP_OPS and RE_NO_BK_REFS is added. */ +#define RE_SYNTAX_POSIX_MINIMAL_EXTENDED \ + (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES \ + | 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. */ +#ifdef RE_DUP_MAX +# undef RE_DUP_MAX +#endif +/* If sizeof(int) == 2, then ((1 << 15) - 1) overflows. */ +#define RE_DUP_MAX (0x7fff) + + +/* POSIX `cflags' bits (i.e., information for `regcomp'). */ + +/* If this bit is set, then use extended regular expression syntax. + If not set, then use basic regular expression syntax. */ +#define REG_EXTENDED 1 + +/* If this bit is set, then ignore case when matching. + If not set, then case is significant. */ +#define REG_ICASE (REG_EXTENDED << 1) + +/* If this bit is set, then anchors do not match at newline + characters in the string. + If not set, then anchors do match at newlines. */ +#define REG_NEWLINE (REG_ICASE << 1) + +/* If this bit is set, then report only success or fail in regexec. + If not set, then returns differ between not matching and errors. */ +#define REG_NOSUB (REG_NEWLINE << 1) + + +/* POSIX `eflags' bits (i.e., information for regexec). */ + +/* If this bit is set, then the beginning-of-line operator doesn't match + the beginning of the string (presumably because it's not the + beginning of a line). + If not set, then the beginning-of-line operator does match the + beginning of the string. */ +#define REG_NOTBOL 1 + +/* Like REG_NOTBOL, except for the end-of-line. */ +#define REG_NOTEOL (1 << 1) + + +/* If any error codes are removed, changed, or added, update the + `re_error_msg' table in regex.c. */ +typedef enum +{ +#ifdef _XOPEN_SOURCE + REG_ENOSYS = -1, /* This will never happen for this implementation. */ +#endif + + REG_NOERROR = 0, /* Success. */ + REG_NOMATCH, /* Didn't find a match (for regexec). */ + + /* POSIX regcomp return error codes. (In the order listed in the + standard.) */ + REG_BADPAT, /* Invalid pattern. */ + REG_ECOLLATE, /* Not implemented. */ + REG_ECTYPE, /* Invalid character class name. */ + REG_EESCAPE, /* Trailing backslash. */ + REG_ESUBREG, /* Invalid back reference. */ + REG_EBRACK, /* Unmatched left bracket. */ + REG_EPAREN, /* Parenthesis imbalance. */ + REG_EBRACE, /* Unmatched \{. */ + REG_BADBR, /* Invalid contents of \{\}. */ + REG_ERANGE, /* Invalid range end. */ + REG_ESPACE, /* Ran out of memory. */ + REG_BADRPT, /* No preceding re for repetition op. */ + + /* Error codes we've added. */ + REG_EEND, /* Premature end. */ + 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 + compiled, the `re_nsub' field is available. All other fields are + private to the regex routines. */ + +#ifndef RE_TRANSLATE_TYPE +# define RE_TRANSLATE_TYPE char * +#endif + +struct re_pattern_buffer +{ +/* [[[begin pattern_buffer]]] */ + /* Space that holds the compiled pattern. It is declared as + `unsigned char *' because its elements are + sometimes used as array indexes. */ + unsigned char *buffer; + + /* Number of bytes to which `buffer' points. */ + unsigned long int allocated; + + /* Number of bytes actually used in `buffer'. */ + unsigned long int used; + + /* Syntax setting with which the pattern was compiled. */ + reg_syntax_t syntax; + + /* Pointer to a fastmap, if any, otherwise zero. re_search uses + the fastmap, if there is one, to skip over impossible + starting points for matches. */ + char *fastmap; + + /* Either a translate table to apply to all characters before + comparing them, or zero for no translation. The translation + is applied to a pattern when it is compiled and to a string + when it is matched. */ + RE_TRANSLATE_TYPE translate; + + /* Number of subexpressions found by the compiler. */ + size_t re_nsub; + + /* Zero if this pattern cannot match the empty string, one else. + Well, in truth it's used only in `re_search_2', to see + whether or not we should use the fastmap, so we don't set + this absolutely perfectly; see `re_compile_fastmap' (the + `duplicate' case). */ + unsigned can_be_null : 1; + + /* If REGS_UNALLOCATED, allocate space in the `regs' structure + for `max (RE_NREGS, re_nsub + 1)' groups. + If REGS_REALLOCATE, reallocate space if necessary. + If REGS_FIXED, use what's there. */ +#define REGS_UNALLOCATED 0 +#define REGS_REALLOCATE 1 +#define REGS_FIXED 2 + unsigned regs_allocated : 2; + + /* Set to zero when `regex_compile' compiles a pattern; set to one + by `re_compile_fastmap' if it updates the fastmap. */ + unsigned fastmap_accurate : 1; + + /* If set, `re_match_2' does not return information about + subexpressions. */ + unsigned no_sub : 1; + + /* If set, a beginning-of-line anchor doesn't match at the + beginning of the string. */ + unsigned not_bol : 1; + + /* Similarly for an end-of-line anchor. */ + unsigned not_eol : 1; + + /* If true, an anchor at a newline matches. */ + unsigned newline_anchor : 1; + +/* [[[end pattern_buffer]]] */ +}; + +typedef struct re_pattern_buffer regex_t; + +/* Type for byte offsets within the string. POSIX mandates this. */ +typedef int regoff_t; + + +/* This is the structure we store register match data in. See + regex.texinfo for a full description of what registers match. */ +struct re_registers +{ + unsigned num_regs; + regoff_t *start; + regoff_t *end; +}; + + +/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer, + `re_match_2' returns information about at least this many registers + the first time a `regs' structure is passed. */ +#ifndef RE_NREGS +# define RE_NREGS 30 +#endif + + +/* POSIX specification for registers. Aside from the different names than + `re_registers', POSIX uses an array of structures, instead of a + structure of arrays. */ +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 + prototype (if we are ANSI), and once without (if we aren't) -- we + use the following macro to declare argument types. This + unfortunately clutters up the declarations a bit, but I think it's + worth it. */ + +#if __STDC__ + +# define _RE_ARGS(args) args + +#else /* not __STDC__ */ + +# define _RE_ARGS(args) () + +#endif /* not __STDC__ */ + +/* Sets the current default syntax to SYNTAX, and return the old syntax. + You can also simply assign to the `re_syntax_options' variable. */ +extern reg_syntax_t re_set_syntax _RE_ARGS ((reg_syntax_t syntax)); + +/* Compile the regular expression PATTERN, with length LENGTH + and syntax given by the global `re_syntax_options', into the buffer + BUFFER. Return NULL if successful, and an error string if not. */ +extern const char *re_compile_pattern + _RE_ARGS ((const char *pattern, size_t length, + struct re_pattern_buffer *buffer)); + + +/* Compile a fastmap for the compiled pattern in BUFFER; used to + accelerate searches. Return 0 if successful and -2 if was an + internal error. */ +extern int re_compile_fastmap _RE_ARGS ((struct re_pattern_buffer *buffer)); + + +/* Search in the string STRING (with length LENGTH) for the pattern + compiled into BUFFER. Start searching at position START, for RANGE + characters. Return the starting position of the match, -1 for no + match, or -2 for an internal error. Also return register + information in REGS (if REGS and BUFFER->no_sub are nonzero). */ +extern int re_search + _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string, + int length, int start, int range, struct re_registers *regs)); + + +/* Like `re_search', but search in the concatenation of STRING1 and + STRING2. Also, stop searching at index START + STOP. */ +extern int re_search_2 + _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1, + int length1, const char *string2, int length2, + int start, int range, struct re_registers *regs, int stop)); + + +/* Like `re_search', but return how many characters in STRING the regexp + in BUFFER matched, starting at position START. */ +extern int re_match + _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string, + int length, int start, struct re_registers *regs)); + + +/* Relates to `re_match' as `re_search_2' relates to `re_search'. */ +extern int re_match_2 + _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1, + int length1, const char *string2, int length2, + int start, struct re_registers *regs, int stop)); + + +/* Set REGS to hold NUM_REGS registers, storing them in STARTS and + ENDS. Subsequent matches using BUFFER and REGS will use this memory + for recording register information. STARTS and ENDS must be + allocated with malloc, and must each be at least `NUM_REGS * sizeof + (regoff_t)' bytes long. + + If NUM_REGS == 0, then subsequent matches should allocate their own + register data. + + Unless this function is called, the first search or match using + PATTERN_BUFFER will allocate its own register data, without + freeing the old data. */ +extern void re_set_registers + _RE_ARGS ((struct re_pattern_buffer *buffer, struct re_registers *regs, + unsigned num_regs, regoff_t *starts, regoff_t *ends)); + +#if defined _REGEX_RE_COMP || defined _LIBC +# ifndef _CRAY +/* 4.2 bsd compatibility. */ +extern char *re_comp _RE_ARGS ((const char *)); +extern int re_exec _RE_ARGS ((const char *)); +# endif +#endif + +/* POSIX compatibility. */ +extern int regcomp _RE_ARGS ((regex_t *__preg, const char *__pattern, + int __cflags)); + +extern int regexec _RE_ARGS ((const regex_t *__preg, + const char *__string, size_t __nmatch, + regmatch_t __pmatch[], int __eflags)); + +extern size_t regerror _RE_ARGS ((int __errcode, const regex_t *__preg, + char *__errbuf, size_t __errbuf_size)); + +extern void regfree _RE_ARGS ((regex_t *__preg)); + + +#ifdef __cplusplus +} +#endif /* C++ */ + +#endif /* regex.h */ + +/* +Local variables: +make-backup-files: t +version-control: t +trim-versions-without-asking: nil +End: +*/ diff --git a/lib/regex.c b/lib/regex.c new file mode 100644 index 0000000..122f447 --- /dev/null +++ b/lib/regex.c @@ -0,0 +1,5895 @@ +/* Extended regular expression matching and search library, + version 0.12. + (Implements POSIX draft P1003.2/D11.2, except for some of the + internationalization features.) + Copyright (C) 1993, 94, 95, 96, 97, 98, 99 Free Software Foundation, Inc. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* AIX requires this to be the first thing in the file. */ +#if defined _AIX && !defined REGEX_MALLOC + #pragma alloca +#endif + +#undef _GNU_SOURCE +#define _GNU_SOURCE + +#ifdef HAVE_CONFIG_H +# include +#endif +#ifdef _WIN32 +/* Windows does not provide unistd.h, which is required for abort() */ +#include +#endif /* _WIN32 */ + +#ifndef PARAMS +# if defined __GNUC__ || (defined __STDC__ && __STDC__) +# define PARAMS(args) args +# else +# define PARAMS(args) () +# endif /* GCC. */ +#endif /* Not PARAMS. */ + +#if defined STDC_HEADERS && !defined emacs +# include +#else +/* We need this for `regex.h', and perhaps for the Emacs include files. */ +# include +#endif + +#define WIDE_CHAR_SUPPORT (HAVE_WCTYPE_H && HAVE_WCHAR_H && HAVE_BTOWC) + +/* For platform which support the ISO C amendement 1 functionality we + support user defined character classes. */ +#if defined _LIBC || WIDE_CHAR_SUPPORT +/* Solaris 2.5 has a bug: must be included before . */ +# include +# include +#endif + +#ifdef _LIBC +/* We have to keep the namespace clean. */ +# define regfree(preg) __regfree (preg) +# define regexec(pr, st, nm, pm, ef) __regexec (pr, st, nm, pm, ef) +# define regcomp(preg, pattern, cflags) __regcomp (preg, pattern, cflags) +# define regerror(errcode, preg, errbuf, errbuf_size) \ + __regerror(errcode, preg, errbuf, errbuf_size) +# define re_set_registers(bu, re, nu, st, en) \ + __re_set_registers (bu, re, nu, st, en) +# define re_match_2(bufp, string1, size1, string2, size2, pos, regs, stop) \ + __re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) +# define re_match(bufp, string, size, pos, regs) \ + __re_match (bufp, string, size, pos, regs) +# define re_search(bufp, string, size, startpos, range, regs) \ + __re_search (bufp, string, size, startpos, range, regs) +# define re_compile_pattern(pattern, length, bufp) \ + __re_compile_pattern (pattern, length, bufp) +# define re_set_syntax(syntax) __re_set_syntax (syntax) +# define re_search_2(bufp, st1, s1, st2, s2, startpos, range, regs, stop) \ + __re_search_2 (bufp, st1, s1, st2, s2, startpos, range, regs, stop) +# define re_compile_fastmap(bufp) __re_compile_fastmap (bufp) + +#define btowc __btowc +#endif + +/* This is for other GNU distributions with internationalized messages. */ +#if HAVE_LIBINTL_H || defined _LIBC +# include +#else +# define gettext(msgid) (msgid) +#endif + +#ifndef gettext_noop +/* This define is so xgettext can find the internationalizable + strings. */ +# define gettext_noop(String) String +#endif + +/* The `emacs' switch turns on certain matching commands + that make sense only in Emacs. */ +#ifdef emacs + +# include "lisp.h" +# include "buffer.h" +# include "syntax.h" + +#else /* not emacs */ + +/* If we are not linking with Emacs proper, + we can't use the relocating allocator + even if config.h says that we can. */ +# undef REL_ALLOC + +# if defined STDC_HEADERS || defined _LIBC +# include +# else +char *malloc (); +char *realloc (); +# endif + +/* When used in Emacs's lib-src, we need to get bzero and bcopy somehow. + If nothing else has been done, use the method below. */ +# ifdef INHIBIT_STRING_HEADER +# if !(defined HAVE_BZERO && defined HAVE_BCOPY) +# if !defined bzero && !defined bcopy +# undef INHIBIT_STRING_HEADER +# endif +# endif +# endif + +/* This is the normal way of making sure we have a bcopy and a bzero. + This is used in most programs--a few other programs avoid this + by defining INHIBIT_STRING_HEADER. */ +# ifndef INHIBIT_STRING_HEADER +# if defined HAVE_STRING_H || defined STDC_HEADERS || defined _LIBC +# include +# ifndef bzero +# ifndef _LIBC +# define bzero(s, n) (memset (s, '\0', n), (s)) +# else +# define bzero(s, n) __bzero (s, n) +# endif +# endif +# else +# include +# ifndef memcmp +# define memcmp(s1, s2, n) bcmp (s1, s2, n) +# endif +# ifndef memcpy +# define memcpy(d, s, n) (bcopy (s, d, n), (d)) +# endif +# endif +# endif + +/* Define the syntax stuff for \<, \>, etc. */ + +/* This must be nonzero for the wordchar and notwordchar pattern + commands in re_match_2. */ +# ifndef Sword +# define Sword 1 +# endif + +# ifdef SWITCH_ENUM_BUG +# define SWITCH_ENUM_CAST(x) ((int)(x)) +# else +# define SWITCH_ENUM_CAST(x) (x) +# endif + +/* How many characters in the character set. */ +# define CHAR_SET_SIZE 256 + +# ifdef SYNTAX_TABLE + +extern char *re_syntax_table; + +# else /* not SYNTAX_TABLE */ + +static char re_syntax_table[CHAR_SET_SIZE]; + +static void +init_syntax_once () +{ + register int c; + static int done; + + if (done) + return; + + memset (re_syntax_table, 0, sizeof re_syntax_table); + + for (c = 'a'; c <= 'z'; c++) + re_syntax_table[c] = Sword; + + for (c = 'A'; c <= 'Z'; c++) + re_syntax_table[c] = Sword; + + for (c = '0'; c <= '9'; c++) + re_syntax_table[c] = Sword; + + re_syntax_table['_'] = Sword; + + done = 1; +} + +# endif /* not SYNTAX_TABLE */ + +# define SYNTAX(c) re_syntax_table[c] + +#endif /* not emacs */ + +/* Get the interface, including the syntax bits. */ +#include + +/* isalpha etc. are used for the character classes. */ +#include + +/* Jim Meyering writes: + + "... Some ctype macros are valid only for character codes that + isascii says are ASCII (SGI's IRIX-4.0.5 is one such system --when + using /bin/cc or gcc but without giving an ansi option). So, all + ctype uses should be through macros like ISPRINT... If + STDC_HEADERS is defined, then autoconf has verified that the ctype + macros don't need to be guarded with references to isascii. ... + Defining isascii to 1 should let any compiler worth its salt + eliminate the && through constant folding." + Solaris defines some of these symbols so we must undefine them first. */ + +#undef ISASCII +#if defined STDC_HEADERS || (!defined isascii && !defined HAVE_ISASCII) +# define ISASCII(c) 1 +#else +# define ISASCII(c) isascii(c) +#endif + +#ifdef isblank +# define ISBLANK(c) (ISASCII (c) && isblank (c)) +#else +# define ISBLANK(c) ((c) == ' ' || (c) == '\t') +#endif +#ifdef isgraph +# define ISGRAPH(c) (ISASCII (c) && isgraph (c)) +#else +# define ISGRAPH(c) (ISASCII (c) && isprint (c) && !isspace (c)) +#endif + +#undef ISPRINT +#define ISPRINT(c) (ISASCII (c) && isprint (c)) +#define ISDIGIT(c) (ISASCII (c) && isdigit (c)) +#define ISALNUM(c) (ISASCII (c) && isalnum (c)) +#define ISALPHA(c) (ISASCII (c) && isalpha (c)) +#define ISCNTRL(c) (ISASCII (c) && iscntrl (c)) +#define ISLOWER(c) (ISASCII (c) && islower (c)) +#define ISPUNCT(c) (ISASCII (c) && ispunct (c)) +#define ISSPACE(c) (ISASCII (c) && isspace (c)) +#define ISUPPER(c) (ISASCII (c) && isupper (c)) +#define ISXDIGIT(c) (ISASCII (c) && isxdigit (c)) + +#ifdef _tolower +# define TOLOWER(c) _tolower(c) +#else +# define TOLOWER(c) tolower(c) +#endif + +#ifndef NULL +# define NULL (void *)0 +#endif + +/* We remove any previous definition of `SIGN_EXTEND_CHAR', + since ours (we hope) works properly with all combinations of + machines, compilers, `char' and `unsigned char' argument types. + (Per Bothner suggested the basic approach.) */ +#undef SIGN_EXTEND_CHAR +#if __STDC__ +# define SIGN_EXTEND_CHAR(c) ((signed char) (c)) +#else /* not __STDC__ */ +/* 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 + Emacs; also, malloc is slower and causes storage fragmentation. On + the other hand, malloc is more portable, and easier to debug. + + Because we sometimes use alloca, some routines have to be macros, + not functions -- `alloca'-allocated space disappears at the end of the + function it is called in. */ + +#ifdef REGEX_MALLOC + +# define REGEX_ALLOCATE malloc +# define REGEX_REALLOCATE(source, osize, nsize) realloc (source, nsize) +# define REGEX_FREE free + +#else /* not REGEX_MALLOC */ + +/* Emacs already defines alloca, sometimes. */ +# ifndef alloca + +/* Make alloca work the best possible way. */ +# ifdef __GNUC__ +# define alloca __builtin_alloca +# else /* not __GNUC__ */ +# if HAVE_ALLOCA_H +# include +# endif /* HAVE_ALLOCA_H */ +# endif /* not __GNUC__ */ + +# endif /* not alloca */ + +# define REGEX_ALLOCATE alloca + +/* Assumes a `char *destination' variable. */ +# define REGEX_REALLOCATE(source, osize, nsize) \ + (destination = (char *) alloca (nsize), \ + memcpy (destination, source, osize)) + +/* No need to do anything to free, after alloca. */ +# define REGEX_FREE(arg) ((void)0) /* Do nothing! But inhibit gcc warning. */ + +#endif /* not REGEX_MALLOC */ + +/* Define how to allocate the failure stack. */ + +#if defined REL_ALLOC && defined REGEX_MALLOC + +# define REGEX_ALLOCATE_STACK(size) \ + r_alloc (&failure_stack_ptr, (size)) +# define REGEX_REALLOCATE_STACK(source, osize, nsize) \ + r_re_alloc (&failure_stack_ptr, (nsize)) +# define REGEX_FREE_STACK(ptr) \ + r_alloc_free (&failure_stack_ptr) + +#else /* not using relocating allocator */ + +# ifdef REGEX_MALLOC + +# define REGEX_ALLOCATE_STACK malloc +# define REGEX_REALLOCATE_STACK(source, osize, nsize) realloc (source, nsize) +# define REGEX_FREE_STACK free + +# else /* not REGEX_MALLOC */ + +# define REGEX_ALLOCATE_STACK alloca + +# define REGEX_REALLOCATE_STACK(source, osize, nsize) \ + REGEX_REALLOCATE (source, osize, nsize) +/* No need to explicitly free anything. */ +# define REGEX_FREE_STACK(arg) + +# endif /* not REGEX_MALLOC */ +#endif /* not using relocating allocator */ + + +/* True if `size1' is non-NULL and PTR is pointing anywhere inside + `string1' or just past its end. This works if PTR is NULL, which is + a good thing. */ +#define FIRST_STRING_P(ptr) \ + (size1 && string1 <= (ptr) && (ptr) <= string1 + size1) + +/* (Re)Allocate N items of type T using malloc, or fail. */ +#define TALLOC(n, t) ((t *) malloc ((n) * sizeof (t))) +#define RETALLOC(addr, n, t) ((addr) = (t *) realloc (addr, (n) * sizeof (t))) +#define RETALLOC_IF(addr, n, t) \ + if (addr) RETALLOC((addr), (n), t); else (addr) = TALLOC ((n), t) +#define REGEX_TALLOC(n, t) ((t *) REGEX_ALLOCATE ((n) * sizeof (t))) + +#define BYTEWIDTH 8 /* In bits. */ + +#define STREQ(s1, s2) ((strcmp (s1, s2) == 0)) + +#undef MAX +#undef MIN +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +typedef char boolean; +#define false 0 +#define true 1 + +static int re_match_2_internal PARAMS ((struct re_pattern_buffer *bufp, + const char *string1, int size1, + const char *string2, int size2, + 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 + arguments. Zero bytes may appear in the compiled regular expression. */ + +typedef enum +{ + no_op = 0, + + /* Succeed right away--no more backtracking. */ + succeed, + + /* Followed by one byte giving n, then by n literal bytes. */ + exactn, + + /* Matches any (more or less) character. */ + anychar, + + /* Matches any one char belonging to specified set. First + following byte is number of bitmap bytes. Then come bytes + for a bitmap saying which chars are in. Bits in each byte + are ordered low-bit-first. A character is in the set if its + bit is 1. A character too large to have a bit in the map is + automatically not in the set. */ + charset, + + /* Same parameters as charset, but match any character that is + not one of those specified. */ + charset_not, + + /* Start remembering the text that is matched, for storing in a + register. Followed by one byte with the register number, in + the range 0 to one less than the pattern buffer's re_nsub + field. Then followed by one byte with the number of groups + inner to this one. (This last has to be part of the + start_memory only because we need it in the on_failure_jump + of re_match_2.) */ + start_memory, + + /* Stop remembering the text that is matched and store it in a + memory register. Followed by one byte with the register + number, in the range 0 to one less than `re_nsub' in the + pattern buffer, and one byte with the number of inner groups, + just like `start_memory'. (We need the number of inner + groups here because we don't have any easy way of finding the + corresponding start_memory when we're at a stop_memory.) */ + stop_memory, + + /* Match a duplicate of something remembered. Followed by one + byte containing the register number. */ + duplicate, + + /* Fail unless at beginning of line. */ + begline, + + /* Fail unless at end of line. */ + endline, + + /* Succeeds if at beginning of buffer (if emacs) or at beginning + of string to be matched (if not). */ + begbuf, + + /* Analogously, for end of buffer/string. */ + endbuf, + + /* Followed by two byte relative address to which to jump. */ + jump, + + /* Same as jump, but marks the end of an alternative. */ + jump_past_alt, + + /* Followed by two-byte relative address of place to resume at + in case of failure. */ + on_failure_jump, + + /* Like on_failure_jump, but pushes a placeholder instead of the + current string position when executed. */ + on_failure_keep_string_jump, + + /* Throw away latest failure point and then jump to following + two-byte relative address. */ + pop_failure_jump, + + /* Change to pop_failure_jump if know won't have to backtrack to + match; otherwise change to jump. This is used to jump + back to the beginning of a repeat. If what follows this jump + clearly won't match what the repeat does, such that we can be + sure that there is no use backtracking out of repetitions + already matched, then we change it to a pop_failure_jump. + Followed by two-byte address. */ + maybe_pop_jump, + + /* Jump to following two-byte address, and push a dummy failure + point. This failure point will be thrown away if an attempt + is made to use it for a failure. A `+' construct makes this + before the first repeat. Also used as an intermediary kind + of jump when compiling an alternative. */ + dummy_failure_jump, + + /* Push a dummy failure point and continue. Used at the end of + alternatives. */ + push_dummy_failure, + + /* Followed by two-byte relative address and two-byte number n. + After matching N times, jump to the address upon failure. */ + succeed_n, + + /* Followed by two-byte relative address, and two-byte number n. + Jump to the address N times, then fail. */ + jump_n, + + /* Set the following two-byte relative address to the + subsequent two-byte number. The address *includes* the two + bytes of number. */ + set_number_at, + + wordchar, /* Matches any word-constituent character. */ + notwordchar, /* Matches any char that is not a word-constituent. */ + + wordbeg, /* Succeeds if at word beginning. */ + wordend, /* Succeeds if at word end. */ + + wordbound, /* Succeeds if at a word boundary. */ + notwordbound /* Succeeds if not at a word boundary. */ + +#ifdef emacs + ,before_dot, /* Succeeds if before point. */ + at_dot, /* Succeeds if at point. */ + after_dot, /* Succeeds if after point. */ + + /* Matches any character whose syntax is specified. Followed by + a byte which contains a syntax code, e.g., Sword. */ + syntaxspec, + + /* Matches any character whose syntax is not that specified. */ + notsyntaxspec +#endif /* emacs */ +} re_opcode_t; + +/* Common operations on the compiled pattern. */ + +/* Store NUMBER in two contiguous bytes starting at DESTINATION. */ + +#define STORE_NUMBER(destination, number) \ + do { \ + (destination)[0] = (number) & 0377; \ + (destination)[1] = (number) >> 8; \ + } while (0) + +/* Same as STORE_NUMBER, except increment DESTINATION to + the byte after where the number is stored. Therefore, DESTINATION + must be an lvalue. */ + +#define STORE_NUMBER_AND_INCR(destination, number) \ + do { \ + STORE_NUMBER (destination, number); \ + (destination) += 2; \ + } while (0) + +/* Put into DESTINATION a number stored in two contiguous bytes starting + at SOURCE. */ + +#define EXTRACT_NUMBER(destination, source) \ + do { \ + (destination) = *(source) & 0377; \ + (destination) += SIGN_EXTEND_CHAR (*((source) + 1)) << 8; \ + } while (0) + +#ifdef DEBUG +static void extract_number _RE_ARGS ((int *dest, unsigned char *source)); +static void +extract_number (dest, source) + int *dest; + unsigned char *source; +{ + int temp = SIGN_EXTEND_CHAR (*(source + 1)); + *dest = *source & 0377; + *dest += temp << 8; +} + +# ifndef EXTRACT_MACROS /* To debug the macros. */ +# undef EXTRACT_NUMBER +# define EXTRACT_NUMBER(dest, src) extract_number (&dest, src) +# endif /* not EXTRACT_MACROS */ + +#endif /* DEBUG */ + +/* Same as EXTRACT_NUMBER, except increment SOURCE to after the number. + SOURCE must be an lvalue. */ + +#define EXTRACT_NUMBER_AND_INCR(destination, source) \ + do { \ + EXTRACT_NUMBER (destination, source); \ + (source) += 2; \ + } while (0) + +#ifdef DEBUG +static void extract_number_and_incr _RE_ARGS ((int *destination, + unsigned char **source)); +static void +extract_number_and_incr (destination, source) + int *destination; + unsigned char **source; +{ + extract_number (destination, *source); + *source += 2; +} + +# ifndef EXTRACT_MACROS +# undef EXTRACT_NUMBER_AND_INCR +# define EXTRACT_NUMBER_AND_INCR(dest, src) \ + extract_number_and_incr (&dest, &src) +# 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 + interactively. And if linked with the main program in `main.c' and + the other test files, you can run the already-written tests. */ + +#ifdef DEBUG + +/* We use standard I/O for debugging. */ +# include + +/* It is useful to test things that ``must'' be true when debugging. */ +# include "zassert.h" + +static int debug; + +# define DEBUG_STATEMENT(e) e +# define DEBUG_PRINT1(x) if (debug) printf (x) +# define DEBUG_PRINT2(x1, x2) if (debug) printf (x1, x2) +# define DEBUG_PRINT3(x1, x2, x3) if (debug) printf (x1, x2, x3) +# define DEBUG_PRINT4(x1, x2, x3, x4) if (debug) printf (x1, x2, x3, x4) +# define DEBUG_PRINT_COMPILED_PATTERN(p, s, e) \ + if (debug) print_partial_compiled_pattern (s, e) +# define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2) \ + if (debug) print_double_string (w, s1, sz1, s2, sz2) + + +/* Print the fastmap in human-readable form. */ + +void +print_fastmap (fastmap) + char *fastmap; +{ + unsigned was_a_range = 0; + unsigned i = 0; + + while (i < (1 << BYTEWIDTH)) + { + if (fastmap[i++]) + { + was_a_range = 0; + putchar (i - 1); + while (i < (1 << BYTEWIDTH) && fastmap[i]) + { + was_a_range = 1; + i++; + } + if (was_a_range) + { + printf ("-"); + putchar (i - 1); + } + } + } + putchar ('\n'); +} + + +/* Print a compiled pattern string in human-readable form, starting at + the START pointer into it and ending just before the pointer END. */ + +void +print_partial_compiled_pattern (start, end) + unsigned char *start; + unsigned char *end; +{ + int mcnt, mcnt2; + unsigned char *p1; + unsigned char *p = start; + unsigned char *pend = end; + + if (start == NULL) + { + printf ("(null)\n"); + return; + } + + /* Loop over pattern commands. */ + while (p < pend) + { + printf ("%d:\t", p - start); + + switch ((re_opcode_t) *p++) + { + case no_op: + printf ("/no_op"); + break; + + case exactn: + mcnt = *p++; + printf ("/exactn/%d", mcnt); + do + { + putchar ('/'); + putchar (*p++); + } + while (--mcnt); + break; + + case start_memory: + mcnt = *p++; + printf ("/start_memory/%d/%d", mcnt, *p++); + break; + + case stop_memory: + mcnt = *p++; + printf ("/stop_memory/%d/%d", mcnt, *p++); + break; + + case duplicate: + printf ("/duplicate/%d", *p++); + break; + + case anychar: + printf ("/anychar"); + break; + + case charset: + case charset_not: + { + register int c, last = -100; + register int in_range = 0; + + printf ("/charset [%s", + (re_opcode_t) *(p - 1) == charset_not ? "^" : ""); + + assert (p + *p < pend); + + for (c = 0; c < 256; c++) + if (c / 8 < *p + && (p[1 + (c/8)] & (1 << (c % 8)))) + { + /* Are we starting a range? */ + if (last + 1 == c && ! in_range) + { + putchar ('-'); + in_range = 1; + } + /* Have we broken a range? */ + else if (last + 1 != c && in_range) + { + putchar (last); + in_range = 0; + } + + if (! in_range) + putchar (c); + + last = c; + } + + if (in_range) + putchar (last); + + putchar (']'); + + p += 1 + *p; + } + break; + + case begline: + printf ("/begline"); + break; + + case endline: + printf ("/endline"); + break; + + case on_failure_jump: + extract_number_and_incr (&mcnt, &p); + printf ("/on_failure_jump to %d", p + mcnt - start); + break; + + case on_failure_keep_string_jump: + extract_number_and_incr (&mcnt, &p); + printf ("/on_failure_keep_string_jump to %d", p + mcnt - start); + break; + + case dummy_failure_jump: + extract_number_and_incr (&mcnt, &p); + printf ("/dummy_failure_jump to %d", p + mcnt - start); + break; + + case push_dummy_failure: + printf ("/push_dummy_failure"); + break; + + case maybe_pop_jump: + extract_number_and_incr (&mcnt, &p); + printf ("/maybe_pop_jump to %d", p + mcnt - start); + break; + + case pop_failure_jump: + extract_number_and_incr (&mcnt, &p); + printf ("/pop_failure_jump to %d", p + mcnt - start); + break; + + case jump_past_alt: + extract_number_and_incr (&mcnt, &p); + printf ("/jump_past_alt to %d", p + mcnt - start); + break; + + case jump: + extract_number_and_incr (&mcnt, &p); + printf ("/jump to %d", p + mcnt - start); + break; + + case succeed_n: + extract_number_and_incr (&mcnt, &p); + p1 = p + mcnt; + extract_number_and_incr (&mcnt2, &p); + printf ("/succeed_n to %d, %d times", p1 - start, mcnt2); + break; + + case jump_n: + extract_number_and_incr (&mcnt, &p); + p1 = p + mcnt; + extract_number_and_incr (&mcnt2, &p); + printf ("/jump_n to %d, %d times", p1 - start, mcnt2); + break; + + case set_number_at: + extract_number_and_incr (&mcnt, &p); + p1 = p + mcnt; + extract_number_and_incr (&mcnt2, &p); + printf ("/set_number_at location %d to %d", p1 - start, mcnt2); + break; + + case wordbound: + printf ("/wordbound"); + break; + + case notwordbound: + printf ("/notwordbound"); + break; + + case wordbeg: + printf ("/wordbeg"); + break; + + case wordend: + printf ("/wordend"); + +# ifdef emacs + case before_dot: + printf ("/before_dot"); + break; + + case at_dot: + printf ("/at_dot"); + break; + + case after_dot: + printf ("/after_dot"); + break; + + case syntaxspec: + printf ("/syntaxspec"); + mcnt = *p++; + printf ("/%d", mcnt); + break; + + case notsyntaxspec: + printf ("/notsyntaxspec"); + mcnt = *p++; + printf ("/%d", mcnt); + break; +# endif /* emacs */ + + case wordchar: + printf ("/wordchar"); + break; + + case notwordchar: + printf ("/notwordchar"); + break; + + case begbuf: + printf ("/begbuf"); + break; + + case endbuf: + printf ("/endbuf"); + break; + + default: + printf ("?%d", *(p-1)); + } + + putchar ('\n'); + } + + printf ("%d:\tend of pattern.\n", p - start); +} + + +void +print_compiled_pattern (bufp) + struct re_pattern_buffer *bufp; +{ + unsigned char *buffer = bufp->buffer; + + print_partial_compiled_pattern (buffer, buffer + bufp->used); + printf ("%ld bytes used/%ld bytes allocated.\n", + bufp->used, bufp->allocated); + + if (bufp->fastmap_accurate && bufp->fastmap) + { + printf ("fastmap: "); + print_fastmap (bufp->fastmap); + } + + printf ("re_nsub: %d\t", bufp->re_nsub); + printf ("regs_alloc: %d\t", bufp->regs_allocated); + printf ("can_be_null: %d\t", bufp->can_be_null); + printf ("newline_anchor: %d\n", bufp->newline_anchor); + printf ("no_sub: %d\t", bufp->no_sub); + printf ("not_bol: %d\t", bufp->not_bol); + printf ("not_eol: %d\t", bufp->not_eol); + printf ("syntax: %lx\n", bufp->syntax); + /* Perhaps we should print the translate table? */ +} + + +void +print_double_string (where, string1, size1, string2, size2) + const char *where; + const char *string1; + const char *string2; + int size1; + int size2; +{ + int this_char; + + if (where == NULL) + printf ("(null)"); + else + { + if (FIRST_STRING_P (where)) + { + for (this_char = where - string1; this_char < size1; this_char++) + putchar (string1[this_char]); + + where = string2; + } + + for (this_char = where - string2; this_char < size2; this_char++) + putchar (string2[this_char]); + } +} + +void +printchar (c) + int c; +{ + putc (c, stderr); +} + +#else /* not DEBUG */ + +# undef assert +# define assert(e) + +# define DEBUG_STATEMENT(e) +# define DEBUG_PRINT1(x) +# define DEBUG_PRINT2(x1, x2) +# define DEBUG_PRINT3(x1, x2, x3) +# define DEBUG_PRINT4(x1, x2, x3, x4) +# define DEBUG_PRINT_COMPILED_PATTERN(p, s, e) +# 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. */ +/* This has no initializer because initialized variables in Emacs + become read-only after dumping. */ +reg_syntax_t re_syntax_options; + + +/* Specify the precise syntax of regexps for compilation. This provides + for compatibility for various utilities which historically have + different, incompatible syntaxes. + + The argument SYNTAX is a bit mask comprised of the various bits + defined in regex.h. We return the old syntax. */ + +reg_syntax_t +re_set_syntax (syntax) + reg_syntax_t syntax; +{ + reg_syntax_t ret = re_syntax_options; + + re_syntax_options = syntax; +#ifdef DEBUG + if (syntax & RE_DEBUG) + debug = 1; + else if (debug) /* was on but now is not */ + debug = 0; +#endif /* DEBUG */ + return ret; +} +#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, + but why not be nice? */ + +static const char re_error_msgid[] = + { +#define REG_NOERROR_IDX 0 + gettext_noop ("Success") /* REG_NOERROR */ + "\0" +#define REG_NOMATCH_IDX (REG_NOERROR_IDX + sizeof "Success") + gettext_noop ("No match") /* REG_NOMATCH */ + "\0" +#define REG_BADPAT_IDX (REG_NOMATCH_IDX + sizeof "No match") + gettext_noop ("Invalid regular expression") /* REG_BADPAT */ + "\0" +#define REG_ECOLLATE_IDX (REG_BADPAT_IDX + sizeof "Invalid regular expression") + gettext_noop ("Invalid collation character") /* REG_ECOLLATE */ + "\0" +#define REG_ECTYPE_IDX (REG_ECOLLATE_IDX + sizeof "Invalid collation character") + gettext_noop ("Invalid character class name") /* REG_ECTYPE */ + "\0" +#define REG_EESCAPE_IDX (REG_ECTYPE_IDX + sizeof "Invalid character class name") + gettext_noop ("Trailing backslash") /* REG_EESCAPE */ + "\0" +#define REG_ESUBREG_IDX (REG_EESCAPE_IDX + sizeof "Trailing backslash") + gettext_noop ("Invalid back reference") /* REG_ESUBREG */ + "\0" +#define REG_EBRACK_IDX (REG_ESUBREG_IDX + sizeof "Invalid back reference") + gettext_noop ("Unmatched [ or [^") /* REG_EBRACK */ + "\0" +#define REG_EPAREN_IDX (REG_EBRACK_IDX + sizeof "Unmatched [ or [^") + gettext_noop ("Unmatched ( or \\(") /* REG_EPAREN */ + "\0" +#define REG_EBRACE_IDX (REG_EPAREN_IDX + sizeof "Unmatched ( or \\(") + gettext_noop ("Unmatched \\{") /* REG_EBRACE */ + "\0" +#define REG_BADBR_IDX (REG_EBRACE_IDX + sizeof "Unmatched \\{") + gettext_noop ("Invalid content of \\{\\}") /* REG_BADBR */ + "\0" +#define REG_ERANGE_IDX (REG_BADBR_IDX + sizeof "Invalid content of \\{\\}") + gettext_noop ("Invalid range end") /* REG_ERANGE */ + "\0" +#define REG_ESPACE_IDX (REG_ERANGE_IDX + sizeof "Invalid range end") + gettext_noop ("Memory exhausted") /* REG_ESPACE */ + "\0" +#define REG_BADRPT_IDX (REG_ESPACE_IDX + sizeof "Memory exhausted") + gettext_noop ("Invalid preceding regular expression") /* REG_BADRPT */ + "\0" +#define REG_EEND_IDX (REG_BADRPT_IDX + sizeof "Invalid preceding regular expression") + gettext_noop ("Premature end of regular expression") /* REG_EEND */ + "\0" +#define REG_ESIZE_IDX (REG_EEND_IDX + sizeof "Premature end of regular expression") + gettext_noop ("Regular expression too big") /* REG_ESIZE */ + "\0" +#define REG_ERPAREN_IDX (REG_ESIZE_IDX + sizeof "Regular expression too big") + gettext_noop ("Unmatched ) or \\)"), /* REG_ERPAREN */ + }; + +static const size_t re_error_msgid_idx[] = + { + REG_NOERROR_IDX, + REG_NOMATCH_IDX, + REG_BADPAT_IDX, + REG_ECOLLATE_IDX, + REG_ECTYPE_IDX, + REG_EESCAPE_IDX, + REG_ESUBREG_IDX, + REG_EBRACK_IDX, + REG_EPAREN_IDX, + REG_EBRACE_IDX, + REG_BADBR_IDX, + REG_ERANGE_IDX, + REG_ESPACE_IDX, + REG_BADRPT_IDX, + REG_EEND_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 + searching and matching functions should not call alloca. On some + systems, alloca is implemented in terms of malloc, and if we're + using the relocating allocator routines, then malloc could cause a + relocation, which might (if the strings being searched are in the + ralloc heap) shift the data out from underneath the regexp + routines. + + Here's another reason to avoid allocation: Emacs + processes input from X in a signal handler; processing X input may + call malloc; if input arrives while a matching routine is calling + malloc, then we're scrod. But Emacs can't just block input while + calling matching routines; then we don't notice interrupts when + they come in. So, Emacs blocks input around all regexp calls + except the matching calls, which it leaves unprotected, in the + faith that they will not malloc. */ + +/* Normally, this is fine. */ +#define MATCH_MAY_ALLOCATE + +/* When using GNU C, we are not REALLY using the C alloca, no matter + what config.h may say. So don't take precautions for it. */ +#ifdef __GNUC__ +# undef C_ALLOCA +#endif + +/* The match routines may not allocate if (1) they would do it with malloc + and (2) it's not safe for them to use malloc. + Note that if REL_ALLOC is defined, matching would not use malloc for the + failure stack, but we would still use it for the register vectors; + so REL_ALLOC should not affect this. */ +#if (defined C_ALLOCA || defined REGEX_MALLOC) && defined emacs +# 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. */ + + +/* Number of failure points for which to initially allocate space + when matching. If this number is exceeded, we allocate more + space, so it is not a hard limit. */ +#ifndef INIT_FAILURE_ALLOC +# define INIT_FAILURE_ALLOC 5 +#endif + +/* Roughly the maximum number of failure points on the stack. Would be + exactly that if always used MAX_FAILURE_ITEMS items each time we failed. + This is a variable only so users of regex can assign to it; we never + change it ourselves. */ + +#ifdef INT_IS_16BIT + +# if defined MATCH_MAY_ALLOCATE +/* 4400 was enough to cause a crash on Alpha OSF/1, + whose default stack limit is 2mb. */ +long int re_max_failures = 4000; +# else +long int re_max_failures = 2000; +# endif + +union fail_stack_elt +{ + unsigned char *pointer; + long int integer; +}; + +typedef union fail_stack_elt fail_stack_elt_t; + +typedef struct +{ + fail_stack_elt_t *stack; + unsigned long int size; + unsigned long int avail; /* Offset of next open position. */ +} fail_stack_type; + +#else /* not INT_IS_16BIT */ + +# if defined MATCH_MAY_ALLOCATE +/* 4400 was enough to cause a crash on Alpha OSF/1, + whose default stack limit is 2mb. */ +int re_max_failures = 20000; +# else +int re_max_failures = 2000; +# endif + +union fail_stack_elt +{ + unsigned char *pointer; + int integer; +}; + +typedef union fail_stack_elt fail_stack_elt_t; + +typedef struct +{ + fail_stack_elt_t *stack; + unsigned size; + unsigned avail; /* Offset of next open position. */ +} fail_stack_type; + +#endif /* INT_IS_16BIT */ + +#define FAIL_STACK_EMPTY() (fail_stack.avail == 0) +#define FAIL_STACK_PTR_EMPTY() (fail_stack_ptr->avail == 0) +#define FAIL_STACK_FULL() (fail_stack.avail == fail_stack.size) + + +/* Define macros to initialize and free the failure stack. + Do `return -2' if the alloc fails. */ + +#ifdef MATCH_MAY_ALLOCATE +# define INIT_FAIL_STACK() \ + do { \ + fail_stack.stack = (fail_stack_elt_t *) \ + REGEX_ALLOCATE_STACK (INIT_FAILURE_ALLOC * sizeof (fail_stack_elt_t)); \ + \ + if (fail_stack.stack == NULL) \ + return -2; \ + \ + fail_stack.size = INIT_FAILURE_ALLOC; \ + fail_stack.avail = 0; \ + } while (0) + +# define RESET_FAIL_STACK() REGEX_FREE_STACK (fail_stack.stack) +#else +# define INIT_FAIL_STACK() \ + do { \ + fail_stack.avail = 0; \ + } while (0) + +# define RESET_FAIL_STACK() +#endif + + +/* Double the size of FAIL_STACK, up to approximately `re_max_failures' items. + + Return 1 if succeeds, and 0 if either ran out of memory + allocating space for it or it was already too large. + + REGEX_REALLOCATE_STACK requires `destination' be declared. */ + +#define DOUBLE_FAIL_STACK(fail_stack) \ + ((fail_stack).size > (unsigned) (re_max_failures * MAX_FAILURE_ITEMS) \ + ? 0 \ + : ((fail_stack).stack = (fail_stack_elt_t *) \ + REGEX_REALLOCATE_STACK ((fail_stack).stack, \ + (fail_stack).size * sizeof (fail_stack_elt_t), \ + ((fail_stack).size << 1) * sizeof (fail_stack_elt_t)), \ + \ + (fail_stack).stack == NULL \ + ? 0 \ + : ((fail_stack).size <<= 1, \ + 1))) + + +/* Push pointer POINTER on FAIL_STACK. + Return 1 if was able to do so and 0 if ran out of memory allocating + space to do so. */ +#define PUSH_PATTERN_OP(POINTER, FAIL_STACK) \ + ((FAIL_STACK_FULL () \ + && !DOUBLE_FAIL_STACK (FAIL_STACK)) \ + ? 0 \ + : ((FAIL_STACK).stack[(FAIL_STACK).avail++].pointer = POINTER, \ + 1)) + +/* Push a pointer value onto the failure stack. + Assumes the variable `fail_stack'. Probably should only + be called from within `PUSH_FAILURE_POINT'. */ +#define PUSH_FAILURE_POINTER(item) \ + fail_stack.stack[fail_stack.avail++].pointer = (unsigned char *) (item) + +/* This pushes an integer-valued item onto the failure stack. + Assumes the variable `fail_stack'. Probably should only + be called from within `PUSH_FAILURE_POINT'. */ +#define PUSH_FAILURE_INT(item) \ + fail_stack.stack[fail_stack.avail++].integer = (item) + +/* Push a fail_stack_elt_t value onto the failure stack. + Assumes the variable `fail_stack'. Probably should only + be called from within `PUSH_FAILURE_POINT'. */ +#define PUSH_FAILURE_ELT(item) \ + fail_stack.stack[fail_stack.avail++] = (item) + +/* These three POP... operations complement the three PUSH... operations. + All assume that `fail_stack' is nonempty. */ +#define POP_FAILURE_POINTER() fail_stack.stack[--fail_stack.avail].pointer +#define POP_FAILURE_INT() fail_stack.stack[--fail_stack.avail].integer +#define POP_FAILURE_ELT() fail_stack.stack[--fail_stack.avail] + +/* Used to omit pushing failure point id's when we're not debugging. */ +#ifdef DEBUG +# define DEBUG_PUSH PUSH_FAILURE_INT +# define DEBUG_POP(item_addr) *(item_addr) = POP_FAILURE_INT () +#else +# define DEBUG_PUSH(item) +# define DEBUG_POP(item_addr) +#endif + + +/* Push the information about the state we will need + if we ever fail back to it. + + Requires variables fail_stack, regstart, regend, reg_info, and + num_regs_pushed be declared. DOUBLE_FAIL_STACK requires `destination' + be declared. + + Does `return FAILURE_CODE' if runs out of memory. */ + +#define PUSH_FAILURE_POINT(pattern_place, string_place, failure_code) \ + do { \ + char *destination; \ + /* Must be int, so when we don't save any registers, the arithmetic \ + of 0 + -1 isn't done as unsigned. */ \ + /* Can't be int, since there is not a shred of a guarantee that int \ + is wide enough to hold a value of something to which pointer can \ + be assigned */ \ + active_reg_t this_reg; \ + \ + DEBUG_STATEMENT (failure_id++); \ + DEBUG_STATEMENT (nfailure_points_pushed++); \ + DEBUG_PRINT2 ("\nPUSH_FAILURE_POINT #%u:\n", failure_id); \ + DEBUG_PRINT2 (" Before push, next avail: %d\n", (fail_stack).avail);\ + DEBUG_PRINT2 (" size: %d\n", (fail_stack).size);\ + \ + DEBUG_PRINT2 (" slots needed: %ld\n", NUM_FAILURE_ITEMS); \ + DEBUG_PRINT2 (" available: %d\n", REMAINING_AVAIL_SLOTS); \ + \ + /* Ensure we have enough space allocated for what we will push. */ \ + while (REMAINING_AVAIL_SLOTS < NUM_FAILURE_ITEMS) \ + { \ + if (!DOUBLE_FAIL_STACK (fail_stack)) \ + return failure_code; \ + \ + DEBUG_PRINT2 ("\n Doubled stack; size now: %d\n", \ + (fail_stack).size); \ + DEBUG_PRINT2 (" slots available: %d\n", REMAINING_AVAIL_SLOTS);\ + } \ + \ + /* Push the info, starting with the registers. */ \ + DEBUG_PRINT1 ("\n"); \ + \ + if (1) \ + for (this_reg = lowest_active_reg; this_reg <= highest_active_reg; \ + this_reg++) \ + { \ + DEBUG_PRINT2 (" Pushing reg: %lu\n", this_reg); \ + DEBUG_STATEMENT (num_regs_pushed++); \ + \ + DEBUG_PRINT2 (" start: %p\n", regstart[this_reg]); \ + PUSH_FAILURE_POINTER (regstart[this_reg]); \ + \ + DEBUG_PRINT2 (" end: %p\n", regend[this_reg]); \ + PUSH_FAILURE_POINTER (regend[this_reg]); \ + \ + DEBUG_PRINT2 (" info: %p\n ", \ + reg_info[this_reg].word.pointer); \ + DEBUG_PRINT2 (" match_null=%d", \ + REG_MATCH_NULL_STRING_P (reg_info[this_reg])); \ + DEBUG_PRINT2 (" active=%d", IS_ACTIVE (reg_info[this_reg])); \ + DEBUG_PRINT2 (" matched_something=%d", \ + MATCHED_SOMETHING (reg_info[this_reg])); \ + DEBUG_PRINT2 (" ever_matched=%d", \ + EVER_MATCHED_SOMETHING (reg_info[this_reg])); \ + DEBUG_PRINT1 ("\n"); \ + PUSH_FAILURE_ELT (reg_info[this_reg].word); \ + } \ + \ + DEBUG_PRINT2 (" Pushing low active reg: %ld\n", lowest_active_reg);\ + PUSH_FAILURE_INT (lowest_active_reg); \ + \ + DEBUG_PRINT2 (" Pushing high active reg: %ld\n", highest_active_reg);\ + PUSH_FAILURE_INT (highest_active_reg); \ + \ + DEBUG_PRINT2 (" Pushing pattern %p:\n", pattern_place); \ + DEBUG_PRINT_COMPILED_PATTERN (bufp, pattern_place, pend); \ + PUSH_FAILURE_POINTER (pattern_place); \ + \ + DEBUG_PRINT2 (" Pushing string %p: `", string_place); \ + DEBUG_PRINT_DOUBLE_STRING (string_place, string1, size1, string2, \ + size2); \ + DEBUG_PRINT1 ("'\n"); \ + PUSH_FAILURE_POINTER (string_place); \ + \ + DEBUG_PRINT2 (" Pushing failure id: %u\n", failure_id); \ + DEBUG_PUSH (failure_id); \ + } while (0) + +/* This is the number of items that are pushed and popped on the stack + for each register. */ +#define NUM_REG_ITEMS 3 + +/* Individual items aside from the registers. */ +#ifdef DEBUG +# define NUM_NONREG_ITEMS 5 /* Includes failure point id. */ +#else +# define NUM_NONREG_ITEMS 4 +#endif + +/* We push at most this many items on the stack. */ +/* We used to use (num_regs - 1), which is the number of registers + this regexp will save; but that was changed to 5 + to avoid stack overflow for a regexp with lots of parens. */ +#define MAX_FAILURE_ITEMS (5 * NUM_REG_ITEMS + NUM_NONREG_ITEMS) + +/* We actually push this many items. */ +#define NUM_FAILURE_ITEMS \ + (((0 \ + ? 0 : highest_active_reg - lowest_active_reg + 1) \ + * NUM_REG_ITEMS) \ + + NUM_NONREG_ITEMS) + +/* How many items can still be added to the stack without overflowing it. */ +#define REMAINING_AVAIL_SLOTS ((fail_stack).size - (fail_stack).avail) + + +/* Pops what PUSH_FAIL_STACK pushes. + + We restore into the parameters, all of which should be lvalues: + STR -- the saved data position. + PAT -- the saved pattern position. + LOW_REG, HIGH_REG -- the highest and lowest active registers. + REGSTART, REGEND -- arrays of string positions. + REG_INFO -- array of information about each subexpression. + + Also assumes the variables `fail_stack' and (if debugging), `bufp', + `pend', `string1', `size1', `string2', and `size2'. */ + +#define POP_FAILURE_POINT(str, pat, low_reg, high_reg, regstart, regend, reg_info)\ +{ \ + DEBUG_STATEMENT (unsigned failure_id;) \ + active_reg_t this_reg; \ + const unsigned char *string_temp; \ + \ + assert (!FAIL_STACK_EMPTY ()); \ + \ + /* Remove failure points and point to how many regs pushed. */ \ + DEBUG_PRINT1 ("POP_FAILURE_POINT:\n"); \ + DEBUG_PRINT2 (" Before pop, next avail: %d\n", fail_stack.avail); \ + DEBUG_PRINT2 (" size: %d\n", fail_stack.size); \ + \ + assert (fail_stack.avail >= NUM_NONREG_ITEMS); \ + \ + DEBUG_POP (&failure_id); \ + DEBUG_PRINT2 (" Popping failure id: %u\n", failure_id); \ + \ + /* If the saved string location is NULL, it came from an \ + on_failure_keep_string_jump opcode, and we want to throw away the \ + saved NULL, thus retaining our current position in the string. */ \ + string_temp = POP_FAILURE_POINTER (); \ + if (string_temp != NULL) \ + str = (const char *) string_temp; \ + \ + DEBUG_PRINT2 (" Popping string %p: `", str); \ + DEBUG_PRINT_DOUBLE_STRING (str, string1, size1, string2, size2); \ + DEBUG_PRINT1 ("'\n"); \ + \ + pat = (unsigned char *) POP_FAILURE_POINTER (); \ + DEBUG_PRINT2 (" Popping pattern %p:\n", pat); \ + DEBUG_PRINT_COMPILED_PATTERN (bufp, pat, pend); \ + \ + /* Restore register info. */ \ + high_reg = (active_reg_t) POP_FAILURE_INT (); \ + DEBUG_PRINT2 (" Popping high active reg: %ld\n", high_reg); \ + \ + low_reg = (active_reg_t) POP_FAILURE_INT (); \ + DEBUG_PRINT2 (" Popping low active reg: %ld\n", low_reg); \ + \ + if (1) \ + for (this_reg = high_reg; this_reg >= low_reg; this_reg--) \ + { \ + DEBUG_PRINT2 (" Popping reg: %ld\n", this_reg); \ + \ + reg_info[this_reg].word = POP_FAILURE_ELT (); \ + DEBUG_PRINT2 (" info: %p\n", \ + reg_info[this_reg].word.pointer); \ + \ + regend[this_reg] = (const char *) POP_FAILURE_POINTER (); \ + DEBUG_PRINT2 (" end: %p\n", regend[this_reg]); \ + \ + regstart[this_reg] = (const char *) POP_FAILURE_POINTER (); \ + DEBUG_PRINT2 (" start: %p\n", regstart[this_reg]); \ + } \ + else \ + { \ + for (this_reg = highest_active_reg; this_reg > high_reg; this_reg--) \ + { \ + reg_info[this_reg].word.integer = 0; \ + regend[this_reg] = 0; \ + regstart[this_reg] = 0; \ + } \ + highest_active_reg = high_reg; \ + } \ + \ + set_regs_matched_done = 0; \ + DEBUG_STATEMENT (nfailure_points_popped++); \ +} /* 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 + inner groups (which is a bits list) are maintained in separate + variables. + + We are making a (strictly speaking) nonportable assumption here: that + the compiler will pack our bit fields into something that fits into + the type of `word', i.e., is something that fits into one item on the + failure stack. */ + + +/* Declarations and macros for re_match_2. */ + +typedef union +{ + fail_stack_elt_t word; + struct + { + /* This field is one if this group can match the empty string, + zero if not. If not yet determined, `MATCH_NULL_UNSET_VALUE'. */ +#define MATCH_NULL_UNSET_VALUE 3 + unsigned match_null_string_p : 2; + unsigned is_active : 1; + unsigned matched_something : 1; + unsigned ever_matched_something : 1; + } bits; +} register_info_type; + +#define REG_MATCH_NULL_STRING_P(R) ((R).bits.match_null_string_p) +#define IS_ACTIVE(R) ((R).bits.is_active) +#define MATCHED_SOMETHING(R) ((R).bits.matched_something) +#define EVER_MATCHED_SOMETHING(R) ((R).bits.ever_matched_something) + + +/* Call this when have matched a real character; it sets `matched' flags + for the subexpressions which we are currently inside. Also records + that those subexprs have matched. */ +#define SET_REGS_MATCHED() \ + do \ + { \ + if (!set_regs_matched_done) \ + { \ + active_reg_t r; \ + set_regs_matched_done = 1; \ + for (r = lowest_active_reg; r <= highest_active_reg; r++) \ + { \ + MATCHED_SOMETHING (reg_info[r]) \ + = EVER_MATCHED_SOMETHING (reg_info[r]) \ + = 1; \ + } \ + } \ + } \ + while (0) + +/* Registers are set to a sentinel when they haven't yet matched. */ +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, + reg_syntax_t syntax, + struct re_pattern_buffer *bufp)); +static void store_op1 _RE_ARGS ((re_opcode_t op, unsigned char *loc, int arg)); +static void store_op2 _RE_ARGS ((re_opcode_t op, unsigned char *loc, + int arg1, int arg2)); +static void insert_op1 _RE_ARGS ((re_opcode_t op, unsigned char *loc, + int arg, unsigned char *end)); +static void insert_op2 _RE_ARGS ((re_opcode_t op, unsigned char *loc, + int arg1, int arg2, unsigned char *end)); +static boolean at_begline_loc_p _RE_ARGS ((const char *pattern, const char *p, + reg_syntax_t syntax)); +static boolean at_endline_loc_p _RE_ARGS ((const char *p, const char *pend, + reg_syntax_t syntax)); +static reg_errcode_t compile_range _RE_ARGS ((const char **p_ptr, + const char *pend, + char *translate, + reg_syntax_t syntax, + unsigned char *b)); + +/* Fetch the next character in the uncompiled pattern---translating it + if necessary. Also cast from a signed character in the constant + string passed to us by the user to an unsigned char that we can use + as an array index (in, e.g., `translate'). */ +#ifndef PATFETCH +# define PATFETCH(c) \ + do {if (p == pend) return REG_EEND; \ + c = (unsigned char) *p++; \ + if (translate) c = (unsigned char) translate[c]; \ + } while (0) +#endif + +/* Fetch the next character in the uncompiled pattern, with no + translation. */ +#define PATFETCH_RAW(c) \ + do {if (p == pend) return REG_EEND; \ + c = (unsigned char) *p++; \ + } while (0) + +/* Go backwards one character in the pattern. */ +#define PATUNFETCH p-- + + +/* If `translate' is non-null, return translate[D], else just D. We + cast the subscript to translate because some data is declared as + `char *', to avoid warnings when a string constant is passed. But + when we use a character as a subscript we must make it unsigned. */ +#ifndef TRANSLATE +# define TRANSLATE(d) \ + (translate ? (char) translate[(unsigned char) (d)] : (d)) +#endif + + +/* Macros for outputting the compiled pattern into `buffer'. */ + +/* If the buffer isn't allocated when it comes in, use this. */ +#define INIT_BUF_SIZE 32 + +/* Make sure we have at least N more bytes of space in buffer. */ +#define GET_BUFFER_SPACE(n) \ + while ((unsigned long) (b - bufp->buffer + (n)) > bufp->allocated) \ + EXTEND_BUFFER () + +/* Make sure we have one more byte of buffer space and then add C to it. */ +#define BUF_PUSH(c) \ + do { \ + GET_BUFFER_SPACE (1); \ + *b++ = (unsigned char) (c); \ + } while (0) + + +/* Ensure we have two more bytes of buffer space and then append C1 and C2. */ +#define BUF_PUSH_2(c1, c2) \ + do { \ + GET_BUFFER_SPACE (2); \ + *b++ = (unsigned char) (c1); \ + *b++ = (unsigned char) (c2); \ + } while (0) + + +/* As with BUF_PUSH_2, except for three bytes. */ +#define BUF_PUSH_3(c1, c2, c3) \ + do { \ + GET_BUFFER_SPACE (3); \ + *b++ = (unsigned char) (c1); \ + *b++ = (unsigned char) (c2); \ + *b++ = (unsigned char) (c3); \ + } while (0) + + +/* Store a jump with opcode OP at LOC to location TO. We store a + relative address offset by the three bytes the jump itself occupies. */ +#define STORE_JUMP(op, loc, to) \ + store_op1 (op, loc, (int) ((to) - (loc) - 3)) + +/* Likewise, for a two-argument jump. */ +#define STORE_JUMP2(op, loc, to, arg) \ + store_op2 (op, loc, (int) ((to) - (loc) - 3), arg) + +/* Like `STORE_JUMP', but for inserting. Assume `b' is the buffer end. */ +#define INSERT_JUMP(op, loc, to) \ + insert_op1 (op, loc, (int) ((to) - (loc) - 3), b) + +/* Like `STORE_JUMP2', but for inserting. Assume `b' is the buffer end. */ +#define INSERT_JUMP2(op, loc, to, arg) \ + insert_op2 (op, loc, (int) ((to) - (loc) - 3), arg, b) + + +/* This is not an arbitrary limit: the arguments which represent offsets + into the pattern are two bytes long. So if 2^16 bytes turns out to + be too small, many things would have to change. */ +/* Any other compiler which, like MSC, has allocation limit below 2^16 + bytes will have to use approach similar to what was done below for + MSC and drop MAX_BUF_SIZE a bit. Otherwise you may end up + reallocating to 0 bytes. Such thing is not going to work too well. + You have been warned!! */ +#if defined _MSC_VER && !defined _WIN32 +/* Microsoft C 16-bit versions limit malloc to approx 65512 bytes. + The REALLOC define eliminates a flurry of conversion warnings, + but is not required. */ +# define MAX_BUF_SIZE 65500L +# define REALLOC(p,s) realloc ((p), (size_t) (s)) +#else +# define MAX_BUF_SIZE (1L << 16) +# define REALLOC(p,s) realloc ((p), (s)) +#endif + +/* Extend the buffer by twice its current size via realloc and + reset the pointers that pointed into the old block to point to the + correct places in the new one. If extending the buffer results in it + being larger than MAX_BUF_SIZE, then flag memory exhausted. */ +#define EXTEND_BUFFER() \ + do { \ + unsigned char *old_buffer = bufp->buffer; \ + if (bufp->allocated == MAX_BUF_SIZE) \ + return REG_ESIZE; \ + bufp->allocated <<= 1; \ + if (bufp->allocated > MAX_BUF_SIZE) \ + bufp->allocated = MAX_BUF_SIZE; \ + bufp->buffer = (unsigned char *) REALLOC (bufp->buffer, bufp->allocated);\ + if (bufp->buffer == NULL) \ + return REG_ESPACE; \ + /* If the buffer moved, move all the pointers into it. */ \ + if (old_buffer != bufp->buffer) \ + { \ + b = (b - old_buffer) + bufp->buffer; \ + begalt = (begalt - old_buffer) + bufp->buffer; \ + if (fixup_alt_jump) \ + fixup_alt_jump = (fixup_alt_jump - old_buffer) + bufp->buffer;\ + if (laststart) \ + laststart = (laststart - old_buffer) + bufp->buffer; \ + if (pending_exact) \ + pending_exact = (pending_exact - old_buffer) + bufp->buffer; \ + } \ + } while (0) + + +/* Since we have one byte reserved for the register number argument to + {start,stop}_memory, the maximum number of groups we can report + things about is what fits in that byte. */ +#define MAX_REGNUM 255 + +/* But patterns can have more than `MAX_REGNUM' registers. We just + ignore the excess. */ +typedef unsigned regnum_t; + + +/* Macros for the compile stack. */ + +/* Since offsets can go either forwards or backwards, this type needs to + be able to hold values from -(MAX_BUF_SIZE - 1) to MAX_BUF_SIZE - 1. */ +/* int may be not enough when sizeof(int) == 2. */ +typedef long pattern_offset_t; + +typedef struct +{ + pattern_offset_t begalt_offset; + pattern_offset_t fixup_alt_jump; + pattern_offset_t inner_group_offset; + pattern_offset_t laststart_offset; + regnum_t regnum; +} compile_stack_elt_t; + + +typedef struct +{ + compile_stack_elt_t *stack; + unsigned size; + unsigned avail; /* Offset of next open position. */ +} compile_stack_type; + + +#define INIT_COMPILE_STACK_SIZE 32 + +#define COMPILE_STACK_EMPTY (compile_stack.avail == 0) +#define COMPILE_STACK_FULL (compile_stack.avail == compile_stack.size) + +/* The next available element. */ +#define COMPILE_STACK_TOP (compile_stack.stack[compile_stack.avail]) + + +/* Set the bit for character C in a list. */ +#define SET_LIST_BIT(c) \ + (b[((unsigned char) (c)) / BYTEWIDTH] \ + |= 1 << (((unsigned char) c) % BYTEWIDTH)) + + +/* Get the next unsigned number in the uncompiled pattern. */ +#define GET_UNSIGNED_NUMBER(num) \ + { if (p != pend) \ + { \ + PATFETCH (c); \ + while (ISDIGIT (c)) \ + { \ + if (num < 0) \ + num = 0; \ + num = num * 10 + c - '0'; \ + if (p == pend) \ + break; \ + PATFETCH (c); \ + } \ + } \ + } + +#if defined _LIBC || WIDE_CHAR_SUPPORT +/* The GNU C library provides support for user-defined character classes + and the functions from ISO C amendement 1. */ +# ifdef CHARCLASS_NAME_MAX +# define CHAR_CLASS_MAX_LENGTH CHARCLASS_NAME_MAX +# else +/* This shouldn't happen but some implementation might still have this + problem. Use a reasonable default value. */ +# define CHAR_CLASS_MAX_LENGTH 256 +# endif + +# ifdef _LIBC +# define IS_CHAR_CLASS(string) __wctype (string) +# else +# define IS_CHAR_CLASS(string) wctype (string) +# endif +#else +# define CHAR_CLASS_MAX_LENGTH 6 /* Namely, `xdigit'. */ + +# define IS_CHAR_CLASS(string) \ + (STREQ (string, "alpha") || STREQ (string, "upper") \ + || STREQ (string, "lower") || STREQ (string, "digit") \ + || STREQ (string, "alnum") || STREQ (string, "xdigit") \ + || STREQ (string, "space") || STREQ (string, "print") \ + || 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, + we make the fail stack and register vectors global. + The fail stack, we grow to the maximum size when a regexp + is compiled. + The register vectors, we adjust in size each time we + compile a regexp, according to the number of registers it needs. */ + +static fail_stack_type fail_stack; + +/* Size with which the following vectors are currently allocated. + That is so we can make them bigger as needed, + but never make them smaller. */ +static int regs_allocated_size; + +static const char ** regstart, ** regend; +static const char ** old_regstart, ** old_regend; +static const char **best_regstart, **best_regend; +static register_info_type *reg_info; +static const char **reg_dummy; +static register_info_type *reg_info_dummy; + +/* Make the register vectors big enough for NUM_REGS registers, + but don't make them smaller. */ + +static +regex_grow_registers (num_regs) + int num_regs; +{ + if (num_regs > regs_allocated_size) + { + RETALLOC_IF (regstart, num_regs, const char *); + RETALLOC_IF (regend, num_regs, const char *); + RETALLOC_IF (old_regstart, num_regs, const char *); + RETALLOC_IF (old_regend, num_regs, const char *); + RETALLOC_IF (best_regstart, num_regs, const char *); + RETALLOC_IF (best_regend, num_regs, const char *); + RETALLOC_IF (reg_info, num_regs, register_info_type); + RETALLOC_IF (reg_dummy, num_regs, const char *); + RETALLOC_IF (reg_info_dummy, num_regs, register_info_type); + + regs_allocated_size = num_regs; + } +} + +#endif /* not MATCH_MAY_ALLOCATE */ + +static boolean group_in_compile_stack _RE_ARGS ((compile_stack_type + compile_stack, + regnum_t regnum)); + +/* `regex_compile' compiles PATTERN (of length SIZE) according to SYNTAX. + Returns one of error codes defined in `regex.h', or zero for success. + + Assumes the `allocated' (and perhaps `buffer') and `translate' + fields are set in BUFP on entry. + + If it succeeds, results are put in BUFP (if it returns an error, the + contents of BUFP are undefined): + `buffer' is the compiled pattern; + `syntax' is set to SYNTAX; + `used' is set to the length of the compiled pattern; + `fastmap_accurate' is zero; + `re_nsub' is the number of subexpressions in PATTERN; + `not_bol' and `not_eol' are zero; + + The `fastmap' and `newline_anchor' fields are neither + examined nor set. */ + +/* Return, freeing storage we allocated. */ +#define FREE_STACK_RETURN(value) \ + return (free (compile_stack.stack), value) + +static reg_errcode_t +regex_compile (pattern, size, syntax, bufp) + const char *pattern; + size_t size; + reg_syntax_t syntax; + struct re_pattern_buffer *bufp; +{ + /* We fetch characters from PATTERN here. Even though PATTERN is + `char *' (i.e., signed), we declare these variables as unsigned, so + they can be reliably used as array indices. */ + register unsigned char c, c1; + + /* A random temporary spot in PATTERN. */ + const char *p1; + + /* Points to the end of the buffer, where we should append. */ + register unsigned char *b; + + /* Keeps track of unclosed groups. */ + compile_stack_type compile_stack; + + /* Points to the current (ending) position in the pattern. */ + const char *p = pattern; + const char *pend = pattern + size; + + /* How to translate the characters in the pattern. */ + RE_TRANSLATE_TYPE translate = bufp->translate; + + /* Address of the count-byte of the most recently inserted `exactn' + command. This makes it possible to tell if a new exact-match + character can be added to that command or if the character requires + a new `exactn' command. */ + unsigned char *pending_exact = 0; + + /* Address of start of the most recently finished expression. + This tells, e.g., postfix * where to find the start of its + operand. Reset at the beginning of groups and alternatives. */ + unsigned char *laststart = 0; + + /* Address of beginning of regexp, or inside of last group. */ + unsigned char *begalt; + + /* Place in the uncompiled pattern (i.e., the {) to + which to go back if the interval is invalid. */ + const char *beg_interval; + + /* Address of the place where a forward jump should go to the end of + the containing expression. Each alternative of an `or' -- except the + last -- ends with a forward jump of this sort. */ + unsigned char *fixup_alt_jump = 0; + + /* Counts open-groups as they are encountered. Remembered for the + matching close-group on the compile stack, so the same register + number is put in the stop_memory as the start_memory. */ + regnum_t regnum = 0; + +#ifdef DEBUG + DEBUG_PRINT1 ("\nCompiling pattern: "); + if (debug) + { + unsigned debug_count; + + for (debug_count = 0; debug_count < size; debug_count++) + putchar (pattern[debug_count]); + putchar ('\n'); + } +#endif /* DEBUG */ + + /* Initialize the compile stack. */ + compile_stack.stack = TALLOC (INIT_COMPILE_STACK_SIZE, compile_stack_elt_t); + if (compile_stack.stack == NULL) + return REG_ESPACE; + + compile_stack.size = INIT_COMPILE_STACK_SIZE; + compile_stack.avail = 0; + + /* Initialize the pattern buffer. */ + bufp->syntax = syntax; + bufp->fastmap_accurate = 0; + bufp->not_bol = bufp->not_eol = 0; + + /* Set `used' to zero, so that if we return an error, the pattern + printer (for debugging) will think there's no pattern. We reset it + at the end. */ + bufp->used = 0; + + /* Always count groups, whether or not bufp->no_sub is set. */ + bufp->re_nsub = 0; + +#if !defined emacs && !defined SYNTAX_TABLE + /* Initialize the syntax table. */ + init_syntax_once (); +#endif + + if (bufp->allocated == 0) + { + if (bufp->buffer) + { /* If zero allocated, but buffer is non-null, try to realloc + enough space. This loses if buffer's address is bogus, but + that is the user's responsibility. */ + RETALLOC (bufp->buffer, INIT_BUF_SIZE, unsigned char); + } + else + { /* Caller did not allocate a buffer. Do it for them. */ + bufp->buffer = TALLOC (INIT_BUF_SIZE, unsigned char); + } + if (!bufp->buffer) FREE_STACK_RETURN (REG_ESPACE); + + bufp->allocated = INIT_BUF_SIZE; + } + + begalt = b = bufp->buffer; + + /* Loop through the uncompiled pattern until we're at the end. */ + while (p != pend) + { + PATFETCH (c); + + switch (c) + { + case '^': + { + if ( /* If at start of pattern, it's an operator. */ + p == pattern + 1 + /* If context independent, it's an operator. */ + || syntax & RE_CONTEXT_INDEP_ANCHORS + /* Otherwise, depends on what's come before. */ + || at_begline_loc_p (pattern, p, syntax)) + BUF_PUSH (begline); + else + goto normal_char; + } + break; + + + case '$': + { + if ( /* If at end of pattern, it's an operator. */ + p == pend + /* If context independent, it's an operator. */ + || syntax & RE_CONTEXT_INDEP_ANCHORS + /* Otherwise, depends on what's next. */ + || at_endline_loc_p (p, pend, syntax)) + BUF_PUSH (endline); + else + goto normal_char; + } + break; + + + case '+': + case '?': + if ((syntax & RE_BK_PLUS_QM) + || (syntax & RE_LIMITED_OPS)) + goto normal_char; + handle_plus: + case '*': + /* If there is no previous pattern... */ + if (!laststart) + { + if (syntax & RE_CONTEXT_INVALID_OPS) + FREE_STACK_RETURN (REG_BADRPT); + else if (!(syntax & RE_CONTEXT_INDEP_OPS)) + goto normal_char; + } + + { + /* Are we optimizing this jump? */ + boolean keep_string_p = false; + + /* 1 means zero (many) matches is allowed. */ + char zero_times_ok = 0, many_times_ok = 0; + + /* If there is a sequence of repetition chars, collapse it + down to just one (the right one). We can't combine + interval operators with these because of, e.g., `a{2}*', + which should only match an even number of `a's. */ + + for (;;) + { + zero_times_ok |= c != '+'; + many_times_ok |= c != '?'; + + if (p == pend) + break; + + PATFETCH (c); + + if (c == '*' + || (!(syntax & RE_BK_PLUS_QM) && (c == '+' || c == '?'))) + ; + + else if (syntax & RE_BK_PLUS_QM && c == '\\') + { + if (p == pend) FREE_STACK_RETURN (REG_EESCAPE); + + PATFETCH (c1); + if (!(c1 == '+' || c1 == '?')) + { + PATUNFETCH; + PATUNFETCH; + break; + } + + c = c1; + } + else + { + PATUNFETCH; + break; + } + + /* If we get here, we found another repeat character. */ + } + + /* Star, etc. applied to an empty pattern is equivalent + to an empty pattern. */ + if (!laststart) + break; + + /* Now we know whether or not zero matches is allowed + and also whether or not two or more matches is allowed. */ + if (many_times_ok) + { /* More than one repetition is allowed, so put in at the + end a backward relative jump from `b' to before the next + jump we're going to put in below (which jumps from + laststart to after this jump). + + But if we are at the `*' in the exact sequence `.*\n', + insert an unconditional jump backwards to the ., + instead of the beginning of the loop. This way we only + push a failure point once, instead of every time + through the loop. */ + assert (p - 1 > pattern); + + /* Allocate the space for the jump. */ + GET_BUFFER_SPACE (3); + + /* We know we are not at the first character of the pattern, + because laststart was nonzero. And we've already + incremented `p', by the way, to be the character after + the `*'. Do we have to do something analogous here + for null bytes, because of RE_DOT_NOT_NULL? */ + if (TRANSLATE (*(p - 2)) == TRANSLATE ('.') + && zero_times_ok + && p < pend && TRANSLATE (*p) == TRANSLATE ('\n') + && !(syntax & RE_DOT_NEWLINE)) + { /* We have .*\n. */ + STORE_JUMP (jump, b, laststart); + keep_string_p = true; + } + else + /* Anything else. */ + STORE_JUMP (maybe_pop_jump, b, laststart - 3); + + /* We've added more stuff to the buffer. */ + b += 3; + } + + /* On failure, jump from laststart to b + 3, which will be the + end of the buffer after this jump is inserted. */ + GET_BUFFER_SPACE (3); + INSERT_JUMP (keep_string_p ? on_failure_keep_string_jump + : on_failure_jump, + laststart, b + 3); + pending_exact = 0; + b += 3; + + if (!zero_times_ok) + { + /* At least one repetition is required, so insert a + `dummy_failure_jump' before the initial + `on_failure_jump' instruction of the loop. This + effects a skip over that instruction the first time + we hit that loop. */ + GET_BUFFER_SPACE (3); + INSERT_JUMP (dummy_failure_jump, laststart, laststart + 6); + b += 3; + } + } + break; + + + case '.': + laststart = b; + BUF_PUSH (anychar); + break; + + + case '[': + { + boolean had_char_class = false; + + if (p == pend) FREE_STACK_RETURN (REG_EBRACK); + + /* Ensure that we have enough space to push a charset: the + opcode, the length count, and the bitset; 34 bytes in all. */ + GET_BUFFER_SPACE (34); + + laststart = b; + + /* We test `*p == '^' twice, instead of using an if + statement, so we only need one BUF_PUSH. */ + BUF_PUSH (*p == '^' ? charset_not : charset); + if (*p == '^') + p++; + + /* Remember the first position in the bracket expression. */ + p1 = p; + + /* Push the number of bytes in the bitmap. */ + BUF_PUSH ((1 << BYTEWIDTH) / BYTEWIDTH); + + /* Clear the whole map. */ + memset (b, 0, (1 << BYTEWIDTH) / BYTEWIDTH); + + /* charset_not matches newline according to a syntax bit. */ + if ((re_opcode_t) b[-2] == charset_not + && (syntax & RE_HAT_LISTS_NOT_NEWLINE)) + SET_LIST_BIT ('\n'); + + /* Read in characters and ranges, setting map bits. */ + for (;;) + { + if (p == pend) FREE_STACK_RETURN (REG_EBRACK); + + PATFETCH (c); + + /* \ might escape characters inside [...] and [^...]. */ + if ((syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) && c == '\\') + { + if (p == pend) FREE_STACK_RETURN (REG_EESCAPE); + + PATFETCH (c1); + SET_LIST_BIT (c1); + continue; + } + + /* Could be the end of the bracket expression. If it's + not (i.e., when the bracket expression is `[]' so + far), the ']' character bit gets set way below. */ + if (c == ']' && p != p1 + 1) + break; + + /* Look ahead to see if it's a range when the last thing + was a character class. */ + if (had_char_class && c == '-' && *p != ']') + FREE_STACK_RETURN (REG_ERANGE); + + /* Look ahead to see if it's a range when the last thing + was a character: if this is a hyphen not at the + beginning or the end of a list, then it's the range + operator. */ + if (c == '-' + && !(p - 2 >= pattern && p[-2] == '[') + && !(p - 3 >= pattern && p[-3] == '[' && p[-2] == '^') + && *p != ']') + { + reg_errcode_t ret + = compile_range (&p, pend, translate, syntax, b); + if (ret != REG_NOERROR) FREE_STACK_RETURN (ret); + } + + else if (p[0] == '-' && p[1] != ']') + { /* This handles ranges made up of characters only. */ + reg_errcode_t ret; + + /* Move past the `-'. */ + PATFETCH (c1); + + ret = compile_range (&p, pend, translate, syntax, b); + if (ret != REG_NOERROR) FREE_STACK_RETURN (ret); + } + + /* See if we're at the beginning of a possible character + class. */ + + else if (syntax & RE_CHAR_CLASSES && c == '[' && *p == ':') + { /* Leave room for the null. */ + char str[CHAR_CLASS_MAX_LENGTH + 1]; + + PATFETCH (c); + c1 = 0; + + /* If pattern is `[[:'. */ + if (p == pend) FREE_STACK_RETURN (REG_EBRACK); + + for (;;) + { + PATFETCH (c); + if ((c == ':' && *p == ']') || p == pend) + break; + if (c1 < CHAR_CLASS_MAX_LENGTH) + str[c1++] = c; + else + /* This is in any case an invalid class name. */ + str[0] = '\0'; + } + str[c1] = '\0'; + + /* If isn't a word bracketed by `[:' and `:]': + undo the ending character, the letters, and leave + the leading `:' and `[' (but set bits for them). */ + if (c == ':' && *p == ']') + { +#if defined _LIBC || WIDE_CHAR_SUPPORT + boolean is_lower = STREQ (str, "lower"); + boolean is_upper = STREQ (str, "upper"); + wctype_t wt; + int ch; + + wt = IS_CHAR_CLASS (str); + if (wt == 0) + FREE_STACK_RETURN (REG_ECTYPE); + + /* Throw away the ] at the end of the character + class. */ + PATFETCH (c); + + if (p == pend) FREE_STACK_RETURN (REG_EBRACK); + + for (ch = 0; ch < 1 << BYTEWIDTH; ++ch) + { +# ifdef _LIBC + if (__iswctype (__btowc (ch), wt)) + SET_LIST_BIT (ch); +# else + if (iswctype (btowc (ch), wt)) + SET_LIST_BIT (ch); +# endif + + if (translate && (is_upper || is_lower) + && (ISUPPER (ch) || ISLOWER (ch))) + SET_LIST_BIT (ch); + } + + had_char_class = true; +#else + int ch; + boolean is_alnum = STREQ (str, "alnum"); + boolean is_alpha = STREQ (str, "alpha"); + boolean is_blank = STREQ (str, "blank"); + boolean is_cntrl = STREQ (str, "cntrl"); + boolean is_digit = STREQ (str, "digit"); + boolean is_graph = STREQ (str, "graph"); + boolean is_lower = STREQ (str, "lower"); + boolean is_print = STREQ (str, "print"); + boolean is_punct = STREQ (str, "punct"); + boolean is_space = STREQ (str, "space"); + boolean is_upper = STREQ (str, "upper"); + boolean is_xdigit = STREQ (str, "xdigit"); + + if (!IS_CHAR_CLASS (str)) + FREE_STACK_RETURN (REG_ECTYPE); + + /* Throw away the ] at the end of the character + class. */ + PATFETCH (c); + + if (p == pend) FREE_STACK_RETURN (REG_EBRACK); + + for (ch = 0; ch < 1 << BYTEWIDTH; ch++) + { + /* This was split into 3 if's to + avoid an arbitrary limit in some compiler. */ + if ( (is_alnum && ISALNUM (ch)) + || (is_alpha && ISALPHA (ch)) + || (is_blank && ISBLANK (ch)) + || (is_cntrl && ISCNTRL (ch))) + SET_LIST_BIT (ch); + if ( (is_digit && ISDIGIT (ch)) + || (is_graph && ISGRAPH (ch)) + || (is_lower && ISLOWER (ch)) + || (is_print && ISPRINT (ch))) + SET_LIST_BIT (ch); + if ( (is_punct && ISPUNCT (ch)) + || (is_space && ISSPACE (ch)) + || (is_upper && ISUPPER (ch)) + || (is_xdigit && ISXDIGIT (ch))) + SET_LIST_BIT (ch); + if ( translate && (is_upper || is_lower) + && (ISUPPER (ch) || ISLOWER (ch))) + SET_LIST_BIT (ch); + } + had_char_class = true; +#endif /* libc || wctype.h */ + } + else + { + c1++; + while (c1--) + PATUNFETCH; + SET_LIST_BIT ('['); + SET_LIST_BIT (':'); + had_char_class = false; + } + } + else + { + had_char_class = false; + SET_LIST_BIT (c); + } + } + + /* Discard any (non)matching list bytes that are all 0 at the + end of the map. Decrease the map-length byte too. */ + while ((int) b[-1] > 0 && b[b[-1] - 1] == 0) + b[-1]--; + b += b[-1]; + } + break; + + + case '(': + if (syntax & RE_NO_BK_PARENS) + goto handle_open; + else + goto normal_char; + + + case ')': + if (syntax & RE_NO_BK_PARENS) + goto handle_close; + else + goto normal_char; + + + case '\n': + if (syntax & RE_NEWLINE_ALT) + goto handle_alt; + else + goto normal_char; + + + case '|': + if (syntax & RE_NO_BK_VBAR) + goto handle_alt; + else + goto normal_char; + + + case '{': + if (syntax & RE_INTERVALS && syntax & RE_NO_BK_BRACES) + goto handle_interval; + else + goto normal_char; + + + case '\\': + if (p == pend) FREE_STACK_RETURN (REG_EESCAPE); + + /* Do not translate the character after the \, so that we can + distinguish, e.g., \B from \b, even if we normally would + translate, e.g., B to b. */ + PATFETCH_RAW (c); + + switch (c) + { + case '(': + if (syntax & RE_NO_BK_PARENS) + goto normal_backslash; + + handle_open: + bufp->re_nsub++; + regnum++; + + if (COMPILE_STACK_FULL) + { + RETALLOC (compile_stack.stack, compile_stack.size << 1, + compile_stack_elt_t); + if (compile_stack.stack == NULL) return REG_ESPACE; + + compile_stack.size <<= 1; + } + + /* These are the values to restore when we hit end of this + group. They are all relative offsets, so that if the + whole pattern moves because of realloc, they will still + be valid. */ + COMPILE_STACK_TOP.begalt_offset = begalt - bufp->buffer; + COMPILE_STACK_TOP.fixup_alt_jump + = fixup_alt_jump ? fixup_alt_jump - bufp->buffer + 1 : 0; + COMPILE_STACK_TOP.laststart_offset = b - bufp->buffer; + COMPILE_STACK_TOP.regnum = regnum; + + /* We will eventually replace the 0 with the number of + groups inner to this one. But do not push a + start_memory for groups beyond the last one we can + represent in the compiled pattern. */ + if (regnum <= MAX_REGNUM) + { + COMPILE_STACK_TOP.inner_group_offset = b - bufp->buffer + 2; + BUF_PUSH_3 (start_memory, regnum, 0); + } + + compile_stack.avail++; + + fixup_alt_jump = 0; + laststart = 0; + begalt = b; + /* If we've reached MAX_REGNUM groups, then this open + won't actually generate any code, so we'll have to + clear pending_exact explicitly. */ + pending_exact = 0; + break; + + + case ')': + if (syntax & RE_NO_BK_PARENS) goto normal_backslash; + + if (COMPILE_STACK_EMPTY) + { + if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD) + goto normal_backslash; + else + FREE_STACK_RETURN (REG_ERPAREN); + } + + handle_close: + if (fixup_alt_jump) + { /* Push a dummy failure point at the end of the + alternative for a possible future + `pop_failure_jump' to pop. See comments at + `push_dummy_failure' in `re_match_2'. */ + BUF_PUSH (push_dummy_failure); + + /* We allocated space for this jump when we assigned + to `fixup_alt_jump', in the `handle_alt' case below. */ + STORE_JUMP (jump_past_alt, fixup_alt_jump, b - 1); + } + + /* See similar code for backslashed left paren above. */ + if (COMPILE_STACK_EMPTY) + { + if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD) + goto normal_char; + else + FREE_STACK_RETURN (REG_ERPAREN); + } + + /* Since we just checked for an empty stack above, this + ``can't happen''. */ + assert (compile_stack.avail != 0); + { + /* We don't just want to restore into `regnum', because + later groups should continue to be numbered higher, + as in `(ab)c(de)' -- the second group is #2. */ + regnum_t this_group_regnum; + + compile_stack.avail--; + begalt = bufp->buffer + COMPILE_STACK_TOP.begalt_offset; + fixup_alt_jump + = COMPILE_STACK_TOP.fixup_alt_jump + ? bufp->buffer + COMPILE_STACK_TOP.fixup_alt_jump - 1 + : 0; + laststart = bufp->buffer + COMPILE_STACK_TOP.laststart_offset; + this_group_regnum = COMPILE_STACK_TOP.regnum; + /* If we've reached MAX_REGNUM groups, then this open + won't actually generate any code, so we'll have to + clear pending_exact explicitly. */ + pending_exact = 0; + + /* We're at the end of the group, so now we know how many + groups were inside this one. */ + if (this_group_regnum <= MAX_REGNUM) + { + unsigned char *inner_group_loc + = bufp->buffer + COMPILE_STACK_TOP.inner_group_offset; + + *inner_group_loc = regnum - this_group_regnum; + BUF_PUSH_3 (stop_memory, this_group_regnum, + regnum - this_group_regnum); + } + } + break; + + + case '|': /* `\|'. */ + if (syntax & RE_LIMITED_OPS || syntax & RE_NO_BK_VBAR) + goto normal_backslash; + handle_alt: + if (syntax & RE_LIMITED_OPS) + goto normal_char; + + /* Insert before the previous alternative a jump which + jumps to this alternative if the former fails. */ + GET_BUFFER_SPACE (3); + INSERT_JUMP (on_failure_jump, begalt, b + 6); + pending_exact = 0; + b += 3; + + /* The alternative before this one has a jump after it + which gets executed if it gets matched. Adjust that + jump so it will jump to this alternative's analogous + jump (put in below, which in turn will jump to the next + (if any) alternative's such jump, etc.). The last such + jump jumps to the correct final destination. A picture: + _____ _____ + | | | | + | v | v + a | b | c + + If we are at `b', then fixup_alt_jump right now points to a + three-byte space after `a'. We'll put in the jump, set + fixup_alt_jump to right after `b', and leave behind three + bytes which we'll fill in when we get to after `c'. */ + + if (fixup_alt_jump) + STORE_JUMP (jump_past_alt, fixup_alt_jump, b); + + /* Mark and leave space for a jump after this alternative, + to be filled in later either by next alternative or + when know we're at the end of a series of alternatives. */ + fixup_alt_jump = b; + GET_BUFFER_SPACE (3); + b += 3; + + laststart = 0; + begalt = b; + break; + + + case '{': + /* If \{ is a literal. */ + if (!(syntax & RE_INTERVALS) + /* If we're at `\{' and it's not the open-interval + operator. */ + || ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES)) + || (p - 2 == pattern && p == pend)) + goto normal_backslash; + + handle_interval: + { + /* If got here, then the syntax allows intervals. */ + + /* At least (most) this many matches must be made. */ + int lower_bound = -1, upper_bound = -1; + + beg_interval = p - 1; + + if (p == pend) + { + if (syntax & RE_NO_BK_BRACES) + goto unfetch_interval; + else + FREE_STACK_RETURN (REG_EBRACE); + } + + GET_UNSIGNED_NUMBER (lower_bound); + + if (c == ',') + { + GET_UNSIGNED_NUMBER (upper_bound); + if (upper_bound < 0) upper_bound = RE_DUP_MAX; + } + else + /* Interval such as `{1}' => match exactly once. */ + upper_bound = lower_bound; + + if (lower_bound < 0 || upper_bound > RE_DUP_MAX + || lower_bound > upper_bound) + { + if (syntax & RE_NO_BK_BRACES) + goto unfetch_interval; + else + FREE_STACK_RETURN (REG_BADBR); + } + + if (!(syntax & RE_NO_BK_BRACES)) + { + if (c != '\\') FREE_STACK_RETURN (REG_EBRACE); + + PATFETCH (c); + } + + if (c != '}') + { + if (syntax & RE_NO_BK_BRACES) + goto unfetch_interval; + else + FREE_STACK_RETURN (REG_BADBR); + } + + /* We just parsed a valid interval. */ + + /* If it's invalid to have no preceding re. */ + if (!laststart) + { + if (syntax & RE_CONTEXT_INVALID_OPS) + FREE_STACK_RETURN (REG_BADRPT); + else if (syntax & RE_CONTEXT_INDEP_OPS) + laststart = b; + else + goto unfetch_interval; + } + + /* If the upper bound is zero, don't want to succeed at + all; jump from `laststart' to `b + 3', which will be + the end of the buffer after we insert the jump. */ + if (upper_bound == 0) + { + GET_BUFFER_SPACE (3); + INSERT_JUMP (jump, laststart, b + 3); + b += 3; + } + + /* Otherwise, we have a nontrivial interval. When + we're all done, the pattern will look like: + set_number_at + set_number_at + succeed_n + + jump_n + (The upper bound and `jump_n' are omitted if + `upper_bound' is 1, though.) */ + else + { /* If the upper bound is > 1, we need to insert + more at the end of the loop. */ + unsigned nbytes = 10 + (upper_bound > 1) * 10; + + GET_BUFFER_SPACE (nbytes); + + /* Initialize lower bound of the `succeed_n', even + though it will be set during matching by its + attendant `set_number_at' (inserted next), + because `re_compile_fastmap' needs to know. + Jump to the `jump_n' we might insert below. */ + INSERT_JUMP2 (succeed_n, laststart, + b + 5 + (upper_bound > 1) * 5, + lower_bound); + b += 5; + + /* Code to initialize the lower bound. Insert + before the `succeed_n'. The `5' is the last two + bytes of this `set_number_at', plus 3 bytes of + the following `succeed_n'. */ + insert_op2 (set_number_at, laststart, 5, lower_bound, b); + b += 5; + + if (upper_bound > 1) + { /* More than one repetition is allowed, so + append a backward jump to the `succeed_n' + that starts this interval. + + When we've reached this during matching, + we'll have matched the interval once, so + jump back only `upper_bound - 1' times. */ + STORE_JUMP2 (jump_n, b, laststart + 5, + upper_bound - 1); + b += 5; + + /* The location we want to set is the second + parameter of the `jump_n'; that is `b-2' as + an absolute address. `laststart' will be + the `set_number_at' we're about to insert; + `laststart+3' the number to set, the source + for the relative address. But we are + inserting into the middle of the pattern -- + so everything is getting moved up by 5. + Conclusion: (b - 2) - (laststart + 3) + 5, + i.e., b - laststart. + + We insert this at the beginning of the loop + so that if we fail during matching, we'll + reinitialize the bounds. */ + insert_op2 (set_number_at, laststart, b - laststart, + upper_bound - 1, b); + b += 5; + } + } + pending_exact = 0; + beg_interval = NULL; + } + break; + + unfetch_interval: + /* If an invalid interval, match the characters as literals. */ + assert (beg_interval); + p = beg_interval; + beg_interval = NULL; + + /* normal_char and normal_backslash need `c'. */ + PATFETCH (c); + + if (!(syntax & RE_NO_BK_BRACES)) + { + if (p > pattern && p[-1] == '\\') + goto normal_backslash; + } + goto normal_char; + +#ifdef emacs + /* There is no way to specify the before_dot and after_dot + operators. rms says this is ok. --karl */ + case '=': + BUF_PUSH (at_dot); + break; + + case 's': + laststart = b; + PATFETCH (c); + BUF_PUSH_2 (syntaxspec, syntax_spec_code[c]); + break; + + case 'S': + laststart = b; + PATFETCH (c); + BUF_PUSH_2 (notsyntaxspec, syntax_spec_code[c]); + break; +#endif /* emacs */ + + + case 'w': + if (syntax & RE_NO_GNU_OPS) + goto normal_char; + laststart = b; + BUF_PUSH (wordchar); + break; + + + case 'W': + if (syntax & RE_NO_GNU_OPS) + goto normal_char; + laststart = b; + BUF_PUSH (notwordchar); + break; + + + case '<': + if (syntax & RE_NO_GNU_OPS) + goto normal_char; + BUF_PUSH (wordbeg); + break; + + case '>': + if (syntax & RE_NO_GNU_OPS) + goto normal_char; + BUF_PUSH (wordend); + break; + + case 'b': + if (syntax & RE_NO_GNU_OPS) + goto normal_char; + BUF_PUSH (wordbound); + break; + + case 'B': + if (syntax & RE_NO_GNU_OPS) + goto normal_char; + BUF_PUSH (notwordbound); + break; + + case '`': + if (syntax & RE_NO_GNU_OPS) + goto normal_char; + BUF_PUSH (begbuf); + break; + + case '\'': + if (syntax & RE_NO_GNU_OPS) + goto normal_char; + BUF_PUSH (endbuf); + break; + + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': + if (syntax & RE_NO_BK_REFS) + goto normal_char; + + c1 = c - '0'; + + if (c1 > regnum) + FREE_STACK_RETURN (REG_ESUBREG); + + /* Can't back reference to a subexpression if inside of it. */ + if (group_in_compile_stack (compile_stack, (regnum_t) c1)) + goto normal_char; + + laststart = b; + BUF_PUSH_2 (duplicate, c1); + break; + + + case '+': + case '?': + if (syntax & RE_BK_PLUS_QM) + goto handle_plus; + else + goto normal_backslash; + + default: + normal_backslash: + /* You might think it would be useful for \ to mean + not to translate; but if we don't translate it + it will never match anything. */ + c = TRANSLATE (c); + goto normal_char; + } + break; + + + default: + /* Expects the character in `c'. */ + normal_char: + /* If no exactn currently being built. */ + if (!pending_exact + + /* If last exactn not at current position. */ + || pending_exact + *pending_exact + 1 != b + + /* We have only one byte following the exactn for the count. */ + || *pending_exact == (1 << BYTEWIDTH) - 1 + + /* If followed by a repetition operator. */ + || *p == '*' || *p == '^' + || ((syntax & RE_BK_PLUS_QM) + ? *p == '\\' && (p[1] == '+' || p[1] == '?') + : (*p == '+' || *p == '?')) + || ((syntax & RE_INTERVALS) + && ((syntax & RE_NO_BK_BRACES) + ? *p == '{' + : (p[0] == '\\' && p[1] == '{')))) + { + /* Start building a new exactn. */ + + laststart = b; + + BUF_PUSH_2 (exactn, 0); + pending_exact = b - 1; + } + + BUF_PUSH (c); + (*pending_exact)++; + break; + } /* switch (c) */ + } /* while p != pend */ + + + /* Through the pattern now. */ + + if (fixup_alt_jump) + STORE_JUMP (jump_past_alt, fixup_alt_jump, b); + + if (!COMPILE_STACK_EMPTY) + FREE_STACK_RETURN (REG_EPAREN); + + /* If we don't want backtracking, force success + the first time we reach the end of the compiled pattern. */ + if (syntax & RE_NO_POSIX_BACKTRACKING) + BUF_PUSH (succeed); + + free (compile_stack.stack); + + /* We have succeeded; set the length of the buffer. */ + bufp->used = b - bufp->buffer; + +#ifdef DEBUG + if (debug) + { + DEBUG_PRINT1 ("\nCompiled pattern: \n"); + print_compiled_pattern (bufp); + } +#endif /* DEBUG */ + +#ifndef MATCH_MAY_ALLOCATE + /* Initialize the failure stack to the largest possible stack. This + isn't necessary unless we're trying to avoid calling alloca in + the search and match routines. */ + { + int num_regs = bufp->re_nsub + 1; + + /* Since DOUBLE_FAIL_STACK refuses to double only if the current size + is strictly greater than re_max_failures, the largest possible stack + is 2 * re_max_failures failure points. */ + if (fail_stack.size < (2 * re_max_failures * MAX_FAILURE_ITEMS)) + { + fail_stack.size = (2 * re_max_failures * MAX_FAILURE_ITEMS); + +# ifdef emacs + if (! fail_stack.stack) + fail_stack.stack + = (fail_stack_elt_t *) xmalloc (fail_stack.size + * sizeof (fail_stack_elt_t)); + else + fail_stack.stack + = (fail_stack_elt_t *) xrealloc (fail_stack.stack, + (fail_stack.size + * sizeof (fail_stack_elt_t))); +# else /* not emacs */ + if (! fail_stack.stack) + fail_stack.stack + = (fail_stack_elt_t *) malloc (fail_stack.size + * sizeof (fail_stack_elt_t)); + else + fail_stack.stack + = (fail_stack_elt_t *) realloc (fail_stack.stack, + (fail_stack.size + * sizeof (fail_stack_elt_t))); +# endif /* not emacs */ + } + + regex_grow_registers (num_regs); + } +#endif /* not MATCH_MAY_ALLOCATE */ + + return REG_NOERROR; +} /* regex_compile */ + +/* Subroutines for `regex_compile'. */ + +/* Store OP at LOC followed by two-byte integer parameter ARG. */ + +static void +store_op1 (op, loc, arg) + re_opcode_t op; + unsigned char *loc; + int arg; +{ + *loc = (unsigned char) op; + STORE_NUMBER (loc + 1, arg); +} + + +/* Like `store_op1', but for two two-byte parameters ARG1 and ARG2. */ + +static void +store_op2 (op, loc, arg1, arg2) + re_opcode_t op; + unsigned char *loc; + int arg1, arg2; +{ + *loc = (unsigned char) op; + STORE_NUMBER (loc + 1, arg1); + STORE_NUMBER (loc + 3, arg2); +} + + +/* Copy the bytes from LOC to END to open up three bytes of space at LOC + for OP followed by two-byte integer parameter ARG. */ + +static void +insert_op1 (op, loc, arg, end) + re_opcode_t op; + unsigned char *loc; + int arg; + unsigned char *end; +{ + register unsigned char *pfrom = end; + register unsigned char *pto = end + 3; + + while (pfrom != loc) + *--pto = *--pfrom; + + store_op1 (op, loc, arg); +} + + +/* Like `insert_op1', but for two two-byte parameters ARG1 and ARG2. */ + +static void +insert_op2 (op, loc, arg1, arg2, end) + re_opcode_t op; + unsigned char *loc; + int arg1, arg2; + unsigned char *end; +{ + register unsigned char *pfrom = end; + register unsigned char *pto = end + 5; + + while (pfrom != loc) + *--pto = *--pfrom; + + store_op2 (op, loc, arg1, arg2); +} + + +/* P points to just after a ^ in PATTERN. Return true if that ^ comes + after an alternative or a begin-subexpression. We assume there is at + least one character before the ^. */ + +static boolean +at_begline_loc_p (pattern, p, syntax) + const char *pattern, *p; + reg_syntax_t syntax; +{ + const char *prev = p - 2; + boolean prev_prev_backslash = prev > pattern && prev[-1] == '\\'; + + return + /* After a subexpression? */ + (*prev == '(' && (syntax & RE_NO_BK_PARENS || prev_prev_backslash)) + /* After an alternative? */ + || (*prev == '|' && (syntax & RE_NO_BK_VBAR || prev_prev_backslash)); +} + + +/* The dual of at_begline_loc_p. This one is for $. We assume there is + at least one character after the $, i.e., `P < PEND'. */ + +static boolean +at_endline_loc_p (p, pend, syntax) + const char *p, *pend; + reg_syntax_t syntax; +{ + const char *next = p; + boolean next_backslash = *next == '\\'; + const char *next_next = p + 1 < pend ? p + 1 : 0; + + return + /* Before a subexpression? */ + (syntax & RE_NO_BK_PARENS ? *next == ')' + : next_backslash && next_next && *next_next == ')') + /* Before an alternative? */ + || (syntax & RE_NO_BK_VBAR ? *next == '|' + : next_backslash && next_next && *next_next == '|'); +} + + +/* Returns true if REGNUM is in one of COMPILE_STACK's elements and + false if it's not. */ + +static boolean +group_in_compile_stack (compile_stack, regnum) + compile_stack_type compile_stack; + regnum_t regnum; +{ + int this_element; + + for (this_element = compile_stack.avail - 1; + this_element >= 0; + this_element--) + if (compile_stack.stack[this_element].regnum == regnum) + return true; + + return false; +} + + +/* Read the ending character of a range (in a bracket expression) from the + uncompiled pattern *P_PTR (which ends at PEND). We assume the + starting character is in `P[-2]'. (`P[-1]' is the character `-'.) + Then we set the translation of all bits between the starting and + ending characters (inclusive) in the compiled pattern B. + + Return an error code. + + We use these short variable names so we can use the same macros as + `regex_compile' itself. */ + +static reg_errcode_t +compile_range (p_ptr, pend, translate, syntax, b) + const char **p_ptr, *pend; + RE_TRANSLATE_TYPE translate; + reg_syntax_t syntax; + unsigned char *b; +{ + unsigned this_char; + + const char *p = *p_ptr; + unsigned int range_start, range_end; + + if (p == pend) + return REG_ERANGE; + + /* Even though the pattern is a signed `char *', we need to fetch + with unsigned char *'s; if the high bit of the pattern character + is set, the range endpoints will be negative if we fetch using a + signed char *. + + We also want to fetch the endpoints without translating them; the + appropriate translation is done in the bit-setting loop below. */ + /* The SVR4 compiler on the 3B2 had trouble with unsigned const char *. */ + range_start = ((const unsigned char *) p)[-2]; + range_end = ((const unsigned char *) p)[0]; + + /* Have to increment the pointer into the pattern string, so the + caller isn't still at the ending character. */ + (*p_ptr)++; + + /* If the start is after the end, the range is empty. */ + if (range_start > range_end) + return syntax & RE_NO_EMPTY_RANGES ? REG_ERANGE : REG_NOERROR; + + /* Here we see why `this_char' has to be larger than an `unsigned + char' -- the range is inclusive, so if `range_end' == 0xff + (assuming 8-bit characters), we would otherwise go into an infinite + loop, since all characters <= 0xff. */ + for (this_char = range_start; this_char <= range_end; this_char++) + { + SET_LIST_BIT (TRANSLATE (this_char)); + } + + 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 + is used by re_search to skip quickly over impossible starting points. + + The caller must supply the address of a (1 << BYTEWIDTH)-byte data + area as BUFP->fastmap. + + We set the `fastmap', `fastmap_accurate', and `can_be_null' fields in + the pattern buffer. + + Returns 0 if we succeed, -2 if an internal error. */ + +int +re_compile_fastmap (bufp) + struct re_pattern_buffer *bufp; +{ + int j, k; +#ifdef MATCH_MAY_ALLOCATE + fail_stack_type fail_stack; +#endif +#ifndef REGEX_MALLOC + char *destination; +#endif + + register char *fastmap = bufp->fastmap; + unsigned char *pattern = bufp->buffer; + unsigned char *p = pattern; + register unsigned char *pend = pattern + bufp->used; + +#ifdef REL_ALLOC + /* This holds the pointer to the failure stack, when + it is allocated relocatably. */ + fail_stack_elt_t *failure_stack_ptr; +#endif + + /* Assume that each path through the pattern can be null until + proven otherwise. We set this false at the bottom of switch + statement, to which we get only if a particular path doesn't + match the empty string. */ + boolean path_can_be_null = true; + + /* We aren't doing a `succeed_n' to begin with. */ + boolean succeed_n_p = false; + + assert (fastmap != NULL && p != NULL); + + INIT_FAIL_STACK (); + memset (fastmap, 0, 1 << BYTEWIDTH); /* Assume nothing's valid. */ + bufp->fastmap_accurate = 1; /* It will be when we're done. */ + bufp->can_be_null = 0; + + while (1) + { + if (p == pend || *p == succeed) + { + /* We have reached the (effective) end of pattern. */ + if (!FAIL_STACK_EMPTY ()) + { + bufp->can_be_null |= path_can_be_null; + + /* Reset for next path. */ + path_can_be_null = true; + + p = fail_stack.stack[--fail_stack.avail].pointer; + + continue; + } + else + break; + } + + /* We should never be about to go beyond the end of the pattern. */ + assert (p < pend); + + switch (SWITCH_ENUM_CAST ((re_opcode_t) *p++)) + { + + /* I guess the idea here is to simply not bother with a fastmap + if a backreference is used, since it's too hard to figure out + the fastmap for the corresponding group. Setting + `can_be_null' stops `re_search_2' from using the fastmap, so + that is all we do. */ + case duplicate: + bufp->can_be_null = 1; + goto done; + + + /* Following are the cases which match a character. These end + with `break'. */ + + case exactn: + fastmap[p[1]] = 1; + break; + + + case charset: + for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--) + if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))) + fastmap[j] = 1; + break; + + + case charset_not: + /* Chars beyond end of map must be allowed. */ + for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++) + fastmap[j] = 1; + + for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--) + if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))) + fastmap[j] = 1; + break; + + + case wordchar: + for (j = 0; j < (1 << BYTEWIDTH); j++) + if (SYNTAX (j) == Sword) + fastmap[j] = 1; + break; + + + case notwordchar: + for (j = 0; j < (1 << BYTEWIDTH); j++) + if (SYNTAX (j) != Sword) + fastmap[j] = 1; + break; + + + case anychar: + { + int fastmap_newline = fastmap['\n']; + + /* `.' matches anything ... */ + for (j = 0; j < (1 << BYTEWIDTH); j++) + fastmap[j] = 1; + + /* ... except perhaps newline. */ + if (!(bufp->syntax & RE_DOT_NEWLINE)) + fastmap['\n'] = fastmap_newline; + + /* Return if we have already set `can_be_null'; if we have, + then the fastmap is irrelevant. Something's wrong here. */ + else if (bufp->can_be_null) + goto done; + + /* Otherwise, have to check alternative paths. */ + break; + } + +#ifdef emacs + case syntaxspec: + k = *p++; + for (j = 0; j < (1 << BYTEWIDTH); j++) + if (SYNTAX (j) == (enum syntaxcode) k) + fastmap[j] = 1; + break; + + + case notsyntaxspec: + k = *p++; + for (j = 0; j < (1 << BYTEWIDTH); j++) + if (SYNTAX (j) != (enum syntaxcode) k) + fastmap[j] = 1; + break; + + + /* All cases after this match the empty string. These end with + `continue'. */ + + + case before_dot: + case at_dot: + case after_dot: + continue; +#endif /* emacs */ + + + case no_op: + case begline: + case endline: + case begbuf: + case endbuf: + case wordbound: + case notwordbound: + case wordbeg: + case wordend: + case push_dummy_failure: + continue; + + + case jump_n: + case pop_failure_jump: + case maybe_pop_jump: + case jump: + case jump_past_alt: + case dummy_failure_jump: + EXTRACT_NUMBER_AND_INCR (j, p); + p += j; + if (j > 0) + continue; + + /* Jump backward implies we just went through the body of a + loop and matched nothing. Opcode jumped to should be + `on_failure_jump' or `succeed_n'. Just treat it like an + ordinary jump. For a * loop, it has pushed its failure + point already; if so, discard that as redundant. */ + if ((re_opcode_t) *p != on_failure_jump + && (re_opcode_t) *p != succeed_n) + continue; + + p++; + EXTRACT_NUMBER_AND_INCR (j, p); + p += j; + + /* If what's on the stack is where we are now, pop it. */ + if (!FAIL_STACK_EMPTY () + && fail_stack.stack[fail_stack.avail - 1].pointer == p) + fail_stack.avail--; + + continue; + + + case on_failure_jump: + case on_failure_keep_string_jump: + handle_on_failure_jump: + EXTRACT_NUMBER_AND_INCR (j, p); + + /* For some patterns, e.g., `(a?)?', `p+j' here points to the + end of the pattern. We don't want to push such a point, + since when we restore it above, entering the switch will + increment `p' past the end of the pattern. We don't need + to push such a point since we obviously won't find any more + fastmap entries beyond `pend'. Such a pattern can match + the null string, though. */ + if (p + j < pend) + { + if (!PUSH_PATTERN_OP (p + j, fail_stack)) + { + RESET_FAIL_STACK (); + return -2; + } + } + else + bufp->can_be_null = 1; + + if (succeed_n_p) + { + EXTRACT_NUMBER_AND_INCR (k, p); /* Skip the n. */ + succeed_n_p = false; + } + + continue; + + + case succeed_n: + /* Get to the number of times to succeed. */ + p += 2; + + /* Increment p past the n for when k != 0. */ + EXTRACT_NUMBER_AND_INCR (k, p); + if (k == 0) + { + p -= 4; + succeed_n_p = true; /* Spaghetti code alert. */ + goto handle_on_failure_jump; + } + continue; + + + case set_number_at: + p += 4; + continue; + + + case start_memory: + case stop_memory: + p += 2; + continue; + + + default: + abort (); /* We have listed all the cases. */ + } /* switch *p++ */ + + /* Getting here means we have found the possible starting + characters for one path of the pattern -- and that the empty + string does not match. We need not follow this path further. + Instead, look at the next alternative (remembered on the + stack), or quit if no more. The test at the top of the loop + does these things. */ + path_can_be_null = false; + p = pend; + } /* while p */ + + /* Set `can_be_null' for the last path (also the first path, if the + pattern is empty). */ + bufp->can_be_null |= path_can_be_null; + + done: + RESET_FAIL_STACK (); + return 0; +} /* re_compile_fastmap */ +#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 + must be allocated using the malloc library routine, and must each + be at least NUM_REGS * sizeof (regoff_t) bytes long. + + If NUM_REGS == 0, then subsequent matches should allocate their own + register data. + + Unless this function is called, the first search or match using + PATTERN_BUFFER will allocate its own register data, without + freeing the old data. */ + +void +re_set_registers (bufp, regs, num_regs, starts, ends) + struct re_pattern_buffer *bufp; + struct re_registers *regs; + unsigned num_regs; + regoff_t *starts, *ends; +{ + if (num_regs) + { + bufp->regs_allocated = REGS_REALLOCATE; + regs->num_regs = num_regs; + regs->start = starts; + regs->end = ends; + } + else + { + bufp->regs_allocated = REGS_UNALLOCATED; + regs->num_regs = 0; + regs->start = regs->end = (regoff_t *) 0; + } +} +#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 + doesn't let you say where to stop matching. */ + +int +re_search (bufp, string, size, startpos, range, regs) + struct re_pattern_buffer *bufp; + const char *string; + int size, startpos, range; + struct re_registers *regs; +{ + return re_search_2 (bufp, NULL, 0, string, size, startpos, range, + regs, size); +} +#ifdef _LIBC +weak_alias (__re_search, re_search) +#endif + + +/* Using the compiled pattern in BUFP->buffer, first tries to match the + virtual concatenation of STRING1 and STRING2, starting first at index + STARTPOS, then at STARTPOS + 1, and so on. + + STRING1 and STRING2 have length SIZE1 and SIZE2, respectively. + + RANGE is how far to scan while trying to match. RANGE = 0 means try + only at STARTPOS; in general, the last start tried is STARTPOS + + RANGE. + + In REGS, return the indices of the virtual concatenation of STRING1 + and STRING2 that matched the entire BUFP->buffer and its contained + subexpressions. + + Do not consider matching one past the index STOP in the virtual + concatenation of STRING1 and STRING2. + + We return either the position in the strings at which the match was + found, -1 if no match, or -2 if error (such as failure + stack overflow). */ + +int +re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop) + struct re_pattern_buffer *bufp; + const char *string1, *string2; + int size1, size2; + int startpos; + int range; + struct re_registers *regs; + int stop; +{ + int val; + register char *fastmap = bufp->fastmap; + register RE_TRANSLATE_TYPE translate = bufp->translate; + int total_size = size1 + size2; + int endpos = startpos + range; + + /* Check for out-of-range STARTPOS. */ + if (startpos < 0 || startpos > total_size) + return -1; + + /* Fix up RANGE if it might eventually take us outside + the virtual concatenation of STRING1 and STRING2. + Make sure we won't move STARTPOS below 0 or above TOTAL_SIZE. */ + if (endpos < 0) + range = 0 - startpos; + else if (endpos > total_size) + range = total_size - startpos; + + /* If the search isn't to be a backwards one, don't waste time in a + search for a pattern that must be anchored. */ + if (bufp->used > 0 && range > 0 + && ((re_opcode_t) bufp->buffer[0] == begbuf + /* `begline' is like `begbuf' if it cannot match at newlines. */ + || ((re_opcode_t) bufp->buffer[0] == begline + && !bufp->newline_anchor))) + { + if (startpos > 0) + return -1; + else + range = 1; + } + +#ifdef emacs + /* In a forward search for something that starts with \=. + don't keep searching past point. */ + if (bufp->used > 0 && (re_opcode_t) bufp->buffer[0] == at_dot && range > 0) + { + range = PT - startpos; + if (range <= 0) + return -1; + } +#endif /* emacs */ + + /* Update the fastmap now if not correct already. */ + if (fastmap && !bufp->fastmap_accurate) + if (re_compile_fastmap (bufp) == -2) + return -2; + + /* Loop through the string, looking for a place to start matching. */ + for (;;) + { + /* If a fastmap is supplied, skip quickly over characters that + cannot be the start of a match. If the pattern can match the + null string, however, we don't need to skip characters; we want + the first null string. */ + if (fastmap && startpos < total_size && !bufp->can_be_null) + { + if (range > 0) /* Searching forwards. */ + { + register const char *d; + register int lim = 0; + int irange = range; + + if (startpos < size1 && startpos + range >= size1) + lim = range - (size1 - startpos); + + d = (startpos >= size1 ? string2 - size1 : string1) + startpos; + + /* Written out as an if-else to avoid testing `translate' + inside the loop. */ + if (translate) + while (range > lim + && !fastmap[(unsigned char) + translate[(unsigned char) *d++]]) + range--; + else + while (range > lim && !fastmap[(unsigned char) *d++]) + range--; + + startpos += irange - range; + } + else /* Searching backwards. */ + { + register char c = (size1 == 0 || startpos >= size1 + ? string2[startpos - size1] + : string1[startpos]); + + if (!fastmap[(unsigned char) TRANSLATE (c)]) + goto advance; + } + } + + /* If can't match the null string, and that's all we have left, fail. */ + if (range >= 0 && startpos == total_size && fastmap + && !bufp->can_be_null) + return -1; + + val = re_match_2_internal (bufp, string1, size1, string2, size2, + startpos, regs, stop); +#ifndef REGEX_MALLOC +# ifdef C_ALLOCA + alloca (0); +# endif +#endif + + if (val >= 0) + return startpos; + + if (val == -2) + return -2; + + advance: + if (!range) + break; + else if (range > 0) + { + range--; + startpos++; + } + else + { + range++; + startpos--; + } + } + return -1; +} /* re_search_2 */ +#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) \ + (FIRST_STRING_P (ptr) \ + ? ((regoff_t) ((ptr) - string1)) \ + : ((regoff_t) ((ptr) - string2 + size1))) + +/* Macros for dealing with the split strings in re_match_2. */ + +#define MATCHING_IN_FIRST_STRING (dend == end_match_1) + +/* Call before fetching a character with *d. This switches over to + string2 if necessary. */ +#define PREFETCH() \ + while (d == dend) \ + { \ + /* End of string2 => fail. */ \ + if (dend == end_match_2) \ + goto fail; \ + /* End of string1 => advance to string2. */ \ + d = string2; \ + dend = end_match_2; \ + } + + +/* Test if at very beginning or at very end of the virtual concatenation + of `string1' and `string2'. If only one string, it's `string2'. */ +#define AT_STRINGS_BEG(d) ((d) == (size1 ? string1 : string2) || !size2) +#define AT_STRINGS_END(d) ((d) == end2) + + +/* Test if D points to a character which is word-constituent. We have + two special cases to check for: if past the end of string1, look at + the first character in string2; and if before the beginning of + string2, look at the last character in string1. */ +#define WORDCHAR_P(d) \ + (SYNTAX ((d) == end1 ? *string2 \ + : (d) == string2 - 1 ? *(end1 - 1) : *(d)) \ + == Sword) + +/* Disabled due to a compiler bug -- see comment at case wordbound */ +#if 0 +/* Test if the character before D and the one at D differ with respect + to being word-constituent. */ +#define AT_WORD_BOUNDARY(d) \ + (AT_STRINGS_BEG (d) || AT_STRINGS_END (d) \ + || WORDCHAR_P (d - 1) != WORDCHAR_P (d)) +#endif + +/* Free everything we malloc. */ +#ifdef MATCH_MAY_ALLOCATE +# define FREE_VAR(var) if (var) REGEX_FREE (var); var = NULL +# define FREE_VARIABLES() \ + do { \ + REGEX_FREE_STACK (fail_stack.stack); \ + FREE_VAR (regstart); \ + FREE_VAR (regend); \ + FREE_VAR (old_regstart); \ + FREE_VAR (old_regend); \ + FREE_VAR (best_regstart); \ + FREE_VAR (best_regend); \ + FREE_VAR (reg_info); \ + FREE_VAR (reg_dummy); \ + FREE_VAR (reg_info_dummy); \ + } while (0) +#else +# define FREE_VARIABLES() ((void)0) /* Do nothing! But inhibit gcc warning. */ +#endif /* not MATCH_MAY_ALLOCATE */ + +/* These values must meet several constraints. They must not be valid + register values; since we have a limit of 255 registers (because + we use only one byte in the pattern for the register number), we can + use numbers larger than 255. They must differ by 1, because of + NUM_FAILURE_ITEMS above. And the value for the lowest register must + be larger than the value for the highest register, so we do not try + 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. */ +/* re_match is like re_match_2 except it takes only a single string. */ + +int +re_match (bufp, string, size, pos, regs) + struct re_pattern_buffer *bufp; + const char *string; + int size, pos; + struct re_registers *regs; +{ + int result = re_match_2_internal (bufp, NULL, 0, string, size, + pos, regs, size); +# ifndef REGEX_MALLOC +# ifdef C_ALLOCA + alloca (0); +# endif +# endif + return result; +} +# ifdef _LIBC +weak_alias (__re_match, re_match) +# endif +#endif /* not emacs */ + +static boolean group_match_null_string_p _RE_ARGS ((unsigned char **p, + unsigned char *end, + register_info_type *reg_info)); +static boolean alt_match_null_string_p _RE_ARGS ((unsigned char *p, + unsigned char *end, + register_info_type *reg_info)); +static boolean common_op_match_null_string_p _RE_ARGS ((unsigned char **p, + unsigned char *end, + register_info_type *reg_info)); +static int bcmp_translate _RE_ARGS ((const char *s1, const char *s2, + int len, char *translate)); + +/* re_match_2 matches the compiled pattern in BUFP against the + the (virtual) concatenation of STRING1 and STRING2 (of length SIZE1 + and SIZE2, respectively). We start matching at POS, and stop + matching at STOP. + + If REGS is non-null and the `no_sub' field of BUFP is nonzero, we + store offsets for the substring each group matched in REGS. See the + documentation for exactly how many groups we fill. + + We return -1 if no match, -2 if an internal error (such as the + failure stack overflowing). Otherwise, we return the length of the + matched substring. */ + +int +re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) + struct re_pattern_buffer *bufp; + const char *string1, *string2; + int size1, size2; + int pos; + struct re_registers *regs; + int stop; +{ + int result = re_match_2_internal (bufp, string1, size1, string2, size2, + pos, regs, stop); +#ifndef REGEX_MALLOC +# ifdef C_ALLOCA + alloca (0); +# endif +#endif + return result; +} +#ifdef _LIBC +weak_alias (__re_match_2, re_match_2) +#endif + +/* This is a separate function so that we can force an alloca cleanup + afterwards. */ +static int +re_match_2_internal (bufp, string1, size1, string2, size2, pos, regs, stop) + struct re_pattern_buffer *bufp; + const char *string1, *string2; + int size1, size2; + int pos; + struct re_registers *regs; + int stop; +{ + /* General temporaries. */ + int mcnt; + unsigned char *p1; + + /* Just past the end of the corresponding string. */ + const char *end1, *end2; + + /* Pointers into string1 and string2, just past the last characters in + each to consider matching. */ + const char *end_match_1, *end_match_2; + + /* Where we are in the data, and the end of the current string. */ + const char *d, *dend; + + /* Where we are in the pattern, and the end of the pattern. */ + unsigned char *p = bufp->buffer; + register unsigned char *pend = p + bufp->used; + + /* Mark the opcode just after a start_memory, so we can test for an + empty subpattern when we get to the stop_memory. */ + unsigned char *just_past_start_mem = 0; + + /* We use this to map every character in the string. */ + RE_TRANSLATE_TYPE translate = bufp->translate; + + /* Failure point stack. Each place that can handle a failure further + down the line pushes a failure point on this stack. It consists of + restart, regend, and reg_info for all registers corresponding to + the subexpressions we're currently inside, plus the number of such + registers, and, finally, two char *'s. The first char * is where + to resume scanning the pattern; the second one is where to resume + scanning the strings. If the latter is zero, the failure point is + a ``dummy''; if a failure happens and the failure point is a dummy, + it gets discarded and the next next one is tried. */ +#ifdef MATCH_MAY_ALLOCATE /* otherwise, this is global. */ + fail_stack_type fail_stack; +#endif +#ifdef DEBUG + static unsigned failure_id; + unsigned nfailure_points_pushed = 0, nfailure_points_popped = 0; +#endif + +#ifdef REL_ALLOC + /* This holds the pointer to the failure stack, when + it is allocated relocatably. */ + fail_stack_elt_t *failure_stack_ptr; +#endif + + /* We fill all the registers internally, independent of what we + return, for use in backreferences. The number here includes + an element for register zero. */ + size_t num_regs = bufp->re_nsub + 1; + + /* The currently active registers. */ + active_reg_t lowest_active_reg = NO_LOWEST_ACTIVE_REG; + active_reg_t highest_active_reg = NO_HIGHEST_ACTIVE_REG; + + /* Information on the contents of registers. These are pointers into + the input strings; they record just what was matched (on this + attempt) by a subexpression part of the pattern, that is, the + regnum-th regstart pointer points to where in the pattern we began + matching and the regnum-th regend points to right after where we + stopped matching the regnum-th subexpression. (The zeroth register + keeps track of what the whole pattern matches.) */ +#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global. */ + const char **regstart, **regend; +#endif + + /* If a group that's operated upon by a repetition operator fails to + match anything, then the register for its start will need to be + restored because it will have been set to wherever in the string we + are when we last see its open-group operator. Similarly for a + register's end. */ +#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global. */ + const char **old_regstart, **old_regend; +#endif + + /* The is_active field of reg_info helps us keep track of which (possibly + nested) subexpressions we are currently in. The matched_something + field of reg_info[reg_num] helps us tell whether or not we have + matched any of the pattern so far this time through the reg_num-th + subexpression. These two fields get reset each time through any + loop their register is in. */ +#ifdef MATCH_MAY_ALLOCATE /* otherwise, this is global. */ + register_info_type *reg_info; +#endif + + /* The following record the register info as found in the above + variables when we find a match better than any we've seen before. + This happens as we backtrack through the failure points, which in + turn happens only if we have not yet matched the entire string. */ + unsigned best_regs_set = false; +#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global. */ + const char **best_regstart, **best_regend; +#endif + + /* Logically, this is `best_regend[0]'. But we don't want to have to + allocate space for that if we're not allocating space for anything + else (see below). Also, we never need info about register 0 for + any of the other register vectors, and it seems rather a kludge to + treat `best_regend' differently than the rest. So we keep track of + the end of the best match so far in a separate variable. We + initialize this to NULL so that when we backtrack the first time + and need to test it, it's not garbage. */ + const char *match_end = NULL; + + /* This helps SET_REGS_MATCHED avoid doing redundant work. */ + int set_regs_matched_done = 0; + + /* Used when we pop values we don't care about. */ +#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global. */ + const char **reg_dummy; + register_info_type *reg_info_dummy; +#endif + +#ifdef DEBUG + /* Counts the total number of registers pushed. */ + unsigned num_regs_pushed = 0; +#endif + + DEBUG_PRINT1 ("\n\nEntering re_match_2.\n"); + + INIT_FAIL_STACK (); + +#ifdef MATCH_MAY_ALLOCATE + /* Do not bother to initialize all the register variables if there are + no groups in the pattern, as it takes a fair amount of time. If + there are groups, we include space for register 0 (the whole + pattern), even though we never use it, since it simplifies the + array indexing. We should fix this. */ + if (bufp->re_nsub) + { + regstart = REGEX_TALLOC (num_regs, const char *); + regend = REGEX_TALLOC (num_regs, const char *); + old_regstart = REGEX_TALLOC (num_regs, const char *); + old_regend = REGEX_TALLOC (num_regs, const char *); + best_regstart = REGEX_TALLOC (num_regs, const char *); + best_regend = REGEX_TALLOC (num_regs, const char *); + reg_info = REGEX_TALLOC (num_regs, register_info_type); + reg_dummy = REGEX_TALLOC (num_regs, const char *); + reg_info_dummy = REGEX_TALLOC (num_regs, register_info_type); + + if (!(regstart && regend && old_regstart && old_regend && reg_info + && best_regstart && best_regend && reg_dummy && reg_info_dummy)) + { + FREE_VARIABLES (); + return -2; + } + } + else + { + /* We must initialize all our variables to NULL, so that + `FREE_VARIABLES' doesn't try to free them. */ + regstart = regend = old_regstart = old_regend = best_regstart + = best_regend = reg_dummy = NULL; + reg_info = reg_info_dummy = (register_info_type *) NULL; + } +#endif /* MATCH_MAY_ALLOCATE */ + + /* The starting position is bogus. */ + if (pos < 0 || pos > size1 + size2) + { + FREE_VARIABLES (); + return -1; + } + + /* Initialize subexpression text positions to -1 to mark ones that no + start_memory/stop_memory has been seen for. Also initialize the + register information struct. */ + for (mcnt = 1; (unsigned) mcnt < num_regs; mcnt++) + { + regstart[mcnt] = regend[mcnt] + = old_regstart[mcnt] = old_regend[mcnt] = REG_UNSET_VALUE; + + REG_MATCH_NULL_STRING_P (reg_info[mcnt]) = MATCH_NULL_UNSET_VALUE; + IS_ACTIVE (reg_info[mcnt]) = 0; + MATCHED_SOMETHING (reg_info[mcnt]) = 0; + EVER_MATCHED_SOMETHING (reg_info[mcnt]) = 0; + } + + /* We move `string1' into `string2' if the latter's empty -- but not if + `string1' is null. */ + if (size2 == 0 && string1 != NULL) + { + string2 = string1; + size2 = size1; + string1 = 0; + size1 = 0; + } + end1 = string1 + size1; + end2 = string2 + size2; + + /* Compute where to stop matching, within the two strings. */ + if (stop <= size1) + { + end_match_1 = string1 + stop; + end_match_2 = string2; + } + else + { + end_match_1 = end1; + end_match_2 = string2 + stop - size1; + } + + /* `p' scans through the pattern as `d' scans through the data. + `dend' is the end of the input string that `d' points within. `d' + is advanced into the following input string whenever necessary, but + this happens before fetching; therefore, at the beginning of the + loop, `d' can be pointing at the end of a string, but it cannot + equal `string2'. */ + if (size1 > 0 && pos <= size1) + { + d = string1 + pos; + dend = end_match_1; + } + else + { + d = string2 + pos - size1; + dend = end_match_2; + } + + DEBUG_PRINT1 ("The compiled pattern is:\n"); + DEBUG_PRINT_COMPILED_PATTERN (bufp, p, pend); + DEBUG_PRINT1 ("The string to match is: `"); + DEBUG_PRINT_DOUBLE_STRING (d, string1, size1, string2, size2); + DEBUG_PRINT1 ("'\n"); + + /* This loops over pattern commands. It exits by returning from the + function if the match is complete, or it drops through if the match + fails at this starting point in the input data. */ + for (;;) + { +#ifdef _LIBC + DEBUG_PRINT2 ("\n%p: ", p); +#else + DEBUG_PRINT2 ("\n0x%x: ", p); +#endif + + if (p == pend) + { /* End of pattern means we might have succeeded. */ + DEBUG_PRINT1 ("end of pattern ... "); + + /* If we haven't matched the entire string, and we want the + longest match, try backtracking. */ + if (d != end_match_2) + { + /* 1 if this match ends in the same string (string1 or string2) + as the best previous match. */ + boolean same_str_p = (FIRST_STRING_P (match_end) + == MATCHING_IN_FIRST_STRING); + /* 1 if this match is the best seen so far. */ + boolean best_match_p; + + /* AIX compiler got confused when this was combined + with the previous declaration. */ + if (same_str_p) + best_match_p = d > match_end; + else + best_match_p = !MATCHING_IN_FIRST_STRING; + + DEBUG_PRINT1 ("backtracking.\n"); + + if (!FAIL_STACK_EMPTY ()) + { /* More failure points to try. */ + + /* If exceeds best match so far, save it. */ + if (!best_regs_set || best_match_p) + { + best_regs_set = true; + match_end = d; + + DEBUG_PRINT1 ("\nSAVING match as best so far.\n"); + + for (mcnt = 1; (unsigned) mcnt < num_regs; mcnt++) + { + best_regstart[mcnt] = regstart[mcnt]; + best_regend[mcnt] = regend[mcnt]; + } + } + goto fail; + } + + /* If no failure points, don't restore garbage. And if + last match is real best match, don't restore second + best one. */ + else if (best_regs_set && !best_match_p) + { + restore_best_regs: + /* Restore best match. It may happen that `dend == + end_match_1' while the restored d is in string2. + For example, the pattern `x.*y.*z' against the + strings `x-' and `y-z-', if the two strings are + not consecutive in memory. */ + DEBUG_PRINT1 ("Restoring best registers.\n"); + + d = match_end; + dend = ((d >= string1 && d <= end1) + ? end_match_1 : end_match_2); + + for (mcnt = 1; (unsigned) mcnt < num_regs; mcnt++) + { + regstart[mcnt] = best_regstart[mcnt]; + regend[mcnt] = best_regend[mcnt]; + } + } + } /* d != end_match_2 */ + + succeed_label: + DEBUG_PRINT1 ("Accepting match.\n"); + + /* If caller wants register contents data back, do it. */ + if (regs && !bufp->no_sub) + { + /* Have the register data arrays been allocated? */ + if (bufp->regs_allocated == REGS_UNALLOCATED) + { /* No. So allocate them with malloc. We need one + extra element beyond `num_regs' for the `-1' marker + GNU code uses. */ + regs->num_regs = MAX (RE_NREGS, num_regs + 1); + regs->start = TALLOC (regs->num_regs, regoff_t); + regs->end = TALLOC (regs->num_regs, regoff_t); + if (regs->start == NULL || regs->end == NULL) + { + FREE_VARIABLES (); + return -2; + } + bufp->regs_allocated = REGS_REALLOCATE; + } + else if (bufp->regs_allocated == REGS_REALLOCATE) + { /* Yes. If we need more elements than were already + allocated, reallocate them. If we need fewer, just + leave it alone. */ + if (regs->num_regs < num_regs + 1) + { + regs->num_regs = num_regs + 1; + RETALLOC (regs->start, regs->num_regs, regoff_t); + RETALLOC (regs->end, regs->num_regs, regoff_t); + if (regs->start == NULL || regs->end == NULL) + { + FREE_VARIABLES (); + return -2; + } + } + } + else + { + /* These braces fend off a "empty body in an else-statement" + warning under GCC when assert expands to nothing. */ + assert (bufp->regs_allocated == REGS_FIXED); + } + + /* Convert the pointer data in `regstart' and `regend' to + indices. Register zero has to be set differently, + since we haven't kept track of any info for it. */ + if (regs->num_regs > 0) + { + regs->start[0] = pos; + regs->end[0] = (MATCHING_IN_FIRST_STRING + ? ((regoff_t) (d - string1)) + : ((regoff_t) (d - string2 + size1))); + } + + /* Go through the first `min (num_regs, regs->num_regs)' + registers, since that is all we initialized. */ + for (mcnt = 1; (unsigned) mcnt < MIN (num_regs, regs->num_regs); + mcnt++) + { + if (REG_UNSET (regstart[mcnt]) || REG_UNSET (regend[mcnt])) + regs->start[mcnt] = regs->end[mcnt] = -1; + else + { + regs->start[mcnt] + = (regoff_t) POINTER_TO_OFFSET (regstart[mcnt]); + regs->end[mcnt] + = (regoff_t) POINTER_TO_OFFSET (regend[mcnt]); + } + } + + /* If the regs structure we return has more elements than + were in the pattern, set the extra elements to -1. If + we (re)allocated the registers, this is the case, + because we always allocate enough to have at least one + -1 at the end. */ + for (mcnt = num_regs; (unsigned) mcnt < regs->num_regs; mcnt++) + regs->start[mcnt] = regs->end[mcnt] = -1; + } /* regs && !bufp->no_sub */ + + DEBUG_PRINT4 ("%u failure points pushed, %u popped (%u remain).\n", + nfailure_points_pushed, nfailure_points_popped, + nfailure_points_pushed - nfailure_points_popped); + DEBUG_PRINT2 ("%u registers pushed.\n", num_regs_pushed); + + mcnt = d - pos - (MATCHING_IN_FIRST_STRING + ? string1 + : string2 - size1); + + DEBUG_PRINT2 ("Returning %d from re_match_2.\n", mcnt); + + FREE_VARIABLES (); + return mcnt; + } + + /* Otherwise match next pattern command. */ + switch (SWITCH_ENUM_CAST ((re_opcode_t) *p++)) + { + /* Ignore these. Used to ignore the n of succeed_n's which + currently have n == 0. */ + case no_op: + DEBUG_PRINT1 ("EXECUTING no_op.\n"); + break; + + case succeed: + DEBUG_PRINT1 ("EXECUTING succeed.\n"); + goto succeed_label; + + /* Match the next n pattern characters exactly. The following + byte in the pattern defines n, and the n bytes after that + are the characters to match. */ + case exactn: + mcnt = *p++; + DEBUG_PRINT2 ("EXECUTING exactn %d.\n", mcnt); + + /* This is written out as an if-else so we don't waste time + testing `translate' inside the loop. */ + if (translate) + { + do + { + PREFETCH (); + if ((unsigned char) translate[(unsigned char) *d++] + != (unsigned char) *p++) + goto fail; + } + while (--mcnt); + } + else + { + do + { + PREFETCH (); + if (*d++ != (char) *p++) goto fail; + } + while (--mcnt); + } + SET_REGS_MATCHED (); + break; + + + /* Match any character except possibly a newline or a null. */ + case anychar: + DEBUG_PRINT1 ("EXECUTING anychar.\n"); + + PREFETCH (); + + if ((!(bufp->syntax & RE_DOT_NEWLINE) && TRANSLATE (*d) == '\n') + || (bufp->syntax & RE_DOT_NOT_NULL && TRANSLATE (*d) == '\000')) + goto fail; + + SET_REGS_MATCHED (); + DEBUG_PRINT2 (" Matched `%d'.\n", *d); + d++; + break; + + + case charset: + case charset_not: + { + register unsigned char c; + boolean not = (re_opcode_t) *(p - 1) == charset_not; + + DEBUG_PRINT2 ("EXECUTING charset%s.\n", not ? "_not" : ""); + + PREFETCH (); + c = TRANSLATE (*d); /* The character to match. */ + + /* Cast to `unsigned' instead of `unsigned char' in case the + bit list is a full 32 bytes long. */ + if (c < (unsigned) (*p * BYTEWIDTH) + && p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH))) + not = !not; + + p += 1 + *p; + + if (!not) goto fail; + + SET_REGS_MATCHED (); + d++; + break; + } + + + /* The beginning of a group is represented by start_memory. + The arguments are the register number in the next byte, and the + number of groups inner to this one in the next. The text + matched within the group is recorded (in the internal + registers data structure) under the register number. */ + case start_memory: + DEBUG_PRINT3 ("EXECUTING start_memory %d (%d):\n", *p, p[1]); + + /* Find out if this group can match the empty string. */ + p1 = p; /* To send to group_match_null_string_p. */ + + if (REG_MATCH_NULL_STRING_P (reg_info[*p]) == MATCH_NULL_UNSET_VALUE) + REG_MATCH_NULL_STRING_P (reg_info[*p]) + = group_match_null_string_p (&p1, pend, reg_info); + + /* Save the position in the string where we were the last time + we were at this open-group operator in case the group is + operated upon by a repetition operator, e.g., with `(a*)*b' + against `ab'; then we want to ignore where we are now in + the string in case this attempt to match fails. */ + old_regstart[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p]) + ? REG_UNSET (regstart[*p]) ? d : regstart[*p] + : regstart[*p]; + DEBUG_PRINT2 (" old_regstart: %d\n", + POINTER_TO_OFFSET (old_regstart[*p])); + + regstart[*p] = d; + DEBUG_PRINT2 (" regstart: %d\n", POINTER_TO_OFFSET (regstart[*p])); + + IS_ACTIVE (reg_info[*p]) = 1; + MATCHED_SOMETHING (reg_info[*p]) = 0; + + /* Clear this whenever we change the register activity status. */ + set_regs_matched_done = 0; + + /* This is the new highest active register. */ + highest_active_reg = *p; + + /* If nothing was active before, this is the new lowest active + register. */ + if (lowest_active_reg == NO_LOWEST_ACTIVE_REG) + lowest_active_reg = *p; + + /* Move past the register number and inner group count. */ + p += 2; + just_past_start_mem = p; + + break; + + + /* The stop_memory opcode represents the end of a group. Its + arguments are the same as start_memory's: the register + number, and the number of inner groups. */ + case stop_memory: + DEBUG_PRINT3 ("EXECUTING stop_memory %d (%d):\n", *p, p[1]); + + /* We need to save the string position the last time we were at + this close-group operator in case the group is operated + upon by a repetition operator, e.g., with `((a*)*(b*)*)*' + against `aba'; then we want to ignore where we are now in + the string in case this attempt to match fails. */ + old_regend[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p]) + ? REG_UNSET (regend[*p]) ? d : regend[*p] + : regend[*p]; + DEBUG_PRINT2 (" old_regend: %d\n", + POINTER_TO_OFFSET (old_regend[*p])); + + regend[*p] = d; + DEBUG_PRINT2 (" regend: %d\n", POINTER_TO_OFFSET (regend[*p])); + + /* This register isn't active anymore. */ + IS_ACTIVE (reg_info[*p]) = 0; + + /* Clear this whenever we change the register activity status. */ + set_regs_matched_done = 0; + + /* If this was the only register active, nothing is active + anymore. */ + if (lowest_active_reg == highest_active_reg) + { + lowest_active_reg = NO_LOWEST_ACTIVE_REG; + highest_active_reg = NO_HIGHEST_ACTIVE_REG; + } + else + { /* We must scan for the new highest active register, since + it isn't necessarily one less than now: consider + (a(b)c(d(e)f)g). When group 3 ends, after the f), the + new highest active register is 1. */ + unsigned char r = *p - 1; + while (r > 0 && !IS_ACTIVE (reg_info[r])) + r--; + + /* If we end up at register zero, that means that we saved + the registers as the result of an `on_failure_jump', not + a `start_memory', and we jumped to past the innermost + `stop_memory'. For example, in ((.)*) we save + registers 1 and 2 as a result of the *, but when we pop + back to the second ), we are at the stop_memory 1. + Thus, nothing is active. */ + if (r == 0) + { + lowest_active_reg = NO_LOWEST_ACTIVE_REG; + highest_active_reg = NO_HIGHEST_ACTIVE_REG; + } + else + highest_active_reg = r; + } + + /* If just failed to match something this time around with a + group that's operated on by a repetition operator, try to + force exit from the ``loop'', and restore the register + information for this group that we had before trying this + last match. */ + if ((!MATCHED_SOMETHING (reg_info[*p]) + || just_past_start_mem == p - 1) + && (p + 2) < pend) + { + boolean is_a_jump_n = false; + + p1 = p + 2; + mcnt = 0; + switch ((re_opcode_t) *p1++) + { + case jump_n: + is_a_jump_n = true; + case pop_failure_jump: + case maybe_pop_jump: + case jump: + case dummy_failure_jump: + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + if (is_a_jump_n) + p1 += 2; + break; + + default: + /* do nothing */ ; + } + p1 += mcnt; + + /* If the next operation is a jump backwards in the pattern + to an on_failure_jump right before the start_memory + corresponding to this stop_memory, exit from the loop + by forcing a failure after pushing on the stack the + on_failure_jump's jump in the pattern, and d. */ + if (mcnt < 0 && (re_opcode_t) *p1 == on_failure_jump + && (re_opcode_t) p1[3] == start_memory && p1[4] == *p) + { + /* If this group ever matched anything, then restore + what its registers were before trying this last + failed match, e.g., with `(a*)*b' against `ab' for + regstart[1], and, e.g., with `((a*)*(b*)*)*' + against `aba' for regend[3]. + + Also restore the registers for inner groups for, + e.g., `((a*)(b*))*' against `aba' (register 3 would + otherwise get trashed). */ + + if (EVER_MATCHED_SOMETHING (reg_info[*p])) + { + unsigned r; + + EVER_MATCHED_SOMETHING (reg_info[*p]) = 0; + + /* Restore this and inner groups' (if any) registers. */ + for (r = *p; r < (unsigned) *p + (unsigned) *(p + 1); + r++) + { + regstart[r] = old_regstart[r]; + + /* xx why this test? */ + if (old_regend[r] >= regstart[r]) + regend[r] = old_regend[r]; + } + } + p1++; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + PUSH_FAILURE_POINT (p1 + mcnt, d, -2); + + goto fail; + } + } + + /* Move past the register number and the inner group count. */ + p += 2; + break; + + + /* \ has been turned into a `duplicate' command which is + followed by the numeric value of as the register number. */ + case duplicate: + { + register const char *d2, *dend2; + int regno = *p++; /* Get which register to match against. */ + DEBUG_PRINT2 ("EXECUTING duplicate %d.\n", regno); + + /* Can't back reference a group which we've never matched. */ + if (REG_UNSET (regstart[regno]) || REG_UNSET (regend[regno])) + goto fail; + + /* Where in input to try to start matching. */ + d2 = regstart[regno]; + + /* Where to stop matching; if both the place to start and + the place to stop matching are in the same string, then + set to the place to stop, otherwise, for now have to use + the end of the first string. */ + + dend2 = ((FIRST_STRING_P (regstart[regno]) + == FIRST_STRING_P (regend[regno])) + ? regend[regno] : end_match_1); + for (;;) + { + /* If necessary, advance to next segment in register + contents. */ + while (d2 == dend2) + { + if (dend2 == end_match_2) break; + if (dend2 == regend[regno]) break; + + /* End of string1 => advance to string2. */ + d2 = string2; + dend2 = regend[regno]; + } + /* At end of register contents => success */ + if (d2 == dend2) break; + + /* If necessary, advance to next segment in data. */ + PREFETCH (); + + /* How many characters left in this segment to match. */ + mcnt = dend - d; + + /* Want how many consecutive characters we can match in + one shot, so, if necessary, adjust the count. */ + if (mcnt > dend2 - d2) + mcnt = dend2 - d2; + + /* Compare that many; failure if mismatch, else move + past them. */ + if (translate + ? bcmp_translate (d, d2, mcnt, translate) + : memcmp (d, d2, mcnt)) + goto fail; + d += mcnt, d2 += mcnt; + + /* Do this because we've match some characters. */ + SET_REGS_MATCHED (); + } + } + break; + + + /* begline matches the empty string at the beginning of the string + (unless `not_bol' is set in `bufp'), and, if + `newline_anchor' is set, after newlines. */ + case begline: + DEBUG_PRINT1 ("EXECUTING begline.\n"); + + if (AT_STRINGS_BEG (d)) + { + if (!bufp->not_bol) break; + } + else if (d[-1] == '\n' && bufp->newline_anchor) + { + break; + } + /* In all other cases, we fail. */ + goto fail; + + + /* endline is the dual of begline. */ + case endline: + DEBUG_PRINT1 ("EXECUTING endline.\n"); + + if (AT_STRINGS_END (d)) + { + if (!bufp->not_eol) break; + } + + /* We have to ``prefetch'' the next character. */ + else if ((d == end1 ? *string2 : *d) == '\n' + && bufp->newline_anchor) + { + break; + } + goto fail; + + + /* Match at the very beginning of the data. */ + case begbuf: + DEBUG_PRINT1 ("EXECUTING begbuf.\n"); + if (AT_STRINGS_BEG (d)) + break; + goto fail; + + + /* Match at the very end of the data. */ + case endbuf: + DEBUG_PRINT1 ("EXECUTING endbuf.\n"); + if (AT_STRINGS_END (d)) + break; + goto fail; + + + /* on_failure_keep_string_jump is used to optimize `.*\n'. It + pushes NULL as the value for the string on the stack. Then + `pop_failure_point' will keep the current value for the + string, instead of restoring it. To see why, consider + matching `foo\nbar' against `.*\n'. The .* matches the foo; + then the . fails against the \n. But the next thing we want + to do is match the \n against the \n; if we restored the + string value, we would be back at the foo. + + Because this is used only in specific cases, we don't need to + check all the things that `on_failure_jump' does, to make + sure the right things get saved on the stack. Hence we don't + share its code. The only reason to push anything on the + stack at all is that otherwise we would have to change + `anychar's code to do something besides goto fail in this + case; that seems worse than this. */ + case on_failure_keep_string_jump: + DEBUG_PRINT1 ("EXECUTING on_failure_keep_string_jump"); + + EXTRACT_NUMBER_AND_INCR (mcnt, p); +#ifdef _LIBC + DEBUG_PRINT3 (" %d (to %p):\n", mcnt, p + mcnt); +#else + DEBUG_PRINT3 (" %d (to 0x%x):\n", mcnt, p + mcnt); +#endif + + PUSH_FAILURE_POINT (p + mcnt, NULL, -2); + break; + + + /* Uses of on_failure_jump: + + Each alternative starts with an on_failure_jump that points + to the beginning of the next alternative. Each alternative + except the last ends with a jump that in effect jumps past + the rest of the alternatives. (They really jump to the + ending jump of the following alternative, because tensioning + these jumps is a hassle.) + + Repeats start with an on_failure_jump that points past both + the repetition text and either the following jump or + pop_failure_jump back to this on_failure_jump. */ + case on_failure_jump: + on_failure: + DEBUG_PRINT1 ("EXECUTING on_failure_jump"); + + EXTRACT_NUMBER_AND_INCR (mcnt, p); +#ifdef _LIBC + DEBUG_PRINT3 (" %d (to %p)", mcnt, p + mcnt); +#else + DEBUG_PRINT3 (" %d (to 0x%x)", mcnt, p + mcnt); +#endif + + /* If this on_failure_jump comes right before a group (i.e., + the original * applied to a group), save the information + for that group and all inner ones, so that if we fail back + to this point, the group's information will be correct. + For example, in \(a*\)*\1, we need the preceding group, + and in \(zz\(a*\)b*\)\2, we need the inner group. */ + + /* We can't use `p' to check ahead because we push + a failure point to `p + mcnt' after we do this. */ + p1 = p; + + /* We need to skip no_op's before we look for the + start_memory in case this on_failure_jump is happening as + the result of a completed succeed_n, as in \(a\)\{1,3\}b\1 + against aba. */ + while (p1 < pend && (re_opcode_t) *p1 == no_op) + p1++; + + if (p1 < pend && (re_opcode_t) *p1 == start_memory) + { + /* We have a new highest active register now. This will + get reset at the start_memory we are about to get to, + but we will have saved all the registers relevant to + this repetition op, as described above. */ + highest_active_reg = *(p1 + 1) + *(p1 + 2); + if (lowest_active_reg == NO_LOWEST_ACTIVE_REG) + lowest_active_reg = *(p1 + 1); + } + + DEBUG_PRINT1 (":\n"); + PUSH_FAILURE_POINT (p + mcnt, d, -2); + break; + + + /* A smart repeat ends with `maybe_pop_jump'. + We change it to either `pop_failure_jump' or `jump'. */ + case maybe_pop_jump: + EXTRACT_NUMBER_AND_INCR (mcnt, p); + DEBUG_PRINT2 ("EXECUTING maybe_pop_jump %d.\n", mcnt); + { + register unsigned char *p2 = p; + + /* Compare the beginning of the repeat with what in the + pattern follows its end. If we can establish that there + is nothing that they would both match, i.e., that we + would have to backtrack because of (as in, e.g., `a*a') + then we can change to pop_failure_jump, because we'll + never have to backtrack. + + This is not true in the case of alternatives: in + `(a|ab)*' we do need to backtrack to the `ab' alternative + (e.g., if the string was `ab'). But instead of trying to + detect that here, the alternative has put on a dummy + failure point which is what we will end up popping. */ + + /* Skip over open/close-group commands. + If what follows this loop is a ...+ construct, + look at what begins its body, since we will have to + match at least one of that. */ + while (1) + { + if (p2 + 2 < pend + && ((re_opcode_t) *p2 == stop_memory + || (re_opcode_t) *p2 == start_memory)) + p2 += 3; + else if (p2 + 6 < pend + && (re_opcode_t) *p2 == dummy_failure_jump) + p2 += 6; + else + break; + } + + p1 = p + mcnt; + /* p1[0] ... p1[2] are the `on_failure_jump' corresponding + to the `maybe_finalize_jump' of this case. Examine what + follows. */ + + /* If we're at the end of the pattern, we can change. */ + if (p2 == pend) + { + /* Consider what happens when matching ":\(.*\)" + against ":/". I don't really understand this code + yet. */ + p[-3] = (unsigned char) pop_failure_jump; + DEBUG_PRINT1 + (" End of pattern: change to `pop_failure_jump'.\n"); + } + + else if ((re_opcode_t) *p2 == exactn + || (bufp->newline_anchor && (re_opcode_t) *p2 == endline)) + { + register unsigned char c + = *p2 == (unsigned char) endline ? '\n' : p2[2]; + + if ((re_opcode_t) p1[3] == exactn && p1[5] != c) + { + p[-3] = (unsigned char) pop_failure_jump; + DEBUG_PRINT3 (" %c != %c => pop_failure_jump.\n", + c, p1[5]); + } + + else if ((re_opcode_t) p1[3] == charset + || (re_opcode_t) p1[3] == charset_not) + { + int not = (re_opcode_t) p1[3] == charset_not; + + if (c < (unsigned char) (p1[4] * BYTEWIDTH) + && p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH))) + not = !not; + + /* `not' is equal to 1 if c would match, which means + that we can't change to pop_failure_jump. */ + if (!not) + { + p[-3] = (unsigned char) pop_failure_jump; + DEBUG_PRINT1 (" No match => pop_failure_jump.\n"); + } + } + } + else if ((re_opcode_t) *p2 == charset) + { +#ifdef DEBUG + register unsigned char c + = *p2 == (unsigned char) endline ? '\n' : p2[2]; +#endif + +#if 0 + if ((re_opcode_t) p1[3] == exactn + && ! ((int) p2[1] * BYTEWIDTH > (int) p1[5] + && (p2[2 + p1[5] / BYTEWIDTH] + & (1 << (p1[5] % BYTEWIDTH))))) +#else + if ((re_opcode_t) p1[3] == exactn + && ! ((int) p2[1] * BYTEWIDTH > (int) p1[4] + && (p2[2 + p1[4] / BYTEWIDTH] + & (1 << (p1[4] % BYTEWIDTH))))) +#endif + { + p[-3] = (unsigned char) pop_failure_jump; + DEBUG_PRINT3 (" %c != %c => pop_failure_jump.\n", + c, p1[5]); + } + + else if ((re_opcode_t) p1[3] == charset_not) + { + int idx; + /* We win if the charset_not inside the loop + lists every character listed in the charset after. */ + for (idx = 0; idx < (int) p2[1]; idx++) + if (! (p2[2 + idx] == 0 + || (idx < (int) p1[4] + && ((p2[2 + idx] & ~ p1[5 + idx]) == 0)))) + break; + + if (idx == p2[1]) + { + p[-3] = (unsigned char) pop_failure_jump; + DEBUG_PRINT1 (" No match => pop_failure_jump.\n"); + } + } + else if ((re_opcode_t) p1[3] == charset) + { + int idx; + /* We win if the charset inside the loop + has no overlap with the one after the loop. */ + for (idx = 0; + idx < (int) p2[1] && idx < (int) p1[4]; + idx++) + if ((p2[2 + idx] & p1[5 + idx]) != 0) + break; + + if (idx == p2[1] || idx == p1[4]) + { + p[-3] = (unsigned char) pop_failure_jump; + DEBUG_PRINT1 (" No match => pop_failure_jump.\n"); + } + } + } + } + p -= 2; /* Point at relative address again. */ + if ((re_opcode_t) p[-1] != pop_failure_jump) + { + p[-1] = (unsigned char) jump; + DEBUG_PRINT1 (" Match => jump.\n"); + goto unconditional_jump; + } + /* Note fall through. */ + + + /* The end of a simple repeat has a pop_failure_jump back to + its matching on_failure_jump, where the latter will push a + failure point. The pop_failure_jump takes off failure + points put on by this pop_failure_jump's matching + on_failure_jump; we got through the pattern to here from the + matching on_failure_jump, so didn't fail. */ + case pop_failure_jump: + { + /* We need to pass separate storage for the lowest and + highest registers, even though we don't care about the + actual values. Otherwise, we will restore only one + register from the stack, since lowest will == highest in + `pop_failure_point'. */ + active_reg_t dummy_low_reg, dummy_high_reg; + unsigned char *pdummy; + const char *sdummy; + + DEBUG_PRINT1 ("EXECUTING pop_failure_jump.\n"); + POP_FAILURE_POINT (sdummy, pdummy, + dummy_low_reg, dummy_high_reg, + reg_dummy, reg_dummy, reg_info_dummy); + } + /* Note fall through. */ + + unconditional_jump: +#ifdef _LIBC + DEBUG_PRINT2 ("\n%p: ", p); +#else + DEBUG_PRINT2 ("\n0x%x: ", p); +#endif + /* Note fall through. */ + + /* Unconditionally jump (without popping any failure points). */ + case jump: + EXTRACT_NUMBER_AND_INCR (mcnt, p); /* Get the amount to jump. */ + DEBUG_PRINT2 ("EXECUTING jump %d ", mcnt); + p += mcnt; /* Do the jump. */ +#ifdef _LIBC + DEBUG_PRINT2 ("(to %p).\n", p); +#else + DEBUG_PRINT2 ("(to 0x%x).\n", p); +#endif + break; + + + /* We need this opcode so we can detect where alternatives end + in `group_match_null_string_p' et al. */ + case jump_past_alt: + DEBUG_PRINT1 ("EXECUTING jump_past_alt.\n"); + goto unconditional_jump; + + + /* Normally, the on_failure_jump pushes a failure point, which + then gets popped at pop_failure_jump. We will end up at + pop_failure_jump, also, and with a pattern of, say, `a+', we + are skipping over the on_failure_jump, so we have to push + something meaningless for pop_failure_jump to pop. */ + case dummy_failure_jump: + DEBUG_PRINT1 ("EXECUTING dummy_failure_jump.\n"); + /* It doesn't matter what we push for the string here. What + the code at `fail' tests is the value for the pattern. */ + PUSH_FAILURE_POINT (NULL, NULL, -2); + goto unconditional_jump; + + + /* At the end of an alternative, we need to push a dummy failure + point in case we are followed by a `pop_failure_jump', because + we don't want the failure point for the alternative to be + popped. For example, matching `(a|ab)*' against `aab' + requires that we match the `ab' alternative. */ + case push_dummy_failure: + DEBUG_PRINT1 ("EXECUTING push_dummy_failure.\n"); + /* See comments just above at `dummy_failure_jump' about the + two zeroes. */ + PUSH_FAILURE_POINT (NULL, NULL, -2); + break; + + /* Have to succeed matching what follows at least n times. + After that, handle like `on_failure_jump'. */ + case succeed_n: + EXTRACT_NUMBER (mcnt, p + 2); + DEBUG_PRINT2 ("EXECUTING succeed_n %d.\n", mcnt); + + assert (mcnt >= 0); + /* Originally, this is how many times we HAVE to succeed. */ + if (mcnt > 0) + { + mcnt--; + p += 2; + STORE_NUMBER_AND_INCR (p, mcnt); +#ifdef _LIBC + DEBUG_PRINT3 (" Setting %p to %d.\n", p - 2, mcnt); +#else + DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p - 2, mcnt); +#endif + } + else if (mcnt == 0) + { +#ifdef _LIBC + DEBUG_PRINT2 (" Setting two bytes from %p to no_op.\n", p+2); +#else + DEBUG_PRINT2 (" Setting two bytes from 0x%x to no_op.\n", p+2); +#endif + p[2] = (unsigned char) no_op; + p[3] = (unsigned char) no_op; + goto on_failure; + } + break; + + case jump_n: + EXTRACT_NUMBER (mcnt, p + 2); + DEBUG_PRINT2 ("EXECUTING jump_n %d.\n", mcnt); + + /* Originally, this is how many times we CAN jump. */ + if (mcnt) + { + mcnt--; + STORE_NUMBER (p + 2, mcnt); +#ifdef _LIBC + DEBUG_PRINT3 (" Setting %p to %d.\n", p + 2, mcnt); +#else + DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p + 2, mcnt); +#endif + goto unconditional_jump; + } + /* If don't have to jump any more, skip over the rest of command. */ + else + p += 4; + break; + + case set_number_at: + { + DEBUG_PRINT1 ("EXECUTING set_number_at.\n"); + + EXTRACT_NUMBER_AND_INCR (mcnt, p); + p1 = p + mcnt; + EXTRACT_NUMBER_AND_INCR (mcnt, p); +#ifdef _LIBC + DEBUG_PRINT3 (" Setting %p to %d.\n", p1, mcnt); +#else + DEBUG_PRINT3 (" Setting 0x%x to %d.\n", p1, mcnt); +#endif + STORE_NUMBER (p1, mcnt); + break; + } + +#if 0 + /* The DEC Alpha C compiler 3.x generates incorrect code for the + test WORDCHAR_P (d - 1) != WORDCHAR_P (d) in the expansion of + AT_WORD_BOUNDARY, so this code is disabled. Expanding the + macro and introducing temporary variables works around the bug. */ + + case wordbound: + DEBUG_PRINT1 ("EXECUTING wordbound.\n"); + if (AT_WORD_BOUNDARY (d)) + break; + goto fail; + + case notwordbound: + DEBUG_PRINT1 ("EXECUTING notwordbound.\n"); + if (AT_WORD_BOUNDARY (d)) + goto fail; + break; +#else + case wordbound: + { + boolean prevchar, thischar; + + DEBUG_PRINT1 ("EXECUTING wordbound.\n"); + if (AT_STRINGS_BEG (d) || AT_STRINGS_END (d)) + break; + + prevchar = WORDCHAR_P (d - 1); + thischar = WORDCHAR_P (d); + if (prevchar != thischar) + break; + goto fail; + } + + case notwordbound: + { + boolean prevchar, thischar; + + DEBUG_PRINT1 ("EXECUTING notwordbound.\n"); + if (AT_STRINGS_BEG (d) || AT_STRINGS_END (d)) + goto fail; + + prevchar = WORDCHAR_P (d - 1); + thischar = WORDCHAR_P (d); + if (prevchar != thischar) + goto fail; + break; + } +#endif + + case wordbeg: + DEBUG_PRINT1 ("EXECUTING wordbeg.\n"); + if (WORDCHAR_P (d) && (AT_STRINGS_BEG (d) || !WORDCHAR_P (d - 1))) + break; + goto fail; + + case wordend: + DEBUG_PRINT1 ("EXECUTING wordend.\n"); + if (!AT_STRINGS_BEG (d) && WORDCHAR_P (d - 1) + && (!WORDCHAR_P (d) || AT_STRINGS_END (d))) + break; + goto fail; + +#ifdef emacs + case before_dot: + DEBUG_PRINT1 ("EXECUTING before_dot.\n"); + if (PTR_CHAR_POS ((unsigned char *) d) >= point) + goto fail; + break; + + case at_dot: + DEBUG_PRINT1 ("EXECUTING at_dot.\n"); + if (PTR_CHAR_POS ((unsigned char *) d) != point) + goto fail; + break; + + case after_dot: + DEBUG_PRINT1 ("EXECUTING after_dot.\n"); + if (PTR_CHAR_POS ((unsigned char *) d) <= point) + goto fail; + break; + + case syntaxspec: + DEBUG_PRINT2 ("EXECUTING syntaxspec %d.\n", mcnt); + mcnt = *p++; + goto matchsyntax; + + case wordchar: + DEBUG_PRINT1 ("EXECUTING Emacs wordchar.\n"); + mcnt = (int) Sword; + matchsyntax: + PREFETCH (); + /* Can't use *d++ here; SYNTAX may be an unsafe macro. */ + d++; + if (SYNTAX (d[-1]) != (enum syntaxcode) mcnt) + goto fail; + SET_REGS_MATCHED (); + break; + + case notsyntaxspec: + DEBUG_PRINT2 ("EXECUTING notsyntaxspec %d.\n", mcnt); + mcnt = *p++; + goto matchnotsyntax; + + case notwordchar: + DEBUG_PRINT1 ("EXECUTING Emacs notwordchar.\n"); + mcnt = (int) Sword; + matchnotsyntax: + PREFETCH (); + /* Can't use *d++ here; SYNTAX may be an unsafe macro. */ + d++; + if (SYNTAX (d[-1]) == (enum syntaxcode) mcnt) + goto fail; + SET_REGS_MATCHED (); + break; + +#else /* not emacs */ + case wordchar: + DEBUG_PRINT1 ("EXECUTING non-Emacs wordchar.\n"); + PREFETCH (); + if (!WORDCHAR_P (d)) + goto fail; + SET_REGS_MATCHED (); + d++; + break; + + case notwordchar: + DEBUG_PRINT1 ("EXECUTING non-Emacs notwordchar.\n"); + PREFETCH (); + if (WORDCHAR_P (d)) + goto fail; + SET_REGS_MATCHED (); + d++; + break; +#endif /* not emacs */ + + default: + abort (); + } + continue; /* Successfully executed one pattern command; keep going. */ + + + /* We goto here if a matching operation fails. */ + fail: + if (!FAIL_STACK_EMPTY ()) + { /* A restart point is known. Restore to that state. */ + DEBUG_PRINT1 ("\nFAIL:\n"); + POP_FAILURE_POINT (d, p, + lowest_active_reg, highest_active_reg, + regstart, regend, reg_info); + + /* If this failure point is a dummy, try the next one. */ + if (!p) + goto fail; + + /* If we failed to the end of the pattern, don't examine *p. */ + assert (p <= pend); + if (p < pend) + { + boolean is_a_jump_n = false; + + /* If failed to a backwards jump that's part of a repetition + loop, need to pop this failure point and use the next one. */ + switch ((re_opcode_t) *p) + { + case jump_n: + is_a_jump_n = true; + case maybe_pop_jump: + case pop_failure_jump: + case jump: + p1 = p + 1; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + p1 += mcnt; + + if ((is_a_jump_n && (re_opcode_t) *p1 == succeed_n) + || (!is_a_jump_n + && (re_opcode_t) *p1 == on_failure_jump)) + goto fail; + break; + default: + /* do nothing */ ; + } + } + + if (d >= string1 && d <= end1) + dend = end_match_1; + } + else + break; /* Matching at this starting point really fails. */ + } /* for (;;) */ + + if (best_regs_set) + goto restore_best_regs; + + FREE_VARIABLES (); + + return -1; /* Failure to match. */ +} /* re_match_2 */ + +/* Subroutine definitions for re_match_2. */ + + +/* We are passed P pointing to a register number after a start_memory. + + Return true if the pattern up to the corresponding stop_memory can + match the empty string, and false otherwise. + + If we find the matching stop_memory, sets P to point to one past its number. + Otherwise, sets P to an undefined byte less than or equal to END. + + We don't handle duplicates properly (yet). */ + +static boolean +group_match_null_string_p (p, end, reg_info) + unsigned char **p, *end; + register_info_type *reg_info; +{ + int mcnt; + /* Point to after the args to the start_memory. */ + unsigned char *p1 = *p + 2; + + while (p1 < end) + { + /* Skip over opcodes that can match nothing, and return true or + false, as appropriate, when we get to one that can't, or to the + matching stop_memory. */ + + switch ((re_opcode_t) *p1) + { + /* Could be either a loop or a series of alternatives. */ + case on_failure_jump: + p1++; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + + /* If the next operation is not a jump backwards in the + pattern. */ + + if (mcnt >= 0) + { + /* Go through the on_failure_jumps of the alternatives, + seeing if any of the alternatives cannot match nothing. + The last alternative starts with only a jump, + whereas the rest start with on_failure_jump and end + with a jump, e.g., here is the pattern for `a|b|c': + + /on_failure_jump/0/6/exactn/1/a/jump_past_alt/0/6 + /on_failure_jump/0/6/exactn/1/b/jump_past_alt/0/3 + /exactn/1/c + + So, we have to first go through the first (n-1) + alternatives and then deal with the last one separately. */ + + + /* Deal with the first (n-1) alternatives, which start + with an on_failure_jump (see above) that jumps to right + past a jump_past_alt. */ + + while ((re_opcode_t) p1[mcnt-3] == jump_past_alt) + { + /* `mcnt' holds how many bytes long the alternative + is, including the ending `jump_past_alt' and + its number. */ + + if (!alt_match_null_string_p (p1, p1 + mcnt - 3, + reg_info)) + return false; + + /* Move to right after this alternative, including the + jump_past_alt. */ + p1 += mcnt; + + /* Break if it's the beginning of an n-th alternative + that doesn't begin with an on_failure_jump. */ + if ((re_opcode_t) *p1 != on_failure_jump) + break; + + /* Still have to check that it's not an n-th + alternative that starts with an on_failure_jump. */ + p1++; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + if ((re_opcode_t) p1[mcnt-3] != jump_past_alt) + { + /* Get to the beginning of the n-th alternative. */ + p1 -= 3; + break; + } + } + + /* Deal with the last alternative: go back and get number + of the `jump_past_alt' just before it. `mcnt' contains + the length of the alternative. */ + EXTRACT_NUMBER (mcnt, p1 - 2); + + if (!alt_match_null_string_p (p1, p1 + mcnt, reg_info)) + return false; + + p1 += mcnt; /* Get past the n-th alternative. */ + } /* if mcnt > 0 */ + break; + + + case stop_memory: + assert (p1[1] == **p); + *p = p1 + 2; + return true; + + + default: + if (!common_op_match_null_string_p (&p1, end, reg_info)) + return false; + } + } /* while p1 < end */ + + return false; +} /* group_match_null_string_p */ + + +/* Similar to group_match_null_string_p, but doesn't deal with alternatives: + It expects P to be the first byte of a single alternative and END one + byte past the last. The alternative can contain groups. */ + +static boolean +alt_match_null_string_p (p, end, reg_info) + unsigned char *p, *end; + register_info_type *reg_info; +{ + int mcnt; + unsigned char *p1 = p; + + while (p1 < end) + { + /* Skip over opcodes that can match nothing, and break when we get + to one that can't. */ + + switch ((re_opcode_t) *p1) + { + /* It's a loop. */ + case on_failure_jump: + p1++; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + p1 += mcnt; + break; + + default: + if (!common_op_match_null_string_p (&p1, end, reg_info)) + return false; + } + } /* while p1 < end */ + + return true; +} /* alt_match_null_string_p */ + + +/* Deals with the ops common to group_match_null_string_p and + alt_match_null_string_p. + + Sets P to one after the op and its arguments, if any. */ + +static boolean +common_op_match_null_string_p (p, end, reg_info) + unsigned char **p, *end; + register_info_type *reg_info; +{ + int mcnt; + boolean ret; + int reg_no; + unsigned char *p1 = *p; + + switch ((re_opcode_t) *p1++) + { + case no_op: + case begline: + case endline: + case begbuf: + case endbuf: + case wordbeg: + case wordend: + case wordbound: + case notwordbound: +#ifdef emacs + case before_dot: + case at_dot: + case after_dot: +#endif + break; + + case start_memory: + reg_no = *p1; + assert (reg_no > 0 && reg_no <= MAX_REGNUM); + ret = group_match_null_string_p (&p1, end, reg_info); + + /* Have to set this here in case we're checking a group which + contains a group and a back reference to it. */ + + if (REG_MATCH_NULL_STRING_P (reg_info[reg_no]) == MATCH_NULL_UNSET_VALUE) + REG_MATCH_NULL_STRING_P (reg_info[reg_no]) = ret; + + if (!ret) + return false; + break; + + /* If this is an optimized succeed_n for zero times, make the jump. */ + case jump: + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + if (mcnt >= 0) + p1 += mcnt; + else + return false; + break; + + case succeed_n: + /* Get to the number of times to succeed. */ + p1 += 2; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + + if (mcnt == 0) + { + p1 -= 4; + EXTRACT_NUMBER_AND_INCR (mcnt, p1); + p1 += mcnt; + } + else + return false; + break; + + case duplicate: + if (!REG_MATCH_NULL_STRING_P (reg_info[*p1])) + return false; + break; + + case set_number_at: + p1 += 4; + + default: + /* All other opcodes mean we cannot match the empty string. */ + return false; + } + + *p = p1; + return true; +} /* common_op_match_null_string_p */ + + +/* Return zero if TRANSLATE[S1] and TRANSLATE[S2] are identical for LEN + bytes; nonzero otherwise. */ + +static int +bcmp_translate (s1, s2, len, translate) + const char *s1, *s2; + register int len; + RE_TRANSLATE_TYPE translate; +{ + register const unsigned char *p1 = (const unsigned char *) s1; + register const unsigned char *p2 = (const unsigned char *) s2; + while (len) + { + if (translate[*p1++] != translate[*p2++]) return 1; + len--; + } + return 0; +} + +/* Entry points for GNU code. */ + +/* re_compile_pattern is the GNU regular expression compiler: it + compiles PATTERN (of length SIZE) and puts the result in BUFP. + Returns 0 if the pattern was valid, otherwise an error string. + + Assumes the `allocated' (and perhaps `buffer') and `translate' fields + are set in BUFP on entry. + + We call regex_compile to do the actual compilation. */ + +const char * +re_compile_pattern (pattern, length, bufp) + const char *pattern; + size_t length; + struct re_pattern_buffer *bufp; +{ + reg_errcode_t ret; + + /* GNU code is written to assume at least RE_NREGS registers will be set + (and at least one extra will be -1). */ + bufp->regs_allocated = REGS_UNALLOCATED; + + /* And GNU code determines whether or not to get register information + by passing null for the REGS argument to re_match, etc., not by + setting no_sub. */ + bufp->no_sub = 0; + + /* Match anchors at newline. */ + bufp->newline_anchor = 1; + + ret = regex_compile (pattern, length, re_syntax_options, bufp); + + if (!ret) + return NULL; + return gettext (re_error_msgid + re_error_msgid_idx[(int) ret]); +} +#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. */ + +#if defined _REGEX_RE_COMP || defined _LIBC + +/* BSD has one and only one pattern buffer. */ +static struct re_pattern_buffer re_comp_buf; + +char * +#ifdef _LIBC +/* Make these definitions weak in libc, so POSIX programs can redefine + these names if they don't use our functions, and still use + regcomp/regexec below without link errors. */ +weak_function +#endif +re_comp (s) + const char *s; +{ + reg_errcode_t ret; + + if (!s) + { + if (!re_comp_buf.buffer) + return gettext ("No previous regular expression"); + return 0; + } + + if (!re_comp_buf.buffer) + { + re_comp_buf.buffer = (unsigned char *) malloc (200); + if (re_comp_buf.buffer == NULL) + return (char *) gettext (re_error_msgid + + re_error_msgid_idx[(int) REG_ESPACE]); + re_comp_buf.allocated = 200; + + re_comp_buf.fastmap = (char *) malloc (1 << BYTEWIDTH); + if (re_comp_buf.fastmap == NULL) + return (char *) gettext (re_error_msgid + + re_error_msgid_idx[(int) REG_ESPACE]); + } + + /* Since `re_exec' always passes NULL for the `regs' argument, we + don't need to initialize the pattern buffer fields which affect it. */ + + /* Match anchors at newlines. */ + re_comp_buf.newline_anchor = 1; + + ret = regex_compile (s, strlen (s), re_syntax_options, &re_comp_buf); + + if (!ret) + return NULL; + + /* Yes, we're discarding `const' here if !HAVE_LIBINTL. */ + return (char *) gettext (re_error_msgid + re_error_msgid_idx[(int) ret]); +} + + +int +#ifdef _LIBC +weak_function +#endif +re_exec (s) + const char *s; +{ + const int len = strlen (s); + return + 0 <= re_search (&re_comp_buf, s, len, 0, len, (struct re_registers *) 0); +} + +#endif /* _REGEX_RE_COMP */ + +/* POSIX.2 functions. Don't define these for Emacs. */ + +#ifndef emacs + +/* regcomp takes a regular expression as a string and compiles it. + + PREG is a regex_t *. We do not expect any fields to be initialized, + since POSIX says we shouldn't. Thus, we set + + `buffer' to the compiled pattern; + `used' to the length of the compiled pattern; + `syntax' to RE_SYNTAX_POSIX_EXTENDED if the + REG_EXTENDED bit in CFLAGS is set; otherwise, to + RE_SYNTAX_POSIX_BASIC; + `newline_anchor' to REG_NEWLINE being set in CFLAGS; + `fastmap' to an allocated space for the fastmap; + `fastmap_accurate' to zero; + `re_nsub' to the number of subexpressions in PATTERN. + + PATTERN is the address of the pattern string. + + CFLAGS is a series of bits which affect compilation. + + If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we + use POSIX basic syntax. + + If REG_NEWLINE is set, then . and [^...] don't match newline. + Also, regexec will try a match beginning after every newline. + + If REG_ICASE is set, then we considers upper- and lowercase + versions of letters to be equivalent when matching. + + If REG_NOSUB is set, then when PREG is passed to regexec, that + routine will report only success or failure, and nothing about the + registers. + + It returns 0 if it succeeds, nonzero if it doesn't. (See regex.h for + the return codes and their meanings.) */ + +int +regcomp (preg, pattern, cflags) + regex_t *preg; + const char *pattern; + int cflags; +{ + reg_errcode_t ret; + reg_syntax_t syntax + = (cflags & REG_EXTENDED) ? + RE_SYNTAX_POSIX_EXTENDED : RE_SYNTAX_POSIX_BASIC; + + /* regex_compile will allocate the space for the compiled pattern. */ + preg->buffer = 0; + preg->allocated = 0; + preg->used = 0; + + /* Try to allocate space for the fastmap. */ + preg->fastmap = (char *) malloc (1 << BYTEWIDTH); + + if (cflags & REG_ICASE) + { + unsigned i; + + preg->translate + = (RE_TRANSLATE_TYPE) malloc (CHAR_SET_SIZE + * sizeof (*(RE_TRANSLATE_TYPE)0)); + if (preg->translate == NULL) + return (int) REG_ESPACE; + + /* Map uppercase characters to corresponding lowercase ones. */ + for (i = 0; i < CHAR_SET_SIZE; i++) + preg->translate[i] = ISUPPER (i) ? TOLOWER (i) : i; + } + else + preg->translate = NULL; + + /* If REG_NEWLINE is set, newlines are treated differently. */ + if (cflags & REG_NEWLINE) + { /* REG_NEWLINE implies neither . nor [^...] match newline. */ + syntax &= ~RE_DOT_NEWLINE; + syntax |= RE_HAT_LISTS_NOT_NEWLINE; + /* It also changes the matching behavior. */ + preg->newline_anchor = 1; + } + else + preg->newline_anchor = 0; + + preg->no_sub = !!(cflags & REG_NOSUB); + + /* POSIX says a null character in the pattern terminates it, so we + can use strlen here in compiling the pattern. */ + ret = regex_compile (pattern, strlen (pattern), syntax, preg); + + /* POSIX doesn't distinguish between an unmatched open-group and an + unmatched close-group: both are REG_EPAREN. */ + if (ret == REG_ERPAREN) ret = REG_EPAREN; + + if (ret == REG_NOERROR && preg->fastmap) + { + /* Compute the fastmap now, since regexec cannot modify the pattern + buffer. */ + if (re_compile_fastmap (preg) == -2) + { + /* Some error occured while computing the fastmap, just forget + about it. */ + free (preg->fastmap); + preg->fastmap = NULL; + } + } + + return (int) ret; +} +#ifdef _LIBC +weak_alias (__regcomp, regcomp) +#endif + + +/* regexec searches for a given pattern, specified by PREG, in the + string STRING. + + If NMATCH is zero or REG_NOSUB was set in the cflags argument to + `regcomp', we ignore PMATCH. Otherwise, we assume PMATCH has at + least NMATCH elements, and we set them to the offsets of the + corresponding matched substrings. + + EFLAGS specifies `execution flags' which affect matching: if + REG_NOTBOL is set, then ^ does not match at the beginning of the + string; if REG_NOTEOL is set, then $ does not match at the end. + + We return 0 if we find a match and REG_NOMATCH if not. */ + +int +regexec (preg, string, nmatch, pmatch, eflags) + const regex_t *preg; + const char *string; + size_t nmatch; + regmatch_t pmatch[]; + int eflags; +{ + int ret; + struct re_registers regs; + regex_t private_preg; + int len = strlen (string); + boolean want_reg_info = !preg->no_sub && nmatch > 0; + + private_preg = *preg; + + private_preg.not_bol = !!(eflags & REG_NOTBOL); + private_preg.not_eol = !!(eflags & REG_NOTEOL); + + /* The user has told us exactly how many registers to return + information about, via `nmatch'. We have to pass that on to the + matching routines. */ + private_preg.regs_allocated = REGS_FIXED; + + if (want_reg_info) + { + regs.num_regs = nmatch; + regs.start = TALLOC (nmatch * 2, regoff_t); + if (regs.start == NULL) + return (int) REG_NOMATCH; + regs.end = regs.start + nmatch; + } + + /* Perform the searching operation. */ + ret = re_search (&private_preg, string, len, + /* start: */ 0, /* range: */ len, + want_reg_info ? ®s : (struct re_registers *) 0); + + /* Copy the register information to the POSIX structure. */ + if (want_reg_info) + { + if (ret >= 0) + { + unsigned r; + + for (r = 0; r < nmatch; r++) + { + pmatch[r].rm_so = regs.start[r]; + pmatch[r].rm_eo = regs.end[r]; + } + } + + /* If we needed the temporary register info, free the space now. */ + free (regs.start); + } + + /* We want zero return to mean success, unlike `re_search'. */ + return ret >= 0 ? (int) REG_NOERROR : (int) REG_NOMATCH; +} +#ifdef _LIBC +weak_alias (__regexec, regexec) +#endif + + +/* Returns a message corresponding to an error code, ERRCODE, returned + from either regcomp or regexec. We don't use PREG here. */ + +size_t +regerror (err, preg, errbuf, errbuf_size) + int err; + const regex_t *preg; + char *errbuf; + size_t errbuf_size; +{ + const char *msg; + size_t msg_size; + + if (err < 0 + || err >= (int) (sizeof (re_error_msgid_idx) + / sizeof (re_error_msgid_idx[0]))) + /* Only error codes returned by the rest of the code should be passed + to this routine. If we are given anything else, or if other regex + code generates an invalid error code, then the program has a bug. + Dump core so we can fix it. */ + abort (); + + msg = gettext (re_error_msgid + re_error_msgid_idx[err]); + + msg_size = strlen (msg) + 1; /* Includes the null. */ + + if (errbuf_size != 0) + { + if (msg_size > errbuf_size) + { +#if defined HAVE_MEMPCPY || defined _LIBC + *((char *) __mempcpy (errbuf, msg, errbuf_size - 1)) = '\0'; +#else + memcpy (errbuf, msg, errbuf_size - 1); + errbuf[errbuf_size - 1] = 0; +#endif + } + else + memcpy (errbuf, msg, msg_size); + } + + return msg_size; +} +#ifdef _LIBC +weak_alias (__regerror, regerror) +#endif + + +/* Free dynamically allocated space used by PREG. */ + +void +regfree (preg) + regex_t *preg; +{ + if (preg->buffer != NULL) + free (preg->buffer); + preg->buffer = NULL; + + preg->allocated = 0; + preg->used = 0; + + if (preg->fastmap != NULL) + free (preg->fastmap); + preg->fastmap = NULL; + preg->fastmap_accurate = 0; + + if (preg->translate != NULL) + free (preg->translate); + preg->translate = NULL; +} +#ifdef _LIBC +weak_alias (__regfree, regfree) +#endif + +#endif /* not emacs */ diff --git a/lib/route_types.pl b/lib/route_types.pl new file mode 100755 index 0000000..e1595af --- /dev/null +++ b/lib/route_types.pl @@ -0,0 +1,199 @@ +#!/usr/bin/perl +## +## Scan a file of route-type definitions (see eg route_types.txt) and +## generate a corresponding header file with: +## +## - enum of Zserv route-types +## - redistribute strings for the various Quagga daemons +## +## See route_types.txt for the format. +## +## +## Copyright (C) 2009 David Lamparter. +## 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 strict; + +# input processing +# +my @protos; +my %protodetail; + +my %daemons; + +while () { + # skip comments and empty lines + next if (/^\s*(#|$)/); + + # strip whitespace + chomp; + $_ =~ s/^\s*//; + $_ =~ s/\s*$//; + + # match help strings + if (/^(ZEBRA_ROUTE_[^\s]+)\s*,\s*"(.*)"$/) { + $protodetail{$1}->{'longhelp'} = $2; + next; + } + + $_ =~ s/\s*,\s*/,/g; + + # else: 7-field line + my @f = split(/,/, $_); + unless (@f == 7) { + die "invalid input on route_types line $.\n"; + } + + my $proto = $f[0]; + $f[3] = $1 if ($f[3] =~ /^'(.*)'$/); + $f[6] = $1 if ($f[6] =~ /^"(.*)"$/); + + $protodetail{$proto} = { + "number" => scalar @protos, + "type" => $f[0], + "cname" => $f[1], + "daemon" => $f[2], + "char" => $f[3], + "ipv4" => int($f[4]), + "ipv6" => int($f[5]), + "shorthelp" => $f[6], + }; + push @protos, $proto; + $daemons{$f[2]} = { + "ipv4" => int($f[4]), + "ipv6" => int($f[5]) + } unless ($f[2] eq "NULL"); +} + +# output +printf <{"ipv4"}); + push @protosv6, $p if ($protodetail{$p}->{"ipv6"}); +} +pop @protos; + +sub codelist { + my (@protos) = @_; + my (@lines) = (); + my $str = " \"Codes: "; + for my $p (@protos) { + my $s = sprintf("%s - %s, ", + $protodetail{$p}->{"char"}, + $protodetail{$p}->{"shorthelp"}); + if (length($str . $s) > 70) { + $str =~ s/ $//; + push @lines, $str . "%s\" \\\n"; + $str = " \" "; + } + $str .= $s; + } + $str =~ s/ $//; + push @lines, $str . "%s\" \\\n"; + push @lines, " \" > - selected route, * - FIB route%s%s\", \\\n"; + my @nl = (); + for (my $c = 0; $c < @lines + 1; $c++) { + push @nl, "VTY_NEWLINE" + } + return join("", @lines) ." ". join(", ", @nl); +} + +print "\n"; +printf "#define SHOW_ROUTE_V4_HEADER \\\n%s\n", codelist(@protosv4); +printf "#define SHOW_ROUTE_V6_HEADER \\\n%s\n", codelist(@protosv6); +print "\n"; + +sub collect { + my ($daemon, $ipv4, $ipv6) = @_; + my (@names, @help) = ((), ()); + for my $p (@protos) { + next if ($protodetail{$p}->{"daemon"} eq $daemon && $daemon ne "zebra"); + next unless (($ipv4 && $protodetail{$p}->{"ipv4"}) + || ($ipv6 && $protodetail{$p}->{"ipv6"})); + push @names, $protodetail{$p}->{"cname"}; + push @help, " \"".$protodetail{$p}->{"longhelp"}."\\n\""; + } + return ("\"(" . join("|", @names) . ")\"", join(" \\\n", @help)); +} + +for my $daemon (sort keys %daemons) { + next unless ($daemons{$daemon}->{"ipv4"} || $daemons{$daemon}->{"ipv6"}); + printf "/* %s */\n", $daemon; + if ($daemons{$daemon}->{"ipv4"} && $daemons{$daemon}->{"ipv6"}) { + my ($names, $help) = collect($daemon, 1, 1); + printf "#define QUAGGA_REDIST_STR_%s \\\n %s\n", uc $daemon, $names; + printf "#define QUAGGA_REDIST_HELP_STR_%s \\\n%s\n", uc $daemon, $help; + ($names, $help) = collect($daemon, 1, 0); + printf "#define QUAGGA_IP_REDIST_STR_%s \\\n %s\n", uc $daemon, $names; + printf "#define QUAGGA_IP_REDIST_HELP_STR_%s \\\n%s\n", uc $daemon, $help; + ($names, $help) = collect($daemon, 0, 1); + printf "#define QUAGGA_IP6_REDIST_STR_%s \\\n %s\n", uc $daemon, $names; + printf "#define QUAGGA_IP6_REDIST_HELP_STR_%s \\\n%s\n", uc $daemon, $help; + } else { + my ($names, $help) = collect($daemon, + $daemons{$daemon}->{"ipv4"}, $daemons{$daemon}->{"ipv6"}); + printf "#define QUAGGA_REDIST_STR_%s \\\n %s\n", uc $daemon, $names; + printf "#define QUAGGA_REDIST_HELP_STR_%s \\\n%s\n", uc $daemon, $help; + } + print "\n"; +} + +print <{"cname"}, $protodetail{$p}->{"char"}; +} + +print < + +#include "linklist.h" +#include "memory.h" +#include "vector.h" +#include "prefix.h" +#include "routemap.h" +#include "command.h" +#include "vty.h" +#include "log.h" + +/* Vector for route match rules. */ +static vector route_match_vec; + +/* Vector for route set rules. */ +static vector route_set_vec; + +/* Route map rule. This rule has both `match' rule and `set' rule. */ +struct route_map_rule +{ + /* Rule type. */ + struct route_map_rule_cmd *cmd; + + /* For pretty printing. */ + char *rule_str; + + /* Pre-compiled match rule. */ + void *value; + + /* Linked list. */ + struct route_map_rule *next; + struct route_map_rule *prev; +}; + +/* Making route map list. */ +struct route_map_list +{ + struct route_map *head; + struct route_map *tail; + + void (*add_hook) (const char *); + void (*delete_hook) (const char *); + void (*event_hook) (route_map_event_t, const char *); +}; + +/* Master list of route map. */ +static struct route_map_list route_map_master = { NULL, NULL, NULL, NULL }; + +static void +route_map_rule_delete (struct route_map_rule_list *, + struct route_map_rule *); + +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 * +route_map_new (const char *name) +{ + struct route_map *new; + + new = XCALLOC (MTYPE_ROUTE_MAP, sizeof (struct route_map)); + new->name = XSTRDUP (MTYPE_ROUTE_MAP_NAME, name); + return new; +} + +/* Add new name to route_map. */ +static struct route_map * +route_map_add (const char *name) +{ + struct route_map *map; + struct route_map_list *list; + + map = route_map_new (name); + list = &route_map_master; + + map->next = NULL; + map->prev = list->tail; + if (list->tail) + list->tail->next = map; + else + list->head = map; + list->tail = map; + + /* Execute hook. */ + if (route_map_master.add_hook) + (*route_map_master.add_hook) (name); + + return map; +} + +/* Route map delete from list. */ +static void +route_map_delete (struct route_map *map) +{ + struct route_map_list *list; + struct route_map_index *index; + char *name; + + while ((index = map->head) != NULL) + route_map_index_delete (index, 0); + + name = map->name; + + list = &route_map_master; + + if (map->next) + map->next->prev = map->prev; + else + list->tail = map->prev; + + if (map->prev) + map->prev->next = map->next; + else + list->head = map->next; + + XFREE (MTYPE_ROUTE_MAP, map); + + /* Execute deletion hook. */ + if (route_map_master.delete_hook) + (*route_map_master.delete_hook) (name); + + if (name) + XFREE (MTYPE_ROUTE_MAP_NAME, name); + +} + +/* Lookup route map by route map name string. */ +struct route_map * +route_map_lookup_by_name (const char *name) +{ + struct route_map *map; + + for (map = route_map_master.head; map; map = map->next) + if (strcmp (map->name, name) == 0) + return map; + return NULL; +} + +/* Lookup route map. If there isn't route map create one and return + it. */ +static struct route_map * +route_map_get (const char *name) +{ + struct route_map *map; + + map = route_map_lookup_by_name (name); + if (map == NULL) + map = route_map_add (name); + return map; +} + +/* Return route map's type string. */ +static const char * +route_map_type_str (enum route_map_type type) +{ + switch (type) + { + case RMAP_PERMIT: + return "permit"; + break; + case RMAP_DENY: + return "deny"; + break; + default: + return ""; + break; + } +} + +static int +route_map_empty (struct route_map *map) +{ + if (map->head == NULL && map->tail == NULL) + return 1; + else + return 0; +} + +/* show route-map */ +static void +vty_show_route_map_entry (struct vty *vty, struct route_map *map) +{ + struct route_map_index *index; + struct route_map_rule *rule; + + /* Print the name of the protocol */ + if (zlog_default) + vty_out (vty, "%s:%s", zlog_proto_names[zlog_default->protocol], + VTY_NEWLINE); + + for (index = map->head; index; index = index->next) + { + vty_out (vty, "route-map %s, %s, sequence %d%s", + map->name, route_map_type_str (index->type), + index->pref, VTY_NEWLINE); + + /* Description */ + if (index->description) + vty_out (vty, " Description:%s %s%s", VTY_NEWLINE, + index->description, VTY_NEWLINE); + + /* Match clauses */ + vty_out (vty, " Match clauses:%s", VTY_NEWLINE); + for (rule = index->match_list.head; rule; rule = rule->next) + vty_out (vty, " %s %s%s", + rule->cmd->str, rule->rule_str, VTY_NEWLINE); + + vty_out (vty, " Set clauses:%s", VTY_NEWLINE); + for (rule = index->set_list.head; rule; rule = rule->next) + vty_out (vty, " %s %s%s", + rule->cmd->str, rule->rule_str, VTY_NEWLINE); + + /* Call clause */ + vty_out (vty, " Call clause:%s", VTY_NEWLINE); + if (index->nextrm) + vty_out (vty, " Call %s%s", index->nextrm, VTY_NEWLINE); + + /* Exit Policy */ + vty_out (vty, " Action:%s", VTY_NEWLINE); + if (index->exitpolicy == RMAP_GOTO) + vty_out (vty, " Goto %d%s", index->nextpref, VTY_NEWLINE); + else if (index->exitpolicy == RMAP_NEXT) + vty_out (vty, " Continue to next entry%s", VTY_NEWLINE); + else if (index->exitpolicy == RMAP_EXIT) + vty_out (vty, " Exit routemap%s", VTY_NEWLINE); + } +} + +static int +vty_show_route_map (struct vty *vty, const char *name) +{ + struct route_map *map; + + if (name) + { + map = route_map_lookup_by_name (name); + + if (map) + { + vty_show_route_map_entry (vty, map); + return CMD_SUCCESS; + } + else + { + vty_out (vty, "%%route-map %s not found%s", name, VTY_NEWLINE); + return CMD_WARNING; + } + } + else + { + for (map = route_map_master.head; map; map = map->next) + vty_show_route_map_entry (vty, map); + } + return CMD_SUCCESS; +} + + +/* New route map allocation. Please note route map's name must be + specified. */ +static struct route_map_index * +route_map_index_new (void) +{ + struct route_map_index *new; + + new = XCALLOC (MTYPE_ROUTE_MAP_INDEX, sizeof (struct route_map_index)); + new->exitpolicy = RMAP_EXIT; /* Default to Cisco-style */ + return new; +} + +/* Free route map index. */ +static void +route_map_index_delete (struct route_map_index *index, int notify) +{ + struct route_map_rule *rule; + + /* Free route match. */ + while ((rule = index->match_list.head) != NULL) + route_map_rule_delete (&index->match_list, rule); + + /* Free route set. */ + while ((rule = index->set_list.head) != NULL) + route_map_rule_delete (&index->set_list, rule); + + /* Remove index from route map list. */ + if (index->next) + index->next->prev = index->prev; + else + index->map->tail = index->prev; + + if (index->prev) + index->prev->next = index->next; + else + index->map->head = index->next; + + /* Free 'char *nextrm' if not NULL */ + if (index->nextrm) + XFREE (MTYPE_ROUTE_MAP_NAME, index->nextrm); + + /* Execute event hook. */ + if (route_map_master.event_hook && notify) + (*route_map_master.event_hook) (RMAP_EVENT_INDEX_DELETED, + index->map->name); + + XFREE (MTYPE_ROUTE_MAP_INDEX, index); +} + +/* Lookup index from route map. */ +static struct route_map_index * +route_map_index_lookup (struct route_map *map, enum route_map_type type, + int pref) +{ + struct route_map_index *index; + + for (index = map->head; index; index = index->next) + if ((index->type == type || type == RMAP_ANY) + && index->pref == pref) + return index; + return NULL; +} + +/* Add new index to route map. */ +static struct route_map_index * +route_map_index_add (struct route_map *map, enum route_map_type type, + int pref) +{ + struct route_map_index *index; + struct route_map_index *point; + + /* Allocate new route map inex. */ + index = route_map_index_new (); + index->map = map; + index->type = type; + index->pref = pref; + + /* Compare preference. */ + for (point = map->head; point; point = point->next) + if (point->pref >= pref) + break; + + if (map->head == NULL) + { + map->head = map->tail = index; + } + else if (point == NULL) + { + index->prev = map->tail; + map->tail->next = index; + map->tail = index; + } + else if (point == map->head) + { + index->next = map->head; + map->head->prev = index; + map->head = index; + } + else + { + index->next = point; + index->prev = point->prev; + if (point->prev) + point->prev->next = index; + point->prev = index; + } + + /* Execute event hook. */ + if (route_map_master.event_hook) + (*route_map_master.event_hook) (RMAP_EVENT_INDEX_ADDED, + map->name); + + return index; +} + +/* Get route map index. */ +static struct route_map_index * +route_map_index_get (struct route_map *map, enum route_map_type type, + int pref) +{ + struct route_map_index *index; + + index = route_map_index_lookup (map, RMAP_ANY, pref); + if (index && index->type != type) + { + /* Delete index from route map. */ + route_map_index_delete (index, 1); + index = NULL; + } + if (index == NULL) + index = route_map_index_add (map, type, pref); + return index; +} + +/* New route map rule */ +static struct route_map_rule * +route_map_rule_new (void) +{ + struct route_map_rule *new; + + 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) +{ + vector_set (route_match_vec, cmd); +} + +/* Install rule command to the set list. */ +void +route_map_install_set (struct route_map_rule_cmd *cmd) +{ + vector_set (route_set_vec, cmd); +} + +/* Lookup rule command from match list. */ +static struct route_map_rule_cmd * +route_map_lookup_match (const char *name) +{ + unsigned int i; + struct route_map_rule_cmd *rule; + + for (i = 0; i < vector_active (route_match_vec); i++) + if ((rule = vector_slot (route_match_vec, i)) != NULL) + if (strcmp (rule->str, name) == 0) + return rule; + return NULL; +} + +/* Lookup rule command from set list. */ +static struct route_map_rule_cmd * +route_map_lookup_set (const char *name) +{ + unsigned int i; + struct route_map_rule_cmd *rule; + + for (i = 0; i < vector_active (route_set_vec); i++) + if ((rule = vector_slot (route_set_vec, i)) != NULL) + if (strcmp (rule->str, name) == 0) + return rule; + return NULL; +} + +/* Add match and set rule to rule list. */ +static void +route_map_rule_add (struct route_map_rule_list *list, + struct route_map_rule *rule) +{ + rule->next = NULL; + rule->prev = list->tail; + if (list->tail) + list->tail->next = rule; + else + list->head = rule; + list->tail = rule; +} + +/* Delete rule from rule list. */ +static void +route_map_rule_delete (struct route_map_rule_list *list, + struct route_map_rule *rule) +{ + if (rule->cmd->func_free) + (*rule->cmd->func_free) (rule->value); + + if (rule->rule_str) + XFREE (MTYPE_ROUTE_MAP_RULE_STR, rule->rule_str); + + if (rule->next) + rule->next->prev = rule->prev; + else + list->tail = rule->prev; + if (rule->prev) + rule->prev->next = rule->next; + else + list->head = rule->next; + + XFREE (MTYPE_ROUTE_MAP_RULE, rule); +} + +/* strcmp wrapper function which don't crush even argument is NULL. */ +static int +rulecmp (const char *dst, const char *src) +{ + if (dst == NULL) + { + if (src == NULL) + return 0; + else + return 1; + } + else + { + if (src == NULL) + return 1; + else + return strcmp (dst, src); + } + return 1; +} + +/* Add match statement to route map. */ +int +route_map_add_match (struct route_map_index *index, const char *match_name, + const char *match_arg) +{ + struct route_map_rule *rule; + struct route_map_rule *next; + struct route_map_rule_cmd *cmd; + void *compile; + int replaced = 0; + + /* First lookup rule for add match statement. */ + cmd = route_map_lookup_match (match_name); + if (cmd == NULL) + return RMAP_RULE_MISSING; + + /* Next call compile function for this match statement. */ + if (cmd->func_compile) + { + compile= (*cmd->func_compile)(match_arg); + if (compile == NULL) + return RMAP_COMPILE_ERROR; + } + else + compile = NULL; + + /* If argument is completely same ignore it. */ + for (rule = index->match_list.head; rule; rule = next) + { + next = rule->next; + if (rule->cmd == cmd) + { + route_map_rule_delete (&index->match_list, rule); + replaced = 1; + } + } + + /* Add new route map match rule. */ + rule = route_map_rule_new (); + rule->cmd = cmd; + rule->value = compile; + if (match_arg) + rule->rule_str = XSTRDUP (MTYPE_ROUTE_MAP_RULE_STR, match_arg); + else + rule->rule_str = NULL; + + /* Add new route match rule to linked list. */ + route_map_rule_add (&index->match_list, rule); + + /* Execute event hook. */ + if (route_map_master.event_hook) + (*route_map_master.event_hook) (replaced ? + RMAP_EVENT_MATCH_REPLACED: + RMAP_EVENT_MATCH_ADDED, + index->map->name); + + return 0; +} + +/* Delete specified route match rule. */ +int +route_map_delete_match (struct route_map_index *index, const char *match_name, + const char *match_arg) +{ + struct route_map_rule *rule; + struct route_map_rule_cmd *cmd; + + cmd = route_map_lookup_match (match_name); + if (cmd == NULL) + return 1; + + for (rule = index->match_list.head; rule; rule = rule->next) + if (rule->cmd == cmd && + (rulecmp (rule->rule_str, match_arg) == 0 || match_arg == NULL)) + { + route_map_rule_delete (&index->match_list, rule); + /* Execute event hook. */ + if (route_map_master.event_hook) + (*route_map_master.event_hook) (RMAP_EVENT_MATCH_DELETED, + index->map->name); + return 0; + } + /* Can't find matched rule. */ + return 1; +} + +/* Add route-map set statement to the route map. */ +int +route_map_add_set (struct route_map_index *index, const char *set_name, + const char *set_arg) +{ + struct route_map_rule *rule; + struct route_map_rule *next; + struct route_map_rule_cmd *cmd; + void *compile; + int replaced = 0; + + cmd = route_map_lookup_set (set_name); + if (cmd == NULL) + return RMAP_RULE_MISSING; + + /* Next call compile function for this match statement. */ + if (cmd->func_compile) + { + compile= (*cmd->func_compile)(set_arg); + if (compile == NULL) + return RMAP_COMPILE_ERROR; + } + else + compile = NULL; + + /* Add by WJL. if old set command of same kind exist, delete it first + to ensure only one set command of same kind exist under a + route_map_index. */ + for (rule = index->set_list.head; rule; rule = next) + { + next = rule->next; + if (rule->cmd == cmd) + { + route_map_rule_delete (&index->set_list, rule); + replaced = 1; + } + } + + /* Add new route map match rule. */ + rule = route_map_rule_new (); + rule->cmd = cmd; + rule->value = compile; + if (set_arg) + rule->rule_str = XSTRDUP (MTYPE_ROUTE_MAP_RULE_STR, set_arg); + else + rule->rule_str = NULL; + + /* Add new route match rule to linked list. */ + route_map_rule_add (&index->set_list, rule); + + /* Execute event hook. */ + if (route_map_master.event_hook) + (*route_map_master.event_hook) (replaced ? + RMAP_EVENT_SET_REPLACED: + RMAP_EVENT_SET_ADDED, + index->map->name); + return 0; +} + +/* Delete route map set rule. */ +int +route_map_delete_set (struct route_map_index *index, const char *set_name, + const char *set_arg) +{ + struct route_map_rule *rule; + struct route_map_rule_cmd *cmd; + + cmd = route_map_lookup_set (set_name); + if (cmd == NULL) + return 1; + + for (rule = index->set_list.head; rule; rule = rule->next) + if ((rule->cmd == cmd) && + (rulecmp (rule->rule_str, set_arg) == 0 || set_arg == NULL)) + { + route_map_rule_delete (&index->set_list, rule); + /* Execute event hook. */ + if (route_map_master.event_hook) + (*route_map_master.event_hook) (RMAP_EVENT_SET_DELETED, + index->map->name); + return 0; + } + /* Can't find matched rule. */ + return 1; +} + +/* Apply route map's each index to the object. + + The matrix for a route-map looks like this: + (note, this includes the description for the "NEXT" + and "GOTO" frobs now + + Match | No Match + | + permit action | cont + | + ------------------+--------------- + | + deny deny | cont + | + + action) + -Apply Set statements, accept route + -If Call statement is present jump to the specified route-map, if it + denies the route we finish. + -If NEXT is specified, goto NEXT statement + -If GOTO is specified, goto the first clause where pref > nextpref + -If nothing is specified, do as Cisco and finish + deny) + -Route is denied by route-map. + cont) + -Goto Next index + + If we get no matches after we've processed all updates, then the route + is dropped too. + + Some notes on the new "CALL", "NEXT" and "GOTO" + call WORD - If this clause is matched, then the set statements + are executed and then we jump to route-map 'WORD'. If + this route-map denies the route, we finish, in other case we + do whatever the exit policy (EXIT, NEXT or GOTO) tells. + on-match next - If this clause is matched, then the set statements + are executed and then we drop through to the next clause + on-match goto n - If this clause is matched, then the set statments + are executed and then we goto the nth clause, or the + first clause greater than this. In order to ensure + route-maps *always* exit, you cannot jump backwards. + Sorry ;) + + We need to make sure our route-map processing matches the above +*/ + +static route_map_result_t +route_map_apply_match (struct route_map_rule_list *match_list, + struct prefix *prefix, route_map_object_t type, + void *object) +{ + route_map_result_t ret = RMAP_NOMATCH; + struct route_map_rule *match; + + + /* Check all match rule and if there is no match rule, go to the + set statement. */ + if (!match_list->head) + ret = RMAP_MATCH; + else + { + for (match = match_list->head; match; match = match->next) + { + /* Try each match statement in turn, If any do not return + RMAP_MATCH, return, otherwise continue on to next match + statement. All match statements must match for end-result + to be a match. */ + ret = (*match->cmd->func_apply) (match->value, prefix, + type, object); + if (ret != RMAP_MATCH) + return ret; + } + } + return ret; +} + +/* Apply route map to the object. */ +route_map_result_t +route_map_apply (struct route_map *map, struct prefix *prefix, + route_map_object_t type, void *object) +{ + static int recursion = 0; + int ret = 0; + struct route_map_index *index; + struct route_map_rule *set; + + if (recursion > RMAP_RECURSION_LIMIT) + { + zlog (NULL, LOG_WARNING, + "route-map recursion limit (%d) reached, discarding route", + RMAP_RECURSION_LIMIT); + recursion = 0; + return RMAP_DENYMATCH; + } + + if (map == NULL) + return RMAP_DENYMATCH; + + for (index = map->head; index; index = index->next) + { + /* Apply this index. */ + ret = route_map_apply_match (&index->match_list, prefix, type, object); + + /* Now we apply the matrix from above */ + if (ret == RMAP_NOMATCH) + /* 'cont' from matrix - continue to next route-map sequence */ + continue; + else if (ret == RMAP_MATCH) + { + if (index->type == RMAP_PERMIT) + /* 'action' */ + { + /* permit+match must execute sets */ + for (set = index->set_list.head; set; set = set->next) + ret = (*set->cmd->func_apply) (set->value, prefix, + type, object); + + /* Call another route-map if available */ + if (index->nextrm) + { + struct route_map *nextrm = + route_map_lookup_by_name (index->nextrm); + + if (nextrm) /* Target route-map found, jump to it */ + { + recursion++; + ret = route_map_apply (nextrm, prefix, type, object); + recursion--; + } + + /* If nextrm returned 'deny', finish. */ + if (ret == RMAP_DENYMATCH) + return ret; + } + + switch (index->exitpolicy) + { + case RMAP_EXIT: + return ret; + case RMAP_NEXT: + continue; + case RMAP_GOTO: + { + /* Find the next clause to jump to */ + struct route_map_index *next = index->next; + int nextpref = index->nextpref; + + while (next && next->pref < nextpref) + { + index = next; + next = next->next; + } + if (next == NULL) + { + /* No clauses match! */ + return ret; + } + } + } + } + else if (index->type == RMAP_DENY) + /* 'deny' */ + { + return RMAP_DENYMATCH; + } + } + } + /* Finally route-map does not match at all. */ + return RMAP_DENYMATCH; +} + +void +route_map_add_hook (void (*func) (const char *)) +{ + route_map_master.add_hook = func; +} + +void +route_map_delete_hook (void (*func) (const char *)) +{ + route_map_master.delete_hook = func; +} + +void +route_map_event_hook (void (*func) (route_map_event_t, const char *)) +{ + route_map_master.event_hook = func; +} + +void +route_map_init (void) +{ + /* Make vector for match and set. */ + route_match_vec = vector_init (1); + route_set_vec = vector_init (1); +} + +void +route_map_finish (void) +{ + vector_free (route_match_vec); + 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, + "route-map WORD (deny|permit) <1-65535>", + "Create route-map or enter route-map command mode\n" + "Route map tag\n" + "Route map denies set operations\n" + "Route map permits set operations\n" + "Sequence to insert to/delete from existing route-map entry\n") +{ + int permit; + unsigned long pref; + struct route_map *map; + struct route_map_index *index; + char *endptr = NULL; + + /* Permit check. */ + if (strncmp (argv[1], "permit", strlen (argv[1])) == 0) + permit = RMAP_PERMIT; + else if (strncmp (argv[1], "deny", strlen (argv[1])) == 0) + permit = RMAP_DENY; + else + { + vty_out (vty, "the third field must be [permit|deny]%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Preference check. */ + pref = strtoul (argv[2], &endptr, 10); + if (pref == ULONG_MAX || *endptr != '\0') + { + vty_out (vty, "the fourth field must be positive integer%s", + VTY_NEWLINE); + return CMD_WARNING; + } + if (pref == 0 || pref > 65535) + { + vty_out (vty, "the fourth field must be <1-65535>%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Get route map. */ + map = route_map_get (argv[0]); + index = route_map_index_get (map, permit, pref); + + vty->index = index; + vty->node = RMAP_NODE; + return CMD_SUCCESS; +} + +DEFUN (no_route_map_all, + no_route_map_all_cmd, + "no route-map WORD", + NO_STR + "Create route-map or enter route-map command mode\n" + "Route map tag\n") +{ + struct route_map *map; + + map = route_map_lookup_by_name (argv[0]); + if (map == NULL) + { + vty_out (vty, "%% Could not find route-map %s%s", + argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + route_map_delete (map); + + return CMD_SUCCESS; +} + +DEFUN (no_route_map, + no_route_map_cmd, + "no route-map WORD (deny|permit) <1-65535>", + NO_STR + "Create route-map or enter route-map command mode\n" + "Route map tag\n" + "Route map denies set operations\n" + "Route map permits set operations\n" + "Sequence to insert to/delete from existing route-map entry\n") +{ + int permit; + unsigned long pref; + struct route_map *map; + struct route_map_index *index; + char *endptr = NULL; + + /* Permit check. */ + if (strncmp (argv[1], "permit", strlen (argv[1])) == 0) + permit = RMAP_PERMIT; + else if (strncmp (argv[1], "deny", strlen (argv[1])) == 0) + permit = RMAP_DENY; + else + { + vty_out (vty, "the third field must be [permit|deny]%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Preference. */ + pref = strtoul (argv[2], &endptr, 10); + if (pref == ULONG_MAX || *endptr != '\0') + { + vty_out (vty, "the fourth field must be positive integer%s", + VTY_NEWLINE); + return CMD_WARNING; + } + if (pref == 0 || pref > 65535) + { + vty_out (vty, "the fourth field must be <1-65535>%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Existence check. */ + map = route_map_lookup_by_name (argv[0]); + if (map == NULL) + { + vty_out (vty, "%% Could not find route-map %s%s", + argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + /* Lookup route map index. */ + index = route_map_index_lookup (map, permit, pref); + if (index == NULL) + { + vty_out (vty, "%% Could not find route-map entry %s %s%s", + argv[0], argv[2], VTY_NEWLINE); + return CMD_WARNING; + } + + /* Delete index from route map. */ + route_map_index_delete (index, 1); + + /* If this route rule is the last one, delete route map itself. */ + if (route_map_empty (map)) + route_map_delete (map); + + return CMD_SUCCESS; +} + +DEFUN (rmap_onmatch_next, + rmap_onmatch_next_cmd, + "on-match next", + "Exit policy on matches\n" + "Next clause\n") +{ + struct route_map_index *index; + + index = vty->index; + + if (index) + index->exitpolicy = RMAP_NEXT; + + return CMD_SUCCESS; +} + +DEFUN (no_rmap_onmatch_next, + no_rmap_onmatch_next_cmd, + "no on-match next", + NO_STR + "Exit policy on matches\n" + "Next clause\n") +{ + struct route_map_index *index; + + index = vty->index; + + if (index) + index->exitpolicy = RMAP_EXIT; + + return CMD_SUCCESS; +} + +DEFUN (rmap_onmatch_goto, + rmap_onmatch_goto_cmd, + "on-match goto <1-65535>", + "Exit policy on matches\n" + "Goto Clause number\n" + "Number\n") +{ + struct route_map_index *index = vty->index; + int d = 0; + + if (index) + { + if (argc == 1 && argv[0]) + VTY_GET_INTEGER_RANGE("route-map index", d, argv[0], 1, 65536); + else + d = index->pref + 1; + + if (d <= index->pref) + { + /* Can't allow you to do that, Dave */ + vty_out (vty, "can't jump backwards in route-maps%s", + VTY_NEWLINE); + return CMD_WARNING; + } + else + { + index->exitpolicy = RMAP_GOTO; + index->nextpref = d; + } + } + return CMD_SUCCESS; +} + +DEFUN (no_rmap_onmatch_goto, + no_rmap_onmatch_goto_cmd, + "no on-match goto", + NO_STR + "Exit policy on matches\n" + "Goto Clause number\n") +{ + struct route_map_index *index; + + index = vty->index; + + if (index) + index->exitpolicy = RMAP_EXIT; + + return CMD_SUCCESS; +} + +/* Cisco/GNU Zebra compatible ALIASes for on-match next */ +ALIAS (rmap_onmatch_goto, + rmap_continue_cmd, + "continue", + "Continue on a different entry within the route-map\n") + +ALIAS (no_rmap_onmatch_goto, + no_rmap_continue_cmd, + "no continue", + NO_STR + "Continue on a different entry within the route-map\n") + +/* GNU Zebra compatible */ +ALIAS (rmap_onmatch_goto, + rmap_continue_seq_cmd, + "continue <1-65535>", + "Continue on a different entry within the route-map\n" + "Route-map entry sequence number\n") + +ALIAS (no_rmap_onmatch_goto, + no_rmap_continue_seq, + "no continue <1-65535>", + NO_STR + "Continue on a different entry within the route-map\n" + "Route-map entry sequence number\n") + +DEFUN (rmap_show_name, + rmap_show_name_cmd, + "show route-map [WORD]", + SHOW_STR + "route-map information\n" + "route-map name\n") +{ + const char *name = NULL; + if (argc) + name = argv[0]; + return vty_show_route_map (vty, name); +} + +ALIAS (rmap_onmatch_goto, + rmap_continue_index_cmd, + "continue <1-65536>", + "Exit policy on matches\n" + "Goto Clause number\n") + +DEFUN (rmap_call, + rmap_call_cmd, + "call WORD", + "Jump to another Route-Map after match+set\n" + "Target route-map name\n") +{ + struct route_map_index *index; + + index = vty->index; + if (index) + { + if (index->nextrm) + XFREE (MTYPE_ROUTE_MAP_NAME, index->nextrm); + index->nextrm = XSTRDUP (MTYPE_ROUTE_MAP_NAME, argv[0]); + } + return CMD_SUCCESS; +} + +DEFUN (no_rmap_call, + no_rmap_call_cmd, + "no call", + NO_STR + "Jump to another Route-Map after match+set\n") +{ + struct route_map_index *index; + + index = vty->index; + + if (index->nextrm) + { + XFREE (MTYPE_ROUTE_MAP_NAME, index->nextrm); + index->nextrm = NULL; + } + + return CMD_SUCCESS; +} + +DEFUN (rmap_description, + rmap_description_cmd, + "description .LINE", + "Route-map comment\n" + "Comment describing this route-map rule\n") +{ + struct route_map_index *index; + + index = vty->index; + if (index) + { + if (index->description) + XFREE (MTYPE_TMP, index->description); + index->description = argv_concat (argv, argc, 0); + } + return CMD_SUCCESS; +} + +DEFUN (no_rmap_description, + no_rmap_description_cmd, + "no description", + NO_STR + "Route-map comment\n") +{ + struct route_map_index *index; + + index = vty->index; + if (index) + { + if (index->description) + XFREE (MTYPE_TMP, index->description); + index->description = NULL; + } + return CMD_SUCCESS; +} + +/* Configuration write function. */ +static int +route_map_config_write (struct vty *vty) +{ + struct route_map *map; + struct route_map_index *index; + struct route_map_rule *rule; + int first = 1; + int write = 0; + + for (map = route_map_master.head; map; map = map->next) + for (index = map->head; index; index = index->next) + { + if (!first) + vty_out (vty, "!%s", VTY_NEWLINE); + else + first = 0; + + vty_out (vty, "route-map %s %s %d%s", + map->name, + route_map_type_str (index->type), + index->pref, VTY_NEWLINE); + + if (index->description) + vty_out (vty, " description %s%s", index->description, VTY_NEWLINE); + + for (rule = index->match_list.head; rule; rule = rule->next) + vty_out (vty, " match %s %s%s", rule->cmd->str, + rule->rule_str ? rule->rule_str : "", + VTY_NEWLINE); + + for (rule = index->set_list.head; rule; rule = rule->next) + vty_out (vty, " set %s %s%s", rule->cmd->str, + rule->rule_str ? rule->rule_str : "", + VTY_NEWLINE); + if (index->nextrm) + vty_out (vty, " call %s%s", index->nextrm, VTY_NEWLINE); + if (index->exitpolicy == RMAP_GOTO) + vty_out (vty, " on-match goto %d%s", index->nextpref, VTY_NEWLINE); + if (index->exitpolicy == RMAP_NEXT) + vty_out (vty," on-match next%s", VTY_NEWLINE); + + write++; + } + return write; +} + +/* Route map node structure. */ +static struct cmd_node rmap_node = +{ + RMAP_NODE, + "%s(config-route-map)# ", + 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) +{ + /* Install route map top node. */ + install_node (&rmap_node, route_map_config_write); + + /* Install route map commands. */ + install_default (RMAP_NODE); + install_element (CONFIG_NODE, &route_map_cmd); + install_element (CONFIG_NODE, &no_route_map_cmd); + install_element (CONFIG_NODE, &no_route_map_all_cmd); + + /* Install the on-match stuff */ + install_element (RMAP_NODE, &route_map_cmd); + install_element (RMAP_NODE, &rmap_onmatch_next_cmd); + install_element (RMAP_NODE, &no_rmap_onmatch_next_cmd); + install_element (RMAP_NODE, &rmap_onmatch_goto_cmd); + install_element (RMAP_NODE, &no_rmap_onmatch_goto_cmd); + + /* Install the continue stuff (ALIAS of on-match). */ + install_element (RMAP_NODE, &rmap_continue_cmd); + install_element (RMAP_NODE, &no_rmap_continue_cmd); + install_element (RMAP_NODE, &rmap_continue_index_cmd); + + /* Install the call stuff. */ + install_element (RMAP_NODE, &rmap_call_cmd); + install_element (RMAP_NODE, &no_rmap_call_cmd); + + /* Install description commands. */ + install_element (RMAP_NODE, &rmap_description_cmd); + install_element (RMAP_NODE, &no_rmap_description_cmd); + + /* Install show command */ + install_element (ENABLE_NODE, &rmap_show_name_cmd); +} diff --git a/lib/routemap.h b/lib/routemap.h new file mode 100644 index 0000000..68129e1 --- /dev/null +++ b/lib/routemap.h @@ -0,0 +1,204 @@ +/* Route map function. + * Copyright (C) 1998 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_ROUTEMAP_H +#define _ZEBRA_ROUTEMAP_H + +#include "prefix.h" + +/* Route map's type. */ +enum route_map_type +{ + RMAP_PERMIT, + RMAP_DENY, + RMAP_ANY +}; + +typedef enum +{ + RMAP_MATCH, + RMAP_DENYMATCH, + RMAP_NOMATCH, + RMAP_ERROR, + RMAP_OKAY +} route_map_result_t; + +typedef enum +{ + RMAP_RIP, + RMAP_RIPNG, + RMAP_BABEL, + RMAP_OSPF, + RMAP_OSPF6, + RMAP_BGP, + RMAP_ZEBRA, + RMAP_ISIS, +} route_map_object_t; + +typedef enum +{ + RMAP_EXIT, + RMAP_GOTO, + RMAP_NEXT +} route_map_end_t; + +typedef enum +{ + RMAP_EVENT_SET_ADDED, + RMAP_EVENT_SET_DELETED, + RMAP_EVENT_SET_REPLACED, + RMAP_EVENT_MATCH_ADDED, + RMAP_EVENT_MATCH_DELETED, + RMAP_EVENT_MATCH_REPLACED, + RMAP_EVENT_INDEX_ADDED, + RMAP_EVENT_INDEX_DELETED +} route_map_event_t; + +/* Depth limit in RMAP recursion using RMAP_CALL. */ +#define RMAP_RECURSION_LIMIT 10 + +/* Route map rule structure for matching and setting. */ +struct route_map_rule_cmd +{ + /* Route map rule name (e.g. as-path, metric) */ + const char *str; + + /* Function for value set or match. */ + route_map_result_t (*func_apply)(void *, struct prefix *, + route_map_object_t, void *); + + /* Compile argument and return result as void *. */ + void *(*func_compile)(const char *); + + /* Free allocated value by func_compile (). */ + void (*func_free)(void *); +}; + +/* Route map apply error. */ +enum +{ + /* Route map rule is missing. */ + RMAP_RULE_MISSING = 1, + + /* Route map rule can't compile */ + RMAP_COMPILE_ERROR +}; + +/* Route map rule list. */ +struct route_map_rule_list +{ + struct route_map_rule *head; + struct route_map_rule *tail; +}; + +/* Route map index structure. */ +struct route_map_index +{ + struct route_map *map; + char *description; + + /* Preference of this route map rule. */ + int pref; + + /* Route map type permit or deny. */ + enum route_map_type type; + + /* Do we follow old rules, or hop forward? */ + route_map_end_t exitpolicy; + + /* If we're using "GOTO", to where do we go? */ + int nextpref; + + /* If we're using "CALL", to which route-map do ew go? */ + char *nextrm; + + /* Matching rule list. */ + struct route_map_rule_list match_list; + struct route_map_rule_list set_list; + + /* Make linked list. */ + struct route_map_index *next; + struct route_map_index *prev; +}; + +/* Route map list structure. */ +struct route_map +{ + /* Name of route map. */ + char *name; + + /* Route map's rule. */ + struct route_map_index *head; + struct route_map_index *tail; + + /* Make linked list. */ + struct route_map *next; + struct route_map *prev; +}; + +/* Prototypes. */ +extern void route_map_init (void); +extern void route_map_init_vty (void); +extern void route_map_finish (void); + +/* Add match statement to route map. */ +extern int route_map_add_match (struct route_map_index *index, + const char *match_name, + const char *match_arg); + +/* Delete specified route match rule. */ +extern int route_map_delete_match (struct route_map_index *index, + const char *match_name, + const char *match_arg); + +/* Add route-map set statement to the route map. */ +extern int route_map_add_set (struct route_map_index *index, + const char *set_name, + const char *set_arg); + +/* Delete route map set rule. */ +extern int route_map_delete_set (struct route_map_index *index, + const char *set_name, + const char *set_arg); + +/* Install rule command to the match list. */ +extern void route_map_install_match (struct route_map_rule_cmd *cmd); + +/* Install rule command to the set list. */ +extern void route_map_install_set (struct route_map_rule_cmd *cmd); + +/* Lookup route map by name. */ +extern struct route_map * route_map_lookup_by_name (const char *name); + +/* Apply route map to the object. */ +extern route_map_result_t route_map_apply (struct route_map *map, + struct prefix *, + route_map_object_t object_type, + void *object); + +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 new file mode 100644 index 0000000..a120028 --- /dev/null +++ b/lib/sigevent.c @@ -0,0 +1,367 @@ +/* Quagga signal handling functions. + * 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 +#include + +#ifdef SA_SIGINFO +#ifdef HAVE_UCONTEXT_H +#ifdef GNU_LINUX +/* get REG_EIP from ucontext.h */ +#ifndef __USE_GNU +#define __USE_GNU +#endif /* __USE_GNU */ +#endif /* GNU_LINUX */ +#include +#endif /* HAVE_UCONTEXT_H */ +#endif /* SA_SIGINFO */ + + +/* master signals descriptor struct */ +struct quagga_sigevent_master_t +{ + struct thread *t; + + struct quagga_signal_t *signals; + int sigc; + + volatile sig_atomic_t caught; +} sigmaster; + +/* Generic signal handler + * Schedules signal event thread + */ +static void +quagga_signal_handler (int signo) +{ + int i; + struct quagga_signal_t *sig; + + for (i = 0; i < sigmaster.sigc; i++) + { + sig = &(sigmaster.signals[i]); + + if (sig->signal == signo) + sig->caught = 1; + } + + sigmaster.caught = 1; +} + +/* check if signals have been caught and run appropriate handlers */ +int +quagga_sigevent_process (void) +{ + struct quagga_signal_t *sig; + int i; +#ifdef SIGEVENT_BLOCK_SIGNALS + /* shouldnt need to block signals, but potentially may be needed */ + sigset_t newmask, oldmask; + + /* + * Block most signals, but be careful not to defer SIGTRAP because + * doing so breaks gdb, at least on NetBSD 2.0. Avoid asking to + * block SIGKILL, just because we shouldn't be able to do so. + */ + sigfillset (&newmask); + sigdelset (&newmask, SIGTRAP); + sigdelset (&newmask, SIGKILL); + + if ( (sigprocmask (SIG_BLOCK, &newmask, &oldmask)) < 0) + { + zlog_err ("quagga_signal_timer: couldnt block signals!"); + return -1; + } +#endif /* SIGEVENT_BLOCK_SIGNALS */ + + if (sigmaster.caught > 0) + { + sigmaster.caught = 0; + /* must not read or set sigmaster.caught after here, + * race condition with per-sig caught flags if one does + */ + + for (i = 0; i < sigmaster.sigc; i++) + { + sig = &(sigmaster.signals[i]); + + if (sig->caught > 0) + { + sig->caught = 0; + sig->handler (); + } + } + } + +#ifdef SIGEVENT_BLOCK_SIGNALS + if ( sigprocmask (SIG_UNBLOCK, &oldmask, NULL) < 0 ); + return -1; +#endif /* SIGEVENT_BLOCK_SIGNALS */ + + return 0; +} + +#ifdef SIGEVENT_SCHEDULE_THREAD +/* timer thread to check signals. Shouldnt be needed */ +int +quagga_signal_timer (struct thread *t) +{ + struct quagga_sigevent_master_t *sigm; + struct quagga_signal_t *sig; + int i; + + sigm = THREAD_ARG (t); + sigm->t = thread_add_timer (sigm->t->master, quagga_signal_timer, &sigmaster, + QUAGGA_SIGNAL_TIMER_INTERVAL); + return quagga_sigevent_process (); +} +#endif /* SIGEVENT_SCHEDULE_THREAD */ + +/* Initialization of signal handles. */ +/* Signal wrapper. */ +static int +signal_set (int signo) +{ + int ret; + struct sigaction sig; + struct sigaction osig; + + sig.sa_handler = &quagga_signal_handler; + sigfillset (&sig.sa_mask); + sig.sa_flags = 0; + if (signo == SIGALRM) { +#ifdef SA_INTERRUPT + sig.sa_flags |= SA_INTERRUPT; /* SunOS */ +#endif + } else { +#ifdef SA_RESTART + sig.sa_flags |= SA_RESTART; +#endif /* SA_RESTART */ + } + + ret = sigaction (signo, &sig, &osig); + if (ret < 0) + return ret; + else + return 0; +} + +#ifdef SA_SIGINFO + +/* XXX This function should be enhanced to support more platforms + (it currently works only on Linux/x86). */ +static void * +program_counter(void *context) +{ +#ifdef HAVE_UCONTEXT_H +#ifdef GNU_LINUX + /* 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; +} + +#endif /* SA_SIGINFO */ + +static void __attribute__ ((noreturn)) +exit_handler(int signo +#ifdef SA_SIGINFO + , siginfo_t *siginfo, void *context +#endif + ) +{ + zlog_signal(signo, "exiting..." +#ifdef SA_SIGINFO + , siginfo, program_counter(context) +#endif + ); + _exit(128+signo); +} + +static void __attribute__ ((noreturn)) +core_handler(int signo +#ifdef SA_SIGINFO + , siginfo_t *siginfo, void *context +#endif + ) +{ + zlog_signal(signo, "aborting..." +#ifdef SA_SIGINFO + , siginfo, program_counter(context) +#endif + ); + /* dump memory stats on core */ + log_memstats_stderr ("core_handler"); + abort(); +} + +static void +trap_default_signals(void) +{ + static const int core_signals[] = { + SIGQUIT, + SIGILL, +#ifdef SIGEMT + SIGEMT, +#endif + SIGFPE, + SIGBUS, + SIGSEGV, +#ifdef SIGSYS + SIGSYS, +#endif +#ifdef SIGXCPU + SIGXCPU, +#endif +#ifdef SIGXFSZ + SIGXFSZ, +#endif + }; + static const int exit_signals[] = { + SIGHUP, + SIGINT, + SIGALRM, + SIGTERM, + SIGUSR1, + SIGUSR2, +#ifdef SIGPOLL + SIGPOLL, +#endif +#ifdef SIGVTALRM + SIGVTALRM, +#endif +#ifdef SIGSTKFLT + SIGSTKFLT, +#endif + }; + static const int ignore_signals[] = { + SIGPIPE, + }; + static const struct { + const int *sigs; + u_int nsigs; + void (*handler)(int signo +#ifdef SA_SIGINFO + , siginfo_t *info, void *context +#endif + ); + } sigmap[] = { + { 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 < array_size(sigmap); i++) + { + u_int j; + + for (j = 0; j < sigmap[i].nsigs; j++) + { + struct sigaction oact; + if ((sigaction(sigmap[i].sigs[j],NULL,&oact) == 0) && + (oact.sa_handler == SIG_DFL)) + { + struct sigaction act; + sigfillset (&act.sa_mask); + if (sigmap[i].handler == NULL) + { + act.sa_handler = SIG_IGN; + act.sa_flags = 0; + } + else + { +#ifdef SA_SIGINFO + /* Request extra arguments to signal handler. */ + act.sa_sigaction = sigmap[i].handler; + act.sa_flags = SA_SIGINFO; +#else + act.sa_handler = sigmap[i].handler; + act.sa_flags = 0; +#endif + } + if (sigaction(sigmap[i].sigs[j],&act,NULL) < 0) + zlog_warn("Unable to set signal handler for signal %d: %s", + sigmap[i].sigs[j],safe_strerror(errno)); + + } + } + } +} + +void +signal_init (struct thread_master *m, int sigc, + struct quagga_signal_t signals[]) +{ + + int i = 0; + struct quagga_signal_t *sig; + + /* First establish some default handlers that can be overridden by + the application. */ + trap_default_signals(); + + while (i < sigc) + { + sig = &signals[i]; + if ( signal_set (sig->signal) < 0 ) + exit (-1); + i++; + } + + sigmaster.sigc = sigc; + sigmaster.signals = signals; + +#ifdef SIGEVENT_SCHEDULE_THREAD + sigmaster.t = + thread_add_timer (m, quagga_signal_timer, &sigmaster, + QUAGGA_SIGNAL_TIMER_INTERVAL); +#endif /* SIGEVENT_SCHEDULE_THREAD */ +} diff --git a/lib/sigevent.h b/lib/sigevent.h new file mode 100644 index 0000000..248fa2c --- /dev/null +++ b/lib/sigevent.h @@ -0,0 +1,52 @@ +/* + * Quagga Signal handling header. + * + * 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. + */ + +#ifndef _QUAGGA_SIGNAL_H +#define _QUAGGA_SIGNAL_H + +#include + +#define QUAGGA_SIGNAL_TIMER_INTERVAL 2L + +struct quagga_signal_t +{ + int signal; /* signal number */ + void (*handler) (void); /* handler to call */ + + volatile sig_atomic_t caught; /* private member */ +}; + +/* initialise sigevent system + * takes: + * - pointer to valid struct thread_master + * - number of elements in passed in signals array + * - array of quagga_signal_t's describing signals to handle + * and handlers to use for each signal + */ +extern void signal_init (struct thread_master *m, int sigc, + struct quagga_signal_t *signals); + +/* check whether there are signals to handle, process any found */ +extern int quagga_sigevent_process (void); + +#endif /* _QUAGGA_SIGNAL_H */ diff --git a/lib/smux.c b/lib/smux.c new file mode 100644 index 0000000..03da99f --- /dev/null +++ b/lib/smux.c @@ -0,0 +1,1502 @@ +/* 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 + +#if defined HAVE_SNMP && defined SNMP_SMUX +#include +#include + +#include "log.h" +#include "thread.h" +#include "linklist.h" +#include "command.h" +#include +#include "memory.h" +#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; + +/* SMUX subtree list. */ +struct list *treelist; + +/* SMUX oid. */ +oid *smux_oid = NULL; +size_t smux_oid_len; + +/* SMUX password. */ +char *smux_passwd = NULL; + +/* SMUX read threads. */ +struct thread *smux_read_thread; + +/* SMUX connect thrads. */ +struct thread *smux_connect_thread; + +/* SMUX debug flag. */ +int debug_smux = 0; + +/* SMUX failure count. */ +int fail = 0; + +/* SMUX node. */ +static struct cmd_node smux_node = +{ + SMUX_NODE, + "" /* SMUX has no interface. */ +}; + +/* thread master */ +static struct thread_master *smux_master; + +static int +oid_compare_part (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; + + return 0; +} + +static void +smux_oid_dump (const char *prefix, const oid *oid, size_t oid_len) +{ + unsigned int i; + int first = 1; + char buf[MAX_OID_LEN * 3]; + + buf[0] = '\0'; + + for (i = 0; i < oid_len; i++) + { + sprintf (buf + strlen (buf), "%s%d", first ? "" : ".", (int) oid[i]); + first = 0; + } + zlog_debug ("%s: %s", prefix, buf); +} + +static int +smux_socket (void) +{ + int ret; +#ifdef HAVE_IPV6 + struct addrinfo hints, *res0, *res; + int gai; +#else + struct sockaddr_in serv; + struct servent *sp; +#endif + int sock = 0; + +#ifdef HAVE_IPV6 + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + gai = getaddrinfo(NULL, "smux", &hints, &res0); + if (gai == EAI_SERVICE) + { + char servbuf[NI_MAXSERV]; + sprintf(servbuf,"%d",SMUX_PORT_DEFAULT); + servbuf[sizeof (servbuf) - 1] = '\0'; + gai = getaddrinfo(NULL, servbuf, &hints, &res0); + } + if (gai) + { + zlog_warn("Cannot locate loopback service smux"); + return -1; + } + for(res=res0; res; res=res->ai_next) + { + if (res->ai_family != AF_INET +#ifdef HAVE_IPV6 + && res->ai_family != AF_INET6 +#endif /* HAVE_IPV6 */ + ) + continue; + + sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (sock < 0) + continue; + sockopt_reuseaddr (sock); + sockopt_reuseport (sock); + ret = connect (sock, res->ai_addr, res->ai_addrlen); + if (ret < 0) + { + close(sock); + sock = -1; + continue; + } + break; + } + freeaddrinfo(res0); + if (sock < 0) + zlog_warn ("Can't connect to SNMP agent with SMUX"); +#else + sock = socket (AF_INET, SOCK_STREAM, 0); + if (sock < 0) + { + zlog_warn ("Can't make socket for SNMP"); + return -1; + } + + memset (&serv, 0, sizeof (struct sockaddr_in)); + serv.sin_family = AF_INET; +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + serv.sin_len = sizeof (struct sockaddr_in); +#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ + + sp = getservbyname ("smux", "tcp"); + if (sp != NULL) + serv.sin_port = sp->s_port; + else + serv.sin_port = htons (SMUX_PORT_DEFAULT); + + serv.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + + sockopt_reuseaddr (sock); + sockopt_reuseport (sock); + + ret = connect (sock, (struct sockaddr *) &serv, sizeof (struct sockaddr_in)); + if (ret < 0) + { + close (sock); + smux_sock = -1; + zlog_warn ("Can't connect to SNMP agent with SMUX"); + return -1; + } +#endif + return sock; +} + +static void +smux_getresp_send (oid objid[], size_t objid_len, long reqid, long errstat, + long errindex, u_char val_type, void *arg, size_t arg_len) +{ + u_char buf[BUFSIZ]; + u_char *ptr, *h1, *h1e, *h2, *h2e; + size_t len, length; + + ptr = buf; + len = BUFSIZ; + length = len; + + if (debug_smux) + { + zlog_debug ("SMUX GETRSP send"); + zlog_debug ("SMUX GETRSP reqid: %ld", reqid); + } + + h1 = ptr; + /* Place holder h1 for complete sequence */ + ptr = asn_build_sequence (ptr, &len, (u_char) SMUX_GETRSP, 0); + h1e = ptr; + + ptr = asn_build_int (ptr, &len, + (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), + &reqid, sizeof (reqid)); + + if (debug_smux) + zlog_debug ("SMUX GETRSP errstat: %ld", errstat); + + ptr = asn_build_int (ptr, &len, + (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), + &errstat, sizeof (errstat)); + if (debug_smux) + zlog_debug ("SMUX GETRSP errindex: %ld", errindex); + + ptr = asn_build_int (ptr, &len, + (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), + &errindex, sizeof (errindex)); + + h2 = ptr; + /* Place holder h2 for one variable */ + ptr = asn_build_sequence (ptr, &len, + (u_char)(ASN_SEQUENCE | ASN_CONSTRUCTOR), + 0); + h2e = ptr; + + ptr = snmp_build_var_op (ptr, objid, &objid_len, + val_type, arg_len, arg, &len); + + /* Now variable size is known, fill in size */ + asn_build_sequence(h2,&length,(u_char)(ASN_SEQUENCE|ASN_CONSTRUCTOR),ptr-h2e); + + /* Fill in size of whole sequence */ + asn_build_sequence(h1,&length,(u_char)SMUX_GETRSP,ptr-h1e); + + if (debug_smux) + zlog_debug ("SMUX getresp send: %td", (ptr - buf)); + + send (smux_sock, buf, (ptr - buf), 0); +} + +static u_char * +smux_var (u_char *ptr, size_t len, oid objid[], size_t *objid_len, + size_t *var_val_len, + u_char *var_val_type, + void **var_value) +{ + u_char type; + u_char val_type; + size_t val_len; + u_char *val; + + if (debug_smux) + zlog_debug ("SMUX var parse: len %zd", len); + + /* Parse header. */ + ptr = asn_parse_header (ptr, &len, &type); + + if (debug_smux) + { + zlog_debug ("SMUX var parse: type %d len %zd", type, len); + zlog_debug ("SMUX var parse: type must be %d", + (ASN_SEQUENCE | ASN_CONSTRUCTOR)); + } + + /* Parse var option. */ + *objid_len = MAX_OID_LEN; + ptr = snmp_parse_var_op(ptr, objid, objid_len, &val_type, + &val_len, &val, &len); + + if (var_val_len) + *var_val_len = val_len; + + if (var_value) + *var_value = (void*) val; + + if (var_val_type) + *var_val_type = val_type; + + /* Requested object id length is objid_len. */ + if (debug_smux) + smux_oid_dump ("Request OID", objid, *objid_len); + + if (debug_smux) + zlog_debug ("SMUX val_type: %d", val_type); + + /* Check request value type. */ + if (debug_smux) + switch (val_type) + { + case ASN_NULL: + /* In case of SMUX_GET or SMUX_GET_NEXT val_type is set to + ASN_NULL. */ + zlog_debug ("ASN_NULL"); + break; + + case ASN_INTEGER: + zlog_debug ("ASN_INTEGER"); + break; + case ASN_COUNTER: + case ASN_GAUGE: + case ASN_TIMETICKS: + case ASN_UINTEGER: + zlog_debug ("ASN_COUNTER"); + break; + case ASN_COUNTER64: + zlog_debug ("ASN_COUNTER64"); + break; + case ASN_IPADDRESS: + zlog_debug ("ASN_IPADDRESS"); + break; + case ASN_OCTET_STR: + zlog_debug ("ASN_OCTET_STR"); + break; + case ASN_OPAQUE: + case ASN_NSAP: + case ASN_OBJECT_ID: + zlog_debug ("ASN_OPAQUE"); + break; + case SNMP_NOSUCHOBJECT: + zlog_debug ("SNMP_NOSUCHOBJECT"); + break; + case SNMP_NOSUCHINSTANCE: + zlog_debug ("SNMP_NOSUCHINSTANCE"); + break; + case SNMP_ENDOFMIBVIEW: + zlog_debug ("SNMP_ENDOFMIBVIEW"); + break; + case ASN_BIT_STR: + zlog_debug ("ASN_BIT_STR"); + break; + default: + zlog_debug ("Unknown type"); + break; + } + return ptr; +} + +/* NOTE: all 3 functions (smux_set, smux_get & smux_getnext) are based on + ucd-snmp smux and as such suppose, that the peer receives in the message + only one variable. Fortunately, IBM seems to do the same in AIX. */ + +static int +smux_set (oid *reqid, size_t *reqid_len, + u_char val_type, void *val, size_t val_len, int action) +{ + int j; + struct subtree *subtree; + struct variable *v; + int subresult; + oid *suffix; + size_t suffix_len; + int result; + u_char *statP = NULL; + WriteMethod *write_method = NULL; + struct listnode *node, *nnode; + + /* Check */ + for (ALL_LIST_ELEMENTS (treelist, node, nnode, subtree)) + { + subresult = oid_compare_part (reqid, *reqid_len, + subtree->name, subtree->name_len); + + /* Subtree matched. */ + if (subresult == 0) + { + /* Prepare suffix. */ + suffix = reqid + subtree->name_len; + suffix_len = *reqid_len - subtree->name_len; + result = subresult; + + /* Check variables. */ + for (j = 0; j < subtree->variables_num; j++) + { + v = &subtree->variables[j]; + + /* Always check suffix */ + result = oid_compare_part (suffix, suffix_len, + v->name, v->namelen); + + /* This is exact match so result must be zero. */ + if (result == 0) + { + if (debug_smux) + zlog_debug ("SMUX function call index is %d", v->magic); + + statP = (*v->findVar) (v, suffix, &suffix_len, 1, + &val_len, &write_method); + + if (write_method) + { + return (*write_method)(action, val, val_type, val_len, + statP, suffix, suffix_len); + } + else + { + return SNMP_ERR_READONLY; + } + } + + /* If above execution is failed or oid is small (so + there is no further match). */ + if (result < 0) + return SNMP_ERR_NOSUCHNAME; + } + } + } + return SNMP_ERR_NOSUCHNAME; +} + +static int +smux_get (oid *reqid, size_t *reqid_len, int exact, + u_char *val_type,void **val, size_t *val_len) +{ + int j; + struct subtree *subtree; + struct variable *v; + int subresult; + oid *suffix; + size_t suffix_len; + int result; + WriteMethod *write_method=NULL; + struct listnode *node, *nnode; + + /* Check */ + for (ALL_LIST_ELEMENTS (treelist, node, nnode,subtree)) + { + subresult = oid_compare_part (reqid, *reqid_len, + subtree->name, subtree->name_len); + + /* Subtree matched. */ + if (subresult == 0) + { + /* Prepare suffix. */ + suffix = reqid + subtree->name_len; + suffix_len = *reqid_len - subtree->name_len; + result = subresult; + + /* Check variables. */ + for (j = 0; j < subtree->variables_num; j++) + { + v = &subtree->variables[j]; + + /* Always check suffix */ + result = oid_compare_part (suffix, suffix_len, + v->name, v->namelen); + + /* This is exact match so result must be zero. */ + if (result == 0) + { + if (debug_smux) + zlog_debug ("SMUX function call index is %d", v->magic); + + *val = (*v->findVar) (v, suffix, &suffix_len, exact, + val_len, &write_method); + + /* There is no instance. */ + if (*val == NULL) + return SNMP_NOSUCHINSTANCE; + + /* Call is suceed. */ + *val_type = v->type; + + return 0; + } + + /* If above execution is failed or oid is small (so + there is no further match). */ + if (result < 0) + return SNMP_ERR_NOSUCHNAME; + } + } + } + return SNMP_ERR_NOSUCHNAME; +} + +static int +smux_getnext (oid *reqid, size_t *reqid_len, int exact, + u_char *val_type,void **val, size_t *val_len) +{ + int j; + oid save[MAX_OID_LEN]; + int savelen = 0; + struct subtree *subtree; + struct variable *v; + int subresult; + oid *suffix; + size_t suffix_len; + int result; + WriteMethod *write_method=NULL; + struct listnode *node, *nnode; + + + /* Save incoming request. */ + oid_copy (save, reqid, *reqid_len); + savelen = *reqid_len; + + /* Check */ + for (ALL_LIST_ELEMENTS (treelist, node, nnode, subtree)) + { + subresult = oid_compare_part (reqid, *reqid_len, + subtree->name, subtree->name_len); + + /* If request is in the tree. The agent has to make sure we + only receive requests we have registered for. */ + /* Unfortunately, that's not true. In fact, a SMUX subagent has to + behave as if it manages the whole SNMP MIB tree itself. It's the + duty of the master agent to collect the best answer and return it + to the manager. See RFC 1227 chapter 3.1.6 for the glory details + :-). ucd-snmp really behaves bad here as it actually might ask + multiple times for the same GETNEXT request as it throws away the + answer when it expects it in a different subtree and might come + back later with the very same request. --jochen */ + + if (subresult <= 0) + { + /* Prepare suffix. */ + suffix = reqid + subtree->name_len; + suffix_len = *reqid_len - subtree->name_len; + if (subresult < 0) + { + oid_copy(reqid, subtree->name, subtree->name_len); + *reqid_len = subtree->name_len; + } + for (j = 0; j < subtree->variables_num; j++) + { + result = subresult; + v = &subtree->variables[j]; + + /* Next then check result >= 0. */ + if (result == 0) + result = oid_compare_part (suffix, suffix_len, + v->name, v->namelen); + + if (result <= 0) + { + if (debug_smux) + zlog_debug ("SMUX function call index is %d", v->magic); + if(result<0) + { + oid_copy(suffix, v->name, v->namelen); + suffix_len = v->namelen; + } + *val = (*v->findVar) (v, suffix, &suffix_len, exact, + val_len, &write_method); + *reqid_len = suffix_len + subtree->name_len; + if (*val) + { + *val_type = v->type; + return 0; + } + } + } + } + } + memcpy (reqid, save, savelen * sizeof(oid)); + *reqid_len = savelen; + + return SNMP_ERR_NOSUCHNAME; +} + +/* GET message header. */ +static u_char * +smux_parse_get_header (u_char *ptr, size_t *len, long *reqid) +{ + u_char type; + long errstat; + long errindex; + + /* Request ID. */ + ptr = asn_parse_int (ptr, len, &type, reqid, sizeof (*reqid)); + + if (debug_smux) + zlog_debug ("SMUX GET reqid: %d len: %d", (int) *reqid, (int) *len); + + /* Error status. */ + ptr = asn_parse_int (ptr, len, &type, &errstat, sizeof (errstat)); + + if (debug_smux) + zlog_debug ("SMUX GET errstat %ld len: %zd", errstat, *len); + + /* Error index. */ + ptr = asn_parse_int (ptr, len, &type, &errindex, sizeof (errindex)); + + if (debug_smux) + zlog_debug ("SMUX GET errindex %ld len: %zd", errindex, *len); + + return ptr; +} + +static void +smux_parse_set (u_char *ptr, size_t len, int action) +{ + long reqid; + oid oid[MAX_OID_LEN]; + size_t oid_len; + u_char val_type; + void *val; + size_t val_len; + int ret; + + if (debug_smux) + zlog_debug ("SMUX SET(%s) message parse: len %zd", + (RESERVE1 == action) ? "RESERVE1" : ((FREE == action) ? "FREE" : "COMMIT"), + len); + + /* Parse SET message header. */ + ptr = smux_parse_get_header (ptr, &len, &reqid); + + /* Parse SET message object ID. */ + ptr = smux_var (ptr, len, oid, &oid_len, &val_len, &val_type, &val); + + ret = smux_set (oid, &oid_len, val_type, val, val_len, action); + if (debug_smux) + zlog_debug ("SMUX SET ret %d", ret); + + /* Return result. */ + if (RESERVE1 == action) + smux_getresp_send (oid, oid_len, reqid, ret, 3, ASN_NULL, NULL, 0); +} + +static void +smux_parse_get (u_char *ptr, size_t len, int exact) +{ + long reqid; + oid oid[MAX_OID_LEN]; + size_t oid_len; + u_char val_type; + void *val; + size_t val_len; + int ret; + + if (debug_smux) + zlog_debug ("SMUX GET message parse: len %zd", len); + + /* Parse GET message header. */ + ptr = smux_parse_get_header (ptr, &len, &reqid); + + /* Parse GET message object ID. We needn't the value come */ + ptr = smux_var (ptr, len, oid, &oid_len, NULL, NULL, NULL); + + /* Traditional getstatptr. */ + if (exact) + ret = smux_get (oid, &oid_len, exact, &val_type, &val, &val_len); + else + ret = smux_getnext (oid, &oid_len, exact, &val_type, &val, &val_len); + + /* Return result. */ + if (ret == 0) + smux_getresp_send (oid, oid_len, reqid, 0, 0, val_type, val, val_len); + else + smux_getresp_send (oid, oid_len, reqid, ret, 3, ASN_NULL, NULL, 0); +} + +/* Parse SMUX_CLOSE message. */ +static void +smux_parse_close (u_char *ptr, int len) +{ + long reason = 0; + + while (len--) + { + reason = (reason << 8) | (long) *ptr; + ptr++; + } + zlog_info ("SMUX_CLOSE with reason: %ld", reason); +} + +/* SMUX_RRSP message. */ +static void +smux_parse_rrsp (u_char *ptr, size_t len) +{ + u_char val; + long errstat; + + ptr = asn_parse_int (ptr, &len, &val, &errstat, sizeof (errstat)); + + if (debug_smux) + zlog_debug ("SMUX_RRSP value: %d errstat: %ld", val, errstat); +} + +/* Parse SMUX message. */ +static int +smux_parse (u_char *ptr, size_t len) +{ + /* This buffer we'll use for SOUT message. We could allocate it with + malloc and save only static pointer/lenght, but IMHO static + buffer is a faster solusion. */ + static u_char sout_save_buff[SMUXMAXPKTSIZE]; + static int sout_save_len = 0; + + int len_income = len; /* see note below: YYY */ + u_char type; + u_char rollback; + + rollback = ptr[2]; /* important only for SMUX_SOUT */ + +process_rest: /* see note below: YYY */ + + /* Parse SMUX message type and subsequent length. */ + ptr = asn_parse_header (ptr, &len, &type); + + if (debug_smux) + zlog_debug ("SMUX message received type: %d rest len: %zd", type, len); + + switch (type) + { + case SMUX_OPEN: + /* Open must be not send from SNMP agent. */ + zlog_warn ("SMUX_OPEN received: resetting connection."); + return -1; + break; + case SMUX_RREQ: + /* SMUX_RREQ message is invalid for us. */ + zlog_warn ("SMUX_RREQ received: resetting connection."); + return -1; + break; + case SMUX_SOUT: + /* SMUX_SOUT message is now valied for us. */ + if (debug_smux) + zlog_debug ("SMUX_SOUT(%s)", rollback ? "rollback" : "commit"); + + if (sout_save_len > 0) + { + smux_parse_set (sout_save_buff, sout_save_len, rollback ? FREE : COMMIT); + sout_save_len = 0; + } + else + zlog_warn ("SMUX_SOUT sout_save_len=%d - invalid", (int) sout_save_len); + + if (len_income > 3) + { + /* YYY: this strange code has to solve the "slow peer" + problem: When agent sends SMUX_SOUT message it doesn't + wait any responce and may send some next message to + subagent. Then the peer in 'smux_read()' will recieve + from socket the 'concatenated' buffer, contaning both + SMUX_SOUT message and the next one + (SMUX_GET/SMUX_GETNEXT/SMUX_GET). So we should check: if + the buffer is longer than 3 ( length of SMUX_SOUT ), we + must process the rest of it. This effect may be observed + if 'debug_smux' is set to '1' */ + ptr++; + len = len_income - 3; + goto process_rest; + } + break; + case SMUX_GETRSP: + /* SMUX_GETRSP message is invalid for us. */ + zlog_warn ("SMUX_GETRSP received: resetting connection."); + return -1; + break; + case SMUX_CLOSE: + /* Close SMUX connection. */ + if (debug_smux) + zlog_debug ("SMUX_CLOSE"); + smux_parse_close (ptr, len); + return -1; + break; + case SMUX_RRSP: + /* This is response for register message. */ + if (debug_smux) + zlog_debug ("SMUX_RRSP"); + smux_parse_rrsp (ptr, len); + break; + case SMUX_GET: + /* Exact request for object id. */ + if (debug_smux) + zlog_debug ("SMUX_GET"); + smux_parse_get (ptr, len, 1); + break; + case SMUX_GETNEXT: + /* Next request for object id. */ + if (debug_smux) + zlog_debug ("SMUX_GETNEXT"); + smux_parse_get (ptr, len, 0); + break; + case SMUX_SET: + /* SMUX_SET is supported with some limitations. */ + if (debug_smux) + zlog_debug ("SMUX_SET"); + + /* save the data for future SMUX_SOUT */ + memcpy (sout_save_buff, ptr, len); + sout_save_len = len; + smux_parse_set (ptr, len, RESERVE1); + break; + default: + zlog_info ("Unknown type: %d", type); + break; + } + return 0; +} + +/* SMUX message read function. */ +static int +smux_read (struct thread *t) +{ + int sock; + int len; + u_char buf[SMUXMAXPKTSIZE]; + int ret; + + /* Clear thread. */ + sock = THREAD_FD (t); + smux_read_thread = NULL; + + if (debug_smux) + zlog_debug ("SMUX read start"); + + /* Read message from SMUX socket. */ + len = recv (sock, buf, SMUXMAXPKTSIZE, 0); + + if (len < 0) + { + zlog_warn ("Can't read all SMUX packet: %s", safe_strerror (errno)); + close (sock); + smux_sock = -1; + smux_event (SMUX_CONNECT, 0); + return -1; + } + + if (len == 0) + { + zlog_warn ("SMUX connection closed: %d", sock); + close (sock); + smux_sock = -1; + smux_event (SMUX_CONNECT, 0); + return -1; + } + + if (debug_smux) + zlog_debug ("SMUX read len: %d", len); + + /* Parse the message. */ + ret = smux_parse (buf, len); + + if (ret < 0) + { + close (sock); + smux_sock = -1; + smux_event (SMUX_CONNECT, 0); + return -1; + } + + /* Regiser read thread. */ + smux_event (SMUX_READ, sock); + + return 0; +} + +static int +smux_open (int sock) +{ + u_char buf[BUFSIZ]; + u_char *ptr; + size_t len; + long version; + const char progname[] = QUAGGA_PROGNAME "-" QUAGGA_VERSION; + + if (debug_smux) + { + smux_oid_dump ("SMUX open oid", smux_oid, smux_oid_len); + zlog_debug ("SMUX open progname: %s", progname); + zlog_debug ("SMUX open password: %s", smux_passwd); + } + + ptr = buf; + len = BUFSIZ; + + /* SMUX Header. As placeholder. */ + ptr = asn_build_header (ptr, &len, (u_char) SMUX_OPEN, 0); + + /* SMUX Open. */ + version = 0; + ptr = asn_build_int (ptr, &len, + (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), + &version, sizeof (version)); + + /* SMUX connection oid. */ + ptr = asn_build_objid (ptr, &len, + (u_char) + (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID), + smux_oid, smux_oid_len); + + /* SMUX connection description. */ + ptr = asn_build_string (ptr, &len, + (u_char) + (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), + (const u_char *) progname, strlen (progname)); + + /* SMUX connection password. */ + ptr = asn_build_string (ptr, &len, + (u_char) + (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), + (u_char *)smux_passwd, strlen (smux_passwd)); + + /* Fill in real SMUX header. We exclude ASN header size (2). */ + len = BUFSIZ; + asn_build_header (buf, &len, (u_char) SMUX_OPEN, (ptr - buf) - 2); + + 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 (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) +{ + unsigned int i; + u_char buf[BUFSIZ]; + u_char *ptr; + size_t len, length; + struct in_addr addr; + unsigned long val; + u_char *h1, *h1e; + + ptr = buf; + len = BUFSIZ; + length = len; + + /* When SMUX connection is not established. */ + if (smux_sock < 0) + return 0; + + /* SMUX header. */ + ptr = asn_build_header (ptr, &len, (u_char) SMUX_TRAP, 0); + + /* Sub agent enterprise oid. */ + ptr = asn_build_objid (ptr, &len, + (u_char) + (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID), + smux_oid, smux_oid_len); + + /* IP address. */ + addr.s_addr = 0; + ptr = asn_build_string (ptr, &len, + (u_char) + (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_IPADDRESS), + (u_char *)&addr, sizeof (addr)); + + /* Generic trap integer. */ + val = SNMP_TRAP_ENTERPRISESPECIFIC; + ptr = asn_build_int (ptr, &len, + (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), + (long *)&val, sizeof (val)); + + /* Specific trap integer. */ + val = sptrap; + ptr = asn_build_int (ptr, &len, + (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), + (long *)&val, sizeof (val)); + + /* Timeticks timestamp. */ + val = 0; + ptr = asn_build_unsigned_int (ptr, &len, + (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_TIMETICKS), + &val, sizeof (val)); + + /* Variables. */ + h1 = ptr; + ptr = asn_build_sequence (ptr, &len, + (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR), + 0); + + + /* Iteration for each objects. */ + h1e = ptr; + for (i = 0; i < trapobjlen; i++) + { + int ret; + oid oid[MAX_OID_LEN]; + size_t oid_len; + void *val; + size_t val_len; + u_char val_type; + + /* Make OID. */ + if (trapobj[i].namelen > 0) + { + oid_copy (oid, name, namelen); + oid_copy (oid + namelen, trapobj[i].name, trapobj[i].namelen); + oid_copy (oid + namelen + trapobj[i].namelen, iname, inamelen); + oid_len = namelen + trapobj[i].namelen + inamelen; + } + else + { + oid_copy (oid, name, namelen); + oid_copy (oid + namelen, trapobj[i].name, trapobj[i].namelen * (-1)); + oid_len = namelen + trapobj[i].namelen * (-1) ; + } + + if (debug_smux) + { + smux_oid_dump ("Trap", name, namelen); + if (trapobj[i].namelen < 0) + smux_oid_dump ("Trap", + trapobj[i].name, (- 1) * (trapobj[i].namelen)); + else + { + smux_oid_dump ("Trap", trapobj[i].name, (trapobj[i].namelen)); + smux_oid_dump ("Trap", iname, inamelen); + } + smux_oid_dump ("Trap", oid, oid_len); + zlog_info ("BUFSIZ: %d // oid_len: %lu", BUFSIZ, (u_long)oid_len); + } + + ret = smux_get (oid, &oid_len, 1, &val_type, &val, &val_len); + + if (debug_smux) + zlog_debug ("smux_get result %d", ret); + + if (ret == 0) + ptr = snmp_build_var_op (ptr, oid, &oid_len, + val_type, val_len, val, &len); + } + + /* Now variable size is known, fill in size */ + asn_build_sequence(h1, &length, + (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR), + ptr - h1e); + + /* Fill in size of whole sequence */ + len = BUFSIZ; + asn_build_header (buf, &len, (u_char) SMUX_TRAP, (ptr - buf) - 2); + + return send (smux_sock, buf, (ptr - buf), 0); +} + +static int +smux_register (int sock) +{ + u_char buf[BUFSIZ]; + u_char *ptr; + int ret; + size_t len; + long priority; + long operation; + struct subtree *subtree; + struct listnode *node, *nnode; + + ret = 0; + + for (ALL_LIST_ELEMENTS (treelist, node, nnode, subtree)) + { + ptr = buf; + len = BUFSIZ; + + /* SMUX RReq Header. */ + ptr = asn_build_header (ptr, &len, (u_char) SMUX_RREQ, 0); + + /* Register MIB tree. */ + ptr = asn_build_objid (ptr, &len, + (u_char) + (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID), + subtree->name, subtree->name_len); + + /* Priority. */ + priority = -1; + ptr = asn_build_int (ptr, &len, + (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), + &priority, sizeof (priority)); + + /* Operation. */ + operation = 2; /* Register R/W */ + ptr = asn_build_int (ptr, &len, + (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), + &operation, sizeof (operation)); + + if (debug_smux) + { + smux_oid_dump ("SMUX register oid", subtree->name, subtree->name_len); + zlog_debug ("SMUX register priority: %ld", priority); + zlog_debug ("SMUX register operation: %ld", operation); + } + + len = BUFSIZ; + asn_build_header (buf, &len, (u_char) SMUX_RREQ, (ptr - buf) - 2); + ret = send (sock, buf, (ptr - buf), 0); + if (ret < 0) + return ret; + } + return ret; +} + +/* Try to connect to SNMP agent. */ +static int +smux_connect (struct thread *t) +{ + int ret; + + if (debug_smux) + zlog_debug ("SMUX connect try %d", fail + 1); + + /* Clear thread poner of myself. */ + smux_connect_thread = NULL; + + /* Make socket. Try to connect. */ + smux_sock = smux_socket (); + if (smux_sock < 0) + { + if (++fail < SMUX_MAX_FAILURE) + smux_event (SMUX_CONNECT, 0); + return 0; + } + + /* Send OPEN PDU. */ + ret = smux_open (smux_sock); + if (ret < 0) + { + zlog_warn ("SMUX open message send failed: %s", safe_strerror (errno)); + close (smux_sock); + smux_sock = -1; + if (++fail < SMUX_MAX_FAILURE) + smux_event (SMUX_CONNECT, 0); + return -1; + } + + /* Send any outstanding register PDUs. */ + ret = smux_register (smux_sock); + if (ret < 0) + { + zlog_warn ("SMUX register message send failed: %s", safe_strerror (errno)); + close (smux_sock); + smux_sock = -1; + if (++fail < SMUX_MAX_FAILURE) + smux_event (SMUX_CONNECT, 0); + return -1; + } + + /* Everything goes fine. */ + smux_event (SMUX_READ, smux_sock); + + return 0; +} + +/* Clear all SMUX related resources. */ +static void +smux_stop (void) +{ + if (smux_read_thread) + { + thread_cancel (smux_read_thread); + smux_read_thread = NULL; + } + + if (smux_connect_thread) + { + thread_cancel (smux_connect_thread); + smux_connect_thread = NULL; + } + + if (smux_sock >= 0) + { + close (smux_sock); + smux_sock = -1; + } +} + + + +void +smux_event (enum smux_event event, int sock) +{ + switch (event) + { + case SMUX_SCHEDULE: + smux_connect_thread = thread_add_event (smux_master, smux_connect, NULL, 0); + break; + case SMUX_CONNECT: + smux_connect_thread = thread_add_timer (smux_master, smux_connect, NULL, 10); + break; + case SMUX_READ: + 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) +{ + int len; + int val; + + len = 0; + val = 0; + *oid_len = 0; + + if (*str == '.') + str++; + if (*str == '\0') + return 0; + + while (1) + { + if (! isdigit (*str)) + return -1; + + while (isdigit (*str)) + { + val *= 10; + val += (*str - '0'); + str++; + } + + if (*str == '\0') + break; + if (*str != '.') + return -1; + + oid[len++] = val; + val = 0; + str++; + } + + oid[len++] = val; + *oid_len = len; + + return 0; +} + +static oid * +smux_oid_dup (oid *objid, size_t objid_len) +{ + oid *new; + + new = XMALLOC (MTYPE_TMP, sizeof (oid) * objid_len); + oid_copy (new, objid, objid_len); + + return new; +} + +static int +smux_peer_oid (struct vty *vty, const char *oid_str, const char *passwd_str) +{ + int ret; + oid oid[MAX_OID_LEN]; + size_t oid_len; + + ret = smux_str2oid (oid_str, oid, &oid_len); + if (ret != 0) + { + vty_out (vty, "object ID malformed%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (smux_oid) + { + free (smux_oid); + smux_oid = NULL; + } + + /* careful, smux_passwd might point to string constant */ + if (smux_passwd) + { + free (smux_passwd); + smux_passwd = NULL; + } + + smux_oid = smux_oid_dup (oid, oid_len); + smux_oid_len = oid_len; + + if (passwd_str) + smux_passwd = strdup (passwd_str); + else + smux_passwd = strdup (""); + + return 0; +} + +static int +smux_peer_default (void) +{ + if (smux_oid) + { + free (smux_oid); + smux_oid = NULL; + } + + /* careful, smux_passwd might be pointing at string constant */ + if (smux_passwd) + { + free (smux_passwd); + smux_passwd = NULL; + } + + return CMD_SUCCESS; +} + +DEFUN (smux_peer, + smux_peer_cmd, + "smux peer OID", + "SNMP MUX protocol settings\n" + "SNMP MUX peer settings\n" + "Object ID used in SMUX peering\n") +{ + if (smux_peer_oid (vty, argv[0], NULL) == 0) + { + smux_start(); + return CMD_SUCCESS; + } + else + return CMD_WARNING; +} + +DEFUN (smux_peer_password, + smux_peer_password_cmd, + "smux peer OID PASSWORD", + "SNMP MUX protocol settings\n" + "SNMP MUX peer settings\n" + "SMUX peering object ID\n" + "SMUX peering password\n") +{ + if (smux_peer_oid (vty, argv[0], argv[1]) == 0) + { + smux_start(); + return CMD_SUCCESS; + } + else + return CMD_WARNING; +} + +DEFUN (no_smux_peer, + no_smux_peer_cmd, + "no smux peer", + NO_STR + "SNMP MUX protocol settings\n" + "SNMP MUX peer settings\n") +{ + smux_stop(); + return smux_peer_default (); +} + +ALIAS (no_smux_peer, + no_smux_peer_oid_cmd, + "no smux peer OID", + NO_STR + "SNMP MUX protocol settings\n" + "SNMP MUX peer settings\n" + "SMUX peering object ID\n") + +ALIAS (no_smux_peer, + no_smux_peer_oid_password_cmd, + "no smux peer OID PASSWORD", + NO_STR + "SNMP MUX protocol settings\n" + "SNMP MUX peer settings\n" + "SMUX peering object ID\n" + "SMUX peering password\n") + +static int +config_write_smux (struct vty *vty) +{ + int first = 1; + unsigned int i; + + if (smux_oid) + { + vty_out (vty, "smux peer "); + for (i = 0; i < smux_oid_len; i++) + { + vty_out (vty, "%s%d", first ? "" : ".", (int) smux_oid[i]); + first = 0; + } + vty_out (vty, " %s%s", smux_passwd, VTY_NEWLINE); + } + return 0; +} + +/* Register subtree to smux master tree. */ +void +smux_register_mib (const char *descr, struct variable *var, + size_t width, int num, + oid name[], size_t namelen) +{ + struct subtree *tree; + + tree = (struct subtree *)malloc(sizeof(struct subtree)); + oid_copy (tree->name, name, namelen); + tree->name_len = namelen; + tree->variables = var; + tree->variables_num = num; + tree->variables_width = width; + tree->registered = 0; + listnode_add_sort(treelist, tree); +} + +/* Compare function to keep treelist sorted */ +static int +smux_tree_cmp(struct subtree *tree1, struct subtree *tree2) +{ + return oid_compare(tree1->name, tree1->name_len, + tree2->name, tree2->name_len); +} + +/* Initialize some values then schedule first SMUX connection. */ +void +smux_init (struct thread_master *tm) +{ + /* copy callers thread master */ + smux_master = tm; + + /* Make MIB tree. */ + treelist = list_new(); + treelist->cmp = (int (*)(void *, void *))smux_tree_cmp; + + /* Install commands. */ + install_node (&smux_node, config_write_smux); + + install_element (CONFIG_NODE, &smux_peer_cmd); + install_element (CONFIG_NODE, &smux_peer_password_cmd); + install_element (CONFIG_NODE, &no_smux_peer_cmd); + install_element (CONFIG_NODE, &no_smux_peer_oid_cmd); + install_element (CONFIG_NODE, &no_smux_peer_oid_password_cmd); +} + +void +smux_start(void) +{ + /* Close any existing connections. */ + smux_stop(); + + /* Schedule first connection. */ + smux_event (SMUX_SCHEDULE, 0); +} +#endif /* HAVE_SNMP */ diff --git a/lib/smux.h b/lib/smux.h new file mode 100644 index 0000000..dc91cac --- /dev/null +++ b/lib/smux.h @@ -0,0 +1,116 @@ +/* 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. + */ + +#ifndef _ZEBRA_SNMP_H +#define _ZEBRA_SNMP_H + +#include +#include + +#include "thread.h" + +/* Structures here are mostly compatible with UCD SNMP 4.1.1 */ +#define MATCH_FAILED (-1) +#define MATCH_SUCCEEDED 0 + +/* SYNTAX TruthValue from SNMPv2-TC. */ +#define SNMP_TRUE 1 +#define SNMP_FALSE 2 + +/* SYNTAX RowStatus from SNMPv2-TC. */ +#define SNMP_VALID 1 +#define SNMP_INVALID 2 + +#define IN_ADDR_SIZE sizeof(struct in_addr) + +#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)) + +struct trap_object +{ + 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 __attribute__ ((unused)); \ + static struct in_addr snmp_in_addr_val __attribute__ ((unused)); + +#define SNMP_INTEGER(V) \ + ( \ + *var_len = sizeof (snmp_int_val), \ + snmp_int_val = V, \ + (u_char *) &snmp_int_val \ + ) + +#define SNMP_IPADDRESS(V) \ + ( \ + *var_len = sizeof (struct in_addr), \ + snmp_in_addr_val = V, \ + (u_char *) &snmp_in_addr_val \ + ) + +extern void smux_init (struct thread_master *tm); +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_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); + +#endif /* _ZEBRA_SNMP_H */ diff --git a/lib/snmp.c b/lib/snmp.c new file mode 100644 index 0000000..f6f9845 --- /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 new file mode 100644 index 0000000..3e6ee73 --- /dev/null +++ b/lib/sockopt.c @@ -0,0 +1,669 @@ +/* setsockopt functions + * 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 SUNOS_5 +#include +#endif + +#include "log.h" +#include "sockopt.h" +#include "sockunion.h" + +int +setsockopt_so_recvbuf (int sock, int size) +{ + int ret; + + if ( (ret = setsockopt (sock, SOL_SOCKET, SO_RCVBUF, (char *) + &size, sizeof (int))) < 0) + zlog_err ("fd %d: can't setsockopt SO_RCVBUF to %d: %s", + sock,size,safe_strerror(errno)); + + return ret; +} + +int +setsockopt_so_sendbuf (const int sock, int size) +{ + int ret = setsockopt (sock, SOL_SOCKET, SO_SNDBUF, + (char *)&size, sizeof (int)); + + if (ret < 0) + zlog_err ("fd %d: can't setsockopt SO_SNDBUF to %d: %s", + sock, size, safe_strerror (errno)); + + return ret; +} + +int +getsockopt_so_sendbuf (const int sock) +{ + u_int32_t optval; + socklen_t optlen = sizeof (optval); + int ret = getsockopt (sock, SOL_SOCKET, SO_SNDBUF, + (char *)&optval, &optlen); + if (ret < 0) + { + zlog_err ("fd %d: can't getsockopt SO_SNDBUF: %d (%s)", + sock, errno, safe_strerror (errno)); + return ret; + } + return optval; +} + +static void * +getsockopt_cmsg_data (struct msghdr *msgh, int level, int type) +{ + struct cmsghdr *cmsg; + void *ptr = NULL; + + for (cmsg = ZCMSG_FIRSTHDR(msgh); + cmsg != NULL; + cmsg = CMSG_NXTHDR(msgh, cmsg)) + if (cmsg->cmsg_level == level && cmsg->cmsg_type) + return (ptr = CMSG_DATA(cmsg)); + + return NULL; +} + +#ifdef HAVE_IPV6 +/* Set IPv6 packet info to the socket. */ +int +setsockopt_ipv6_pktinfo (int sock, int val) +{ + int ret; + +#ifdef IPV6_RECVPKTINFO /*2292bis-01*/ + ret = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val)); + if (ret < 0) + zlog_warn ("can't setsockopt IPV6_RECVPKTINFO : %s", safe_strerror (errno)); +#else /*RFC2292*/ + ret = setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, &val, sizeof(val)); + if (ret < 0) + zlog_warn ("can't setsockopt IPV6_PKTINFO : %s", safe_strerror (errno)); +#endif /* INIA_IPV6 */ + return ret; +} + +/* Set multicast hops val to the socket. */ +int +setsockopt_ipv6_checksum (int sock, int val) +{ + int ret; + +#ifdef GNU_LINUX + ret = setsockopt(sock, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val)); +#else + ret = setsockopt(sock, IPPROTO_IPV6, IPV6_CHECKSUM, &val, sizeof(val)); +#endif /* GNU_LINUX */ + if (ret < 0) + zlog_warn ("can't setsockopt IPV6_CHECKSUM"); + return ret; +} + +/* Set multicast hops val to the socket. */ +int +setsockopt_ipv6_multicast_hops (int sock, int val) +{ + int ret; + + ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val)); + if (ret < 0) + zlog_warn ("can't setsockopt IPV6_MULTICAST_HOPS"); + return ret; +} + +/* Set multicast hops val to the socket. */ +int +setsockopt_ipv6_unicast_hops (int sock, int val) +{ + int ret; + + ret = setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val)); + if (ret < 0) + zlog_warn ("can't setsockopt IPV6_UNICAST_HOPS"); + return ret; +} + +int +setsockopt_ipv6_hoplimit (int sock, int val) +{ + int ret; + +#ifdef IPV6_RECVHOPLIMIT /*2292bis-01*/ + ret = setsockopt (sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &val, sizeof(val)); + if (ret < 0) + zlog_warn ("can't setsockopt IPV6_RECVHOPLIMIT"); +#else /*RFC2292*/ + ret = setsockopt (sock, IPPROTO_IPV6, IPV6_HOPLIMIT, &val, sizeof(val)); + if (ret < 0) + zlog_warn ("can't setsockopt IPV6_HOPLIMIT"); +#endif + return ret; +} + +/* Set multicast loop zero to the socket. */ +int +setsockopt_ipv6_multicast_loop (int sock, int val) +{ + int ret; + + ret = setsockopt (sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, + sizeof (val)); + if (ret < 0) + zlog_warn ("can't setsockopt IPV6_MULTICAST_LOOP"); + return ret; +} + +static int +getsockopt_ipv6_ifindex (struct msghdr *msgh) +{ + struct in6_pktinfo *pktinfo; + + pktinfo = getsockopt_cmsg_data (msgh, IPPROTO_IPV6, IPV6_PKTINFO); + + return pktinfo->ipi6_ifindex; +} + +int +setsockopt_ipv6_tclass(int sock, int tclass) +{ + int ret = 0; + +#ifdef IPV6_TCLASS /* RFC3542 */ + ret = setsockopt (sock, IPPROTO_IPV6, IPV6_TCLASS, &tclass, sizeof (tclass)); + if (ret < 0) + zlog_warn ("Can't set IPV6_TCLASS option for fd %d to %#x: %s", + sock, tclass, safe_strerror(errno)); +#endif + return ret; +} +#endif /* HAVE_IPV6 */ + +/* + * Process multicast socket options for IPv4 in an OS-dependent manner. + * Supported options are IP_{ADD,DROP}_MEMBERSHIP. + * + * Many operating systems have a limit on the number of groups that + * can be joined per socket (where each group and local address + * counts). This impacts OSPF, which joins groups on each interface + * using a single socket. The limit is typically 20, derived from the + * original BSD multicast implementation. Some systems have + * mechanisms for increasing this limit. + * + * In many 4.4BSD-derived systems, multicast group operations are not + * allowed on interfaces that are not UP. Thus, a previous attempt to + * leave the group may have failed, leaving it still joined, and we + * drop/join quietly to recover. This may not be necessary, but aims to + * defend against unknown behavior in that we will still return an error + * if the second join fails. It is not clear how other systems + * (e.g. Linux, Solaris) behave when leaving groups on down interfaces, + * but this behavior should not be harmful if they behave the same way, + * allow leaves, or implicitly leave all groups joined to down interfaces. + */ +int +setsockopt_ipv4_multicast(int sock, + int optname, + unsigned int mcast_addr, + ifindex_t ifindex) +{ +#ifdef HAVE_RFC3678 + struct group_req gr; + struct sockaddr_in *si; + int ret; + memset (&gr, 0, sizeof(gr)); + si = (struct sockaddr_in *)&gr.gr_group; + gr.gr_interface = ifindex; + si->sin_family = AF_INET; +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + si->sin_len = sizeof(struct sockaddr_in); +#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ + si->sin_addr.s_addr = mcast_addr; + ret = setsockopt(sock, IPPROTO_IP, (optname == IP_ADD_MEMBERSHIP) ? + MCAST_JOIN_GROUP : MCAST_LEAVE_GROUP, (void *)&gr, sizeof(gr)); + if ((ret < 0) && (optname == IP_ADD_MEMBERSHIP) && (errno == EADDRINUSE)) + { + setsockopt(sock, IPPROTO_IP, MCAST_LEAVE_GROUP, (void *)&gr, sizeof(gr)); + ret = setsockopt(sock, IPPROTO_IP, MCAST_JOIN_GROUP, (void *)&gr, sizeof(gr)); + } + return ret; + +#elif defined(HAVE_STRUCT_IP_MREQN_IMR_IFINDEX) && !defined(__FreeBSD__) + struct ip_mreqn mreqn; + int ret; + + assert(optname == IP_ADD_MEMBERSHIP || optname == IP_DROP_MEMBERSHIP); + memset (&mreqn, 0, sizeof(mreqn)); + + mreqn.imr_multiaddr.s_addr = mcast_addr; + mreqn.imr_ifindex = ifindex; + + ret = setsockopt(sock, IPPROTO_IP, optname, + (void *)&mreqn, sizeof(mreqn)); + if ((ret < 0) && (optname == IP_ADD_MEMBERSHIP) && (errno == EADDRINUSE)) + { + /* see above: handle possible problem when interface comes back up */ + char buf[1][INET_ADDRSTRLEN]; + zlog_info("setsockopt_ipv4_multicast attempting to drop and " + "re-add (fd %d, mcast %s, ifindex %u)", + sock, + inet_ntop(AF_INET, &mreqn.imr_multiaddr, + buf[0], sizeof(buf[0])), ifindex); + setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, + (void *)&mreqn, sizeof(mreqn)); + ret = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, + (void *)&mreqn, sizeof(mreqn)); + } + return ret; + + /* Example defines for another OS, boilerplate off other code in this + function, AND handle optname as per other sections for consistency !! */ + /* #elif defined(BOGON_NIX) && EXAMPLE_VERSION_CODE > -100000 */ + /* Add your favourite OS here! */ + +#elif defined(HAVE_BSD_STRUCT_IP_MREQ_HACK) /* #if OS_TYPE */ + /* standard BSD API */ + + struct in_addr m; + struct ip_mreq mreq; + int ret; + + assert(optname == IP_ADD_MEMBERSHIP || optname == IP_DROP_MEMBERSHIP); + + m.s_addr = htonl(ifindex); + + memset (&mreq, 0, sizeof(mreq)); + mreq.imr_multiaddr.s_addr = mcast_addr; + mreq.imr_interface = m; + + ret = setsockopt (sock, IPPROTO_IP, optname, (void *)&mreq, sizeof(mreq)); + if ((ret < 0) && (optname == IP_ADD_MEMBERSHIP) && (errno == EADDRINUSE)) + { + /* see above: handle possible problem when interface comes back up */ + char buf[1][INET_ADDRSTRLEN]; + zlog_info("setsockopt_ipv4_multicast attempting to drop and " + "re-add (fd %d, mcast %s, ifindex %u)", + sock, + inet_ntop(AF_INET, &mreq.imr_multiaddr, + buf[0], sizeof(buf[0])), ifindex); + setsockopt (sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, + (void *)&mreq, sizeof(mreq)); + ret = setsockopt (sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, + (void *)&mreq, sizeof(mreq)); + } + return ret; + +#else + #error "Unsupported multicast API" +#endif /* #if OS_TYPE */ + +} + +/* + * Set IP_MULTICAST_IF socket option in an OS-dependent manner. + */ +int +setsockopt_ipv4_multicast_if(int sock, ifindex_t ifindex) +{ + +#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX + struct ip_mreqn mreqn; + memset (&mreqn, 0, sizeof(mreqn)); + + mreqn.imr_ifindex = ifindex; + return setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (void *)&mreqn, sizeof(mreqn)); + + /* Example defines for another OS, boilerplate off other code in this + function */ + /* #elif defined(BOGON_NIX) && EXAMPLE_VERSION_CODE > -100000 */ + /* Add your favourite OS here! */ +#elif defined(HAVE_BSD_STRUCT_IP_MREQ_HACK) + struct in_addr m; + + 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, ifindex_t val) +{ + int ret; + +#if defined (IP_PKTINFO) + if ((ret = setsockopt (sock, IPPROTO_IP, IP_PKTINFO, &val, sizeof (val))) < 0) + zlog_warn ("Can't set IP_PKTINFO option for fd %d to %d: %s", + sock,val,safe_strerror(errno)); +#elif defined (IP_RECVIF) + if ((ret = setsockopt (sock, IPPROTO_IP, IP_RECVIF, &val, sizeof (val))) < 0) + zlog_warn ("Can't set IP_RECVIF option for fd %d to %d: %s", + sock,val,safe_strerror(errno)); +#else +#warning "Neither IP_PKTINFO nor IP_RECVIF is available." +#warning "Will not be able to receive link info." +#warning "Things might be seriously broken.." + /* XXX Does this ever happen? Should there be a zlog_warn message here? */ + ret = -1; +#endif + return ret; +} + +int +setsockopt_ipv4_tos(int sock, int tos) +{ + int ret; + + ret = setsockopt (sock, IPPROTO_IP, IP_TOS, &tos, sizeof (tos)); + if (ret < 0) + zlog_warn ("Can't set IP_TOS option for fd %d to %#x: %s", + sock, tos, safe_strerror(errno)); + return ret; +} + + +int +setsockopt_ifindex (int af, int sock, ifindex_t val) +{ + int ret = -1; + + switch (af) + { + case AF_INET: + ret = setsockopt_ipv4_ifindex (sock, val); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + ret = setsockopt_ipv6_pktinfo (sock, val); + break; +#endif + default: + zlog_warn ("setsockopt_ifindex: unknown address family %d", af); + } + return ret; +} + +/* + * Requires: msgh is not NULL and points to a valid struct msghdr, which + * may or may not have control data about the incoming interface. + * + * Returns the interface index (small integer >= 1) if it can be + * determined, or else 0. + */ +static ifindex_t +getsockopt_ipv4_ifindex (struct msghdr *msgh) +{ + /* XXX: initialize to zero? (Always overwritten, so just cosmetic.) */ + ifindex_t ifindex = -1; + +#if defined(IP_PKTINFO) +/* Linux pktinfo based ifindex retrieval */ + struct in_pktinfo *pktinfo; + + pktinfo = + (struct in_pktinfo *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_PKTINFO); + /* XXX Can pktinfo be NULL? Clean up post 0.98. */ + ifindex = pktinfo->ipi_ifindex; + +#elif defined(IP_RECVIF) + + /* retrieval based on IP_RECVIF */ + +#ifndef SUNOS_5 + /* BSD systems use a sockaddr_dl as the control message payload. */ + struct sockaddr_dl *sdl; +#else + /* SUNOS_5 uses an integer with the index. */ + ifindex_t *ifindex_p; +#endif /* SUNOS_5 */ + +#ifndef SUNOS_5 + /* BSD */ + sdl = + (struct sockaddr_dl *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_RECVIF); + if (sdl != NULL) + ifindex = sdl->sdl_index; + else + ifindex = 0; +#else + /* + * Solaris. On Solaris 8, IP_RECVIF is defined, but the call to + * enable it fails with errno=99, and the struct msghdr has + * controllen 0. + */ + ifindex_p = (uint_t *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_RECVIF); + if (ifindex_p != NULL) + ifindex = *ifindex_p; + else + ifindex = 0; +#endif /* SUNOS_5 */ + +#else + /* + * Neither IP_PKTINFO nor IP_RECVIF defined - warn at compile time. + * XXX Decide if this is a core service, or if daemons have to cope. + * Since Solaris 8 and OpenBSD seem not to provide it, it seems that + * daemons have to cope. + */ +#warning "getsockopt_ipv4_ifindex: Neither IP_PKTINFO nor IP_RECVIF defined." +#warning "Some daemons may fail to operate correctly!" + ifindex = 0; + +#endif /* IP_PKTINFO */ + + return ifindex; +} + +/* return ifindex, 0 if none found */ +ifindex_t +getsockopt_ifindex (int af, struct msghdr *msgh) +{ + switch (af) + { + case AF_INET: + return (getsockopt_ipv4_ifindex (msgh)); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + return (getsockopt_ipv6_ifindex (msgh)); + break; +#endif + default: + zlog_warn ("getsockopt_ifindex: unknown address family %d", af); + return 0; + } +} + +/* swab iph between order system uses for IP_HDRINCL and host order */ +void +sockopt_iphdrincl_swab_htosys (struct ip *iph) +{ + /* BSD and derived take iph in network order, except for + * ip_len and ip_off + */ +#ifndef HAVE_IP_HDRINCL_BSD_ORDER + iph->ip_len = htons(iph->ip_len); + iph->ip_off = htons(iph->ip_off); +#endif /* HAVE_IP_HDRINCL_BSD_ORDER */ + + iph->ip_id = htons(iph->ip_id); +} + +void +sockopt_iphdrincl_swab_systoh (struct ip *iph) +{ +#ifndef HAVE_IP_HDRINCL_BSD_ORDER + iph->ip_len = ntohs(iph->ip_len); + iph->ip_off = ntohs(iph->ip_off); +#endif /* HAVE_IP_HDRINCL_BSD_ORDER */ + + 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) +{ +#if defined(HAVE_TCP_MD5_LINUX24) && defined(GNU_LINUX) + /* Support for the old Linux 2.4 TCP-MD5 patch, taken from Hasso Tepper's + * version of the Quagga patch (based on work by Rick Payne, and Bruce + * Simpson) + */ +#define TCP_MD5_AUTH 13 +#define TCP_MD5_AUTH_ADD 1 +#define TCP_MD5_AUTH_DEL 2 + struct tcp_rfc2385_cmd { + u_int8_t command; /* Command - Add/Delete */ + u_int32_t address; /* IPV4 address associated */ + u_int8_t keylen; /* MD5 Key len (do NOT assume 0 terminated ascii) */ + void *key; /* MD5 Key */ + } cmd; + struct in_addr *addr = &su->sin.sin_addr; + + cmd.command = (password != NULL ? TCP_MD5_AUTH_ADD : TCP_MD5_AUTH_DEL); + cmd.address = addr->s_addr; + cmd.keylen = (password != NULL ? strlen (password) : 0); + cmd.key = password; + + return setsockopt (sock, IPPROTO_TCP, TCP_MD5_AUTH, &cmd, sizeof cmd); + +#elif HAVE_DECL_TCP_MD5SIG + int ret; +#ifndef GNU_LINUX + /* + * XXX Need to do PF_KEY operation here to add/remove an SA entry, + * and add/remove an SP entry for this peer's packet flows also. + */ + int md5sig = password && *password ? 1 : 0; +#else + int keylen = password ? strlen (password) : 0; + struct tcp_md5sig md5sig; + union sockunion *su2, *susock; + + /* Figure out whether the socket and the sockunion are the same family.. + * adding AF_INET to AF_INET6 needs to be v4 mapped, you'd think.. + */ + if (!(susock = sockunion_getsockname (sock))) + return -1; + + if (susock->sa.sa_family == su->sa.sa_family) + su2 = su; + else + { + /* oops.. */ + su2 = susock; + + if (su2->sa.sa_family == AF_INET) + { + sockunion_free (susock); + return 0; + } + +#ifdef HAVE_IPV6 + /* If this does not work, then all users of this sockopt will need to + * differentiate between IPv4 and IPv6, and keep seperate sockets for + * each. + * + * Sadly, it doesn't seem to work at present. It's unknown whether + * this is a bug or not. + */ + if (su2->sa.sa_family == AF_INET6 + && su->sa.sa_family == AF_INET) + { + su2->sin6.sin6_family = AF_INET6; + /* V4Map the address */ + memset (&su2->sin6.sin6_addr, 0, sizeof (struct in6_addr)); + su2->sin6.sin6_addr.s6_addr32[2] = htonl(0xffff); + memcpy (&su2->sin6.sin6_addr.s6_addr32[3], &su->sin.sin_addr, 4); + } +#endif + } + + memset (&md5sig, 0, sizeof (md5sig)); + memcpy (&md5sig.tcpm_addr, su2, sizeof (*su2)); + md5sig.tcpm_keylen = keylen; + if (keylen) + memcpy (md5sig.tcpm_key, password, keylen); + sockunion_free (susock); +#endif /* GNU_LINUX */ + if ((ret = setsockopt (sock, IPPROTO_TCP, TCP_MD5SIG, &md5sig, sizeof md5sig)) < 0) + { + /* ENOENT is harmless. It is returned when we clear a password for which + one was not previously set. */ + if (ENOENT == errno) + ret = 0; + else + zlog_err ("sockopt_tcp_signature: setsockopt(%d): %s", + sock, safe_strerror(errno)); + } + return ret; +#else /* HAVE_TCP_MD5SIG */ + return -2; +#endif /* !HAVE_TCP_MD5SIG */ +} diff --git a/lib/sockopt.h b/lib/sockopt.h new file mode 100644 index 0000000..a9b8aca --- /dev/null +++ b/lib/sockopt.h @@ -0,0 +1,105 @@ +/* Router advertisement + * 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 _ZEBRA_SOCKOPT_H +#define _ZEBRA_SOCKOPT_H + +#include "sockunion.h" + +extern int setsockopt_so_recvbuf (int sock, int size); +extern int setsockopt_so_sendbuf (const int sock, int size); +extern int getsockopt_so_sendbuf (const int sock); + +#ifdef HAVE_IPV6 +extern int setsockopt_ipv6_pktinfo (int, int); +extern int setsockopt_ipv6_checksum (int, int); +extern int setsockopt_ipv6_multicast_hops (int, int); +extern int setsockopt_ipv6_unicast_hops (int, int); +extern int setsockopt_ipv6_hoplimit (int, int); +extern int setsockopt_ipv6_multicast_loop (int, int); +extern int setsockopt_ipv6_tclass (int, int); +#endif /* HAVE_IPV6 */ + +/* + * It is OK to reference in6_pktinfo here without a protecting #if + * because this macro will only be used #if HAVE_IPV6, and in6_pktinfo + * is not optional for HAVE_IPV6. + */ +#define SOPT_SIZE_CMSG_PKTINFO_IPV6() (sizeof (struct in6_pktinfo)); + +/* + * Size defines for control messages used to get ifindex. We define + * values for each method, and define a macro that can be used by code + * that is unaware of which method is in use. + * These values are without any alignment needed (see CMSG_SPACE in RFC3542). + */ +#if defined (IP_PKTINFO) +/* Linux in_pktinfo. */ +#define SOPT_SIZE_CMSG_PKTINFO_IPV4() (CMSG_SPACE(sizeof (struct in_pktinfo))) +/* XXX This should perhaps be defined even if IP_PKTINFO is not. */ +#define SOPT_SIZE_CMSG_PKTINFO(af) \ + ((af == AF_INET) ? SOPT_SIZE_CMSG_PKTINFO_IPV4() \ + : SOPT_SIZE_CMSG_PKTINFO_IPV6() +#endif /* IP_PKTINFO */ + +#if defined (IP_RECVIF) +/* BSD/Solaris */ + +#if defined (SUNOS_5) +#define SOPT_SIZE_CMSG_RECVIF_IPV4() (sizeof (uint_t)) +#else +#define SOPT_SIZE_CMSG_RECVIF_IPV4() (sizeof (struct sockaddr_dl)) +#endif /* SUNOS_5 */ +#endif /* IP_RECVIF */ + +/* SOPT_SIZE_CMSG_IFINDEX_IPV4 - portable type */ +#if defined (SOPT_SIZE_CMSG_PKTINFO) +#define SOPT_SIZE_CMSG_IFINDEX_IPV4() SOPT_SIZE_CMSG_PKTINFO_IPV4() +#elif defined (SOPT_SIZE_CMSG_RECVIF_IPV4) +#define SOPT_SIZE_CMSG_IFINDEX_IPV4() SOPT_SIZE_CMSG_RECVIF_IPV4() +#else /* Nothing available */ +#define SOPT_SIZE_CMSG_IFINDEX_IPV4() (sizeof (char *)) +#endif /* SOPT_SIZE_CMSG_IFINDEX_IPV4 */ + +#define SOPT_SIZE_CMSG_IFINDEX(af) \ + (((af) == AF_INET) : SOPT_SIZE_CMSG_IFINDEX_IPV4() \ + ? SOPT_SIZE_CMSG_PKTINFO_IPV6()) + +extern int setsockopt_ipv4_multicast_if(int sock, ifindex_t ifindex); +extern int setsockopt_ipv4_multicast(int sock, int optname, + unsigned int mcast_addr, + 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, 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. + */ +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 new file mode 100644 index 0000000..8e0ec24 --- /dev/null +++ b/lib/sockunion.c @@ -0,0 +1,858 @@ +/* Socket union related function. + * 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 "vty.h" +#include "sockunion.h" +#include "memory.h" +#include "str.h" +#include "log.h" +#include "jhash.h" + +#ifndef HAVE_INET_ATON +int +inet_aton (const char *cp, struct in_addr *inaddr) +{ + int dots = 0; + register u_long addr = 0; + register u_long val = 0, base = 10; + + do + { + register char c = *cp; + + switch (c) + { + case '0': case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': + val = (val * base) + (c - '0'); + break; + case '.': + if (++dots > 3) + return 0; + case '\0': + if (val > 255) + return 0; + addr = addr << 8 | val; + val = 0; + break; + default: + return 0; + } + } while (*cp++) ; + + if (dots < 3) + addr <<= 8 * (3 - dots); + if (inaddr) + inaddr->s_addr = htonl (addr); + return 1; +} +#endif /* ! HAVE_INET_ATON */ + + +#ifndef HAVE_INET_PTON +int +inet_pton (int family, const char *strptr, void *addrptr) +{ + if (family == AF_INET) + { + struct in_addr in_val; + + if (inet_aton (strptr, &in_val)) + { + memcpy (addrptr, &in_val, sizeof (struct in_addr)); + return 1; + } + return 0; + } + errno = EAFNOSUPPORT; + return -1; +} +#endif /* ! HAVE_INET_PTON */ + +#ifndef HAVE_INET_NTOP +const char * +inet_ntop (int family, const void *addrptr, char *strptr, size_t len) +{ + unsigned char *p = (unsigned char *) addrptr; + + if (family == AF_INET) + { + char temp[INET_ADDRSTRLEN]; + + snprintf(temp, sizeof(temp), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + + if (strlen(temp) >= len) + { + errno = ENOSPC; + return NULL; + } + strcpy(strptr, temp); + return strptr; + } + + errno = EAFNOSUPPORT; + return NULL; +} +#endif /* ! HAVE_INET_NTOP */ + +const char * +inet_sutop (const union sockunion *su, char *str) +{ + switch (su->sa.sa_family) + { + case AF_INET: + inet_ntop (AF_INET, &su->sin.sin_addr, str, INET_ADDRSTRLEN); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + inet_ntop (AF_INET6, &su->sin6.sin6_addr, str, INET6_ADDRSTRLEN); + break; +#endif /* HAVE_IPV6 */ + } + return str; +} + +int +str2sockunion (const char *str, union sockunion *su) +{ + int ret; + + memset (su, 0, 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 0; + } +#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 0; + } +#endif /* HAVE_IPV6 */ + return -1; +} + +const char * +sockunion2str (const union sockunion *su, char *buf, size_t 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 + case AF_INET6: + return inet_ntop (AF_INET6, &su->sin6.sin6_addr, buf, len); +#endif /* HAVE_IPV6 */ + } + snprintf (buf, len, "(af %d)", sockunion_family(su)); + return buf; +} + +union sockunion * +sockunion_str2su (const char *str) +{ + union sockunion *su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion)); + + if (!str2sockunion (str, su)) + return su; + + XFREE (MTYPE_SOCKUNION, su); + return NULL; +} + +/* Convert IPv4 compatible IPv6 address to IPv4 address. */ +static void +sockunion_normalise_mapped (union sockunion *su) +{ + struct sockaddr_in sin; + +#ifdef HAVE_IPV6 + if (su->sa.sa_family == AF_INET6 + && IN6_IS_ADDR_V4MAPPED (&su->sin6.sin6_addr)) + { + memset (&sin, 0, sizeof (struct sockaddr_in)); + sin.sin_family = AF_INET; + sin.sin_port = su->sin6.sin6_port; + memcpy (&sin.sin_addr, ((char *)&su->sin6.sin6_addr) + 12, 4); + memcpy (su, &sin, sizeof (struct sockaddr_in)); + } +#endif /* HAVE_IPV6 */ +} + +/* Return socket of sockunion. */ +int +sockunion_socket (const union sockunion *su) +{ + int sock; + + sock = socket (su->sa.sa_family, SOCK_STREAM, 0); + if (sock < 0) + { + zlog (NULL, LOG_WARNING, "Can't make socket : %s", safe_strerror (errno)); + return -1; + } + + return sock; +} + +/* Return accepted new socket file descriptor. */ +int +sockunion_accept (int sock, union sockunion *su) +{ + socklen_t len; + int client_sock; + + len = sizeof (union sockunion); + client_sock = accept (sock, (struct sockaddr *) su, &len); + + sockunion_normalise_mapped (su); + return client_sock; +} + +/* Return sizeof union sockunion. */ +static int +sockunion_sizeof (const union sockunion *su) +{ + int ret; + + ret = 0; + switch (su->sa.sa_family) + { + case AF_INET: + ret = sizeof (struct sockaddr_in); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + ret = sizeof (struct sockaddr_in6); + break; +#endif /* AF_INET6 */ + } + return ret; +} + +/* return sockunion structure : this function should be revised. */ +static const char * +sockunion_log (const union sockunion *su, char *buf, size_t len) +{ + switch (su->sa.sa_family) + { + case AF_INET: + return inet_ntop(AF_INET, &su->sin.sin_addr, buf, len); + +#ifdef HAVE_IPV6 + case AF_INET6: + return inet_ntop(AF_INET6, &(su->sin6.sin6_addr), buf, len); + break; +#endif /* HAVE_IPV6 */ + + default: + snprintf (buf, len, "af_unknown %d ", su->sa.sa_family); + return buf; + } +} + +/* sockunion_connect returns + -1 : error occured + 0 : connect success + 1 : connect is in progress */ +enum connect_result +sockunion_connect (int fd, const union sockunion *peersu, unsigned short port, + ifindex_t ifindex) +{ + int ret; + int val; + union sockunion su; + + memcpy (&su, peersu, sizeof (union sockunion)); + + switch (su.sa.sa_family) + { + case AF_INET: + su.sin.sin_port = port; + break; +#ifdef HAVE_IPV6 + case AF_INET6: + su.sin6.sin6_port = port; +#ifdef KAME + if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr) && ifindex) + { +#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID + /* su.sin6.sin6_scope_id = ifindex; */ +#endif /* HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID */ + SET_IN6_LINKLOCAL_IFINDEX (su.sin6.sin6_addr, ifindex); + } +#endif /* KAME */ + break; +#endif /* HAVE_IPV6 */ + } + + /* Make socket non-block. */ + val = fcntl (fd, F_GETFL, 0); + fcntl (fd, F_SETFL, val|O_NONBLOCK); + + /* Call connect function. */ + ret = connect (fd, (struct sockaddr *) &su, sockunion_sizeof (&su)); + + /* Immediate success */ + if (ret == 0) + { + fcntl (fd, F_SETFL, val); + return connect_success; + } + + /* If connect is in progress then return 1 else it's real error. */ + if (ret < 0) + { + if (errno != EINPROGRESS) + { + char str[SU_ADDRSTRLEN]; + zlog_info ("can't connect to %s fd %d : %s", + sockunion_log (&su, str, sizeof str), + fd, safe_strerror (errno)); + return connect_error; + } + } + + fcntl (fd, F_SETFL, val); + + return connect_in_progress; +} + +/* Make socket from sockunion union. */ +int +sockunion_stream_socket (union sockunion *su) +{ + int sock; + + if (su->sa.sa_family == 0) + su->sa.sa_family = AF_INET_UNION; + + sock = socket (su->sa.sa_family, SOCK_STREAM, 0); + + if (sock < 0) + zlog (NULL, LOG_WARNING, "can't make socket sockunion_stream_socket"); + + return sock; +} + +/* Bind socket to specified address. */ +int +sockunion_bind (int sock, union sockunion *su, unsigned short port, + union sockunion *su_addr) +{ + int size = 0; + int ret; + + if (su->sa.sa_family == AF_INET) + { + size = sizeof (struct sockaddr_in); + su->sin.sin_port = htons (port); +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + su->sin.sin_len = size; +#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ + if (su_addr == NULL) + sockunion2ip (su) = htonl (INADDR_ANY); + } +#ifdef HAVE_IPV6 + else if (su->sa.sa_family == AF_INET6) + { + size = sizeof (struct sockaddr_in6); + su->sin6.sin6_port = htons (port); +#ifdef SIN6_LEN + su->sin6.sin6_len = size; +#endif /* SIN6_LEN */ + if (su_addr == NULL) + { +#ifdef LINUX_IPV6 + memset (&su->sin6.sin6_addr, 0, sizeof (struct in6_addr)); +#else + su->sin6.sin6_addr = in6addr_any; +#endif /* LINUX_IPV6 */ + } + } +#endif /* HAVE_IPV6 */ + + + ret = bind (sock, (struct sockaddr *)su, size); + if (ret < 0) + zlog (NULL, LOG_WARNING, "can't bind socket : %s", safe_strerror (errno)); + + return ret; +} + +int +sockopt_reuseaddr (int sock) +{ + int ret; + int on = 1; + + ret = setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, + (void *) &on, sizeof (on)); + if (ret < 0) + { + zlog (NULL, LOG_WARNING, "can't set sockopt SO_REUSEADDR to socket %d", sock); + return -1; + } + return 0; +} + +#ifdef SO_REUSEPORT +int +sockopt_reuseport (int sock) +{ + int ret; + int on = 1; + + ret = setsockopt (sock, SOL_SOCKET, SO_REUSEPORT, + (void *) &on, sizeof (on)); + if (ret < 0) + { + zlog (NULL, LOG_WARNING, "can't set sockopt SO_REUSEPORT to socket %d", sock); + return -1; + } + return 0; +} +#else +int +sockopt_reuseport (int sock) +{ + return 0; +} +#endif /* 0 */ + +int +sockopt_ttl (int family, int sock, int ttl) +{ + int ret; + +#ifdef IP_TTL + if (family == AF_INET) + { + ret = setsockopt (sock, IPPROTO_IP, IP_TTL, + (void *) &ttl, sizeof (int)); + if (ret < 0) + { + zlog (NULL, LOG_WARNING, "can't set sockopt IP_TTL %d to socket %d", ttl, sock); + return -1; + } + return 0; + } +#endif /* IP_TTL */ +#ifdef HAVE_IPV6 + if (family == AF_INET6) + { + ret = setsockopt (sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, + (void *) &ttl, sizeof (int)); + if (ret < 0) + { + zlog (NULL, LOG_WARNING, "can't set sockopt IPV6_UNICAST_HOPS %d to socket %d", + ttl, sock); + return -1; + } + return 0; + } +#endif /* HAVE_IPV6 */ + return 0; +} + +int +sockopt_cork (int sock, int onoff) +{ +#ifdef TCP_CORK + return setsockopt (sock, IPPROTO_TCP, TCP_CORK, &onoff, sizeof(onoff)); +#else + return 0; +#endif +} + +int +sockopt_minttl (int family, int sock, int minttl) +{ +#ifdef IP_MINTTL + if (family == AF_INET) + { + int ret = setsockopt (sock, IPPROTO_IP, IP_MINTTL, &minttl, sizeof(minttl)); + if (ret < 0) + zlog (NULL, LOG_WARNING, + "can't set sockopt IP_MINTTL to %d on socket %d: %s", + minttl, sock, safe_strerror (errno)); + return ret; + } +#endif /* IP_MINTTL */ +#ifdef IPV6_MINHOPCNT + if (family == AF_INET6) + { + int ret = setsockopt (sock, IPPROTO_IPV6, IPV6_MINHOPCNT, &minttl, sizeof(minttl)); + if (ret < 0) + zlog (NULL, LOG_WARNING, + "can't set sockopt IPV6_MINHOPCNT to %d on socket %d: %s", + minttl, sock, safe_strerror (errno)); + return ret; + } +#endif + + errno = EOPNOTSUPP; + return -1; +} + +int +sockopt_v6only (int family, int sock) +{ + int ret, on = 1; + +#ifdef HAVE_IPV6 +#ifdef IPV6_V6ONLY + if (family == AF_INET6) + { + ret = setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, + (void *) &on, sizeof (int)); + if (ret < 0) + { + zlog (NULL, LOG_WARNING, "can't set sockopt IPV6_V6ONLY " + "to socket %d", sock); + return -1; + } + return 0; + } +#endif /* IPV6_V6ONLY */ +#endif /* HAVE_IPV6 */ + return 0; +} + +/* If same family and same prefix return 1. */ +int +sockunion_same (const union sockunion *su1, const union sockunion *su2) +{ + int ret = 0; + + if (su1->sa.sa_family != su2->sa.sa_family) + return 0; + + switch (su1->sa.sa_family) + { + case AF_INET: + ret = memcmp (&su1->sin.sin_addr, &su2->sin.sin_addr, + sizeof (struct in_addr)); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + ret = memcmp (&su1->sin6.sin6_addr, &su2->sin6.sin6_addr, + sizeof (struct in6_addr)); + break; +#endif /* HAVE_IPV6 */ + } + if (ret == 0) + return 1; + else + 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) +{ + int ret; + socklen_t len; + union + { + struct sockaddr sa; + struct sockaddr_in sin; +#ifdef HAVE_IPV6 + struct sockaddr_in6 sin6; +#endif /* HAVE_IPV6 */ + char tmp_buffer[128]; + } name; + union sockunion *su; + + memset (&name, 0, sizeof name); + len = sizeof name; + + ret = getsockname (fd, (struct sockaddr *)&name, &len); + if (ret < 0) + { + zlog_warn ("Can't get local address and port by getsockname: %s", + safe_strerror (errno)); + return NULL; + } + + if (name.sa.sa_family == AF_INET) + { + su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion)); + memcpy (su, &name, sizeof (struct sockaddr_in)); + return su; + } +#ifdef HAVE_IPV6 + if (name.sa.sa_family == AF_INET6) + { + su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion)); + memcpy (su, &name, sizeof (struct sockaddr_in6)); + sockunion_normalise_mapped (su); + return su; + } +#endif /* HAVE_IPV6 */ + return NULL; +} + +/* After TCP connection is established. Get remote address and port. */ +union sockunion * +sockunion_getpeername (int fd) +{ + int ret; + socklen_t len; + union + { + struct sockaddr sa; + struct sockaddr_in sin; +#ifdef HAVE_IPV6 + struct sockaddr_in6 sin6; +#endif /* HAVE_IPV6 */ + char tmp_buffer[128]; + } name; + union sockunion *su; + + memset (&name, 0, sizeof name); + len = sizeof name; + ret = getpeername (fd, (struct sockaddr *)&name, &len); + if (ret < 0) + { + zlog (NULL, LOG_WARNING, "Can't get remote address and port: %s", + safe_strerror (errno)); + return NULL; + } + + if (name.sa.sa_family == AF_INET) + { + su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion)); + memcpy (su, &name, sizeof (struct sockaddr_in)); + return su; + } +#ifdef HAVE_IPV6 + if (name.sa.sa_family == AF_INET6) + { + su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion)); + memcpy (su, &name, sizeof (struct sockaddr_in6)); + sockunion_normalise_mapped (su); + return su; + } +#endif /* HAVE_IPV6 */ + return NULL; +} + +/* Print sockunion structure */ +static void __attribute__ ((unused)) +sockunion_print (const union sockunion *su) +{ + if (su == NULL) + return; + + switch (su->sa.sa_family) + { + case AF_INET: + printf ("%s\n", inet_ntoa (su->sin.sin_addr)); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + { + char buf [SU_ADDRSTRLEN]; + + printf ("%s\n", inet_ntop (AF_INET6, &(su->sin6.sin6_addr), + buf, sizeof (buf))); + } + break; +#endif /* HAVE_IPV6 */ + +#ifdef AF_LINK + case AF_LINK: + { + struct sockaddr_dl *sdl; + + sdl = (struct sockaddr_dl *)&(su->sa); + printf ("link#%d\n", sdl->sdl_index); + } + break; +#endif /* AF_LINK */ + default: + printf ("af_unknown %d\n", su->sa.sa_family); + break; + } +} + +#ifdef HAVE_IPV6 +static int +in6addr_cmp (const struct in6_addr *addr1, const struct in6_addr *addr2) +{ + unsigned int i; + u_char *p1, *p2; + + p1 = (u_char *)addr1; + p2 = (u_char *)addr2; + + for (i = 0; i < sizeof (struct in6_addr); i++) + { + if (p1[i] > p2[i]) + return 1; + else if (p1[i] < p2[i]) + return -1; + } + return 0; +} +#endif /* HAVE_IPV6 */ + +int +sockunion_cmp (const union sockunion *su1, const union sockunion *su2) +{ + if (su1->sa.sa_family > su2->sa.sa_family) + return 1; + if (su1->sa.sa_family < su2->sa.sa_family) + return -1; + + if (su1->sa.sa_family == AF_INET) + { + if (ntohl (sockunion2ip (su1)) == ntohl (sockunion2ip (su2))) + return 0; + if (ntohl (sockunion2ip (su1)) > ntohl (sockunion2ip (su2))) + return 1; + else + return -1; + } +#ifdef HAVE_IPV6 + if (su1->sa.sa_family == AF_INET6) + return in6addr_cmp (&su1->sin6.sin6_addr, &su2->sin6.sin6_addr); +#endif /* HAVE_IPV6 */ + return 0; +} + +/* Duplicate sockunion. */ +union sockunion * +sockunion_dup (const union sockunion *su) +{ + union sockunion *dup = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion)); + memcpy (dup, su, sizeof (union sockunion)); + return dup; +} + +void +sockunion_free (union sockunion *su) +{ + XFREE (MTYPE_SOCKUNION, su); +} diff --git a/lib/sockunion.h b/lib/sockunion.h new file mode 100644 index 0000000..3613073 --- /dev/null +++ b/lib/sockunion.h @@ -0,0 +1,134 @@ +/* + * Socket union header. + * 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. + */ + +#ifndef _ZEBRA_SOCKUNION_H +#define _ZEBRA_SOCKUNION_H + +#include "if.h" + +#if 0 +union sockunion { + struct sockinet { + u_char si_len; + u_char si_family; + u_short si_port; + } su_si; + struct sockaddr_in su_sin; + struct sockaddr_in6 su_sin6; +}; +#define su_len su_si.si_len +#define su_family su_si.si_family +#define su_port su_si.si_port +#endif /* 0 */ + +union sockunion +{ + struct sockaddr sa; + struct sockaddr_in sin; +#ifdef HAVE_IPV6 + struct sockaddr_in6 sin6; +#endif /* HAVE_IPV6 */ +}; + +enum connect_result +{ + connect_error, + connect_success, + connect_in_progress +}; + +/* Default address family. */ +#ifdef HAVE_IPV6 +#define AF_INET_UNION AF_INET6 +#else +#define AF_INET_UNION AF_INET +#endif + +/* Sockunion address string length. Same as INET6_ADDRSTRLEN. */ +#define SU_ADDRSTRLEN 46 + +/* Macro to set link local index to the IPv6 address. For KAME IPv6 + stack. */ +#ifdef KAME +#define IN6_LINKLOCAL_IFINDEX(a) ((a).s6_addr[2] << 8 | (a).s6_addr[3]) +#define SET_IN6_LINKLOCAL_IFINDEX(a, i) \ + do { \ + (a).s6_addr[2] = ((i) >> 8) & 0xff; \ + (a).s6_addr[3] = (i) & 0xff; \ + } while (0) +#else +#define IN6_LINKLOCAL_IFINDEX(a) +#define SET_IN6_LINKLOCAL_IFINDEX(a, i) +#endif /* KAME */ + +#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 (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 union sockunion *sockunion_str2su (const char *str); +extern int sockunion_accept (int sock, union sockunion *); +extern int sockunion_stream_socket (union sockunion *); +extern int sockopt_reuseaddr (int); +extern int sockopt_reuseport (int); +extern int sockopt_v6only (int family, int sock); +extern int sockunion_bind (int sock, union sockunion *, + unsigned short, 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 (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, + ifindex_t); +extern union sockunion *sockunion_getsockname (int); +extern union sockunion *sockunion_getpeername (int); +extern union sockunion *sockunion_dup (const union sockunion *); +extern void sockunion_free (union sockunion *); + +#ifndef HAVE_INET_NTOP +extern const char * inet_ntop (int family, const void *addrptr, + char *strptr, size_t len); +#endif /* HAVE_INET_NTOP */ + +#ifndef HAVE_INET_PTON +extern int inet_pton (int family, const char *strptr, void *addrptr); +#endif /* HAVE_INET_PTON */ + +#ifndef HAVE_INET_ATON +extern int inet_aton (const char *cp, struct in_addr *inaddr); +#endif + +#endif /* _ZEBRA_SOCKUNION_H */ diff --git a/lib/str.c b/lib/str.c new file mode 100644 index 0000000..d8f039a --- /dev/null +++ b/lib/str.c @@ -0,0 +1,127 @@ +/* + * zebra string function + * + * XXX This version of snprintf does not check bounds! + */ + +/* + The implementations of strlcpy and strlcat are copied from rsync (GPL): + Copyright (C) Andrew Tridgell 1998 + Copyright (C) 2002 by Martin Pool + + Note that these are not terribly efficient, since they make more than one + pass over the argument strings. At some point, they should be optimized. + + The implementation of strndup is copied from glibc-2.3.5: + 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 + +#ifndef HAVE_SNPRINTF +/* + * snprint() is a real basic wrapper around the standard sprintf() + * without any bounds checking + */ +int +snprintf(char *str, size_t size, const char *format, ...) +{ + va_list args; + + va_start (args, format); + + return vsprintf (str, format, args); +} +#endif + +#ifndef HAVE_STRLCPY +/** + * Like strncpy but does not 0 fill the buffer and always null + * terminates. + * + * @param bufsize is the size of the destination buffer. + * + * @return index of the terminating byte. + **/ +size_t +strlcpy(char *d, const char *s, size_t bufsize) +{ + size_t len = strlen(s); + size_t ret = len; + if (bufsize > 0) { + if (len >= bufsize) + len = bufsize-1; + memcpy(d, s, len); + d[len] = 0; + } + return ret; +} +#endif + +#ifndef HAVE_STRLCAT +/** + * Like strncat() but does not 0 fill the buffer and always null + * terminates. + * + * @param bufsize length of the buffer, which should be one more than + * the maximum resulting string length. + **/ +size_t +strlcat(char *d, const char *s, size_t bufsize) +{ + size_t len1 = strlen(d); + size_t len2 = strlen(s); + size_t ret = len1 + len2; + + if (len1 < bufsize - 1) { + if (len2 >= bufsize - len1) + len2 = bufsize - len1 - 1; + memcpy(d+len1, s, len2); + d[len1+len2] = 0; + } + return ret; +} +#endif + +#ifndef HAVE_STRNLEN +size_t +strnlen(const char *s, size_t maxlen) +{ + const char *p; + return (p = (const char *)memchr(s, '\0', maxlen)) ? (size_t)(p-s) : maxlen; +} +#endif + +#ifndef HAVE_STRNDUP +char * +strndup (const char *s, size_t maxlen) +{ + size_t len = strnlen (s, maxlen); + char *new = (char *) malloc (len + 1); + + if (new == NULL) + return NULL; + + new[len] = '\0'; + return (char *) memcpy (new, s, len); +} +#endif diff --git a/lib/str.h b/lib/str.h new file mode 100644 index 0000000..7b83fe1 --- /dev/null +++ b/lib/str.h @@ -0,0 +1,33 @@ +/* + * $Id: str.h,v 1.4 2005/09/19 09:53:21 hasso Exp $ + */ + +#ifndef _ZEBRA_STR_H +#define _ZEBRA_STR_H + +#ifndef HAVE_SNPRINTF +extern int snprintf(char *, size_t, const char *, ...); +#endif + +#ifndef HAVE_VSNPRINTF +#define vsnprintf(buf, size, format, args) vsprintf(buf, format, args) +#endif + +#ifndef HAVE_STRLCPY +extern size_t strlcpy(char *, const char *, size_t); +#endif + +#ifndef HAVE_STRLCAT +extern size_t strlcat(char *, const char *, size_t); +#endif + +#ifndef HAVE_STRNLEN +extern size_t strnlen(const char *s, size_t maxlen); +#endif + +#ifndef HAVE_STRNDUP +extern char * strndup (const char *, size_t); +#endif + +#endif /* _ZEBRA_STR_H */ + diff --git a/lib/stream.c b/lib/stream.c new file mode 100644 index 0000000..ed677c1 --- /dev/null +++ b/lib/stream.c @@ -0,0 +1,1074 @@ + /* + * Packet 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 "stream.h" +#include "memory.h" +#include "network.h" +#include "prefix.h" +#include "log.h" + +/* Tests whether a position is valid */ +#define GETP_VALID(S,G) \ + ((G) <= (S)->endp) +#define PUT_AT_VALID(S,G) GETP_VALID(S,G) +#define ENDP_VALID(S,E) \ + ((E) <= (S)->size) + +/* asserting sanity checks. Following must be true before + * stream functions are called: + * + * Following must always be true of stream elements + * before and after calls to stream functions: + * + * getp <= endp <= size + * + * Note that after a stream function is called following may be true: + * if (getp == endp) then stream is no longer readable + * if (endp == size) then stream is no longer writeable + * + * It is valid to put to anywhere within the size of the stream, but only + * using stream_put..._at() functions. + */ +#define STREAM_WARN_OFFSETS(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)) ) \ + STREAM_WARN_OFFSETS(S); \ + assert ( GETP_VALID(S, (S)->getp) ); \ + assert ( ENDP_VALID(S, (S)->endp) ); \ + } while (0) + +#define STREAM_BOUND_WARN(S, WHAT) \ + do { \ + zlog_warn ("%s: Attempt to %s out of bounds", __func__, (WHAT)); \ + STREAM_WARN_OFFSETS(S); \ + assert (0); \ + } while (0) + +/* XXX: Deprecated macro: do not use */ +#define CHECK_SIZE(S, Z) \ + do { \ + if (((S)->endp + (Z)) > (S)->size) \ + { \ + zlog_warn ("CHECK_SIZE: truncating requested size %lu\n", \ + (unsigned long) (Z)); \ + STREAM_WARN_OFFSETS(S); \ + (Z) = (S)->size - (S)->endp; \ + } \ + } while (0); + +/* Make stream buffer. */ +struct stream * +stream_new (size_t size) +{ + struct stream *s; + + assert (size > 0); + + if (size == 0) + { + zlog_warn ("stream_new(): called with 0 size!"); + return NULL; + } + + s = XCALLOC (MTYPE_STREAM, sizeof (struct stream)); + + if (s == NULL) + return s; + + if ( (s->data = XMALLOC (MTYPE_STREAM_DATA, size)) == NULL) + { + XFREE (MTYPE_STREAM, s); + return NULL; + } + + s->size = size; + return s; +} + +/* Free it now. */ +void +stream_free (struct stream *s) +{ + if (!s) + return; + + XFREE (MTYPE_STREAM_DATA, s->data); + XFREE (MTYPE_STREAM, s); +} + +struct stream * +stream_copy (struct stream *new, struct stream *src) +{ + STREAM_VERIFY_SANE (src); + + assert (new != NULL); + assert (STREAM_SIZE(new) >= src->endp); + + new->endp = src->endp; + new->getp = src->getp; + + memcpy (new->data, src->data, src->endp); + + return new; +} + +struct stream * +stream_dup (struct stream *s) +{ + struct stream *new; + + STREAM_VERIFY_SANE (s); + + if ( (new = stream_new (s->endp)) == NULL) + return NULL; + + 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) +{ + u_char *newdata; + STREAM_VERIFY_SANE (s); + + newdata = XREALLOC (MTYPE_STREAM_DATA, s->data, newsize); + + if (newdata == NULL) + return s->size; + + s->data = newdata; + s->size = newsize; + + if (s->endp > s->size) + s->endp = s->size; + if (s->getp > s->endp) + s->getp = s->endp; + + STREAM_VERIFY_SANE (s); + + return s->size; +} + +size_t +stream_get_getp (struct stream *s) +{ + STREAM_VERIFY_SANE(s); + return s->getp; +} + +size_t +stream_get_endp (struct stream *s) +{ + STREAM_VERIFY_SANE(s); + return s->endp; +} + +size_t +stream_get_size (struct stream *s) +{ + STREAM_VERIFY_SANE(s); + return s->size; +} + +/* Stream structre' stream pointer related functions. */ +void +stream_set_getp (struct stream *s, size_t pos) +{ + STREAM_VERIFY_SANE(s); + + if (!GETP_VALID (s, pos)) + { + STREAM_BOUND_WARN (s, "set getp"); + pos = s->endp; + } + + 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) +{ + STREAM_VERIFY_SANE(s); + + if (!GETP_VALID (s, s->getp + size)) + { + STREAM_BOUND_WARN (s, "seek getp"); + return; + } + + s->getp += size; +} + +void +stream_forward_endp (struct stream *s, size_t size) +{ + STREAM_VERIFY_SANE(s); + + if (!ENDP_VALID (s, s->endp + size)) + { + STREAM_BOUND_WARN (s, "seek endp"); + return; + } + + s->endp += size; +} + +/* Copy from stream to destination. */ +void +stream_get (void *dst, struct stream *s, size_t size) +{ + STREAM_VERIFY_SANE(s); + + if (STREAM_READABLE(s) < size) + { + STREAM_BOUND_WARN (s, "get"); + return; + } + + memcpy (dst, s->data + s->getp, size); + s->getp += size; +} + +/* Get next character from the stream. */ +u_char +stream_getc (struct stream *s) +{ + u_char c; + + STREAM_VERIFY_SANE (s); + + if (STREAM_READABLE(s) < sizeof (u_char)) + { + STREAM_BOUND_WARN (s, "get char"); + return 0; + } + c = s->data[s->getp++]; + + return c; +} + +/* Get next character from the stream. */ +u_char +stream_getc_from (struct stream *s, size_t from) +{ + u_char c; + + STREAM_VERIFY_SANE(s); + + if (!GETP_VALID (s, from + sizeof (u_char))) + { + STREAM_BOUND_WARN (s, "get char"); + return 0; + } + + c = s->data[from]; + + return c; +} + +/* Get next word from the stream. */ +u_int16_t +stream_getw (struct stream *s) +{ + u_int16_t w; + + STREAM_VERIFY_SANE (s); + + if (STREAM_READABLE (s) < sizeof (u_int16_t)) + { + STREAM_BOUND_WARN (s, "get "); + return 0; + } + + w = s->data[s->getp++] << 8; + w |= s->data[s->getp++]; + + return w; +} + +/* Get next word from the stream. */ +u_int16_t +stream_getw_from (struct stream *s, size_t from) +{ + u_int16_t w; + + STREAM_VERIFY_SANE(s); + + if (!GETP_VALID (s, from + sizeof (u_int16_t))) + { + STREAM_BOUND_WARN (s, "get "); + return 0; + } + + w = s->data[from++] << 8; + w |= s->data[from]; + + return w; +} + +/* Get next long word from the stream. */ +u_int32_t +stream_getl_from (struct stream *s, size_t from) +{ + u_int32_t l; + + STREAM_VERIFY_SANE(s); + + if (!GETP_VALID (s, from + sizeof (u_int32_t))) + { + STREAM_BOUND_WARN (s, "get long"); + return 0; + } + + l = s->data[from++] << 24; + l |= s->data[from++] << 16; + l |= s->data[from++] << 8; + l |= s->data[from]; + + return l; +} + +u_int32_t +stream_getl (struct stream *s) +{ + u_int32_t l; + + STREAM_VERIFY_SANE(s); + + if (STREAM_READABLE (s) < sizeof (u_int32_t)) + { + STREAM_BOUND_WARN (s, "get long"); + return 0; + } + + l = s->data[s->getp++] << 24; + l |= s->data[s->getp++] << 16; + l |= s->data[s->getp++] << 8; + l |= s->data[s->getp++]; + + return l; +} + +/* Get next quad word from the stream. */ +uint64_t +stream_getq_from (struct stream *s, size_t from) +{ + uint64_t q; + + STREAM_VERIFY_SANE(s); + + if (!GETP_VALID (s, from + sizeof (uint64_t))) + { + STREAM_BOUND_WARN (s, "get quad"); + return 0; + } + + q = ((uint64_t) s->data[from++]) << 56; + q |= ((uint64_t) s->data[from++]) << 48; + q |= ((uint64_t) s->data[from++]) << 40; + q |= ((uint64_t) s->data[from++]) << 32; + q |= ((uint64_t) s->data[from++]) << 24; + q |= ((uint64_t) s->data[from++]) << 16; + q |= ((uint64_t) s->data[from++]) << 8; + q |= ((uint64_t) s->data[from++]); + + return q; +} + +uint64_t +stream_getq (struct stream *s) +{ + uint64_t q; + + STREAM_VERIFY_SANE(s); + + if (STREAM_READABLE (s) < sizeof (uint64_t)) + { + STREAM_BOUND_WARN (s, "get quad"); + return 0; + } + + q = ((uint64_t) s->data[s->getp++]) << 56; + q |= ((uint64_t) s->data[s->getp++]) << 48; + q |= ((uint64_t) s->data[s->getp++]) << 40; + q |= ((uint64_t) s->data[s->getp++]) << 32; + q |= ((uint64_t) s->data[s->getp++]) << 24; + q |= ((uint64_t) s->data[s->getp++]) << 16; + q |= ((uint64_t) s->data[s->getp++]) << 8; + q |= ((uint64_t) s->data[s->getp++]); + + return q; +} + +/* Get next long word from the stream. */ +u_int32_t +stream_get_ipv4 (struct stream *s) +{ + u_int32_t l; + + STREAM_VERIFY_SANE(s); + + if (STREAM_READABLE (s) < sizeof(u_int32_t)) + { + STREAM_BOUND_WARN (s, "get ipv4"); + return 0; + } + + memcpy (&l, s->data + s->getp, sizeof(u_int32_t)); + s->getp += sizeof(u_int32_t); + + 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 + * around. This should be fixed once the stream updates are working. + * + * stream_write() is saner + */ +void +stream_put (struct stream *s, const void *src, size_t size) +{ + + /* XXX: CHECK_SIZE has strange semantics. It should be deprecated */ + CHECK_SIZE(s, size); + + STREAM_VERIFY_SANE(s); + + if (STREAM_WRITEABLE (s) < size) + { + STREAM_BOUND_WARN (s, "put"); + return; + } + + if (src) + memcpy (s->data + s->endp, src, size); + else + memset (s->data + s->endp, 0, size); + + s->endp += size; +} + +/* Put character to the stream. */ +int +stream_putc (struct stream *s, u_char c) +{ + STREAM_VERIFY_SANE(s); + + if (STREAM_WRITEABLE (s) < sizeof(u_char)) + { + STREAM_BOUND_WARN (s, "put"); + return 0; + } + + s->data[s->endp++] = c; + return sizeof (u_char); +} + +/* Put word to the stream. */ +int +stream_putw (struct stream *s, u_int16_t w) +{ + STREAM_VERIFY_SANE (s); + + if (STREAM_WRITEABLE (s) < sizeof (u_int16_t)) + { + STREAM_BOUND_WARN (s, "put"); + return 0; + } + + s->data[s->endp++] = (u_char)(w >> 8); + s->data[s->endp++] = (u_char) w; + + return 2; +} + +/* Put long word to the stream. */ +int +stream_putl (struct stream *s, u_int32_t l) +{ + STREAM_VERIFY_SANE (s); + + if (STREAM_WRITEABLE (s) < sizeof (u_int32_t)) + { + STREAM_BOUND_WARN (s, "put"); + return 0; + } + + s->data[s->endp++] = (u_char)(l >> 24); + s->data[s->endp++] = (u_char)(l >> 16); + s->data[s->endp++] = (u_char)(l >> 8); + s->data[s->endp++] = (u_char)l; + + return 4; +} + +/* Put quad word to the stream. */ +int +stream_putq (struct stream *s, uint64_t q) +{ + STREAM_VERIFY_SANE (s); + + if (STREAM_WRITEABLE (s) < sizeof (uint64_t)) + { + STREAM_BOUND_WARN (s, "put quad"); + return 0; + } + + s->data[s->endp++] = (u_char)(q >> 56); + s->data[s->endp++] = (u_char)(q >> 48); + s->data[s->endp++] = (u_char)(q >> 40); + s->data[s->endp++] = (u_char)(q >> 32); + s->data[s->endp++] = (u_char)(q >> 24); + s->data[s->endp++] = (u_char)(q >> 16); + s->data[s->endp++] = (u_char)(q >> 8); + s->data[s->endp++] = (u_char)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) +{ + STREAM_VERIFY_SANE(s); + + if (!PUT_AT_VALID (s, putp + sizeof (u_char))) + { + STREAM_BOUND_WARN (s, "put"); + return 0; + } + + s->data[putp] = c; + + return 1; +} + +int +stream_putw_at (struct stream *s, size_t putp, u_int16_t w) +{ + STREAM_VERIFY_SANE(s); + + if (!PUT_AT_VALID (s, putp + sizeof (u_int16_t))) + { + STREAM_BOUND_WARN (s, "put"); + return 0; + } + + s->data[putp] = (u_char)(w >> 8); + s->data[putp + 1] = (u_char) w; + + return 2; +} + +int +stream_putl_at (struct stream *s, size_t putp, u_int32_t l) +{ + STREAM_VERIFY_SANE(s); + + if (!PUT_AT_VALID (s, putp + sizeof (u_int32_t))) + { + STREAM_BOUND_WARN (s, "put"); + return 0; + } + s->data[putp] = (u_char)(l >> 24); + s->data[putp + 1] = (u_char)(l >> 16); + s->data[putp + 2] = (u_char)(l >> 8); + s->data[putp + 3] = (u_char)l; + + return 4; +} + +int +stream_putq_at (struct stream *s, size_t putp, uint64_t q) +{ + STREAM_VERIFY_SANE(s); + + if (!PUT_AT_VALID (s, putp + sizeof (uint64_t))) + { + STREAM_BOUND_WARN (s, "put"); + return 0; + } + s->data[putp] = (u_char)(q >> 56); + s->data[putp + 1] = (u_char)(q >> 48); + s->data[putp + 2] = (u_char)(q >> 40); + s->data[putp + 3] = (u_char)(q >> 32); + s->data[putp + 4] = (u_char)(q >> 24); + s->data[putp + 5] = (u_char)(q >> 16); + s->data[putp + 6] = (u_char)(q >> 8); + s->data[putp + 7] = (u_char)q; + + return 8; +} + +/* Put long word to the stream. */ +int +stream_put_ipv4 (struct stream *s, u_int32_t l) +{ + STREAM_VERIFY_SANE(s); + + if (STREAM_WRITEABLE (s) < sizeof (u_int32_t)) + { + STREAM_BOUND_WARN (s, "put"); + return 0; + } + memcpy (s->data + s->endp, &l, sizeof (u_int32_t)); + s->endp += sizeof (u_int32_t); + + return sizeof (u_int32_t); +} + +/* Put long word to the stream. */ +int +stream_put_in_addr (struct stream *s, struct in_addr *addr) +{ + STREAM_VERIFY_SANE(s); + + if (STREAM_WRITEABLE (s) < sizeof (u_int32_t)) + { + STREAM_BOUND_WARN (s, "put"); + return 0; + } + + memcpy (s->data + s->endp, addr, sizeof (u_int32_t)); + s->endp += sizeof (u_int32_t); + + return sizeof (u_int32_t); +} + +/* Put prefix by nlri type format. */ +int +stream_put_prefix (struct stream *s, struct prefix *p) +{ + size_t psize; + + STREAM_VERIFY_SANE(s); + + psize = PSIZE (p->prefixlen); + + if (STREAM_WRITEABLE (s) < (psize + sizeof (u_char))) + { + STREAM_BOUND_WARN (s, "put"); + return 0; + } + + 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) +{ + int nbytes; + + STREAM_VERIFY_SANE(s); + + if (STREAM_WRITEABLE (s) < size) + { + STREAM_BOUND_WARN (s, "put"); + return 0; + } + + nbytes = readn (fd, s->data + s->endp, size); + + if (nbytes > 0) + s->endp += nbytes; + + return nbytes; +} + +ssize_t +stream_read_try(struct stream *s, int fd, size_t size) +{ + ssize_t nbytes; + + STREAM_VERIFY_SANE(s); + + if (STREAM_WRITEABLE(s) < size) + { + STREAM_BOUND_WARN (s, "put"); + /* Fatal (not transient) error, since retrying will not help + (stream is too small to contain the desired data). */ + return -1; + } + + if ((nbytes = read(fd, s->data + s->endp, size)) >= 0) + { + s->endp += nbytes; + return nbytes; + } + /* Error: was it transient (return -2) or fatal (return -1)? */ + if (ERRNO_IO_RETRY(errno)) + return -2; + zlog_warn("%s: read failed on fd %d: %s", __func__, fd, safe_strerror(errno)); + return -1; +} + +/* Read up to size bytes into the stream from the fd, using recvmsgfrom + * whose arguments match the remaining arguments to this function + */ +ssize_t +stream_recvfrom (struct stream *s, int fd, size_t size, int flags, + struct sockaddr *from, socklen_t *fromlen) +{ + ssize_t nbytes; + + STREAM_VERIFY_SANE(s); + + if (STREAM_WRITEABLE(s) < size) + { + STREAM_BOUND_WARN (s, "put"); + /* Fatal (not transient) error, since retrying will not help + (stream is too small to contain the desired data). */ + return -1; + } + + if ((nbytes = recvfrom (fd, s->data + s->endp, size, + flags, from, fromlen)) >= 0) + { + s->endp += nbytes; + return nbytes; + } + /* Error: was it transient (return -2) or fatal (return -1)? */ + if (ERRNO_IO_RETRY(errno)) + return -2; + zlog_warn("%s: read failed on fd %d: %s", __func__, fd, safe_strerror(errno)); + return -1; +} + +/* Read up to smaller of size or SIZE_REMAIN() bytes to the stream, starting + * from endp. + * First iovec will be used to receive the data. + * Stream need not be empty. + */ +ssize_t +stream_recvmsg (struct stream *s, int fd, struct msghdr *msgh, int flags, + size_t size) +{ + int nbytes; + struct iovec *iov; + + STREAM_VERIFY_SANE(s); + assert (msgh->msg_iovlen > 0); + + if (STREAM_WRITEABLE (s) < size) + { + STREAM_BOUND_WARN (s, "put"); + /* This is a logic error in the calling code: the stream is too small + to hold the desired data! */ + return -1; + } + + iov = &(msgh->msg_iov[0]); + iov->iov_base = (s->data + s->endp); + iov->iov_len = size; + + nbytes = recvmsg (fd, msgh, flags); + + if (nbytes > 0) + s->endp += nbytes; + + return nbytes; +} + +/* Write data to buffer. */ +size_t +stream_write (struct stream *s, const void *ptr, size_t size) +{ + + CHECK_SIZE(s, size); + + STREAM_VERIFY_SANE(s); + + if (STREAM_WRITEABLE (s) < size) + { + STREAM_BOUND_WARN (s, "put"); + return 0; + } + + memcpy (s->data + s->endp, ptr, size); + s->endp += size; + + return size; +} + +/* Return current read pointer. + * DEPRECATED! + * Use stream_get_pnt_to if you must, but decoding streams properly + * is preferred + */ +u_char * +stream_pnt (struct stream *s) +{ + STREAM_VERIFY_SANE(s); + return s->data + s->getp; +} + +/* Check does this stream empty? */ +int +stream_empty (struct stream *s) +{ + STREAM_VERIFY_SANE(s); + + return (s->endp == 0); +} + +/* Reset stream. */ +void +stream_reset (struct stream *s) +{ + STREAM_VERIFY_SANE (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) +{ + int nbytes; + + STREAM_VERIFY_SANE(s); + + nbytes = write (fd, s->data + s->getp, s->endp - s->getp); + + return nbytes; +} + +/* Stream first in first out queue. */ + +struct stream_fifo * +stream_fifo_new (void) +{ + struct stream_fifo *new; + + new = XCALLOC (MTYPE_STREAM_FIFO, sizeof (struct stream_fifo)); + return new; +} + +/* Add new stream to fifo. */ +void +stream_fifo_push (struct stream_fifo *fifo, struct stream *s) +{ + if (fifo->tail) + fifo->tail->next = s; + else + fifo->head = s; + + fifo->tail = s; + + fifo->count++; +} + +/* Delete first stream from fifo. */ +struct stream * +stream_fifo_pop (struct stream_fifo *fifo) +{ + struct stream *s; + + s = fifo->head; + + if (s) + { + fifo->head = s->next; + + if (fifo->head == NULL) + fifo->tail = NULL; + + fifo->count--; + } + + return s; +} + +/* Return first fifo entry. */ +struct stream * +stream_fifo_head (struct stream_fifo *fifo) +{ + return fifo->head; +} + +void +stream_fifo_clean (struct stream_fifo *fifo) +{ + struct stream *s; + struct stream *next; + + for (s = fifo->head; s; s = next) + { + next = s->next; + stream_free (s); + } + fifo->head = fifo->tail = NULL; + fifo->count = 0; +} + +void +stream_fifo_free (struct stream_fifo *fifo) +{ + stream_fifo_clean (fifo); + XFREE (MTYPE_STREAM_FIFO, fifo); +} diff --git a/lib/stream.h b/lib/stream.h new file mode 100644 index 0000000..9127bcd --- /dev/null +++ b/lib/stream.h @@ -0,0 +1,237 @@ +/* + * Packet 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. + */ + +#ifndef _ZEBRA_STREAM_H +#define _ZEBRA_STREAM_H + +#include "prefix.h" + +/* + * A stream is an arbitrary buffer, whose contents generally are assumed to + * be in network order. + * + * A stream has the following attributes associated with it: + * + * - size: the allocated, invariant size of the buffer. + * + * - getp: the get position marker, denoting the offset in the stream where + * the next read (or 'get') will be from. This getp marker is + * automatically adjusted when data is read from the stream, the + * user may also manipulate this offset as they wish, within limits + * (see below) + * + * - endp: the end position marker, denoting the offset in the stream where + * valid data ends, and if the user attempted to write (or + * 'put') data where that data would be written (or 'put') to. + * + * These attributes are all size_t values. + * + * Constraints: + * + * 1. getp can never exceed endp + * + * - hence if getp is equal to endp, there is no more valid data that can be + * gotten from the stream (though, the user may reposition getp to earlier in + * the stream, if they wish). + * + * 2. endp can never exceed size + * + * - hence, if endp is equal to size, then the stream is full, and no more + * data can be written to the stream. + * + * In other words the following must always be true, and the stream + * abstraction is allowed internally to assert that the following property + * holds true for a stream, as and when it wishes: + * + * getp <= endp <= size + * + * It is the users responsibility to ensure this property is never violated. + * + * A stream therefore can be thought of like this: + * + * --------------------------------------------------- + * |XXXXXXXXXXXXXXXXXXXXXXXX | + * --------------------------------------------------- + * ^ ^ ^ + * getp endp size + * + * This shows a stream containing data (shown as 'X') up to the endp offset. + * The stream is empty from endp to size. Without adjusting getp, there are + * still endp-getp bytes of valid data to be read from the stream. + * + * Methods are provided to get and put to/from the stream, as well as + * retrieve the values of the 3 markers and manipulate the getp marker. + * + * Note: + * At the moment, newly allocated streams are zero filled. Hence, one can + * use stream_forward_endp() to effectively create arbitrary zero-fill + * padding. However, note that stream_reset() does *not* zero-out the + * stream. This property should **not** be relied upon. + * + * Best practice is to use stream_put (, NULL, ) to zero out + * any part of a stream which isn't otherwise written to. + */ + +/* Stream buffer. */ +struct stream +{ + struct stream *next; + + /* Remainder is ***private*** to stream + * direct access is frowned upon! + * Use the appropriate functions/macros + */ + size_t getp; /* next get position */ + size_t endp; /* last valid data position */ + size_t size; /* size of data segment */ + unsigned char *data; /* data pointer */ +}; + +/* First in first out queue structure. */ +struct stream_fifo +{ + size_t count; + + struct stream *head; + struct stream *tail; +}; + +/* Utility macros. */ +#define STREAM_SIZE(S) ((S)->size) + /* number of bytes which can still be written */ +#define STREAM_WRITEABLE(S) ((S)->size - (S)->endp) + /* 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) +#define STREAM_REMAIN(S) STREAM_WRITEABLE((S)) + +/* Stream prototypes. + * For stream_{put,get}S, the S suffix mean: + * + * c: character (unsigned byte) + * w: word (two bytes) + * l: long (two words) + * q: quad (four words) + */ +extern struct stream *stream_new (size_t); +extern void stream_free (struct stream *); +extern struct stream * stream_copy (struct stream *, struct stream *src); +extern struct stream *stream_dup (struct stream *); +extern size_t stream_resize (struct stream *, size_t); +extern size_t stream_get_getp (struct stream *); +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); + +/* steam_put: NULL source zeroes out size_t bytes of stream */ +extern void stream_put (struct stream *, const void *, size_t); +extern int stream_putc (struct stream *, u_char); +extern int stream_putc_at (struct stream *, size_t, u_char); +extern int stream_putw (struct stream *, u_int16_t); +extern int stream_putw_at (struct stream *, size_t, u_int16_t); +extern int stream_putl (struct stream *, u_int32_t); +extern int stream_putl_at (struct stream *, size_t, u_int32_t); +extern int stream_putq (struct stream *, uint64_t); +extern int stream_putq_at (struct stream *, size_t, uint64_t); +extern int stream_put_ipv4 (struct stream *, u_int32_t); +extern int stream_put_in_addr (struct stream *, struct in_addr *); +extern int stream_put_prefix (struct stream *, struct prefix *); + +extern void stream_get (void *, struct stream *, size_t); +extern u_char stream_getc (struct stream *); +extern u_char stream_getc_from (struct stream *, size_t); +extern u_int16_t stream_getw (struct stream *); +extern u_int16_t stream_getw_from (struct stream *, size_t); +extern u_int32_t stream_getl (struct stream *); +extern u_int32_t stream_getl_from (struct stream *, size_t); +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 + +/* Deprecated: assumes blocking I/O. Will be removed. + Use stream_read_try instead. */ +extern int stream_read (struct stream *, int, size_t); + +/* Read up to size bytes into the stream. + Return code: + >0: number of bytes read + 0: end-of-file + -1: fatal error + -2: transient error, should retry later (i.e. EAGAIN or EINTR) + This is suitable for use with non-blocking file descriptors. + */ +extern ssize_t stream_read_try(struct stream *s, int fd, size_t size); + +extern ssize_t stream_recvmsg (struct stream *s, int fd, struct msghdr *, + int flags, size_t size); +extern ssize_t stream_recvfrom (struct stream *s, int fd, size_t len, + int flags, struct sockaddr *from, + socklen_t *fromlen); +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? */ + +/* deprecated */ +extern u_char *stream_pnt (struct stream *); + +/* Stream fifo. */ +extern struct stream_fifo *stream_fifo_new (void); +extern void stream_fifo_push (struct stream_fifo *fifo, struct stream *s); +extern struct stream *stream_fifo_pop (struct stream_fifo *fifo); +extern struct stream *stream_fifo_head (struct stream_fifo *fifo); +extern void stream_fifo_clean (struct stream_fifo *fifo); +extern void stream_fifo_free (struct stream_fifo *fifo); + +#endif /* _ZEBRA_STREAM_H */ diff --git a/lib/table.c b/lib/table.c new file mode 100644 index 0000000..da21361 --- /dev/null +++ b/lib/table.c @@ -0,0 +1,809 @@ +/* + * Routing Table functions. + * Copyright (C) 1998 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 "table.h" +#include "memory.h" +#include "sockunion.h" + +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_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; +} + +void +route_table_finish (struct route_table *rt) +{ + route_table_free (rt); +} + +/* Allocate new route node. */ +static struct route_node * +route_node_new (struct route_table *table) +{ + 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, const struct prefix *prefix) +{ + struct route_node *node; + + node = route_node_new (table); + + prefix_copy (&node->p, prefix); + node->table = table; + + return node; +} + +/* Free route node. */ +static void +route_node_free (struct route_table *table, struct route_node *node) +{ + table->delegate->destroy_node (table->delegate, table, node); +} + +/* Free route table. */ +static void +route_table_free (struct route_table *rt) +{ + struct route_node *tmp_node; + struct route_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 route_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 */ + route_node_free (rt, 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); + + XFREE (MTYPE_ROUTE_TABLE, rt); + return; +} + +/* Utility mask array. */ +static const u_char maskbit[] = +{ + 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff +}; + +/* Common prefix route genaration. */ +static void +route_common (const struct prefix *n, const struct prefix *p, struct prefix *new) +{ + int i; + u_char diff; + u_char mask; + + 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++) + { + 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 route_node *node, struct route_node *new) +{ + unsigned int bit = prefix_bit (&new->p.u.prefix, node->p.prefixlen); + + node->link[bit] = new; + new->parent = node; +} + +/* Lock node. */ +struct route_node * +route_lock_node (struct route_node *node) +{ + node->lock++; + return node; +} + +/* Unlock node. */ +void +route_unlock_node (struct route_node *node) +{ + assert (node->lock > 0); + node->lock--; + + if (node->lock == 0) + route_node_delete (node); +} + +/* Find matched prefix. */ +struct route_node * +route_node_match (const struct route_table *table, const struct prefix *p) +{ + struct route_node *node; + struct route_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; + + if (node->p.prefixlen == p->prefixlen) + break; + + node = node->link[prefix_bit(&p->u.prefix, node->p.prefixlen)]; + } + + /* If matched route found, return it. */ + if (matched) + return route_lock_node (matched); + + return NULL; +} + +struct route_node * +route_node_match_ipv4 (const struct route_table *table, + const 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 route_node_match (table, (struct prefix *) &p); +} + +#ifdef HAVE_IPV6 +struct route_node * +route_node_match_ipv6 (const struct route_table *table, + const 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 route_node_match (table, (struct prefix *) &p); +} +#endif /* HAVE_IPV6 */ + +/* Lookup same prefix node. Return NULL when we can't find route. */ +struct route_node * +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 <= prefixlen && + prefix_match (&node->p, p)) + { + if (node->p.prefixlen == prefixlen) + return node->info ? route_lock_node (node) : NULL; + + node = node->link[prefix_bit(prefix, node->p.prefixlen)]; + } + + return NULL; +} + +/* Add node to routing table. */ +struct route_node * +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 <= prefixlen && + prefix_match (&node->p, p)) + { + if (node->p.prefixlen == prefixlen) + return route_lock_node (node); + + match = node; + node = node->link[prefix_bit(prefix, node->p.prefixlen)]; + } + + if (node == NULL) + { + new = route_node_set (table, p); + if (match) + set_link (match, new); + else + table->top = new; + } + else + { + new = route_node_new (table); + 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 = 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. */ +static void +route_node_delete (struct route_node *node) +{ + struct route_node *child; + struct route_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--; + + route_node_free (node->table, node); + + /* If parent node is stub then delete it also. */ + if (parent && parent->lock == 0) + route_node_delete (parent); +} + +/* Get fist node and lock it. This function is useful when one want + to lookup all the node exist in the routing table. */ +struct route_node * +route_top (struct route_table *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. */ + route_lock_node (table->top); + return table->top; +} + +/* Unlock current node and lock next node then return it. */ +struct route_node * +route_next (struct route_node *node) +{ + struct route_node *next; + struct route_node *start; + + /* Node may be deleted from route_unlock_node so we have to preserve + next node's pointer. */ + + if (node->l_left) + { + next = node->l_left; + route_lock_node (next); + route_unlock_node (node); + return next; + } + if (node->l_right) + { + next = node->l_right; + route_lock_node (next); + route_unlock_node (node); + return next; + } + + start = node; + while (node->parent) + { + if (node->parent->l_left == node && node->parent->l_right) + { + next = node->parent->l_right; + route_lock_node (next); + route_unlock_node (start); + return next; + } + node = node->parent; + } + route_unlock_node (start); + return NULL; +} + +/* Unlock current node and lock next node until limit. */ +struct route_node * +route_next_until (struct route_node *node, struct route_node *limit) +{ + struct route_node *next; + struct route_node *start; + + /* Node may be deleted from route_unlock_node so we have to preserve + next node's pointer. */ + + if (node->l_left) + { + next = node->l_left; + route_lock_node (next); + route_unlock_node (node); + return next; + } + if (node->l_right) + { + next = node->l_right; + route_lock_node (next); + route_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; + route_lock_node (next); + route_unlock_node (start); + return next; + } + node = node->parent; + } + 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 new file mode 100644 index 0000000..2ffd79b --- /dev/null +++ b/lib/table.h @@ -0,0 +1,254 @@ +/* + * Routing Table + * Copyright (C) 1998 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_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 +{ + ROUTE_NODE_FIELDS + +#define l_left link[0] +#define l_right link[1] +}; + +typedef struct route_table_iter_t_ route_table_iter_t; + +typedef enum +{ + RT_ITER_STATE_INIT, + RT_ITER_STATE_ITERATING, + RT_ITER_STATE_PAUSED, + RT_ITER_STATE_DONE +} route_table_iter_state_t; + +/* + * 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 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 *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 *); +extern struct route_node *route_node_match_ipv4 (const struct route_table *, + const struct in_addr *); +#ifdef HAVE_IPV6 +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 new file mode 100644 index 0000000..de4d76d --- /dev/null +++ b/lib/thread.c @@ -0,0 +1,1381 @@ +/* Thread management routine + * Copyright (C) 1998, 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. + */ + +/* #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; +/* Relative time, since startup */ +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 + +/* Adjust so that tv_usec is in the range [0,TIMER_SECOND_MICRO). + And change negative values to 0. */ +static struct timeval +timeval_adjust (struct timeval a) +{ + while (a.tv_usec >= TIMER_SECOND_MICRO) + { + a.tv_usec -= TIMER_SECOND_MICRO; + a.tv_sec++; + } + + while (a.tv_usec < 0) + { + a.tv_usec += TIMER_SECOND_MICRO; + a.tv_sec--; + } + + if (a.tv_sec < 0) + /* Change negative timeouts to 0. */ + a.tv_sec = a.tv_usec = 0; + + return a; +} + +static struct timeval +timeval_subtract (struct timeval a, struct timeval b) +{ + struct timeval ret; + + ret.tv_usec = a.tv_usec - b.tv_usec; + ret.tv_sec = a.tv_sec - b.tv_sec; + + return timeval_adjust (ret); +} + +static long +timeval_cmp (struct timeval a, struct timeval b) +{ + return (a.tv_sec == b.tv_sec + ? a.tv_usec - b.tv_usec : a.tv_sec - b.tv_sec); +} + +unsigned long +timeval_elapsed (struct timeval a, struct timeval b) +{ + return (((a.tv_sec - b.tv_sec) * TIMER_SECOND_MICRO) + + (a.tv_usec - b.tv_usec)); +} + +#if !defined(HAVE_CLOCK_MONOTONIC) && !defined(__APPLE__) +static void +quagga_gettimeofday_relative_adjust (void) +{ + struct timeval diff; + if (timeval_cmp (recent_time, last_recent_time) < 0) + { + relative_time.tv_sec++; + relative_time.tv_usec = 0; + } + else + { + diff = timeval_subtract (recent_time, last_recent_time); + relative_time.tv_sec += diff.tv_sec; + relative_time.tv_usec += diff.tv_usec; + relative_time = timeval_adjust (relative_time); + } + last_recent_time = recent_time; +} +#endif /* !HAVE_CLOCK_MONOTONIC && !__APPLE__ */ + +/* gettimeofday wrapper, to keep recent_time updated */ +static int +quagga_gettimeofday (struct timeval *tv) +{ + int ret; + + assert (tv); + + if (!(ret = gettimeofday (&recent_time, NULL))) + { + /* init... */ + if (!timers_inited) + { + relative_time_base = last_recent_time = recent_time; + timers_inited = 1; + } + /* avoid copy if user passed recent_time pointer.. */ + if (tv != &recent_time) + *tv = recent_time; + return 0; + } + return ret; +} + +static int +quagga_get_relative (struct timeval *tv) +{ + int ret; + +#ifdef HAVE_CLOCK_MONOTONIC + { + struct timespec tp; + if (!(ret = clock_gettime (CLOCK_MONOTONIC, &tp))) + { + relative_time.tv_sec = tp.tv_sec; + relative_time.tv_usec = tp.tv_nsec / 1000; + } + } +#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 */ + + if (tv) + *tv = relative_time; + + return ret; +} + +/* Get absolute time stamp, but in terms of the internal timer + * Could be wrong, but at least won't go back. + */ +static void +quagga_real_stabilised (struct timeval *tv) +{ + *tv = relative_time_base; + tv->tv_sec += relative_time.tv_sec; + tv->tv_usec += relative_time.tv_usec; + *tv = timeval_adjust (*tv); +} + +/* Exported Quagga timestamp function. + * Modelled on POSIX clock_gettime. + */ +int +quagga_gettime (enum quagga_clkid clkid, struct timeval *tv) +{ + switch (clkid) + { + case QUAGGA_CLK_REALTIME: + return quagga_gettimeofday (tv); + case QUAGGA_CLK_MONOTONIC: + return quagga_get_relative (tv); + case QUAGGA_CLK_REALTIME_STABILISED: + quagga_real_stabilised (tv); + return 0; + default: + errno = EINVAL; + return -1; + } +} + +/* time_t value in terms of stabilised absolute time. + * replacement for POSIX time() + */ +time_t +quagga_time (time_t *t) +{ + struct timeval tv; + quagga_real_stabilised (&tv); + if (t) + *t = tv.tv_sec; + return tv.tv_sec; +} + +/* Public export of recent_relative_time by value */ +struct timeval +recent_relative_time (void) +{ + return relative_time; +} + +static unsigned int +cpu_record_hash_key (struct cpu_thread_history *a) +{ + return (uintptr_t) a->func; +} + +static int +cpu_record_hash_cmp (const struct cpu_thread_history *a, + const struct cpu_thread_history *b) +{ + return a->func == b->func; +} + +static void * +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 = a->funcname; + return new; +} + +static void +cpu_record_hash_free (void *a) +{ + struct cpu_thread_history *hist = a; + + XFREE (MTYPE_THREAD_STATS, hist); +} + +static void +vty_out_cpu_thread_history(struct vty* vty, + struct cpu_thread_history *a) +{ +#ifdef HAVE_RUSAGE + vty_out(vty, "%7ld.%03ld %9d %8ld %9ld %8ld %9ld", + a->cpu.total/1000, a->cpu.total%1000, a->total_calls, + a->cpu.total/a->total_calls, a->cpu.max, + a->real.total/a->total_calls, a->real.max); +#else + vty_out(vty, "%7ld.%03ld %9d %8ld %9ld", + a->real.total/1000, a->real.total%1000, a->total_calls, + a->real.total/a->total_calls, a->real.max); +#endif + vty_out(vty, " %c%c%c%c%c%c %s%s", + a->types & (1 << THREAD_READ) ? 'R':' ', + a->types & (1 << THREAD_WRITE) ? 'W':' ', + a->types & (1 << THREAD_TIMER) ? 'T':' ', + a->types & (1 << THREAD_EVENT) ? 'E':' ', + a->types & (1 << THREAD_EXECUTE) ? 'X':' ', + a->types & (1 << THREAD_BACKGROUND) ? 'B' : ' ', + a->funcname, VTY_NEWLINE); +} + +static void +cpu_record_hash_print(struct hash_backet *bucket, + void *args[]) +{ + struct cpu_thread_history *totals = args[0]; + struct vty *vty = args[1]; + thread_type *filter = args[2]; + struct cpu_thread_history *a = bucket->data; + + a = bucket->data; + if ( !(a->types & *filter) ) + return; + vty_out_cpu_thread_history(vty,a); + totals->total_calls += a->total_calls; + totals->real.total += a->real.total; + if (totals->real.max < a->real.max) + totals->real.max = a->real.max; +#ifdef HAVE_RUSAGE + totals->cpu.total += a->cpu.total; + if (totals->cpu.max < a->cpu.max) + totals->cpu.max = a->cpu.max; +#endif +} + +static void +cpu_record_print(struct vty *vty, thread_type filter) +{ + struct cpu_thread_history tmp; + void *args[3] = {&tmp, vty, &filter}; + + memset(&tmp, 0, sizeof tmp); + tmp.funcname = "TOTAL"; + tmp.types = filter; + +#ifdef HAVE_RUSAGE + vty_out(vty, "%21s %18s %18s%s", + "", "CPU (user+system):", "Real (wall-clock):", VTY_NEWLINE); +#endif + vty_out(vty, "Runtime(ms) Invoked Avg uSec Max uSecs"); +#ifdef HAVE_RUSAGE + vty_out(vty, " Avg uSec Max uSecs"); +#endif + vty_out(vty, " Type Thread%s", VTY_NEWLINE); + hash_iterate(cpu_record, + (void(*)(struct hash_backet*,void*))cpu_record_hash_print, + args); + + if (tmp.total_calls > 0) + vty_out_cpu_thread_history(vty, &tmp); +} + +DEFUN(show_thread_cpu, + show_thread_cpu_cmd, + "show thread cpu [FILTER]", + SHOW_STR + "Thread information\n" + "Thread CPU usage\n" + "Display filter (rwtexb)\n") +{ + int i = 0; + thread_type filter = (thread_type) -1U; + + if (argc > 0) + { + filter = 0; + while (argv[0][i] != '\0') + { + switch ( argv[0][i] ) + { + case 'r': + case 'R': + filter |= (1 << THREAD_READ); + break; + case 'w': + case 'W': + filter |= (1 << THREAD_WRITE); + break; + case 't': + case 'T': + filter |= (1 << THREAD_TIMER); + break; + case 'e': + case 'E': + filter |= (1 << THREAD_EVENT); + break; + case 'x': + case 'X': + filter |= (1 << THREAD_EXECUTE); + break; + case 'b': + case 'B': + filter |= (1 << THREAD_BACKGROUND); + break; + default: + break; + } + ++i; + } + if (filter == 0) + { + vty_out(vty, "Invalid filter \"%s\" specified," + " must contain at least one of 'RWTEXB'%s", + argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + } + + cpu_record_print(vty, filter); + return CMD_SUCCESS; +} + +static void +cpu_record_hash_clear (struct hash_backet *bucket, + void *args) +{ + thread_type *filter = args; + struct cpu_thread_history *a = bucket->data; + + a = bucket->data; + if ( !(a->types & *filter) ) + return; + + hash_release (cpu_record, bucket->data); +} + +static void +cpu_record_clear (thread_type filter) +{ + thread_type *tmp = &filter; + hash_iterate (cpu_record, + (void (*) (struct hash_backet*,void*)) cpu_record_hash_clear, + tmp); +} + +DEFUN(clear_thread_cpu, + clear_thread_cpu_cmd, + "clear thread cpu [FILTER]", + "Clear stored data\n" + "Thread information\n" + "Thread CPU usage\n" + "Display filter (rwtexb)\n") +{ + int i = 0; + thread_type filter = (thread_type) -1U; + + if (argc > 0) + { + filter = 0; + while (argv[0][i] != '\0') + { + switch ( argv[0][i] ) + { + case 'r': + case 'R': + filter |= (1 << THREAD_READ); + break; + case 'w': + case 'W': + filter |= (1 << THREAD_WRITE); + break; + case 't': + case 'T': + filter |= (1 << THREAD_TIMER); + break; + case 'e': + case 'E': + filter |= (1 << THREAD_EVENT); + break; + case 'x': + case 'X': + filter |= (1 << THREAD_EXECUTE); + break; + case 'b': + case 'B': + filter |= (1 << THREAD_BACKGROUND); + break; + default: + break; + } + ++i; + } + if (filter == 0) + { + vty_out(vty, "Invalid filter \"%s\" specified," + " must contain at least one of 'RWTEXB'%s", + argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + } + + cpu_record_clear (filter); + return CMD_SUCCESS; +} + +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_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 ((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. */ +static void +thread_list_add (struct thread_list *list, struct thread *thread) +{ + thread->next = NULL; + thread->prev = list->tail; + if (list->tail) + list->tail->next = thread; + else + list->head = thread; + list->tail = thread; + list->count++; +} + +/* Delete a thread from the list. */ +static struct thread * +thread_list_delete (struct thread_list *list, struct thread *thread) +{ + if (thread->next) + thread->next->prev = thread->prev; + else + list->tail = thread->prev; + if (thread->prev) + thread->prev->next = thread->next; + else + list->head = thread->next; + thread->next = thread->prev = NULL; + list->count--; + 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) +{ + assert (m != NULL && thread != NULL); + assert (thread->next == NULL); + assert (thread->prev == NULL); + assert (thread->type == THREAD_UNUSED); + thread_list_add (&m->unuse, thread); +} + +/* Free all unused thread. */ +static void +thread_list_free (struct thread_master *m, struct thread_list *list) +{ + struct thread *t; + struct thread *next; + + for (t = list->head; t; t = next) + { + next = t->next; + 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_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_queue_free (m, m->background); + + XFREE (MTYPE_THREAD_MASTER, m); + + if (cpu_record) + { + hash_clean (cpu_record, cpu_record_hash_free); + hash_free (cpu_record); + cpu_record = NULL; + } +} + +/* Thread list is empty or not. */ +static int +thread_empty (struct thread_list *list) +{ + return list->head ? 0 : 1; +} + +/* Delete top of the list and return it. */ +static struct thread * +thread_trim_head (struct thread_list *list) +{ + if (!thread_empty (list)) + return thread_list_delete (list, list->head); + return NULL; +} + +/* Return remain time in second. */ +unsigned long +thread_timer_remain_second (struct thread *thread) +{ + quagga_get_relative (NULL); + + if (thread->u.sands.tv_sec - relative_time.tv_sec > 0) + return thread->u.sands.tv_sec - relative_time.tv_sec; + else + return 0; +} + +struct timeval +thread_timer_remain(struct thread *thread) +{ + quagga_get_relative(NULL); + + 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, debugargdef) +{ + struct thread *thread = thread_trim_head (&m->unuse); + + if (! thread) + { + thread = XCALLOC (MTYPE_THREAD, sizeof (struct thread)); + m->alloc++; + } + thread->type = type; + thread->add_type = type; + thread->master = m; + thread->func = func; + thread->arg = arg; + thread->index = -1; + + thread->funcname = funcname; + thread->schedfrom = schedfrom; + thread->schedfrom_line = fromln; + + return thread; +} + +#define fd_copy_fd_set(X) (X) + +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)); +} + +static int +fd_is_set (int fd, thread_fd_set *fdset) +{ + return FD_ISSET (fd, fdset); +} + +static int +fd_clear_read_write (int fd, thread_fd_set *fdset) +{ + if (!FD_ISSET (fd, fdset)) + return 0; + + FD_CLR (fd, fdset); + return 1; +} + +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 = NULL; + thread_fd_set *fdset = NULL; + + if (dir == THREAD_READ) + fdset = &m->readfd; + else + fdset = &m->writefd; + + if (FD_ISSET (fd, fdset)) + { + zlog (NULL, LOG_WARNING, "There is already %s fd [%d]", + (dir = THREAD_READ) ? "read" : "write", fd); + return NULL; + } + + FD_SET (fd, fdset); + + thread = thread_get (m, dir, func, arg, debugargpass); + thread->u.fd = fd; + 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, + debugargdef) +{ + struct thread *thread; + struct pqueue *queue; + struct timeval alarm_time; + + assert (m != NULL); + + assert (type == THREAD_TIMER || type == THREAD_BACKGROUND); + assert (time_relative); + + 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); + alarm_time.tv_sec = relative_time.tv_sec + time_relative->tv_sec; + alarm_time.tv_usec = relative_time.tv_usec + time_relative->tv_usec; + thread->u.sands = timeval_adjust(alarm_time); + + pqueue_enqueue(thread, queue); + return thread; +} + + +/* Add timer event thread. */ +struct thread * +funcname_thread_add_timer (struct thread_master *m, + int (*func) (struct thread *), + void *arg, long timer, + debugargdef) +{ + struct timeval trel; + + assert (m != NULL); + + trel.tv_sec = timer; + trel.tv_usec = 0; + + return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER, arg, + &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, + debugargdef) +{ + struct timeval trel; + + assert (m != NULL); + + trel.tv_sec = timer / 1000; + trel.tv_usec = 1000*(timer % 1000); + + return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER, + 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, + debugargdef) +{ + struct timeval trel; + + assert (m != NULL); + + if (delay) + { + trel.tv_sec = delay / 1000; + trel.tv_usec = 1000*(delay % 1000); + } + else + { + trel.tv_sec = 0; + trel.tv_usec = 0; + } + + return funcname_thread_add_timer_timeval (m, func, THREAD_BACKGROUND, + 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, + debugargdef) +{ + struct thread *thread; + + assert (m != NULL); + + thread = thread_get (m, THREAD_EVENT, func, arg, debugargpass); + thread->u.val = val; + thread_list_add (&m->event, thread); + + return thread; +} + +/* Cancel thread from scheduler. */ +void +thread_cancel (struct thread *thread) +{ + struct thread_list *list = NULL; + struct pqueue *queue = NULL; + struct thread **thread_array = NULL; + + switch (thread->type) + { + case THREAD_READ: + assert (fd_clear_read_write (thread->u.fd, &thread->master->readfd)); + thread_array = thread->master->read; + break; + case THREAD_WRITE: + assert (fd_clear_read_write (thread->u.fd, &thread->master->writefd)); + thread_array = thread->master->write; + break; + case THREAD_TIMER: + queue = thread->master->timer; + break; + case THREAD_EVENT: + list = &thread->master->event; + break; + case THREAD_READY: + list = &thread->master->ready; + break; + case THREAD_BACKGROUND: + queue = thread->master->background; + break; + default: + return; + break; + } + + 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); +} + +/* Delete all events which has argument value arg. */ +unsigned int +thread_cancel_event (struct thread_master *m, void *arg) +{ + unsigned int ret = 0; + struct thread *thread; + + thread = m->event.head; + while (thread) + { + struct thread *t; + + t = thread; + thread = t->next; + + if (t->arg == arg) + { + ret++; + thread_list_delete (&m->event, t); + t->type = THREAD_UNUSED; + 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 pqueue *queue, struct timeval *timer_val) +{ + if (queue->size) + { + struct thread *next_timer = queue->array[0]; + *timer_val = timeval_subtract (next_timer->u.sands, relative_time); + return timer_val; + } + return NULL; +} + +static struct thread * +thread_run (struct thread_master *m, struct thread *thread, + struct thread *fetch) +{ + *fetch = *thread; + thread->type = THREAD_UNUSED; + thread_add_unuse (m, thread); + return fetch; +} + +static int +thread_process_fds_helper (struct thread_master *m, struct thread *thread, thread_fd_set *fdset) +{ + thread_fd_set *mfdset = NULL; + struct thread **thread_array; + + if (!thread) + return 0; + + if (thread->type == THREAD_READ) + { + mfdset = &m->readfd; + thread_array = m->read; + } + else + { + mfdset = &m->writefd; + thread_array = m->write; + } + + 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 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 pqueue *queue, struct timeval *timenow) +{ + struct thread *thread; + unsigned int ready = 0; + + while (queue->size) + { + thread = queue->array[0]; + if (timeval_cmp (*timenow, thread->u.sands) < 0) + return ready; + pqueue_dequeue(queue); + thread->type = THREAD_READY; + thread_list_add (&thread->master->ready, thread); + ready++; + } + return ready; +} + +/* process a list en masse, e.g. for event thread lists */ +static unsigned int +thread_process (struct thread_list *list) +{ + struct thread *thread; + struct thread *next; + unsigned int ready = 0; + + for (thread = list->head; thread; thread = next) + { + next = thread->next; + thread_list_delete (list, thread); + thread->type = THREAD_READY; + thread_list_add (&thread->master->ready, thread); + ready++; + } + return ready; +} + + +/* Fetch next ready thread. */ +struct thread * +thread_fetch (struct thread_master *m, struct thread *fetch) +{ + struct thread *thread; + 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; + struct timeval *timer_wait_bg; + + while (1) + { + int num = 0; + + /* Signals pre-empt everything */ + quagga_sigevent_process (); + + /* Drain the ready queue of already scheduled jobs, before scheduling + * more. + */ + if ((thread = thread_trim_head (&m->ready)) != NULL) + return thread_run (m, thread, fetch); + + /* To be fair to all kinds of threads, and avoid starvation, we + * need to be careful to consider all thread types for scheduling + * in each quanta. I.e. we should not return early from here on. + */ + + /* Normal event are the next highest priority. */ + thread_process (&m->event); + + /* Structure copy. */ + 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); + + if (timer_wait_bg && + (!timer_wait || (timeval_cmp (*timer_wait, *timer_wait_bg) > 0))) + timer_wait = timer_wait_bg; + } + + num = fd_select (FD_SETSIZE, &readfd, &writefd, &exceptfd, timer_wait); + + /* Signals should get quick treatment */ + if (num < 0) + { + if (errno == EINTR) + continue; /* signal received - process it */ + zlog_warn ("select() error: %s", safe_strerror (errno)); + 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); + + /* Got IO, process it */ + if (num > 0) + thread_process_fds (m, &readfd, &writefd, num); + +#if 0 + /* If any threads were made ready above (I/O or foreground timer), + perhaps we should avoid adding background timers to the ready + list at this time. If this is code is uncommented, then background + timer threads will not run unless there is nothing else to do. */ + if ((thread = thread_trim_head (&m->ready)) != NULL) + return thread_run (m, thread, fetch); +#endif + + /* Background timer/events, lowest priority */ + thread_timer_process (m->background, &relative_time); + + if ((thread = thread_trim_head (&m->ready)) != NULL) + return thread_run (m, thread, fetch); + } +} + +unsigned long +thread_consumed_time (RUSAGE_T *now, RUSAGE_T *start, unsigned long *cputime) +{ +#ifdef HAVE_RUSAGE + /* This is 'user + sys' time. */ + *cputime = timeval_elapsed (now->cpu.ru_utime, start->cpu.ru_utime) + + timeval_elapsed (now->cpu.ru_stime, start->cpu.ru_stime); +#else + *cputime = 0; +#endif /* HAVE_RUSAGE */ + return timeval_elapsed (now->real, start->real); +} + +/* We should aim to yield after THREAD_YIELD_TIME_SLOT milliseconds. + Note: we are using real (wall clock) time for this calculation. + It could be argued that CPU time may make more sense in certain + contexts. The things to consider are whether the thread may have + blocked (in which case wall time increases, but CPU time does not), + or whether the system is heavily loaded with other processes competing + for CPU time. On balance, wall clock time seems to make sense. + Plus it has the added benefit that gettimeofday should be faster + than calling getrusage. */ +int +thread_should_yield (struct thread *thread) +{ + quagga_get_relative (NULL); + unsigned long t = timeval_elapsed(relative_time, thread->real); + return ((t > THREAD_YIELD_TIME_SLOT) ? t : 0); +} + +void +thread_getrusage (RUSAGE_T *r) +{ + quagga_get_relative (NULL); +#ifdef HAVE_RUSAGE + getrusage(RUSAGE_SELF, &(r->cpu)); +#endif + r->real = relative_time; + +#ifdef HAVE_CLOCK_MONOTONIC + /* quagga_get_relative() only updates recent_time if gettimeofday + * based, not when using CLOCK_MONOTONIC. As we export recent_time + * and guarantee to update it before threads are run... + */ + quagga_gettimeofday(&recent_time); +#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. */ +void +thread_call (struct thread *thread) +{ + unsigned long realtime, cputime; + RUSAGE_T before, after; + + /* Cache a pointer to the relevant cpu history thread, if the thread + * does not have it yet. + * + * Callers submitting 'dummy threads' hence must take care that + * thread->cpu is NULL + */ + if (!thread->hist) + { + struct cpu_thread_history tmp; + + tmp.func = thread->func; + tmp.funcname = thread->funcname; + + thread->hist = hash_get (cpu_record, &tmp, + (void * (*) (void *))cpu_record_hash_alloc); + } + + GETRUSAGE (&before); + thread->real = before.real; + + thread_current = thread; + (*thread->func) (thread); + thread_current = NULL; + + GETRUSAGE (&after); + + realtime = thread_consumed_time (&after, &before, &cputime); + thread->hist->real.total += realtime; + if (thread->hist->real.max < realtime) + thread->hist->real.max = realtime; +#ifdef HAVE_RUSAGE + thread->hist->cpu.total += cputime; + if (thread->hist->cpu.max < cputime) + thread->hist->cpu.max = cputime; +#endif + + ++(thread->hist->total_calls); + thread->hist->types |= (1 << thread->add_type); + +#ifdef CONSUMED_TIME_CHECK + if (realtime > CONSUMED_TIME_CHECK) + { + /* + * We have a CPU Hog on our hands. + * Whinge about it now, so we're aware this is yet another task + * to fix. + */ + zlog_warn ("SLOW THREAD: task %s (%lx) ran for %lums (cpu time %lums)", + thread->funcname, + (unsigned long) thread->func, + realtime/1000, cputime/1000); + } +#endif /* CONSUMED_TIME_CHECK */ +} + +/* Execute thread */ +struct thread * +funcname_thread_execute (struct thread_master *m, + int (*func)(struct thread *), + void *arg, + int val, + debugargdef) +{ + struct thread dummy; + + memset (&dummy, 0, sizeof (struct thread)); + + dummy.type = THREAD_EVENT; + dummy.add_type = THREAD_EXECUTE; + dummy.master = NULL; + dummy.func = func; + dummy.arg = arg; + dummy.u.val = val; + + 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 new file mode 100644 index 0000000..3f16216 --- /dev/null +++ b/lib/thread.h @@ -0,0 +1,254 @@ +/* Thread management routine header. + * Copyright (C) 1998 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_THREAD_H +#define _ZEBRA_THREAD_H + +#include + +struct rusage_t +{ +#ifdef HAVE_RUSAGE + struct rusage cpu; +#endif + struct timeval real; +}; +#define RUSAGE_T struct rusage_t + +#define GETRUSAGE(X) thread_getrusage(X) + +/* Linked list of thread. */ +struct thread_list +{ + struct thread *head; + struct thread *tail; + 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 **read; + struct thread **write; + struct pqueue *timer; + struct thread_list event; + struct thread_list ready; + struct thread_list unuse; + struct pqueue *background; + int fd_limit; + thread_fd_set readfd; + thread_fd_set writefd; + thread_fd_set exceptfd; + unsigned long alloc; +}; + +typedef unsigned char thread_type; + +/* Thread itself. */ +struct thread +{ + thread_type type; /* thread type */ + thread_type add_type; /* thread type */ + struct thread *next; /* next pointer of the thread */ + struct thread *prev; /* previous pointer of the thread */ + struct thread_master *master; /* pointer to the struct thread_master. */ + int (*func) (struct thread *); /* event function */ + void *arg; /* event argument */ + union { + int val; /* second argument of the event. */ + int fd; /* file descriptor in case of read/write. */ + struct timeval sands; /* rest of time sands value. */ + } u; + int index; /* used for timers to store position in queue */ + struct timeval real; + struct cpu_thread_history *hist; /* cache pointer to cpu_history */ + const char *funcname; + const char *schedfrom; + int schedfrom_line; +}; + +struct cpu_thread_history +{ + int (*func)(struct thread *); + unsigned int total_calls; + struct time_stats + { + unsigned long total, max; + } real; +#ifdef HAVE_RUSAGE + struct time_stats cpu; +#endif + thread_type types; + const char *funcname; +}; + +/* Clocks supported by Quagga */ +enum quagga_clkid { + QUAGGA_CLK_REALTIME = 0, /* ala gettimeofday() */ + QUAGGA_CLK_MONOTONIC, /* monotonic, against an indeterminate base */ + QUAGGA_CLK_REALTIME_STABILISED, /* like realtime, but non-decrementing */ +}; + +/* Thread types. */ +#define THREAD_READ 0 +#define THREAD_WRITE 1 +#define THREAD_TIMER 2 +#define THREAD_EVENT 3 +#define THREAD_READY 4 +#define THREAD_BACKGROUND 5 +#define THREAD_UNUSED 6 +#define THREAD_EXECUTE 7 + +/* Thread yield time. */ +#define THREAD_YIELD_TIME_SLOT 10 * 1000L /* 10ms */ + +/* Macros. */ +#define THREAD_ARG(X) ((X)->arg) +#define THREAD_FD(X) ((X)->u.fd) +#define THREAD_VAL(X) ((X)->u.val) + +#define THREAD_READ_ON(master,thread,func,arg,sock) \ + do { \ + if (! thread) \ + thread = thread_add_read (master, func, arg, sock); \ + } while (0) + +#define THREAD_WRITE_ON(master,thread,func,arg,sock) \ + do { \ + if (! thread) \ + thread = thread_add_write (master, func, arg, sock); \ + } while (0) + +#define THREAD_TIMER_ON(master,thread,func,arg,time) \ + do { \ + if (! thread) \ + thread = thread_add_timer (master, func, arg, time); \ + } while (0) + +#define THREAD_TIMER_MSEC_ON(master,thread,func,arg,time) \ + do { \ + if (! thread) \ + thread = thread_add_timer_msec (master, func, arg, time); \ + } while (0) + +#define THREAD_OFF(thread) \ + do { \ + if (thread) \ + { \ + thread_cancel (thread); \ + thread = NULL; \ + } \ + } while (0) + +#define THREAD_READ_OFF(thread) THREAD_OFF(thread) +#define THREAD_WRITE_OFF(thread) THREAD_OFF(thread) +#define THREAD_TIMER_OFF(thread) THREAD_OFF(thread) + +#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,__FILE__,__LINE__) + +/* Prototypes. */ +extern struct thread_master *thread_master_create (void); +extern void thread_master_free (struct thread_master *); + +extern struct thread *funcname_thread_add_read (struct thread_master *, + int (*)(struct thread *), + void *, int, debugargdef); +extern struct thread *funcname_thread_add_write (struct thread_master *, + int (*)(struct thread *), + void *, int, debugargdef); +extern struct thread *funcname_thread_add_timer (struct thread_master *, + int (*)(struct thread *), + void *, long, debugargdef); +extern struct thread *funcname_thread_add_timer_msec (struct thread_master *, + int (*)(struct thread *), + 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, debugargdef); +extern struct thread *funcname_thread_add_background (struct thread_master *, + int (*func)(struct thread *), + void *arg, + long milliseconds_to_delay, + debugargdef); +extern struct thread *funcname_thread_execute (struct thread_master *, + int (*)(struct thread *), + 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 *); +extern struct cmd_element show_thread_cpu_cmd; +extern struct cmd_element clear_thread_cpu_cmd; + +/* replacements for the system gettimeofday(), clock_gettime() and + * time() functions, providing support for non-decrementing clock on + * all systems, and fully monotonic on /some/ systems. + */ +extern int quagga_gettime (enum quagga_clkid, struct timeval *); +extern time_t quagga_time (time_t *); + +/* Returns elapsed real (wall clock) time. */ +extern unsigned long thread_consumed_time(RUSAGE_T *after, RUSAGE_T *before, + unsigned long *cpu_time_elapsed); + +/* Global variable containing a recent result from gettimeofday. This can + be used instead of calling gettimeofday if a recent value is sufficient. + This is guaranteed to be refreshed before a thread is called. */ +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/vector.c b/lib/vector.c new file mode 100644 index 0000000..7c14862 --- /dev/null +++ b/lib/vector.c @@ -0,0 +1,189 @@ +/* Generic vector interface routine + * 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 "vector.h" +#include "memory.h" + +/* Initialize vector : allocate memory and return vector. */ +vector +vector_init (unsigned int size) +{ + vector v = XCALLOC (MTYPE_VECTOR, sizeof (struct _vector)); + + /* allocate at least one slot */ + if (size == 0) + size = 1; + + v->alloced = size; + v->active = 0; + v->index = XCALLOC (MTYPE_VECTOR_INDEX, sizeof (void *) * size); + return v; +} + +void +vector_only_wrapper_free (vector v) +{ + XFREE (MTYPE_VECTOR, v); +} + +void +vector_only_index_free (void *index) +{ + XFREE (MTYPE_VECTOR_INDEX, index); +} + +void +vector_free (vector v) +{ + XFREE (MTYPE_VECTOR_INDEX, v->index); + XFREE (MTYPE_VECTOR, v); +} + +vector +vector_copy (vector v) +{ + unsigned int size; + vector new = XCALLOC (MTYPE_VECTOR, sizeof (struct _vector)); + + new->active = v->active; + new->alloced = v->alloced; + + size = sizeof (void *) * (v->alloced); + new->index = XCALLOC (MTYPE_VECTOR_INDEX, size); + memcpy (new->index, v->index, size); + + return new; +} + +/* Check assigned index, and if it runs short double index pointer */ +void +vector_ensure (vector v, unsigned int num) +{ + if (v->alloced > num) + return; + + v->index = XREALLOC (MTYPE_VECTOR_INDEX, + v->index, sizeof (void *) * (v->alloced * 2)); + memset (&v->index[v->alloced], 0, sizeof (void *) * v->alloced); + v->alloced *= 2; + + if (v->alloced <= num) + vector_ensure (v, num); +} + +/* This function only returns next empty slot index. It dose not mean + the slot's index memory is assigned, please call vector_ensure() + after calling this function. */ +int +vector_empty_slot (vector v) +{ + unsigned int i; + + if (v->active == 0) + return 0; + + for (i = 0; i < v->active; i++) + if (v->index[i] == 0) + return i; + + return i; +} + +/* Set value to the smallest empty slot. */ +int +vector_set (vector v, void *val) +{ + unsigned int i; + + i = vector_empty_slot (v); + vector_ensure (v, i); + + v->index[i] = val; + + if (v->active <= i) + v->active = i + 1; + + return i; +} + +/* Set value to specified index slot. */ +int +vector_set_index (vector v, unsigned int i, void *val) +{ + vector_ensure (v, i); + + v->index[i] = val; + + if (v->active <= i) + v->active = i + 1; + + return i; +} + +/* Look up vector. */ +void * +vector_lookup (vector v, unsigned int i) +{ + if (i >= v->active) + return NULL; + return v->index[i]; +} + +/* Lookup vector, ensure it. */ +void * +vector_lookup_ensure (vector v, unsigned int i) +{ + vector_ensure (v, i); + return v->index[i]; +} + +/* Unset value at specified index slot. */ +void +vector_unset (vector v, unsigned int i) +{ + if (i >= v->alloced) + return; + + v->index[i] = NULL; + + if (i + 1 == v->active) + { + v->active--; + while (i && v->index[--i] == NULL && v->active--) + ; /* Is this ugly ? */ + } +} + +/* Count the number of not emplty slot. */ +unsigned int +vector_count (vector v) +{ + unsigned int i; + unsigned count = 0; + + for (i = 0; i < v->active; i++) + if (v->index[i] != NULL) + count++; + + return count; +} diff --git a/lib/vector.h b/lib/vector.h new file mode 100644 index 0000000..6b27fd9 --- /dev/null +++ b/lib/vector.h @@ -0,0 +1,63 @@ +/* + * Generic vector interface header. + * 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. + */ + +#ifndef _ZEBRA_VECTOR_H +#define _ZEBRA_VECTOR_H + +/* struct for vector */ +struct _vector +{ + unsigned int active; /* number of active slots */ + unsigned int alloced; /* number of allocated slot */ + void **index; /* index to data */ +}; +typedef struct _vector *vector; + +#define VECTOR_MIN_SIZE 1 + +/* (Sometimes) usefull macros. This macro convert index expression to + array expression. */ +/* Reference slot at given index, caller must ensure slot is active */ +#define vector_slot(V,I) ((V)->index[(I)]) +/* Number of active slots. + * Note that this differs from vector_count() as it the count returned + * will include any empty slots + */ +#define vector_active(V) ((V)->active) + +/* Prototypes. */ +extern vector vector_init (unsigned int size); +extern void vector_ensure (vector v, unsigned int num); +extern int vector_empty_slot (vector v); +extern int vector_set (vector v, void *val); +extern int vector_set_index (vector v, unsigned int i, void *val); +extern void vector_unset (vector v, unsigned int i); +extern unsigned int vector_count (vector v); +extern void vector_only_wrapper_free (vector v); +extern void vector_only_index_free (void *index); +extern void vector_free (vector v); +extern vector vector_copy (vector v); + +extern void *vector_lookup (vector, unsigned int); +extern void *vector_lookup_ensure (vector, unsigned int); + +#endif /* _ZEBRA_VECTOR_H */ diff --git a/lib/version.h.in b/lib/version.h.in new file mode 100644 index 0000000..aef1d09 --- /dev/null +++ b/lib/version.h.in @@ -0,0 +1,56 @@ +/* @configure_input@ + * + * Quagga version + * Copyright (C) 1997, 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 _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@" GIT_SUFFIX + +#define ZEBRA_BUG_ADDRESS "@PACKAGE_BUGREPORT@" + +#define QUAGGA_URL "http://www.quagga.net" + +#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 +int daemon(int, int); +#endif + +#endif /* _ZEBRA_VERSION_H */ diff --git a/lib/vrf.c b/lib/vrf.c new file mode 100644 index 0000000..29be02a --- /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 0000000..8b29b80 --- /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 new file mode 100644 index 0000000..7ca8354 --- /dev/null +++ b/lib/vty.c @@ -0,0 +1,3209 @@ +/* + * Virtual terminal [aka TeletYpe] interface routine. + * 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 "linklist.h" +#include "thread.h" +#include "buffer.h" +#include +#include "command.h" +#include "sockunion.h" +#include "memory.h" +#include "str.h" +#include "log.h" +#include "prefix.h" +#include "filter.h" +#include "vty.h" +#include "privs.h" +#include "network.h" + +#include +#include + +#define VTY_BUFSIZ 4096 + +/* Vty events */ +enum event +{ + VTY_SERV, + VTY_READ, + VTY_WRITE, + VTY_TIMEOUT_RESET, +#ifdef VTYSH + VTYSH_SERV, + VTYSH_READ, + VTYSH_WRITE +#endif /* VTYSH */ +}; + +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; + +/* Vty timeout value. */ +static unsigned long vty_timeout_val = VTY_TIMEOUT_DEFAULT; + +/* Vty access-class command */ +static char *vty_accesslist_name = NULL; + +/* Vty access-calss for IPv6. */ +static char *vty_ipv6_accesslist_name = NULL; + +/* VTY server thread. */ +static vector Vvty_serv_thread; + +/* Current directory. */ +char *vty_cwd = NULL; + +/* Configure lock. */ +static int vty_config; + +/* Login password check. */ +static int no_password_check = 0; + +/* Restrict unauthenticated logins? */ +static const u_char restricted_mode_default = 0; +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, ...) +{ + va_list args; + int len = 0; + int size = 1024; + char buf[1024]; + char *p = NULL; + + if (vty_shell (vty)) + { + va_start (args, format); + vprintf (format, args); + va_end (args); + } + else + { + /* Try to write to initial buffer. */ + va_start (args, format); + len = vsnprintf (buf, sizeof(buf), format, args); + va_end (args); + + /* Initial buffer is not enough. */ + if (len < 0 || len >= size) + { + while (1) + { + if (len > -1) + size = len + 1; + else + size = size * 2; + + p = XREALLOC (MTYPE_VTY_OUT_BUF, p, size); + if (! p) + return -1; + + va_start (args, format); + len = vsnprintf (p, size, format, args); + va_end (args); + + if (len > -1 && len < size) + break; + } + } + + /* When initial buffer is enough to store all output. */ + if (! p) + p = buf; + + /* Pointer p must point out buffer. */ + buffer_put (vty->obuf, (u_char *) p, len); + + /* If p is not different with buf, it is allocated buffer. */ + if (p != buf) + XFREE (MTYPE_VTY_OUT_BUF, p); + } + + return len; +} + +static int +vty_log_out (struct vty *vty, const char *level, const char *proto_str, + const char *format, struct timestamp_control *ctl, va_list va) +{ + int ret; + int len; + char buf[1024]; + + if (!ctl->already_rendered) + { + ctl->len = quagga_timestamp(ctl->precision, ctl->buf, sizeof(ctl->buf)); + ctl->already_rendered = 1; + } + if (ctl->len+1 >= sizeof(buf)) + return -1; + memcpy(buf, ctl->buf, len = ctl->len); + buf[len++] = ' '; + buf[len] = '\0'; + + if (level) + ret = snprintf(buf+len, sizeof(buf)-len, "%s: %s: ", level, proto_str); + else + ret = snprintf(buf+len, sizeof(buf)-len, "%s: ", proto_str); + if ((ret < 0) || ((size_t)(len += ret) >= sizeof(buf))) + return -1; + + if (((ret = vsnprintf(buf+len, sizeof(buf)-len, format, va)) < 0) || + ((size_t)((len += ret)+2) > sizeof(buf))) + return -1; + + buf[len++] = '\r'; + buf[len++] = '\n'; + + if (write(vty->wfd, buf, len) < 0) + { + if (ERRNO_IO_RETRY(errno)) + /* Kernel buffer is full, probably too much debugging output, so just + drop the data and ignore. */ + return -1; + /* Fatal I/O error. */ + vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ + zlog_warn("%s: write failed to vty client fd %d, closing: %s", + __func__, vty->fd, safe_strerror(errno)); + buffer_reset(vty->obuf); + /* cannot call vty_close, because a parent routine may still try + to access the vty struct */ + vty->status = VTY_CLOSE; + shutdown(vty->fd, SHUT_RDWR); + return -1; + } + return 0; +} + +/* Output current time to the vty. */ +void +vty_time_print (struct vty *vty, int cr) +{ + char buf[QUAGGA_TIMESTAMP_LEN]; + + if (quagga_timestamp(0, buf, sizeof(buf)) == 0) + { + zlog (NULL, LOG_INFO, "quagga_timestamp error"); + return; + } + if (cr) + vty_out (vty, "%s\n", buf); + else + vty_out (vty, "%s ", buf); + + return; +} + +/* Say hello to vty interface. */ +void +vty_hello (struct vty *vty) +{ + if (host.motdfile) + { + FILE *f; + char buf[4096]; + + f = fopen (host.motdfile, "r"); + if (f) + { + while (fgets (buf, sizeof (buf), f)) + { + char *s; + /* work backwards to ignore trailling isspace() */ + for (s = buf + strlen (buf); (s > buf) && isspace ((int)*(s - 1)); + s--); + *s = '\0'; + vty_out (vty, "%s%s", buf, VTY_NEWLINE); + } + fclose (f); + } + else + vty_out (vty, "MOTD file not found%s", VTY_NEWLINE); + } + else if (host.motd) + vty_out (vty, "%s", host.motd); +} + +/* Put out prompt and wait input from user. */ +static void +vty_prompt (struct vty *vty) +{ + struct utsname names; + const char*hostname; + + if (vty->type == VTY_TERM) + { + hostname = host.name; + if (!hostname) + { + uname (&names); + hostname = names.nodename; + } + vty_out (vty, cmd_prompt (vty->node), hostname); + } +} + +/* Send WILL TELOPT_ECHO to remote server. */ +static void +vty_will_echo (struct vty *vty) +{ + unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' }; + vty_out (vty, "%s", cmd); +} + +/* Make suppress Go-Ahead telnet option. */ +static void +vty_will_suppress_go_ahead (struct vty *vty) +{ + unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' }; + vty_out (vty, "%s", cmd); +} + +/* Make don't use linemode over telnet. */ +static void +vty_dont_linemode (struct vty *vty) +{ + unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' }; + vty_out (vty, "%s", cmd); +} + +/* Use window size. */ +static void +vty_do_window_size (struct vty *vty) +{ + unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' }; + vty_out (vty, "%s", cmd); +} + +#if 0 /* Currently not used. */ +/* Make don't use lflow vty interface. */ +static void +vty_dont_lflow_ahead (struct vty *vty) +{ + unsigned char cmd[] = { IAC, DONT, TELOPT_LFLOW, '\0' }; + vty_out (vty, "%s", cmd); +} +#endif /* 0 */ + +/* Allocate new vty struct. */ +struct vty * +vty_new () +{ + struct vty *new = XCALLOC (MTYPE_VTY, sizeof (struct vty)); + + new->obuf = buffer_new(0); /* Use default buffer size. */ + new->buf = XCALLOC (MTYPE_VTY, VTY_BUFSIZ); + new->max = VTY_BUFSIZ; + + return new; +} + +/* Authentication of vty */ +static void +vty_auth (struct vty *vty, char *buf) +{ + char *passwd = NULL; + enum node_type next_node = 0; + int fail; + char *crypt (const char *, const char *); + + switch (vty->node) + { + case AUTH_NODE: + if (host.encrypt) + passwd = host.password_encrypt; + else + passwd = host.password; + if (host.advanced) + next_node = host.enable ? VIEW_NODE : ENABLE_NODE; + else + next_node = VIEW_NODE; + break; + case AUTH_ENABLE_NODE: + if (host.encrypt) + passwd = host.enable_encrypt; + else + passwd = host.enable; + next_node = ENABLE_NODE; + break; + } + + if (passwd) + { + if (host.encrypt) + fail = strcmp (crypt(buf, passwd), passwd); + else + fail = strcmp (buf, passwd); + } + else + fail = 1; + + if (! fail) + { + vty->fail = 0; + vty->node = next_node; /* Success ! */ + } + else + { + vty->fail++; + if (vty->fail >= 3) + { + if (vty->node == AUTH_NODE) + { + vty_out (vty, "%% Bad passwords, too many failures!%s", VTY_NEWLINE); + vty->status = VTY_CLOSE; + } + else + { + /* AUTH_ENABLE_NODE */ + vty->fail = 0; + vty_out (vty, "%% Bad enable passwords, too many failures!%s", VTY_NEWLINE); + vty->node = restricted_mode ? RESTRICTED_NODE : VIEW_NODE; + } + } + } +} + +/* Command execution over the vty interface. */ +static int +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); + + if (vline == NULL) + return CMD_SUCCESS; + +#ifdef CONSUMED_TIME_CHECK + { + RUSAGE_T before; + RUSAGE_T after; + unsigned long realtime, cputime; + + GETRUSAGE(&before); +#endif /* CONSUMED_TIME_CHECK */ + + ret = cmd_execute_command (vline, vty, NULL, 0); + + /* Get the name of the protocol if any */ + if (zlog_default) + protocolname = zlog_proto_names[zlog_default->protocol]; + else + protocolname = zlog_proto_names[ZLOG_NONE]; + +#ifdef CONSUMED_TIME_CHECK + GETRUSAGE(&after); + if ((realtime = thread_consumed_time(&after, &before, &cputime)) > + CONSUMED_TIME_CHECK) + /* Warn about CPU hog that must be fixed. */ + zlog_warn("SLOW COMMAND: command took %lums (cpu time %lums): %s", + realtime/1000, cputime/1000, buf); + } +#endif /* CONSUMED_TIME_CHECK */ + + if (ret != CMD_SUCCESS) + switch (ret) + { + case CMD_WARNING: + if (vty->type == VTY_FILE) + vty_out (vty, "Warning...%s", VTY_NEWLINE); + break; + case CMD_ERR_AMBIGUOUS: + vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE); + break; + case CMD_ERR_NO_MATCH: + vty_out (vty, "%% [%s] Unknown command: %s%s", protocolname, buf, VTY_NEWLINE); + break; + case CMD_ERR_INCOMPLETE: + vty_out (vty, "%% Command incomplete.%s", VTY_NEWLINE); + break; + } + cmd_free_strvec (vline); + + return ret; +} + +static const char telnet_backward_char = 0x08; +static const char telnet_space_char = ' '; + +/* Basic function to write buffer to vty. */ +static void +vty_write (struct vty *vty, const char *buf, size_t nbytes) +{ + if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE)) + return; + + /* Should we do buffering here ? And make vty_flush (vty) ? */ + buffer_put (vty->obuf, buf, nbytes); +} + +/* 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; + + length = vty->length - vty->cp; + memmove (&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length); + 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_buf_assert (vty); +} + +/* Self insert character 'c' in overwrite mode. */ +static void +vty_self_insert_overwrite (struct vty *vty, char c) +{ + 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 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) +{ + 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. */ +static void +vty_beginning_of_line (struct vty *vty) +{ + while (vty->cp) + vty_backward_char (vty); +} + +/* Move to the end of the line. */ +static void +vty_end_of_line (struct vty *vty) +{ + while (vty->cp < vty->length) + vty_forward_char (vty); +} + +static void vty_kill_line_from_beginning (struct vty *); +static void vty_redraw_line (struct vty *); + +/* Print command line history. This function is called from + vty_next_line and vty_previous_line. */ +static void +vty_history_print (struct vty *vty) +{ + int length; + + vty_kill_line_from_beginning (vty); + + /* Get previous line from history buffer */ + 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); +} + +/* Show next command line history. */ +static void +vty_next_line (struct vty *vty) +{ + int try_index; + + if (vty->hp == vty->hindex) + return; + + /* Try is there history exist or not. */ + try_index = vty->hp; + if (try_index == (VTY_MAXHIST - 1)) + try_index = 0; + else + try_index++; + + /* If there is not history return. */ + if (vty->hist[try_index] == NULL) + return; + else + vty->hp = try_index; + + vty_history_print (vty); +} + +/* Show previous command line history. */ +static void +vty_previous_line (struct vty *vty) +{ + int try_index; + + try_index = vty->hp; + if (try_index == 0) + try_index = VTY_MAXHIST - 1; + else + try_index--; + + if (vty->hist[try_index] == NULL) + return; + else + vty->hp = try_index; + + vty_history_print (vty); +} + +/* This function redraw all of the command line character. */ +static void +vty_redraw_line (struct vty *vty) +{ + vty_write (vty, vty->buf, vty->length); + vty->cp = vty->length; + + vty_buf_assert (vty); +} + +/* Forward word. */ +static void +vty_forward_word (struct vty *vty) +{ + while (vty->cp != vty->length && vty->buf[vty->cp] != ' ') + vty_forward_char (vty); + + while (vty->cp != vty->length && vty->buf[vty->cp] == ' ') + vty_forward_char (vty); +} + +/* Backward word without skipping training space. */ +static void +vty_backward_pure_word (struct vty *vty) +{ + while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ') + vty_backward_char (vty); +} + +/* Backward word. */ +static void +vty_backward_word (struct vty *vty) +{ + while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ') + vty_backward_char (vty); + + while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ') + vty_backward_char (vty); +} + +/* When '^D' is typed at the beginning of the line we move to the down + level. */ +static void +vty_down_level (struct vty *vty) +{ + vty_out (vty, "%s", VTY_NEWLINE); + (*config_exit_cmd.func)(NULL, vty, 0, NULL); + vty_prompt (vty); + vty->cp = 0; +} + +/* When '^Z' is received from vty, move down to the enable mode. */ +static void +vty_end_config (struct vty *vty) +{ + vty_out (vty, "%s", VTY_NEWLINE); + + switch (vty->node) + { + case VIEW_NODE: + case ENABLE_NODE: + case RESTRICTED_NODE: + /* Nothing to do. */ + break; + case CONFIG_NODE: + case INTERFACE_NODE: + case ZEBRA_NODE: + case RIP_NODE: + case RIPNG_NODE: + 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: + case BGP_IPV6M_NODE: + case RMAP_NODE: + case OSPF_NODE: + case OSPF6_NODE: + case ISIS_NODE: + case KEYCHAIN_NODE: + case KEYCHAIN_KEY_NODE: + case MASC_NODE: + case PIM_NODE: + case VTY_NODE: + vty_config_unlock (vty); + vty->node = ENABLE_NODE; + break; + default: + /* Unknown node, we have to ignore it. */ + break; + } + + vty_prompt (vty); + vty->cp = 0; +} + +/* Delete a charcter at the current point. */ +static void +vty_delete_char (struct vty *vty) +{ + int i; + int size; + + if (vty->length == 0) + { + 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--; + memmove (&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1); + vty->buf[vty->length] = '\0'; + + if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE) + return; + + vty_write (vty, &vty->buf[vty->cp], size - 1); + vty_write (vty, &telnet_space_char, 1); + + for (i = 0; i < size; i++) + vty_write (vty, &telnet_backward_char, 1); +} + +/* Delete a character before the point. */ +static void +vty_delete_backward_char (struct vty *vty) +{ + if (vty->cp == 0) + return; + + vty_backward_char (vty); + vty_delete_char (vty); +} + +/* Kill rest of line from current point. */ +static void +vty_kill_line (struct vty *vty) +{ + int i; + int size; + + size = vty->length - vty->cp; + + if (size == 0) + return; + + for (i = 0; i < size; i++) + vty_write (vty, &telnet_space_char, 1); + for (i = 0; i < size; i++) + vty_write (vty, &telnet_backward_char, 1); + + memset (&vty->buf[vty->cp], 0, size); + vty->length = vty->cp; + vty_buf_assert (vty); +} + +/* Kill line from the beginning. */ +static void +vty_kill_line_from_beginning (struct vty *vty) +{ + vty_beginning_of_line (vty); + vty_kill_line (vty); +} + +/* Delete a word before the point. */ +static void +vty_forward_kill_word (struct vty *vty) +{ + while (vty->cp != vty->length && vty->buf[vty->cp] == ' ') + vty_delete_char (vty); + while (vty->cp != vty->length && vty->buf[vty->cp] != ' ') + vty_delete_char (vty); +} + +/* Delete a word before the point. */ +static void +vty_backward_kill_word (struct vty *vty) +{ + while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ') + vty_delete_backward_char (vty); + while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ') + vty_delete_backward_char (vty); +} + +/* Transpose chars before or at the point. */ +static void +vty_transpose_chars (struct vty *vty) +{ + char c1, c2; + + /* If length is short or point is near by the beginning of line then + return. */ + if (vty->length < 2 || vty->cp < 1) + return; + + /* In case of point is located at the end of the line. */ + if (vty->cp == vty->length) + { + c1 = vty->buf[vty->cp - 1]; + c2 = vty->buf[vty->cp - 2]; + + vty_backward_char (vty); + vty_backward_char (vty); + vty_self_insert_overwrite (vty, c1); + vty_self_insert_overwrite (vty, c2); + } + else + { + c1 = vty->buf[vty->cp]; + c2 = vty->buf[vty->cp - 1]; + + vty_backward_char (vty); + vty_self_insert_overwrite (vty, c1); + vty_self_insert_overwrite (vty, c2); + } +} + +/* Do completion at vty interface. */ +static void +vty_complete_command (struct vty *vty) +{ + int i; + int ret; + char **matched = NULL; + vector vline; + + if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE) + return; + + vline = cmd_make_strvec (vty->buf); + if (vline == NULL) + return; + + /* In case of 'help \t'. */ + if (isspace ((int) vty->buf[vty->length - 1])) + vector_set (vline, NULL); + + matched = cmd_complete_command_lib (vline, vty, &ret, 1); + + cmd_free_strvec (vline); + + vty_out (vty, "%s", VTY_NEWLINE); + switch (ret) + { + case CMD_ERR_AMBIGUOUS: + vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE); + vty_prompt (vty); + vty_redraw_line (vty); + break; + case CMD_ERR_NO_MATCH: + /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */ + vty_prompt (vty); + vty_redraw_line (vty); + break; + case CMD_COMPLETE_FULL_MATCH: + vty_prompt (vty); + vty_redraw_line (vty); + vty_backward_pure_word (vty); + vty_insert_word_overwrite (vty, matched[0]); + vty_self_insert (vty, ' '); + XFREE (MTYPE_TMP, matched[0]); + break; + case CMD_COMPLETE_MATCH: + vty_prompt (vty); + vty_redraw_line (vty); + vty_backward_pure_word (vty); + vty_insert_word_overwrite (vty, matched[0]); + XFREE (MTYPE_TMP, matched[0]); + vector_only_index_free (matched); + return; + break; + case CMD_COMPLETE_LIST_MATCH: + for (i = 0; matched[i] != NULL; i++) + { + if (i != 0 && ((i % 6) == 0)) + vty_out (vty, "%s", VTY_NEWLINE); + vty_out (vty, "%-10s ", matched[i]); + XFREE (MTYPE_TMP, matched[i]); + } + vty_out (vty, "%s", VTY_NEWLINE); + + vty_prompt (vty); + vty_redraw_line (vty); + break; + case CMD_ERR_NOTHING_TODO: + vty_prompt (vty); + vty_redraw_line (vty); + break; + default: + break; + } + if (matched) + vector_only_index_free (matched); +} + +static void +vty_describe_fold (struct vty *vty, int cmd_width, + unsigned int desc_width, struct cmd_token *token) +{ + char *buf; + const char *cmd, *p; + int pos; + + cmd = token->cmd[0] == '.' ? token->cmd + 1 : token->cmd; + + if (desc_width <= 0) + { + vty_out (vty, " %-*s %s%s", cmd_width, cmd, token->desc, VTY_NEWLINE); + return; + } + + buf = XCALLOC (MTYPE_TMP, strlen (token->desc) + 1); + + for (p = token->desc; strlen (p) > desc_width; p += pos + 1) + { + for (pos = desc_width; pos > 0; pos--) + if (*(p + pos) == ' ') + break; + + if (pos == 0) + break; + + strncpy (buf, p, pos); + buf[pos] = '\0'; + vty_out (vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE); + + cmd = ""; + } + + vty_out (vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE); + + XFREE (MTYPE_TMP, buf); +} + +/* Describe matched command function. */ +static void +vty_describe_command (struct vty *vty) +{ + int ret; + vector vline; + vector describe; + unsigned int i, width, desc_width; + struct cmd_token *token, *token_cr = NULL; + + vline = cmd_make_strvec (vty->buf); + + /* In case of '> ?'. */ + if (vline == NULL) + { + vline = vector_init (1); + vector_set (vline, NULL); + } + else + if (isspace ((int) vty->buf[vty->length - 1])) + vector_set (vline, NULL); + + describe = cmd_describe_command (vline, vty, &ret); + + vty_out (vty, "%s", VTY_NEWLINE); + + /* Ambiguous error. */ + switch (ret) + { + case CMD_ERR_AMBIGUOUS: + vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE); + goto out; + break; + case CMD_ERR_NO_MATCH: + vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); + goto out; + break; + } + + /* Get width of command string. */ + width = 0; + for (i = 0; i < vector_active (describe); i++) + if ((token = vector_slot (describe, i)) != NULL) + { + unsigned int len; + + if (token->cmd[0] == '\0') + continue; + + len = strlen (token->cmd); + if (token->cmd[0] == '.') + len--; + + if (width < len) + width = len; + } + + /* Get width of description string. */ + desc_width = vty->width - (width + 6); + + /* Print out description. */ + for (i = 0; i < vector_active (describe); i++) + if ((token = vector_slot (describe, i)) != NULL) + { + if (token->cmd[0] == '\0') + continue; + + if (strcmp (token->cmd, command_cr) == 0) + { + token_cr = token; + continue; + } + + if (!token->desc) + vty_out (vty, " %-s%s", + token->cmd[0] == '.' ? token->cmd + 1 : token->cmd, + VTY_NEWLINE); + else if (desc_width >= strlen (token->desc)) + vty_out (vty, " %-*s %s%s", width, + token->cmd[0] == '.' ? token->cmd + 1 : token->cmd, + token->desc, VTY_NEWLINE); + else + vty_describe_fold (vty, width, desc_width, token); + +#if 0 + vty_out (vty, " %-*s %s%s", width + desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, + desc->str ? desc->str : "", VTY_NEWLINE); +#endif /* 0 */ + } + + if ((token = token_cr)) + { + if (!token->desc) + vty_out (vty, " %-s%s", + token->cmd[0] == '.' ? token->cmd + 1 : token->cmd, + VTY_NEWLINE); + else if (desc_width >= strlen (token->desc)) + vty_out (vty, " %-*s %s%s", width, + token->cmd[0] == '.' ? token->cmd + 1 : token->cmd, + token->desc, VTY_NEWLINE); + else + vty_describe_fold (vty, width, desc_width, token); + } + +out: + cmd_free_strvec (vline); + if (describe) + vector_free (describe); + + vty_prompt (vty); + vty_redraw_line (vty); +} + +static void +vty_clear_buf (struct vty *vty) +{ + memset (vty->buf, 0, vty->max); +} + +/* ^C stop current input and do not add command line to the history. */ +static void +vty_stop_input (struct vty *vty) +{ + vty->cp = vty->length = 0; + vty_clear_buf (vty); + vty_out (vty, "%s", VTY_NEWLINE); + + switch (vty->node) + { + case VIEW_NODE: + case ENABLE_NODE: + case RESTRICTED_NODE: + /* Nothing to do. */ + break; + case CONFIG_NODE: + case INTERFACE_NODE: + case ZEBRA_NODE: + case RIP_NODE: + case RIPNG_NODE: + case BABEL_NODE: + case BGP_NODE: + case RMAP_NODE: + case OSPF_NODE: + case OSPF6_NODE: + case ISIS_NODE: + case KEYCHAIN_NODE: + case KEYCHAIN_KEY_NODE: + case MASC_NODE: + case PIM_NODE: + case VTY_NODE: + vty_config_unlock (vty); + vty->node = ENABLE_NODE; + break; + default: + /* Unknown node, we have to ignore it. */ + break; + } + vty_prompt (vty); + + /* Set history pointer to the latest one. */ + vty->hp = vty->hindex; +} + +/* Add current command line to the history buffer. */ +static void +vty_hist_add (struct vty *vty) +{ + int index; + + if (vty->length == 0) + return; + + index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1; + + /* Ignore the same string as previous one. */ + if (vty->hist[index]) + if (strcmp (vty->buf, vty->hist[index]) == 0) + { + vty->hp = vty->hindex; + return; + } + + /* Insert history entry. */ + if (vty->hist[vty->hindex]) + XFREE (MTYPE_VTY_HIST, vty->hist[vty->hindex]); + vty->hist[vty->hindex] = XSTRDUP (MTYPE_VTY_HIST, vty->buf); + + /* History index rotation. */ + vty->hindex++; + if (vty->hindex == VTY_MAXHIST) + vty->hindex = 0; + + vty->hp = vty->hindex; +} + +/* #define TELNET_OPTION_DEBUG */ + +/* Get telnet window size. */ +static int +vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes) +{ +#ifdef TELNET_OPTION_DEBUG + int i; + + for (i = 0; i < nbytes; i++) + { + switch (buf[i]) + { + case IAC: + vty_out (vty, "IAC "); + break; + case WILL: + vty_out (vty, "WILL "); + break; + case WONT: + vty_out (vty, "WONT "); + break; + case DO: + vty_out (vty, "DO "); + break; + case DONT: + vty_out (vty, "DONT "); + break; + case SB: + vty_out (vty, "SB "); + break; + case SE: + vty_out (vty, "SE "); + break; + case TELOPT_ECHO: + vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE); + break; + case TELOPT_SGA: + vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE); + break; + case TELOPT_NAWS: + vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE); + break; + default: + vty_out (vty, "%x ", buf[i]); + break; + } + } + vty_out (vty, "%s", VTY_NEWLINE); + +#endif /* TELNET_OPTION_DEBUG */ + + switch (buf[0]) + { + case SB: + vty->sb_len = 0; + vty->iac_sb_in_progress = 1; + return 0; + break; + case SE: + { + if (!vty->iac_sb_in_progress) + return 0; + + if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0')) + { + vty->iac_sb_in_progress = 0; + return 0; + } + switch (vty->sb_buf[0]) + { + case TELOPT_NAWS: + if (vty->sb_len != TELNET_NAWS_SB_LEN) + zlog_warn("RFC 1073 violation detected: telnet NAWS option " + "should send %d characters, but we received %lu", + TELNET_NAWS_SB_LEN, (u_long)vty->sb_len); + else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN) + zlog_err("Bug detected: sizeof(vty->sb_buf) %lu < %d, " + "too small to handle the telnet NAWS option", + (u_long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN); + else + { + vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]); + vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]); +#ifdef TELNET_OPTION_DEBUG + vty_out(vty, "TELNET NAWS window size negotiation completed: " + "width %d, height %d%s", + vty->width, vty->height, VTY_NEWLINE); +#endif + } + break; + } + vty->iac_sb_in_progress = 0; + return 0; + break; + } + default: + break; + } + return 1; +} + +/* Execute current command line. */ +static int +vty_execute (struct vty *vty) +{ + int ret; + + ret = CMD_SUCCESS; + + switch (vty->node) + { + case AUTH_NODE: + case AUTH_ENABLE_NODE: + vty_auth (vty, vty->buf); + break; + default: + ret = vty_command (vty, vty->buf); + if (vty->type == VTY_TERM) + vty_hist_add (vty); + break; + } + + /* Clear command line buffer. */ + vty->cp = vty->length = 0; + vty_clear_buf (vty); + + if (vty->status != VTY_CLOSE ) + vty_prompt (vty); + + return ret; +} + +#define CONTROL(X) ((X) - '@') +#define VTY_NORMAL 0 +#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 +vty_escape_map (unsigned char c, struct vty *vty) +{ + switch (c) + { + case ('A'): + vty_previous_line (vty); + break; + case ('B'): + vty_next_line (vty); + break; + case ('C'): + vty_forward_char (vty); + break; + case ('D'): + vty_backward_char (vty); + break; + default: + break; + } + + /* Go back to normal mode. */ + vty->escape = VTY_NORMAL; +} + +/* Quit print out to the buffer. */ +static void +vty_buffer_reset (struct vty *vty) +{ + buffer_reset (vty->obuf); + vty_prompt (vty); + vty_redraw_line (vty); +} + +/* Read data via vty socket. */ +static int +vty_read (struct thread *thread) +{ + int i; + int nbytes; + unsigned char buf[VTY_READ_BUFSIZ]; + + int vty_sock = THREAD_FD (thread); + struct vty *vty = THREAD_ARG (thread); + vty->t_read = NULL; + + /* Read raw data from socket */ + if ((nbytes = read (vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) + { + if (nbytes < 0) + { + if (ERRNO_IO_RETRY(errno)) + { + vty_event (VTY_READ, vty_sock, vty); + return 0; + } + 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); + } + vty->status = VTY_CLOSE; + } + + for (i = 0; i < nbytes; i++) + { + if (buf[i] == IAC) + { + if (!vty->iac) + { + vty->iac = 1; + continue; + } + else + { + vty->iac = 0; + } + } + + if (vty->iac_sb_in_progress && !vty->iac) + { + if (vty->sb_len < sizeof(vty->sb_buf)) + vty->sb_buf[vty->sb_len] = buf[i]; + vty->sb_len++; + continue; + } + + if (vty->iac) + { + /* In case of telnet command */ + int ret = 0; + ret = vty_telnet_option (vty, buf + i, nbytes - i); + vty->iac = 0; + i += ret; + continue; + } + + + if (vty->status == VTY_MORE) + { + switch (buf[i]) + { + case CONTROL('C'): + case 'q': + case 'Q': + vty_buffer_reset (vty); + break; +#if 0 /* More line does not work for "show ip bgp". */ + case '\n': + case '\r': + vty->status = VTY_MORELINE; + break; +#endif + default: + break; + } + continue; + } + + /* Escape character. */ + if (vty->escape == VTY_ESCAPE) + { + 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) + { + switch (buf[i]) + { + case '[': + vty->escape = VTY_ESCAPE; + break; + case 'b': + vty_backward_word (vty); + vty->escape = VTY_NORMAL; + break; + case 'f': + vty_forward_word (vty); + vty->escape = VTY_NORMAL; + break; + case 'd': + vty_forward_kill_word (vty); + vty->escape = VTY_NORMAL; + break; + case CONTROL('H'): + case 0x7f: + vty_backward_kill_word (vty); + vty->escape = VTY_NORMAL; + break; + default: + vty->escape = VTY_NORMAL; + break; + } + continue; + } + + switch (buf[i]) + { + case CONTROL('A'): + vty_beginning_of_line (vty); + break; + case CONTROL('B'): + vty_backward_char (vty); + break; + case CONTROL('C'): + vty_stop_input (vty); + break; + case CONTROL('D'): + vty_delete_char (vty); + break; + case CONTROL('E'): + vty_end_of_line (vty); + break; + case CONTROL('F'): + vty_forward_char (vty); + break; + case CONTROL('H'): + case 0x7f: + vty_delete_backward_char (vty); + break; + case CONTROL('K'): + vty_kill_line (vty); + break; + case CONTROL('N'): + vty_next_line (vty); + break; + case CONTROL('P'): + vty_previous_line (vty); + break; + case CONTROL('T'): + vty_transpose_chars (vty); + break; + 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; + case CONTROL('Z'): + vty_end_config (vty); + break; + case '\n': + case '\r': + vty_out (vty, "%s", VTY_NEWLINE); + vty_execute (vty); + break; + case '\t': + vty_complete_command (vty); + break; + case '?': + if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE) + vty_self_insert (vty, buf[i]); + else + vty_describe_command (vty); + break; + case '\033': + if (i + 1 < nbytes && buf[i + 1] == '[') + { + vty->escape = VTY_ESCAPE; + i++; + } + else + vty->escape = VTY_PRE_ESCAPE; + break; + default: + if (buf[i] > 31 && buf[i] < 127) + vty_self_insert (vty, buf[i]); + break; + } + } + + /* Check status. */ + if (vty->status == VTY_CLOSE) + vty_close (vty); + else + { + vty_event (VTY_WRITE, vty->wfd, vty); + vty_event (VTY_READ, vty_sock, vty); + } + return 0; +} + +/* Flush buffer to the vty. */ +static int +vty_flush (struct thread *thread) +{ + int erase; + buffer_status_t flushrc; + int vty_sock = THREAD_FD (thread); + struct vty *vty = THREAD_ARG (thread); + + vty->t_write = NULL; + + /* Tempolary disable read thread. */ + if ((vty->lines == 0) && vty->t_read) + { + thread_cancel (vty->t_read); + vty->t_read = NULL; + } + + /* Function execution continue. */ + 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) || (vty->height == 0)) + flushrc = buffer_flush_available(vty->obuf, vty_sock); + else if (vty->status == VTY_MORELINE) + flushrc = buffer_flush_window(vty->obuf, vty_sock, vty->width, + 1, erase, 0); + else + flushrc = buffer_flush_window(vty->obuf, vty_sock, vty->width, + vty->lines >= 0 ? vty->lines : + vty->height, + erase, 0); + switch (flushrc) + { + case BUFFER_ERROR: + vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ + zlog_warn("buffer_flush failed on vty client fd %d, closing", + vty->fd); + buffer_reset(vty->obuf); + vty_close(vty); + return 0; + case BUFFER_EMPTY: + if (vty->status == VTY_CLOSE) + vty_close (vty); + else + { + vty->status = VTY_NORMAL; + if (vty->lines == 0) + vty_event (VTY_READ, vty_sock, vty); + } + break; + case BUFFER_PENDING: + /* There is more data waiting to be written. */ + vty->status = VTY_MORE; + if (vty->lines == 0) + vty_event (VTY_WRITE, vty_sock, vty); + break; + } + + return 0; +} + +/* allocate and initialise vty */ +static struct vty * +vty_new_init (int vty_sock) +{ + struct vty *vty; + + vty = vty_new (); + vty->fd = vty_sock; + vty->wfd = vty_sock; + vty->type = VTY_TERM; + vty->node = AUTH_NODE; + vty->fail = 0; + vty->cp = 0; + vty_clear_buf (vty); + vty->length = 0; + memset (vty->hist, 0, sizeof (vty->hist)); + vty->hp = 0; + vty->hindex = 0; + vector_set_index (vtyvec, vty_sock, vty); + vty->status = VTY_NORMAL; + 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. */ + if (host.password == NULL && host.password_encrypt == NULL) + { + vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE); + vty->status = VTY_CLOSE; + vty_close (vty); + return NULL; + } + } + + /* Say hello to the world. */ + vty_hello (vty); + if (! no_password_check) + vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + + /* Setting up terminal. */ + vty_will_echo (vty); + vty_will_suppress_go_ahead (vty); + + vty_dont_linemode (vty); + vty_do_window_size (vty); + /* vty_dont_lflow_ahead (vty); */ + + vty_prompt (vty); + + /* Add read/write thread. */ + vty_event (VTY_WRITE, vty_sock, vty); + vty_event (VTY_READ, vty_sock, vty); + + 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) +{ + int vty_sock; + union sockunion su; + int ret; + unsigned int on; + int accept_sock; + struct prefix p; + struct access_list *acl = NULL; + char buf[SU_ADDRSTRLEN]; + + accept_sock = THREAD_FD (thread); + + /* We continue hearing vty socket. */ + vty_event (VTY_SERV, accept_sock, NULL); + + memset (&su, 0, sizeof (union sockunion)); + + /* We can handle IPv4 or IPv6 socket. */ + vty_sock = sockunion_accept (accept_sock, &su); + if (vty_sock < 0) + { + zlog_warn ("can't accept vty socket : %s", safe_strerror (errno)); + return -1; + } + set_nonblocking(vty_sock); + + sockunion2hostprefix (&su, &p); + + /* VTY's accesslist apply. */ + 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)) + { + zlog (NULL, LOG_INFO, "Vty connection refused from %s", + sockunion2str (&su, buf, SU_ADDRSTRLEN)); + close (vty_sock); + + /* continue accepting connections */ + vty_event (VTY_SERV, accept_sock, NULL); + + return 0; + } + } + +#ifdef HAVE_IPV6 + /* VTY's ipv6 accesslist apply. */ + 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)) + { + zlog (NULL, LOG_INFO, "Vty connection refused from %s", + sockunion2str (&su, buf, SU_ADDRSTRLEN)); + close (vty_sock); + + /* continue accepting connections */ + vty_event (VTY_SERV, accept_sock, NULL); + + return 0; + } + } +#endif /* HAVE_IPV6 */ + + on = 1; + ret = setsockopt (vty_sock, IPPROTO_TCP, TCP_NODELAY, + (char *) &on, sizeof (on)); + if (ret < 0) + zlog (NULL, LOG_INFO, "can't set sockopt to vty_sock : %s", + safe_strerror (errno)); + + zlog (NULL, LOG_INFO, "Vty connection from %s", + sockunion2str (&su, buf, SU_ADDRSTRLEN)); + + vty_create (vty_sock, &su); + + return 0; +} + +#ifdef HAVE_IPV6 +static void +vty_serv_sock_addrinfo (const char *hostname, unsigned short port) +{ + int ret; + struct addrinfo req; + struct addrinfo *ainfo; + struct addrinfo *ainfo_save; + int sock; + char port_str[BUFSIZ]; + + memset (&req, 0, sizeof (struct addrinfo)); + req.ai_flags = AI_PASSIVE; + req.ai_family = AF_UNSPEC; + req.ai_socktype = SOCK_STREAM; + sprintf (port_str, "%d", port); + port_str[sizeof (port_str) - 1] = '\0'; + + ret = getaddrinfo (hostname, port_str, &req, &ainfo); + + if (ret != 0) + { + fprintf (stderr, "getaddrinfo failed: %s\n", gai_strerror (ret)); + exit (1); + } + + ainfo_save = ainfo; + + do + { + if (ainfo->ai_family != AF_INET +#ifdef HAVE_IPV6 + && ainfo->ai_family != AF_INET6 +#endif /* HAVE_IPV6 */ + ) + continue; + + sock = socket (ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol); + if (sock < 0) + continue; + + sockopt_v6only (ainfo->ai_family, sock); + sockopt_reuseaddr (sock); + sockopt_reuseport (sock); + + ret = bind (sock, ainfo->ai_addr, ainfo->ai_addrlen); + if (ret < 0) + { + close (sock); /* Avoid sd leak. */ + continue; + } + + ret = listen (sock, 3); + if (ret < 0) + { + close (sock); /* Avoid sd leak. */ + continue; + } + + vty_event (VTY_SERV, sock, NULL); + } + while ((ainfo = ainfo->ai_next) != NULL); + + freeaddrinfo (ainfo_save); +} +#else /* HAVE_IPV6 */ + +/* Make vty server socket. */ +static void +vty_serv_sock_family (const char* addr, unsigned short port, int family) +{ + int ret; + union sockunion su; + int accept_sock; + void* naddr=NULL; + + memset (&su, 0, sizeof (union sockunion)); + su.sa.sa_family = family; + if(addr) + switch(family) + { + case AF_INET: + naddr=&su.sin.sin_addr; + break; +#ifdef HAVE_IPV6 + case AF_INET6: + naddr=&su.sin6.sin6_addr; + break; +#endif + } + + if(naddr) + switch(inet_pton(family,addr,naddr)) + { + case -1: + zlog_err("bad address %s",addr); + naddr=NULL; + break; + case 0: + zlog_err("error translating address %s: %s",addr,safe_strerror(errno)); + naddr=NULL; + } + + /* Make new socket. */ + accept_sock = sockunion_stream_socket (&su); + if (accept_sock < 0) + return; + + /* This is server, so reuse address. */ + sockopt_reuseaddr (accept_sock); + sockopt_reuseport (accept_sock); + + /* Bind socket to universal address and given port. */ + ret = sockunion_bind (accept_sock, &su, port, naddr); + if (ret < 0) + { + zlog_warn("can't bind socket"); + close (accept_sock); /* Avoid sd leak. */ + return; + } + + /* Listen socket under queue 3. */ + ret = listen (accept_sock, 3); + if (ret < 0) + { + zlog (NULL, LOG_WARNING, "can't listen socket"); + close (accept_sock); /* Avoid sd leak. */ + return; + } + + /* Add vty server event. */ + vty_event (VTY_SERV, accept_sock, NULL); +} +#endif /* HAVE_IPV6 */ + +#ifdef VTYSH +/* For sockaddr_un. */ +#include + +/* VTY shell UNIX domain socket. */ +static void +vty_serv_un (const char *path) +{ + int ret; + int sock, len; + struct sockaddr_un serv; + mode_t old_mask; + struct zprivs_ids_t ids; + + /* First of all, unlink existing socket */ + unlink (path); + + /* Set umask */ + old_mask = umask (0007); + + /* Make UNIX domain socket. */ + sock = socket (AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) + { + zlog_err("Cannot create unix stream socket: %s", safe_strerror(errno)); + return; + } + + /* Make server socket. */ + memset (&serv, 0, sizeof (struct sockaddr_un)); + serv.sun_family = AF_UNIX; + strncpy (serv.sun_path, path, strlen (path)); +#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN + len = serv.sun_len = SUN_LEN(&serv); +#else + len = sizeof (serv.sun_family) + strlen (serv.sun_path); +#endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */ + + ret = bind (sock, (struct sockaddr *) &serv, len); + if (ret < 0) + { + zlog_err("Cannot bind path %s: %s", path, safe_strerror(errno)); + close (sock); /* Avoid sd leak. */ + return; + } + + ret = listen (sock, 5); + if (ret < 0) + { + zlog_err("listen(fd %d) failed: %s", sock, safe_strerror(errno)); + close (sock); /* Avoid sd leak. */ + return; + } + + umask (old_mask); + + zprivs_get_ids(&ids); + + if (ids.gid_vty > 0) + { + /* set group of socket */ + if ( chown (path, -1, ids.gid_vty) ) + { + zlog_err ("vty_serv_un: could chown socket, %s", + safe_strerror (errno) ); + } + } + + vty_event (VTYSH_SERV, sock, NULL); +} + +/* #define VTYSH_DEBUG 1 */ + +static int +vtysh_accept (struct thread *thread) +{ + int accept_sock; + int sock; + int client_len; + struct sockaddr_un client; + struct vty *vty; + + accept_sock = THREAD_FD (thread); + + vty_event (VTYSH_SERV, accept_sock, NULL); + + memset (&client, 0, sizeof (struct sockaddr_un)); + client_len = sizeof (struct sockaddr_un); + + sock = accept (accept_sock, (struct sockaddr *) &client, + (socklen_t *) &client_len); + + if (sock < 0) + { + zlog_warn ("can't accept vty socket : %s", safe_strerror (errno)); + return -1; + } + + if (set_nonblocking(sock) < 0) + { + zlog_warn ("vtysh_accept: could not set vty socket %d to non-blocking," + " %s, closing", sock, safe_strerror (errno)); + close (sock); + return -1; + } + +#ifdef VTYSH_DEBUG + printf ("VTY shell accept\n"); +#endif /* VTYSH_DEBUG */ + + vty = vty_new (); + vty->fd = sock; + vty->wfd = sock; + vty->type = VTY_SHELL_SERV; + vty->node = VIEW_NODE; + + vty_event (VTYSH_READ, sock, vty); + + return 0; +} + +static int +vtysh_flush(struct vty *vty) +{ + switch (buffer_flush_available(vty->obuf, vty->wfd)) + { + case BUFFER_PENDING: + vty_event(VTYSH_WRITE, vty->wfd, vty); + break; + case BUFFER_ERROR: + vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ + zlog_warn("%s: write error to fd %d, closing", __func__, vty->fd); + buffer_reset(vty->obuf); + vty_close(vty); + return -1; + break; + case BUFFER_EMPTY: + break; + } + return 0; +} + +static int +vtysh_read (struct thread *thread) +{ + int ret; + int sock; + int nbytes; + struct vty *vty; + unsigned char buf[VTY_READ_BUFSIZ]; + unsigned char *p; + u_char header[4] = {0, 0, 0, 0}; + + sock = THREAD_FD (thread); + vty = THREAD_ARG (thread); + vty->t_read = NULL; + + if ((nbytes = read (sock, buf, VTY_READ_BUFSIZ)) <= 0) + { + if (nbytes < 0) + { + if (ERRNO_IO_RETRY(errno)) + { + vty_event (VTYSH_READ, sock, vty); + return 0; + } + vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ + zlog_warn("%s: read failed on vtysh client fd %d, closing: %s", + __func__, sock, safe_strerror(errno)); + } + buffer_reset(vty->obuf); + vty_close (vty); +#ifdef VTYSH_DEBUG + printf ("close vtysh\n"); +#endif /* VTYSH_DEBUG */ + return 0; + } + +#ifdef VTYSH_DEBUG + 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->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 + vty->length to 0. */ + + /* Return result. */ +#ifdef VTYSH_DEBUG + printf ("result: %d\n", ret); + printf ("vtysh node: %d\n", vty->node); +#endif /* VTYSH_DEBUG */ + + header[3] = ret; + buffer_put(vty->obuf, header, 4); + + if (!vty->t_write && (vtysh_flush(vty) < 0)) + /* Try to flush results; exit if a write error occurs. */ + return 0; + } + } + +out: + vty_event (VTYSH_READ, sock, vty); + + return 0; +} + +static int +vtysh_write (struct thread *thread) +{ + struct vty *vty = THREAD_ARG (thread); + + vty->t_write = NULL; + vtysh_flush(vty); + return 0; +} + +#endif /* VTYSH */ + +/* Determine address family to bind. */ +void +vty_serv_sock (const char *addr, unsigned short port, const char *path) +{ + /* If port is set to 0, do not listen on TCP/IP at all! */ + if (port) + { + +#ifdef HAVE_IPV6 + vty_serv_sock_addrinfo (addr, port); +#else /* ! HAVE_IPV6 */ + vty_serv_sock_family (addr,port, AF_INET); +#endif /* HAVE_IPV6 */ + } + +#ifdef VTYSH + vty_serv_un (path); +#endif /* VTYSH */ +} + +/* Close vty interface. Warning: call this only from functions that + will be careful not to access the vty afterwards (since it has + now been freed). This is safest from top-level functions (called + directly by the thread dispatcher). */ +void +vty_close (struct vty *vty) +{ + int i; + + /* Cancel threads.*/ + if (vty->t_read) + thread_cancel (vty->t_read); + if (vty->t_write) + thread_cancel (vty->t_write); + if (vty->t_timeout) + thread_cancel (vty->t_timeout); + + /* Flush buffer. */ + buffer_flush_all (vty->obuf, vty->wfd); + + /* Free input buffer. */ + buffer_free (vty->obuf); + + /* Free command history. */ + for (i = 0; i < VTY_MAXHIST; i++) + if (vty->hist[i]) + XFREE (MTYPE_VTY_HIST, vty->hist[i]); + + /* Unset vector. */ + vector_unset (vtyvec, vty->fd); + + /* Close socket. */ + if (vty->fd > 0) + close (vty->fd); + else + vty_stdio_reset (); + + if (vty->buf) + XFREE (MTYPE_VTY, vty->buf); + + /* Check configure. */ + vty_config_unlock (vty); + + /* OK free vty. */ + XFREE (MTYPE_VTY, vty); +} + +/* When time out occur output message then close connection. */ +static int +vty_timeout (struct thread *thread) +{ + struct vty *vty; + + vty = THREAD_ARG (thread); + vty->t_timeout = NULL; + vty->v_timeout = 0; + + /* Clear buffer*/ + buffer_reset (vty->obuf); + vty_out (vty, "%sVty connection is timed out.%s", VTY_NEWLINE, VTY_NEWLINE); + + /* Close connection. */ + vty->status = VTY_CLOSE; + vty_close (vty); + + return 0; +} + +/* Read up configuration file from file_name. */ +static void +vty_read_file (FILE *confp) +{ + int ret; + struct vty *vty; + unsigned int line_num = 0; + + vty = vty_new (); + 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, &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, "*** Error reading config: Ambiguous command.\n"); + break; + case CMD_ERR_NO_MATCH: + fprintf (stderr, "*** Error reading config: There is no such command.\n"); + break; + } + fprintf (stderr, "*** Error occured processing line %u, below:\n%s\n", + line_num, vty->buf); + vty_close (vty); + exit (1); + } + + vty_close (vty); +} + +static FILE * +vty_use_backup_config (char *fullpath) +{ + char *fullpath_sav, *fullpath_tmp; + FILE *ret = NULL; + struct stat buf; + int tmp, sav; + int c; + char buffer[512]; + + fullpath_sav = malloc (strlen (fullpath) + strlen (CONF_BACKUP_EXT) + 1); + strcpy (fullpath_sav, fullpath); + strcat (fullpath_sav, CONF_BACKUP_EXT); + if (stat (fullpath_sav, &buf) == -1) + { + free (fullpath_sav); + return NULL; + } + + fullpath_tmp = malloc (strlen (fullpath) + 8); + sprintf (fullpath_tmp, "%s.XXXXXX", fullpath); + + /* Open file to configuration write. */ + tmp = mkstemp (fullpath_tmp); + if (tmp < 0) + { + free (fullpath_sav); + free (fullpath_tmp); + return NULL; + } + + sav = open (fullpath_sav, O_RDONLY); + if (sav < 0) + { + unlink (fullpath_tmp); + free (fullpath_sav); + free (fullpath_tmp); + return NULL; + } + + while((c = read (sav, buffer, 512)) > 0) + write (tmp, buffer, c); + + close (sav); + close (tmp); + + if (chmod(fullpath_tmp, CONFIGFILE_MASK) != 0) + { + unlink (fullpath_tmp); + free (fullpath_sav); + free (fullpath_tmp); + return NULL; + } + + if (link (fullpath_tmp, fullpath) == 0) + ret = fopen (fullpath, "r"); + + unlink (fullpath_tmp); + + free (fullpath_sav); + free (fullpath_tmp); + return ret; +} + +/* Read up configuration file from file_name. */ +void +vty_read_config (char *config_file, + char *config_default_dir) +{ + char cwd[MAXPATHLEN]; + FILE *confp = NULL; + char *fullpath; + char *tmp = NULL; + + /* If -f flag specified. */ + if (config_file != NULL) + { + if (! IS_DIRECTORY_SEP (config_file[0])) + { + getcwd (cwd, MAXPATHLEN); + tmp = XMALLOC (MTYPE_TMP, + strlen (cwd) + strlen (config_file) + 2); + sprintf (tmp, "%s/%s", cwd, config_file); + fullpath = tmp; + } + else + fullpath = config_file; + + confp = fopen (fullpath, "r"); + + if (confp == NULL) + { + fprintf (stderr, "%s: failed to open configuration file %s: %s\n", + __func__, fullpath, safe_strerror (errno)); + + confp = vty_use_backup_config (fullpath); + if (confp) + fprintf (stderr, "WARNING: using backup configuration file!\n"); + else + { + fprintf (stderr, "can't open configuration file [%s]\n", + config_file); + exit(1); + } + } + } + else + { +#ifdef VTYSH + int ret; + struct stat conf_stat; + + /* !!!!PLEASE LEAVE!!!! + * This is NEEDED for use with vtysh -b, or else you can get + * a real configuration food fight with a lot garbage in the + * merged configuration file it creates coming from the per + * daemon configuration files. This also allows the daemons + * to start if there default configuration file is not + * present or ignore them, as needed when using vtysh -b to + * configure the daemons at boot - MAG + */ + + /* Stat for vtysh Zebra.conf, if found startup and wait for + * boot configuration + */ + + if ( strstr(config_default_dir, "vtysh") == NULL) + { + ret = stat (integrate_default, &conf_stat); + if (ret >= 0) + return; + } +#endif /* VTYSH */ + + confp = fopen (config_default_dir, "r"); + if (confp == NULL) + { + fprintf (stderr, "%s: failed to open configuration file %s: %s\n", + __func__, config_default_dir, safe_strerror (errno)); + + confp = vty_use_backup_config (config_default_dir); + if (confp) + { + fprintf (stderr, "WARNING: using backup configuration file!\n"); + fullpath = config_default_dir; + } + else + { + fprintf (stderr, "can't open configuration file [%s]\n", + config_default_dir); + exit (1); + } + } + else + fullpath = config_default_dir; + } + + vty_read_file (confp); + + fclose (confp); + + host_config_set (fullpath); + + if (tmp) + XFREE (MTYPE_TMP, fullpath); +} + +/* Small utility function which output log to the VTY. */ +void +vty_log (const char *level, const char *proto_str, + const char *format, struct timestamp_control *ctl, va_list va) +{ + unsigned int i; + struct vty *vty; + + if (!vtyvec) + return; + + for (i = 0; i < vector_active (vtyvec); i++) + if ((vty = vector_slot (vtyvec, i)) != NULL) + if (vty->monitor) + { + va_list ac; + va_copy(ac, va); + vty_log_out (vty, level, proto_str, format, ctl, ac); + va_end(ac); + } +} + +/* Async-signal-safe version of vty_log for fixed strings. */ +void +vty_log_fixed (char *buf, size_t len) +{ + unsigned int i; + struct iovec iov[2]; + + /* vty may not have been initialised */ + if (!vtyvec) + return; + + iov[0].iov_base = buf; + iov[0].iov_len = len; + iov[1].iov_base = (void *)"\r\n"; + iov[1].iov_len = 2; + + for (i = 0; i < vector_active (vtyvec); i++) + { + struct vty *vty; + 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->wfd, iov, 2); + } +} + +int +vty_config_lock (struct vty *vty) +{ + if (vty_config == 0) + { + vty->config = 1; + vty_config = 1; + } + return vty->config; +} + +int +vty_config_unlock (struct vty *vty) +{ + if (vty_config == 1 && vty->config == 1) + { + vty->config = 0; + vty_config = 0; + } + return vty->config; +} + +/* Master of the threads. */ +static struct thread_master *vty_master; + +static void +vty_event (enum event event, int sock, struct vty *vty) +{ + struct thread *vty_serv_thread; + + switch (event) + { + case VTY_SERV: + 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: + 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 (vty_master, vtysh_read, vty, sock); + break; + case VTYSH_WRITE: + vty->t_write = thread_add_write (vty_master, vtysh_write, vty, sock); + break; +#endif /* VTYSH */ + case VTY_READ: + vty->t_read = thread_add_read (vty_master, vty_read, vty, sock); + + /* Time out treatment. */ + if (vty->v_timeout) + { + if (vty->t_timeout) + thread_cancel (vty->t_timeout); + vty->t_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 (vty_master, vty_flush, vty, sock); + break; + case VTY_TIMEOUT_RESET: + if (vty->t_timeout) + { + thread_cancel (vty->t_timeout); + vty->t_timeout = NULL; + } + if (vty->v_timeout) + { + vty->t_timeout = + thread_add_timer (vty_master, vty_timeout, vty, vty->v_timeout); + } + break; + } +} + +DEFUN (who, + who_cmd, + "who", + "Display who is on vty\n") +{ + unsigned int i; + struct vty *v; + + for (i = 0; i < vector_active (vtyvec); i++) + if ((v = vector_slot (vtyvec, i)) != NULL) + vty_out (vty, "%svty[%d] connected from %s.%s", + v->config ? "*" : " ", + i, v->address, VTY_NEWLINE); + return CMD_SUCCESS; +} + +/* Move to vty configuration mode. */ +DEFUN (line_vty, + line_vty_cmd, + "line vty", + "Configure a terminal line\n" + "Virtual terminal\n") +{ + vty->node = VTY_NODE; + return CMD_SUCCESS; +} + +/* Set time out value. */ +static int +exec_timeout (struct vty *vty, const char *min_str, const char *sec_str) +{ + unsigned long timeout = 0; + + /* min_str and sec_str are already checked by parser. So it must be + all digit string. */ + if (min_str) + { + timeout = strtol (min_str, NULL, 10); + timeout *= 60; + } + if (sec_str) + timeout += strtol (sec_str, NULL, 10); + + vty_timeout_val = timeout; + vty->v_timeout = timeout; + vty_event (VTY_TIMEOUT_RESET, 0, vty); + + + return CMD_SUCCESS; +} + +DEFUN (exec_timeout_min, + exec_timeout_min_cmd, + "exec-timeout <0-35791>", + "Set timeout value\n" + "Timeout value in minutes\n") +{ + return exec_timeout (vty, argv[0], NULL); +} + +DEFUN (exec_timeout_sec, + exec_timeout_sec_cmd, + "exec-timeout <0-35791> <0-2147483>", + "Set the EXEC timeout\n" + "Timeout in minutes\n" + "Timeout in seconds\n") +{ + return exec_timeout (vty, argv[0], argv[1]); +} + +DEFUN (no_exec_timeout, + no_exec_timeout_cmd, + "no exec-timeout", + NO_STR + "Set the EXEC timeout\n") +{ + return exec_timeout (vty, NULL, NULL); +} + +/* Set vty access class. */ +DEFUN (vty_access_class, + vty_access_class_cmd, + "access-class WORD", + "Filter connections based on an IP access list\n" + "IP access list\n") +{ + if (vty_accesslist_name) + XFREE(MTYPE_VTY, vty_accesslist_name); + + vty_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]); + + return CMD_SUCCESS; +} + +/* Clear vty access class. */ +DEFUN (no_vty_access_class, + no_vty_access_class_cmd, + "no access-class [WORD]", + NO_STR + "Filter connections based on an IP access list\n" + "IP access list\n") +{ + if (! vty_accesslist_name || (argc && strcmp(vty_accesslist_name, argv[0]))) + { + vty_out (vty, "Access-class is not currently applied to vty%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + XFREE(MTYPE_VTY, vty_accesslist_name); + + vty_accesslist_name = NULL; + + return CMD_SUCCESS; +} + +#ifdef HAVE_IPV6 +/* Set vty access class. */ +DEFUN (vty_ipv6_access_class, + vty_ipv6_access_class_cmd, + "ipv6 access-class WORD", + IPV6_STR + "Filter connections based on an IP access list\n" + "IPv6 access list\n") +{ + if (vty_ipv6_accesslist_name) + XFREE(MTYPE_VTY, vty_ipv6_accesslist_name); + + vty_ipv6_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]); + + return CMD_SUCCESS; +} + +/* Clear vty access class. */ +DEFUN (no_vty_ipv6_access_class, + no_vty_ipv6_access_class_cmd, + "no ipv6 access-class [WORD]", + NO_STR + IPV6_STR + "Filter connections based on an IP access list\n" + "IPv6 access list\n") +{ + if (! vty_ipv6_accesslist_name || + (argc && strcmp(vty_ipv6_accesslist_name, argv[0]))) + { + vty_out (vty, "IPv6 access-class is not currently applied to vty%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + XFREE(MTYPE_VTY, vty_ipv6_accesslist_name); + + vty_ipv6_accesslist_name = NULL; + + return CMD_SUCCESS; +} +#endif /* HAVE_IPV6 */ + +/* vty login. */ +DEFUN (vty_login, + vty_login_cmd, + "login", + "Enable password checking\n") +{ + no_password_check = 0; + return CMD_SUCCESS; +} + +DEFUN (no_vty_login, + no_vty_login_cmd, + "no login", + NO_STR + "Enable password checking\n") +{ + no_password_check = 1; + return CMD_SUCCESS; +} + +/* initial mode. */ +DEFUN (vty_restricted_mode, + vty_restricted_mode_cmd, + "anonymous restricted", + "Restrict view commands available in anonymous, unauthenticated vty\n") +{ + restricted_mode = 1; + return CMD_SUCCESS; +} + +DEFUN (vty_no_restricted_mode, + vty_no_restricted_mode_cmd, + "no anonymous restricted", + NO_STR + "Enable password checking\n") +{ + restricted_mode = 0; + return CMD_SUCCESS; +} + +DEFUN (service_advanced_vty, + service_advanced_vty_cmd, + "service advanced-vty", + "Set up miscellaneous service\n" + "Enable advanced mode vty interface\n") +{ + host.advanced = 1; + return CMD_SUCCESS; +} + +DEFUN (no_service_advanced_vty, + no_service_advanced_vty_cmd, + "no service advanced-vty", + NO_STR + "Set up miscellaneous service\n" + "Enable advanced mode vty interface\n") +{ + host.advanced = 0; + return CMD_SUCCESS; +} + +DEFUN (terminal_monitor, + terminal_monitor_cmd, + "terminal monitor", + "Set terminal line parameters\n" + "Copy debug output to the current terminal line\n") +{ + vty->monitor = 1; + return CMD_SUCCESS; +} + +DEFUN (terminal_no_monitor, + terminal_no_monitor_cmd, + "terminal no monitor", + "Set terminal line parameters\n" + NO_STR + "Copy debug output to the current terminal line\n") +{ + vty->monitor = 0; + return CMD_SUCCESS; +} + +ALIAS (terminal_no_monitor, + no_terminal_monitor_cmd, + "no terminal monitor", + NO_STR + "Set terminal line parameters\n" + "Copy debug output to the current terminal line\n") + +DEFUN (show_history, + show_history_cmd, + "show history", + SHOW_STR + "Display the session command history\n") +{ + int index; + + for (index = vty->hindex + 1; index != vty->hindex;) + { + if (index == VTY_MAXHIST) + { + index = 0; + continue; + } + + if (vty->hist[index] != NULL) + vty_out (vty, " %s%s", vty->hist[index], VTY_NEWLINE); + + index++; + } + + 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) +{ + vty_out (vty, "line vty%s", VTY_NEWLINE); + + if (vty_accesslist_name) + vty_out (vty, " access-class %s%s", + vty_accesslist_name, VTY_NEWLINE); + + if (vty_ipv6_accesslist_name) + vty_out (vty, " ipv6 access-class %s%s", + vty_ipv6_accesslist_name, VTY_NEWLINE); + + /* exec-timeout */ + if (vty_timeout_val != VTY_TIMEOUT_DEFAULT) + vty_out (vty, " exec-timeout %ld %ld%s", + vty_timeout_val / 60, + vty_timeout_val % 60, VTY_NEWLINE); + + /* login */ + if (no_password_check) + vty_out (vty, " no login%s", VTY_NEWLINE); + + if (restricted_mode != restricted_mode_default) + { + if (restricted_mode_default) + vty_out (vty, " no anonymous restricted%s", VTY_NEWLINE); + else + 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; +} + +struct cmd_node vty_node = +{ + VTY_NODE, + "%s(config-line)# ", + 1, +}; + +/* Reset all VTY status. */ +void +vty_reset () +{ + unsigned int i; + struct vty *vty; + struct thread *vty_serv_thread; + + for (i = 0; i < vector_active (vtyvec); i++) + if ((vty = vector_slot (vtyvec, i)) != NULL) + { + buffer_reset (vty->obuf); + vty->status = VTY_CLOSE; + vty_close (vty); + } + + for (i = 0; i < vector_active (Vvty_serv_thread); i++) + if ((vty_serv_thread = vector_slot (Vvty_serv_thread, i)) != NULL) + { + thread_cancel (vty_serv_thread); + vector_slot (Vvty_serv_thread, i) = NULL; + close (i); + } + + vty_timeout_val = VTY_TIMEOUT_DEFAULT; + + if (vty_accesslist_name) + { + XFREE(MTYPE_VTY, vty_accesslist_name); + vty_accesslist_name = NULL; + } + + if (vty_ipv6_accesslist_name) + { + XFREE(MTYPE_VTY, vty_ipv6_accesslist_name); + vty_ipv6_accesslist_name = NULL; + } +} + +static void +vty_save_cwd (void) +{ + char cwd[MAXPATHLEN]; + char *c; + + c = getcwd (cwd, MAXPATHLEN); + + if (!c) + { + chdir (SYSCONFDIR); + getcwd (cwd, MAXPATHLEN); + } + + vty_cwd = XMALLOC (MTYPE_TMP, strlen (cwd) + 1); + strcpy (vty_cwd, cwd); +} + +char * +vty_get_cwd () +{ + return vty_cwd; +} + +int +vty_shell (struct vty *vty) +{ + return vty->type == VTY_SHELL ? 1 : 0; +} + +int +vty_shell_serv (struct vty *vty) +{ + return vty->type == VTY_SHELL_SERV ? 1 : 0; +} + +void +vty_init_vtysh () +{ + vtyvec = vector_init (VECTOR_MIN_SIZE); +} + +/* Install vty's own commands like `who' command. */ +void +vty_init (struct thread_master *master_thread) +{ + /* For further configuration read, preserve current directory. */ + vty_save_cwd (); + + vtyvec = vector_init (VECTOR_MIN_SIZE); + + vty_master = master_thread; + + atexit (vty_stdio_reset); + + /* Initilize server thread vector. */ + Vvty_serv_thread = vector_init (VECTOR_MIN_SIZE); + + /* Install bgp top node. */ + install_node (&vty_node, vty_config_write); + + install_element (RESTRICTED_NODE, &who_cmd); + install_element (RESTRICTED_NODE, &show_history_cmd); + install_element (VIEW_NODE, &who_cmd); + install_element (VIEW_NODE, &show_history_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_default (VTY_NODE); + install_element (VTY_NODE, &exec_timeout_min_cmd); + install_element (VTY_NODE, &exec_timeout_sec_cmd); + install_element (VTY_NODE, &no_exec_timeout_cmd); + install_element (VTY_NODE, &vty_access_class_cmd); + install_element (VTY_NODE, &no_vty_access_class_cmd); + install_element (VTY_NODE, &vty_login_cmd); + install_element (VTY_NODE, &no_vty_login_cmd); + install_element (VTY_NODE, &vty_restricted_mode_cmd); + install_element (VTY_NODE, &vty_no_restricted_mode_cmd); +#ifdef HAVE_IPV6 + install_element (VTY_NODE, &vty_ipv6_access_class_cmd); + install_element (VTY_NODE, &no_vty_ipv6_access_class_cmd); +#endif /* HAVE_IPV6 */ +} + +void +vty_terminate (void) +{ + if (vty_cwd) + XFREE (MTYPE_TMP, vty_cwd); + + if (vtyvec && Vvty_serv_thread) + { + vty_reset (); + vector_free (vtyvec); + vector_free (Vvty_serv_thread); + } +} diff --git a/lib/vty.h b/lib/vty.h new file mode 100644 index 0000000..1e3b124 --- /dev/null +++ b/lib/vty.h @@ -0,0 +1,258 @@ +/* Virtual terminal [aka TeletYpe] interface routine + 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. */ + +#ifndef _ZEBRA_VTY_H +#define _ZEBRA_VTY_H + +#include "thread.h" +#include "log.h" +#include "sockunion.h" + +#define VTY_MAXHIST 20 + +/* VTY struct. */ +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; + + /* Failure count */ + int fail; + + /* Output buffer. */ + struct buffer *obuf; + + /* Command input buffer */ + char *buf; + + /* Command cursor point */ + int cp; + + /* Command length */ + int length; + + /* Command max length. */ + int max; + + /* Histry of command */ + char *hist[VTY_MAXHIST]; + + /* History lookup current point */ + int hp; + + /* History insert end point */ + int hindex; + + /* For current referencing point of interface, route-map, + access-list etc... */ + void *index; + + /* For multiple level index treatment such as key chain and key. */ + void *index_sub; + + /* For escape character. */ + unsigned char escape; + + /* Current vty status. */ + enum {VTY_NORMAL, VTY_CLOSE, VTY_MORE, VTY_MORELINE} status; + + /* IAC handling: was the last character received the + IAC (interpret-as-command) escape character (and therefore the next + character will be the command code)? Refer to Telnet RFC 854. */ + unsigned char iac; + + /* IAC SB (option subnegotiation) handling */ + unsigned char iac_sb_in_progress; + /* At the moment, we care only about the NAWS (window size) negotiation, + and that requires just a 5-character buffer (RFC 1073): + <16-bit width> <16-bit height> */ +#define TELNET_NAWS_SB_LEN 5 + unsigned char sb_buf[TELNET_NAWS_SB_LEN]; + /* How many subnegotiation characters have we received? We just drop + those that do not fit in the buffer. */ + size_t sb_len; + + /* Window width/height. */ + int width; + int height; + + /* Configure lines. */ + int lines; + + /* Terminal monitor. */ + int monitor; + + /* In configure mode. */ + int config; + + /* Read and write thread. */ + struct thread *t_read; + struct thread *t_write; + + /* 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. */ +#define INTEGRATE_DEFAULT_CONFIG "Quagga.conf" + +/* Small macro to determine newline is newline only or linefeed needed. */ +#define VTY_NEWLINE ((vty->type == VTY_TERM) ? "\r\n" : "\n") + +/* Default time out value */ +#define VTY_TIMEOUT_DEFAULT 600 + +/* Vty read buffer size. */ +#define VTY_READ_BUFSIZ 512 + +/* Directory separator. */ +#ifndef DIRECTORY_SEP +#define DIRECTORY_SEP '/' +#endif /* DIRECTORY_SEP */ + +#ifndef IS_DIRECTORY_SEP +#define IS_DIRECTORY_SEP(c) ((c) == DIRECTORY_SEP) +#endif + +/* GCC have printf type attribute check. */ +#ifdef __GNUC__ +#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b))) +#else +#define PRINTF_ATTRIBUTE(a,b) +#endif /* __GNUC__ */ + +/* Utility macros to convert VTY argument to unsigned long */ +#define VTY_GET_ULONG(NAME,V,STR) \ +do { \ + char *endptr = NULL; \ + errno = 0; \ + (V) = strtoul ((STR), &endptr, 10); \ + if (*(STR) == '-' || *endptr != '\0' || errno) \ + { \ + vty_out (vty, "%% Invalid %s value%s", NAME, VTY_NEWLINE); \ + return CMD_WARNING; \ + } \ +} while (0) + +/* + * 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_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 { \ + int retv; \ + retv = inet_aton ((STR), &(V)); \ + if (!retv) \ + { \ + vty_out (vty, "%% Invalid %s value%s", NAME, VTY_NEWLINE); \ + return CMD_WARNING; \ + } \ +} while (0) + +#define VTY_GET_IPV4_PREFIX(NAME,V,STR) \ +do { \ + int retv; \ + retv = str2prefix_ipv4 ((STR), &(V)); \ + if (retv <= 0) \ + { \ + vty_out (vty, "%% Invalid %s value%s", NAME, VTY_NEWLINE); \ + return CMD_WARNING; \ + } \ +} 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[]; + +/* Prototypes. */ +extern void vty_init (struct thread_master *); +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); +extern void vty_serv_sock (const char *, unsigned short, const char *); +extern void vty_close (struct vty *); +extern char *vty_get_cwd (void); +extern void vty_log (const char *level, const char *proto, + const char *fmt, struct timestamp_control *, va_list); +extern int vty_config_lock (struct vty *); +extern int vty_config_unlock (struct vty *); +extern int vty_shell (struct vty *); +extern int vty_shell_serv (struct vty *); +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 (char *buf, size_t len); + +#endif /* _ZEBRA_VTY_H */ diff --git a/lib/workqueue.c b/lib/workqueue.c new file mode 100644 index 0000000..6453e7b --- /dev/null +++ b/lib/workqueue.c @@ -0,0 +1,408 @@ +/* + * Quagga Work Queue Support. + * + * Copyright (C) 2005 Sun Microsystems, Inc. + * + * This file is part of GNU Zebra. + * + * 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 "memory.h" +#include "workqueue.h" +#include "linklist.h" +#include "command.h" +#include "log.h" + +/* master list of 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 + +static struct work_queue_item * +work_queue_item_new (struct work_queue *wq) +{ + struct work_queue_item *item; + assert (wq); + + item = XCALLOC (MTYPE_WORK_QUEUE_ITEM, + sizeof (struct work_queue_item)); + + return item; +} + +static void +work_queue_item_free (struct work_queue_item *item) +{ + XFREE (MTYPE_WORK_QUEUE_ITEM, item); + return; +} + +/* create new work queue */ +struct work_queue * +work_queue_new (struct thread_master *m, const char *queue_name) +{ + struct work_queue *new; + + new = XCALLOC (MTYPE_WORK_QUEUE, sizeof (struct work_queue)); + + if (new == NULL) + return new; + + new->name = XSTRDUP (MTYPE_WORK_QUEUE_NAME, queue_name); + new->master = m; + SET_FLAG (new->flags, WQ_UNPLUGGED); + + if ( (new->items = list_new ()) == NULL) + { + XFREE (MTYPE_WORK_QUEUE_NAME, new->name); + XFREE (MTYPE_WORK_QUEUE, new); + + return NULL; + } + + new->items->del = (void (*)(void *)) work_queue_item_free; + + 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; + + return new; +} + +void +work_queue_free (struct work_queue *wq) +{ + if (wq->thread != NULL) + thread_cancel(wq->thread); + + /* list_delete frees items via callback */ + list_delete (wq->items); + 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) +{ + /* if appropriate, schedule work queue thread */ + if ( CHECK_FLAG (wq->flags, WQ_UNPLUGGED) + && (wq->thread == NULL) + && (listcount (wq->items) > 0) ) + { + wq->thread = thread_add_background (wq->master, work_queue_run, + wq, delay); + return 1; + } + else + return 0; +} + +void +work_queue_add (struct work_queue *wq, void *data) +{ + struct work_queue_item *item; + + assert (wq); + + if (!(item = work_queue_item_new (wq))) + { + zlog_err ("%s: unable to get new queue item", __func__); + return; + } + + item->data = data; + listnode_add (wq->items, item); + + work_queue_schedule (wq, wq->spec.hold); + + return; +} + +static void +work_queue_item_remove (struct work_queue *wq, struct listnode *ln) +{ + struct work_queue_item *item = listgetdata (ln); + + assert (item && item->data); + + /* call private data deletion callback if needed */ + if (wq->spec.del_item_data) + wq->spec.del_item_data (wq, item->data); + + list_delete_node (wq->items, ln); + work_queue_item_free (item); + + return; +} + +static void +work_queue_item_requeue (struct work_queue *wq, struct listnode *ln) +{ + LISTNODE_DETACH (wq->items, ln); + LISTNODE_ATTACH (wq->items, ln); /* attach to end of list */ +} + +DEFUN(show_work_queues, + show_work_queues_cmd, + "show work-queues", + SHOW_STR + "Work Queue information\n") +{ + struct listnode *node; + struct work_queue *wq; + + vty_out (vty, + "%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 %6s %5s %s%s", + 'P', + "Items", + "Hold", + "Total", + "Best","Worst","Gran.","Avg.", "Lat.", + "Name", + VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO (work_queues, node, wq)) + { + 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, + 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); + } + + return CMD_SUCCESS; +} + +/* 'plug' a queue: Stop it from being scheduled, + * ie: prevent the queue from draining. + */ +void +work_queue_plug (struct work_queue *wq) +{ + if (wq->thread) + thread_cancel (wq->thread); + + wq->thread = NULL; + + UNSET_FLAG (wq->flags, WQ_UNPLUGGED); +} + +/* unplug queue, schedule it again, if appropriate + * Ie: Allow the queue to be drained again + */ +void +work_queue_unplug (struct work_queue *wq) +{ + SET_FLAG (wq->flags, WQ_UNPLUGGED); + + /* if thread isnt already waiting, add one */ + work_queue_schedule (wq, wq->spec.hold); +} + +/* timer thread to process a work queue + * will reschedule itself if required, + * otherwise work_queue_item_add + */ +int +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; + char yielded = 0; + + wq = THREAD_ARG (thread); + wq->thread = NULL; + + assert (wq && wq->items); + + /* calculate cycle granularity: + * list iteration == 1 cycle + * granularity == # cycles between checks whether we should yield. + * + * granularity should be > 0, and can increase slowly after each run to + * provide some hysteris, but not past cycles.best or 2*cycles. + * + * 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. + * + * We could use just the average and save some work, however we want to be + * able to adjust quickly to CPU pressure. Average wont shift much if + * daemon has been running a long time. + */ + if (wq->cycles.granularity == 0) + wq->cycles.granularity = WORK_QUEUE_MIN_GRANULARITY; + + for (ALL_LIST_ELEMENTS (wq->items, node, nnode, item)) + { + assert (item && item->data); + + /* dont run items which are past their allowed retries */ + if (item->ran > wq->spec.max_retries) + { + /* run error handler, if any */ + if (wq->spec.errorfunc) + wq->spec.errorfunc (wq, item->data); + work_queue_item_remove (wq, node); + continue; + } + + /* run and take care of items that want to be retried immediately */ + do + { + ret = wq->spec.workfunc (wq, item->data); + item->ran++; + } + while ((ret == WQ_RETRY_NOW) + && (item->ran < wq->spec.max_retries)); + + switch (ret) + { + case WQ_QUEUE_BLOCKED: + { + /* decrement item->ran again, cause this isn't an item + * specific error, and fall through to WQ_RETRY_LATER + */ + item->ran--; + } + case WQ_RETRY_LATER: + { + goto stats; + } + case WQ_REQUEUE: + { + item->ran--; + work_queue_item_requeue (wq, node); + break; + } + case WQ_RETRY_NOW: + /* a RETRY_NOW that gets here has exceeded max_tries, same as ERROR */ + case WQ_ERROR: + { + if (wq->spec.errorfunc) + wq->spec.errorfunc (wq, item); + } + /* fall through here is deliberate */ + case WQ_SUCCESS: + default: + { + work_queue_item_remove (wq, node); + break; + } + } + + /* completed cycle */ + cycles++; + + /* test if we should yield */ + if ( !(cycles % wq->cycles.granularity) + && (took = thread_should_yield (thread))) + { + yielded = 1; + goto stats; + } + } + +stats: + +#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)) + { + /* 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 + + wq->runs++; + wq->cycles.total += cycles; + +#if 0 + printf ("%s: cycles %d, new: best %d, worst %d\n", + __func__, cycles, wq->cycles.best, wq->cycles.granularity); +#endif + + /* Is the queue done yet? If it is, call the completion callback. */ + if (listcount (wq->items) > 0) + work_queue_schedule (wq, 0); + else if (wq->spec.completion_func) + wq->spec.completion_func (wq); + + return 0; +} diff --git a/lib/workqueue.h b/lib/workqueue.h new file mode 100644 index 0000000..aac7860 --- /dev/null +++ b/lib/workqueue.h @@ -0,0 +1,129 @@ +/* + * Quagga Work Queues. + * + * 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. + */ + +#ifndef _QUAGGA_WORK_QUEUE_H +#define _QUAGGA_WORK_QUEUE_H + +/* Hold time for the initial schedule of a queue run, in millisec */ +#define WORK_QUEUE_DEFAULT_HOLD 50 + +/* action value, for use by item processor and item error handlers */ +typedef enum +{ + WQ_SUCCESS = 0, + WQ_ERROR, /* Error, run error handler if provided */ + WQ_RETRY_NOW, /* retry immediately */ + WQ_RETRY_LATER, /* retry later, cease processing work queue */ + WQ_REQUEUE, /* requeue item, continue processing work queue */ + WQ_QUEUE_BLOCKED, /* Queue cant be processed at this time. + * Similar to WQ_RETRY_LATER, but doesn't penalise + * the particular item.. */ +} wq_item_status; + +/* A single work queue item, unsurprisingly */ +struct work_queue_item +{ + void *data; /* opaque data */ + unsigned short ran; /* # of times item has been run */ +}; + +#define WQ_UNPLUGGED (1 << 0) /* available for draining */ + +struct work_queue +{ + /* Everything but the specification struct is private + * the following may be read + */ + struct thread_master *master; /* thread master */ + struct thread *thread; /* thread, if one is active */ + char *name; /* work queue name */ + + /* Specification for this work queue. + * Public, must be set before use by caller. May be modified at will. + */ + struct { + /* optional opaque user data, global to the queue. */ + void *data; + + /* work function to process items with: + * First argument is the workqueue queue. + * Second argument is the item data + */ + wq_item_status (*workfunc) (struct work_queue *, void *); + + /* error handling function, optional */ + void (*errorfunc) (struct work_queue *, struct work_queue_item *); + + /* callback to delete user specific item data */ + void (*del_item_data) (struct work_queue *, void *); + + /* completion callback, called when queue is emptied, optional */ + void (*completion_func) (struct work_queue *); + + /* max number of retries to make for item that errors */ + unsigned int max_retries; + + unsigned int hold; /* hold time for first run, in ms */ + } spec; + + /* 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 */ + + /* private state */ + u_int16_t flags; /* user set flag */ +}; + +/* User API */ + +/* create a new work queue, of given name. + * user must fill in the spec of the returned work queue before adding + * anything to it + */ +extern struct work_queue *work_queue_new (struct thread_master *, + const char *); +/* destroy work queue */ +extern void work_queue_free (struct work_queue *); + +/* Add the supplied data as an item onto the workqueue */ +extern void work_queue_add (struct work_queue *, void *); + +/* plug the queue, ie prevent it from being drained / processed */ +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; +#endif /* _QUAGGA_WORK_QUEUE_H */ diff --git a/lib/zassert.h b/lib/zassert.h new file mode 100644 index 0000000..bf0a851 --- /dev/null +++ b/lib/zassert.h @@ -0,0 +1,44 @@ +/* + * $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 +#define _QUAGGA_ASSERT_H + +extern void _zlog_assert_failed (const char *assertion, const char *file, + unsigned int line, const char *function) + __attribute__ ((noreturn)); + +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define __ASSERT_FUNCTION __func__ +#elif defined(__GNUC__) +#define __ASSERT_FUNCTION __FUNCTION__ +#else +#define __ASSERT_FUNCTION NULL +#endif + +#define zassert(EX) ((void)((EX) ? 0 : \ + (_zlog_assert_failed(#EX, __FILE__, __LINE__, \ + __ASSERT_FUNCTION), 0))) + +#undef assert +#define assert(EX) zassert(EX) + +#endif /* _QUAGGA_ASSERT_H */ diff --git a/lib/zclient.c b/lib/zclient.c new file mode 100644 index 0000000..a32faaa --- /dev/null +++ b/lib/zclient.c @@ -0,0 +1,1311 @@ +/* Zebra's client library. + * Copyright (C) 1999 Kunihiro Ishiguro + * Copyright (C) 2005 Andrew J. Schorr + * + * 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 "stream.h" +#include "buffer.h" +#include "network.h" +#include "if.h" +#include "log.h" +#include "thread.h" +#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 *); + +const char *zclient_serv_path = NULL; + +/* This file local debug flag. */ +int zclient_debug = 0; + +/* Allocate zclient structure. */ +struct zclient * +zclient_new (struct thread_master *master) +{ + struct zclient *zclient; + zclient = XCALLOC (MTYPE_ZCLIENT, sizeof (struct zclient)); + + 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; +} + +/* This function is only called when exiting, because + many parts of the code do not check for I/O errors, so they could + reference an invalid pointer if the structure was ever freed. + + Free zclient structure. */ +void +zclient_free (struct zclient *zclient) +{ + if (zclient->ibuf) + stream_free(zclient->ibuf); + if (zclient->obuf) + stream_free(zclient->obuf); + if (zclient->wb) + buffer_free(zclient->wb); + + XFREE (MTYPE_ZCLIENT, zclient); +} + +/* Initialize zebra client. Argument redist_default is unwanted + redistribute route type. */ +void +zclient_init (struct zclient *zclient, int redist_default) +{ + int i; + + /* Enable zebra client connection by default. */ + zclient->enable = 1; + + /* Set -1 to the default socket value. */ + zclient->sock = -1; + + /* Clear redistribution flags. */ + for (i = 0; i < ZEBRA_ROUTE_MAX; i++) + zclient->redist[i] = vrf_bitmap_init (); + + /* Set unwanted redistribute route. bgpd does not need BGP route + redistribution. */ + zclient->redist_default = redist_default; + + /* Set default-information redistribute to zero. */ + zclient->default_information = vrf_bitmap_init (); + + /* Schedule first zclient connection. */ + if (zclient_debug) + zlog_debug ("zclient start scheduled"); + + zclient_event (ZCLIENT_SCHEDULE, zclient); +} + +/* Stop zebra client services. */ +void +zclient_stop (struct zclient *zclient) +{ + int i; + + if (zclient_debug) + zlog_debug ("zclient stopped"); + + /* Stop threads. */ + THREAD_OFF(zclient->t_read); + THREAD_OFF(zclient->t_connect); + THREAD_OFF(zclient->t_write); + + /* Reset streams. */ + stream_reset(zclient->ibuf); + stream_reset(zclient->obuf); + + /* Empty the write buffer. */ + buffer_reset(zclient->wb); + + /* Close socket. */ + if (zclient->sock >= 0) + { + close (zclient->sock); + 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 +zclient_reset (struct zclient *zclient) +{ + zclient_stop (zclient); + zclient_init (zclient, zclient->redist_default); +} + +#ifdef HAVE_TCP_ZEBRA + +/* Make socket to zebra daemon. Return zebra socket. */ +static int +zclient_socket(void) +{ + int sock; + int ret; + struct sockaddr_in serv; + + /* We should think about IPv6 connection. */ + sock = socket (AF_INET, SOCK_STREAM, 0); + if (sock < 0) + return -1; + + /* Make server socket. */ + memset (&serv, 0, sizeof (struct sockaddr_in)); + serv.sin_family = AF_INET; + serv.sin_port = htons (ZEBRA_PORT); +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + serv.sin_len = sizeof (struct sockaddr_in); +#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ + serv.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + + /* Connect to zebra. */ + ret = connect (sock, (struct sockaddr *) &serv, sizeof (serv)); + if (ret < 0) + { + zlog_warn ("%s connect failure: %d", __PRETTY_FUNCTION__, errno); + close (sock); + return -1; + } + return sock; +} + +#else + +/* For sockaddr_un. */ +#include + +static int +zclient_socket_un (const char *path) +{ + int ret; + int sock, len; + struct sockaddr_un addr; + + sock = socket (AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) + return -1; + + /* Make server socket. */ + memset (&addr, 0, sizeof (struct sockaddr_un)); + addr.sun_family = AF_UNIX; + strncpy (addr.sun_path, path, strlen (path)); +#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN + len = addr.sun_len = SUN_LEN(&addr); +#else + len = sizeof (addr.sun_family) + strlen (addr.sun_path); +#endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */ + + ret = connect (sock, (struct sockaddr *) &addr, len); + if (ret < 0) + { + zlog_warn ("%s connect failure: %d", __PRETTY_FUNCTION__, errno); + close (sock); + return -1; + } + return sock; +} + +#endif /* HAVE_TCP_ZEBRA */ + +/** + * Connect to zebra daemon. + * @param zclient a pointer to zclient structure + * @return socket fd just to make sure that connection established + * @see zclient_init + * @see zclient_new + */ +int +zclient_socket_connect (struct zclient *zclient) +{ +#ifdef HAVE_TCP_ZEBRA + zclient->sock = zclient_socket (); +#else + zclient->sock = zclient_socket_un (zclient_serv_path_get()); +#endif + return zclient->sock; +} + +static int +zclient_failed(struct zclient *zclient) +{ + zclient->fail++; + zclient_stop(zclient); + zclient_event(ZCLIENT_CONNECT, zclient); + return -1; +} + +static int +zclient_flush_data(struct thread *thread) +{ + struct zclient *zclient = THREAD_ARG(thread); + + zclient->t_write = NULL; + if (zclient->sock < 0) + return -1; + switch (buffer_flush_available(zclient->wb, zclient->sock)) + { + case BUFFER_ERROR: + zlog_warn("%s: buffer_flush_available failed on zclient fd %d, closing", + __func__, zclient->sock); + return zclient_failed(zclient); + break; + case BUFFER_PENDING: + zclient->t_write = thread_add_write (zclient->master, zclient_flush_data, + zclient, zclient->sock); + break; + case BUFFER_EMPTY: + break; + } + return 0; +} + +int +zclient_send_message(struct zclient *zclient) +{ + if (zclient->sock < 0) + return -1; + switch (buffer_write(zclient->wb, zclient->sock, STREAM_DATA(zclient->obuf), + stream_get_endp(zclient->obuf))) + { + case BUFFER_ERROR: + zlog_warn("%s: buffer_write failed to zclient fd %d, closing", + __func__, zclient->sock); + return zclient_failed(zclient); + break; + case BUFFER_EMPTY: + THREAD_OFF(zclient->t_write); + break; + case BUFFER_PENDING: + 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, 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, vrf_id_t vrf_id) +{ + struct stream *s; + + /* Get zclient output buffer. */ + s = zclient->obuf; + stream_reset (s); + + /* Send very simple command only Zebra message. */ + zclient_create_header (s, command, vrf_id); + + return zclient_send_message(zclient); +} + +static int +zebra_hello_send (struct zclient *zclient) +{ + struct stream *s; + + if (zclient->redist_default) + { + s = zclient->obuf; + stream_reset (s); + + /* 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); + } + + 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) +{ + if (zclient_debug) + zlog_debug ("zclient_start is called"); + + /* zclient is disabled. */ + if (! zclient->enable) + return 0; + + /* If already connected to the zebra. */ + if (zclient->sock >= 0) + return 0; + + /* Check connect thread. */ + if (zclient->t_connect) + return 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; + } + + if (set_nonblocking(zclient->sock) < 0) + zlog_warn("%s: set_nonblocking(%d) failed", __func__, zclient->sock); + + /* Clear fail count. */ + zclient->fail = 0; + if (zclient_debug) + zlog_debug ("zclient connect success with socket [%d]", zclient->sock); + + /* Create read thread. */ + zclient_event (ZCLIENT_READ, zclient); + + zebra_hello_send (zclient); + + /* Inform the successful connection. */ + if (zclient->zebra_connected) + (*zclient->zebra_connected) (zclient); + + return 0; +} + +/* This function is a wrapper function for calling zclient_start from + timer or event thread. */ +static int +zclient_connect (struct thread *t) +{ + struct zclient *zclient; + + zclient = THREAD_ARG (t); + zclient->t_connect = NULL; + + if (zclient_debug) + zlog_debug ("zclient_connect is called"); + + 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 + * added/deleted to the kernel. Info about the route is specified + * by the caller in a struct zapi_ipv4. zapi_ipv4_read() then writes + * the info down the zclient socket using the stream_* functions. + * + * The corresponding read ("xdr_decode") function on the server + * side is zread_ipv4_add()/zread_ipv4_delete(). + * + * 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Length (2) | Command | Route Type | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ZEBRA Flags | Message Flags | Prefix length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Destination IPv4 Prefix for route | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Nexthop count | + * +-+-+-+-+-+-+-+-+ + * + * + * A number of IPv4 nexthop(s) or nexthop interface index(es) are then + * described, as per the Nexthop count. Each nexthop described as: + * + * +-+-+-+-+-+-+-+-+ + * | Nexthop Type | Set to one of ZEBRA_NEXTHOP_* + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | IPv4 Nexthop address or Interface Index number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Alternatively, if the flags field has ZEBRA_FLAG_BLACKHOLE or + * ZEBRA_FLAG_REJECT is set then Nexthop count is set to 1, then _no_ + * nexthop information is provided, and the message describes a prefix + * to blackhole or reject route. + * + * If ZAPI_MESSAGE_DISTANCE is set, the distance value is written as a 1 + * byte value. + * + * 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 +zapi_ipv4_route (u_char cmd, struct zclient *zclient, struct prefix_ipv4 *p, + struct zapi_ipv4 *api) +{ + int i; + int psize; + struct stream *s; + + /* Reset stream. */ + s = zclient->obuf; + stream_reset (s); + + zclient_create_header (s, cmd, api->vrf_id); + + /* Put type and nexthop. */ + stream_putc (s, api->type); + stream_putc (s, api->flags); + stream_putc (s, api->message); + stream_putw (s, api->safi); + + /* Put prefix information. */ + psize = PSIZE (p->prefixlen); + stream_putc (s, p->prefixlen); + stream_write (s, (u_char *) & p->prefix, psize); + + /* Nexthop, ifindex, distance and metric information. */ + if (CHECK_FLAG (api->message, ZAPI_MESSAGE_NEXTHOP)) + { + if (CHECK_FLAG (api->flags, ZEBRA_FLAG_BLACKHOLE)) + { + stream_putc (s, 1); + stream_putc (s, ZEBRA_NEXTHOP_BLACKHOLE); + /* XXX assert(api->nexthop_num == 0); */ + /* XXX assert(api->ifindex_num == 0); */ + } + else + stream_putc (s, api->nexthop_num + api->ifindex_num); + + for (i = 0; i < api->nexthop_num; i++) + { + stream_putc (s, ZEBRA_NEXTHOP_IPV4); + stream_put_in_addr (s, api->nexthop[i]); + } + for (i = 0; i < api->ifindex_num; i++) + { + stream_putc (s, ZEBRA_NEXTHOP_IFINDEX); + stream_putl (s, api->ifindex[i]); + } + } + + if (CHECK_FLAG (api->message, ZAPI_MESSAGE_DISTANCE)) + 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)); + + return zclient_send_message(zclient); +} + +#ifdef HAVE_IPV6 +int +zapi_ipv6_route (u_char cmd, struct zclient *zclient, struct prefix_ipv6 *p, + struct zapi_ipv6 *api) +{ + int i; + int psize; + struct stream *s; + + /* Reset stream. */ + s = zclient->obuf; + stream_reset (s); + + zclient_create_header (s, cmd, api->vrf_id); + + /* Put type and nexthop. */ + stream_putc (s, api->type); + stream_putc (s, api->flags); + stream_putc (s, api->message); + stream_putw (s, api->safi); + + /* Put prefix information. */ + psize = PSIZE (p->prefixlen); + stream_putc (s, p->prefixlen); + stream_write (s, (u_char *)&p->prefix, psize); + + /* Nexthop, ifindex, distance and metric information. */ + if (CHECK_FLAG (api->message, ZAPI_MESSAGE_NEXTHOP)) + { + stream_putc (s, api->nexthop_num + api->ifindex_num); + + for (i = 0; i < api->nexthop_num; i++) + { + stream_putc (s, ZEBRA_NEXTHOP_IPV6); + stream_write (s, (u_char *)api->nexthop[i], 16); + } + for (i = 0; i < api->ifindex_num; i++) + { + stream_putc (s, ZEBRA_NEXTHOP_IFINDEX); + stream_putl (s, api->ifindex[i]); + } + } + + if (CHECK_FLAG (api->message, ZAPI_MESSAGE_DISTANCE)) + 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)); + + return zclient_send_message(zclient); +} +#endif /* HAVE_IPV6 */ + +/* + * send a ZEBRA_REDISTRIBUTE_ADD or ZEBRA_REDISTRIBUTE_DELETE + * for the route type (ZEBRA_ROUTE_KERNEL etc.). The zebra server will + * then set/unset redist[type] in the client handle (a struct zserv) for the + * sending client + */ +int +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, vrf_id); + stream_putc (s, type); + + stream_putw_at (s, 0, stream_get_endp (s)); + + 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) +{ + /* Fetch interface address. */ + rid->family = stream_getc (s); + + zclient_stream_get_prefix (s, rid); +} + +/* Interface addition from zebra daemon. */ +/* + * The format of the message sent with type ZEBRA_INTERFACE_ADD or + * 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 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ifname | + * | | + * | | + * | | + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ifindex | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | status | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | if_flags | + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | metric | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ifmtu | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ifmtu6 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | bandwidth | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Link Layer Type | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Harware Address Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Hardware Address if HW lenght different from 0 | + * | ... max INTERFACE_HWADDR_MAX | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 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, vrf_id_t vrf_id) +{ + struct interface *ifp; + char ifname_tmp[INTERFACE_NAMSIZ]; + + /* Read interface name. */ + stream_get (ifname_tmp, s, INTERFACE_NAMSIZ); + + /* Lookup/create interface by name. */ + ifp = if_get_by_name_len_vrf (ifname_tmp, + strnlen (ifname_tmp, INTERFACE_NAMSIZ), + vrf_id); + + zebra_interface_if_set_value (s, ifp); + + return ifp; +} + +/* + * Read interface up/down msg (ZEBRA_INTERFACE_UP/ZEBRA_INTERFACE_DOWN) + * from zebra server. The format of this message is the same as + * that sent for ZEBRA_INTERFACE_ADD/ZEBRA_INTERFACE_DELETE (see + * comments for zebra_interface_add_read), except that no sockaddr_dl + * is sent at the tail of the message. + */ +struct interface * +zebra_interface_state_read (struct stream *s, vrf_id_t vrf_id) +{ + struct interface *ifp; + char ifname_tmp[INTERFACE_NAMSIZ]; + + /* Read interface name. */ + stream_get (ifname_tmp, s, INTERFACE_NAMSIZ); + + /* Lookup this by interface index. */ + 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->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)); + + /* 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 + * +-+-+-+-+-+-+-+-+ + * | type | ZEBRA_INTERFACE_ADDRESS_ADD or + * +-+-+-+-+-+-+-+-+ ZEBRA_INTERFACE_ADDRES_DELETE + * | | + * + + + * | ifindex | + * + + + * | | + * + + + * | | + * +-+-+-+-+-+-+-+-+ + * | ifc_flags | flags for connected address + * +-+-+-+-+-+-+-+-+ + * | addr_family | + * +-+-+-+-+-+-+-+-+ + * | addr... | + * : : + * | | + * +-+-+-+-+-+-+-+-+ + * | addr_len | len of addr. E.g., addr_len = 4 for ipv4 addrs. + * +-+-+-+-+-+-+-+-+ + * | daddr.. | + * : : + * | | + * +-+-+-+-+-+-+-+-+ + */ + +static int +memconstant(const void *s, int c, size_t n) +{ + const u_char *p = s; + + while (n-- > 0) + if (*p++ != c) + return 0; + return 1; +} + +struct connected * +zebra_interface_address_read (int type, struct stream *s, vrf_id_t vrf_id) +{ + ifindex_t ifindex; + struct interface *ifp; + struct connected *ifc; + struct prefix p, d, *dp; + int plen; + u_char ifc_flags; + + memset (&p, 0, sizeof(p)); + memset (&d, 0, sizeof(d)); + + /* Get interface index. */ + ifindex = stream_getl (s); + + /* Lookup index. */ + ifp = if_lookup_by_index_vrf (ifindex, vrf_id); + if (ifp == NULL) + { + zlog_warn ("zebra_interface_address_read(%s): " + "Can't find interface by ifindex: %d ", + (type == ZEBRA_INTERFACE_ADDRESS_ADD? "ADD" : "DELETE"), + ifindex); + return NULL; + } + + /* Fetch flag. */ + ifc_flags = stream_getc (s); + + /* Fetch interface address. */ + 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); + + /* 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, 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 + { + assert (type == ZEBRA_INTERFACE_ADDRESS_DELETE); + ifc = connected_delete_by_prefix(ifp, &p); + } + + return ifc; +} + + +/* Zebra client message read function. */ +static int +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. */ + zclient = THREAD_ARG (thread); + zclient->t_read = NULL; + + /* Read zebra header (if we don't have it already). */ + if ((already = stream_get_endp(zclient->ibuf)) < ZEBRA_HEADER_SIZE) + { + ssize_t nbyte; + if (((nbyte = stream_read_try(zclient->ibuf, zclient->sock, + ZEBRA_HEADER_SIZE-already)) == 0) || + (nbyte == -1)) + { + if (zclient_debug) + zlog_debug ("zclient connection closed socket [%d].", zclient->sock); + return zclient_failed(zclient); + } + if (nbyte != (ssize_t)(ZEBRA_HEADER_SIZE-already)) + { + /* Try again later. */ + zclient_event (ZCLIENT_READ, zclient); + return 0; + } + already = ZEBRA_HEADER_SIZE; + } + + /* Reset to read from the beginning of the incoming packet. */ + stream_set_getp(zclient->ibuf, 0); + + /* Fetch header values. */ + 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) + { + zlog_err("%s: socket %d version mismatch, marker %d, version %d", + __func__, zclient->sock, marker, version); + return zclient_failed(zclient); + } + + if (length < ZEBRA_HEADER_SIZE) + { + zlog_err("%s: socket %d message length %u is less than %d ", + __func__, zclient->sock, length, ZEBRA_HEADER_SIZE); + return zclient_failed(zclient); + } + + /* Length check. */ + if (length > STREAM_SIZE(zclient->ibuf)) + { + struct stream *ns; + zlog_warn("%s: message size %u exceeds buffer size %lu, expanding...", + __func__, length, (u_long)STREAM_SIZE(zclient->ibuf)); + ns = stream_new(length); + stream_copy(ns, zclient->ibuf); + stream_free (zclient->ibuf); + zclient->ibuf = ns; + } + + /* Read rest of zebra packet. */ + if (already < length) + { + ssize_t nbyte; + if (((nbyte = stream_read_try(zclient->ibuf, zclient->sock, + length-already)) == 0) || + (nbyte == -1)) + { + if (zclient_debug) + zlog_debug("zclient connection closed socket [%d].", zclient->sock); + return zclient_failed(zclient); + } + if (nbyte != (ssize_t)(length-already)) + { + /* Try again later. */ + zclient_event (ZCLIENT_READ, zclient); + return 0; + } + } + + length -= ZEBRA_HEADER_SIZE; + + if (zclient_debug) + 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, vrf_id); + break; + case ZEBRA_INTERFACE_ADD: + if (zclient->interface_add) + (*zclient->interface_add) (command, zclient, length, vrf_id); + break; + case ZEBRA_INTERFACE_DELETE: + if (zclient->interface_delete) + (*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, vrf_id); + break; + case ZEBRA_INTERFACE_ADDRESS_DELETE: + if (zclient->interface_address_delete) + (*zclient->interface_address_delete) (command, zclient, length, vrf_id); + break; + case ZEBRA_INTERFACE_UP: + if (zclient->interface_up) + (*zclient->interface_up) (command, zclient, length, vrf_id); + break; + case ZEBRA_INTERFACE_DOWN: + if (zclient->interface_down) + (*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, vrf_id); + break; + case ZEBRA_IPV4_ROUTE_DELETE: + if (zclient->ipv4_route_delete) + (*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, vrf_id); + break; + case ZEBRA_IPV6_ROUTE_DELETE: + if (zclient->ipv6_route_delete) + (*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; + } + + if (zclient->sock < 0) + /* Connection was closed during packet processing. */ + return -1; + + /* Register read thread. */ + stream_reset(zclient->ibuf); + zclient_event (ZCLIENT_READ, zclient); + + return 0; +} + +void +zclient_redistribute (int command, struct zclient *zclient, int type, + vrf_id_t vrf_id) +{ + + if (command == ZEBRA_REDISTRIBUTE_ADD) + { + if (vrf_bitmap_check (zclient->redist[type], vrf_id)) + return; + vrf_bitmap_set (zclient->redist[type], vrf_id); + } + else + { + if (!vrf_bitmap_check (zclient->redist[type], vrf_id)) + return; + vrf_bitmap_unset (zclient->redist[type], vrf_id); + } + + if (zclient->sock > 0) + zebra_redistribute_send (command, zclient, type, vrf_id); +} + + +void +zclient_redistribute_default (int command, struct zclient *zclient, + vrf_id_t vrf_id) +{ + + if (command == ZEBRA_REDISTRIBUTE_DEFAULT_ADD) + { + if (vrf_bitmap_check (zclient->default_information, vrf_id)) + return; + vrf_bitmap_set (zclient->default_information, vrf_id); + } + else + { + if (!vrf_bitmap_check (zclient->default_information, vrf_id)) + return; + vrf_bitmap_unset (zclient->default_information, vrf_id); + } + + if (zclient->sock > 0) + zebra_message_send (zclient, command, vrf_id); +} + +static void +zclient_event (enum event event, struct zclient *zclient) +{ + switch (event) + { + case ZCLIENT_SCHEDULE: + if (! zclient->t_connect) + zclient->t_connect = + thread_add_event (zclient->master, zclient_connect, zclient, 0); + break; + case ZCLIENT_CONNECT: + if (zclient->fail >= 10) + return; + if (zclient_debug) + zlog_debug ("zclient connect schedule interval is %d", + zclient->fail < 3 ? 10 : 60); + if (! zclient->t_connect) + zclient->t_connect = + thread_add_timer (zclient->master, zclient_connect, zclient, + zclient->fail < 3 ? 10 : 60); + break; + case ZCLIENT_READ: + zclient->t_read = + 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) +{ + struct stat sb; + + /* reset */ + zclient_serv_path = NULL; + + /* test if `path' is socket. don't set it otherwise. */ + if (stat(path, &sb) == -1) + { + zlog_warn ("%s: zebra socket `%s' does not exist", __func__, path); + return; + } + + if ((sb.st_mode & S_IFMT) != S_IFSOCK) + { + zlog_warn ("%s: `%s' is not unix socket, sir", __func__, path); + return; + } + + /* it seems that path is unix socket */ + zclient_serv_path = path; +} + diff --git a/lib/zclient.h b/lib/zclient.h new file mode 100644 index 0000000..d46728d --- /dev/null +++ b/lib/zclient.h @@ -0,0 +1,229 @@ +/* Zebra's client 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. + */ + +#ifndef _ZEBRA_ZCLIENT_H +#define _ZEBRA_ZCLIENT_H + +/* For struct zapi_ipv{4,6}. */ +#include "prefix.h" + +/* 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 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; + + /* Flag of communication to zebra is enabled or not. Default is on. + This flag is disabled by `no router zebra' statement. */ + int enable; + + /* Connection failure count. */ + int fail; + + /* Input buffer for zebra message. */ + struct stream *ibuf; + + /* Output buffer for zebra message. */ + struct stream *obuf; + + /* Buffer of data waiting to be written to zebra. */ + struct buffer *wb; + + /* Read and connect thread. */ + struct thread *t_read; + struct thread *t_connect; + + /* Thread to write buffered data to zebra. */ + struct thread *t_write; + + /* Redistribute information. */ + u_char redist_default; + vrf_bitmap_t redist[ZEBRA_ROUTE_MAX]; + + /* Redistribute defauilt. */ + vrf_bitmap_t default_information; + + /* Pointer to the callback functions. */ + 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. */ +#define ZAPI_MESSAGE_NEXTHOP 0x01 +#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 +{ + uint16_t length; + uint8_t marker; /* corresponds to command field in old zserv + * always set to 255 in new zserv. + */ + uint8_t version; +#define ZSERV_VERSION 3 + vrf_id_t vrf_id; + uint16_t command; +}; + +/* Zebra IPv4 route message API. */ +struct zapi_ipv4 +{ + u_char type; + + u_char flags; + + u_char message; + + safi_t safi; + + u_char nexthop_num; + struct in_addr **nexthop; + + u_char ifindex_num; + 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 (struct thread_master *); +extern void zclient_init (struct zclient *, int); +extern int zclient_start (struct zclient *); +extern void zclient_stop (struct zclient *); +extern void zclient_reset (struct zclient *); +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, + 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, + 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 *, + 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, 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. */ + +struct zapi_ipv6 +{ + u_char type; + + u_char flags; + + u_char message; + + safi_t safi; + + u_char nexthop_num; + struct in6_addr **nexthop; + + u_char ifindex_num; + 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, + struct prefix_ipv6 *p, struct zapi_ipv6 *api); +#endif /* HAVE_IPV6 */ + +#endif /* _ZEBRA_ZCLIENT_H */ diff --git a/lib/zebra.h b/lib/zebra.h new file mode 100644 index 0000000..a405d46 --- /dev/null +++ b/lib/zebra.h @@ -0,0 +1,541 @@ +/* Zebra common header. + Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 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_H +#define _ZEBRA_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#ifdef SUNOS_5 +#define _XPG4_2 +typedef unsigned int u_int32_t; +typedef unsigned short u_int16_t; +typedef unsigned char u_int8_t; +#endif /* SUNOS_5 */ + +#ifndef HAVE_SOCKLEN_T +typedef int socklen_t; +#endif /* HAVE_SOCKLEN_T */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_STROPTS_H +#include +#endif /* HAVE_STROPTS_H */ +#ifdef HAVE_SYS_SELECT_H +#include +#endif /* HAVE_SYS_SELECT_H */ +#include +#include +#include +#ifdef HAVE_SYS_SYSCTL_H +#ifdef GNU_LINUX +#include +#endif +#include +#endif /* HAVE_SYS_SYSCTL_H */ +#include +#ifdef HAVE_SYS_CONF_H +#include +#endif /* HAVE_SYS_CONF_H */ +#ifdef HAVE_SYS_KSYM_H +#include +#endif /* HAVE_SYS_KSYM_H */ +#include +#ifdef TIME_WITH_SYS_TIME +# include +# include +#else +# ifdef HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif /* TIME_WITH_SYS_TIME */ +#include +#include +#ifdef HAVE_RUSAGE +#include +#endif /* HAVE_RUSAGE */ +#ifdef HAVE_LIMITS_H +#include +#endif /* HAVE_LIMITS_H */ +#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 +#include +#endif /* SUNOS_5 */ + +/* machine dependent includes */ +#ifdef HAVE_LINUX_VERSION_H +#include +#endif /* HAVE_LINUX_VERSION_H */ + +#ifdef HAVE_ASM_TYPES_H +#include +#endif /* HAVE_ASM_TYPES_H */ + +/* misc include group */ +#include +#if !(defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) +/* Not C99; do we need to define va_copy? */ +#ifndef va_copy +#ifdef __va_copy +#define va_copy(DST,SRC) __va_copy(DST,SRC) +#else +/* Now we are desperate; this should work on many typical platforms. + But this is slightly dangerous, because the standard does not require + va_copy to be a macro. */ +#define va_copy(DST,SRC) memcpy(&(DST), &(SRC), sizeof(va_list)) +#warning "Not C99 and no va_copy macro available, falling back to memcpy" +#endif /* __va_copy */ +#endif /* !va_copy */ +#endif /* !C99 */ + + +#ifdef HAVE_LCAPS +#include +#include +#endif /* HAVE_LCAPS */ + +#ifdef HAVE_SOLARIS_CAPABILITIES +#include +#endif /* HAVE_SOLARIS_CAPABILITIES */ + +/* network include group */ + +#include + +#ifdef HAVE_SYS_SOCKIO_H +#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 */ +#include +#include +#include + +#ifdef HAVE_NET_NETOPT_H +#include +#endif /* HAVE_NET_NETOPT_H */ + +#include + +#ifdef HAVE_NET_IF_DL_H +#include +#endif /* HAVE_NET_IF_DL_H */ + +#ifdef HAVE_NET_IF_VAR_H +#include +#endif /* HAVE_NET_IF_VAR_H */ + +#ifdef HAVE_NET_ROUTE_H +#include +#endif /* HAVE_NET_ROUTE_H */ + +#ifdef HAVE_NETLINK +#include +#include +#include +#else +#define RT_TABLE_MAIN 0 +#endif /* HAVE_NETLINK */ + +#ifdef HAVE_NETDB_H +#include +#endif /* HAVE_NETDB_H */ + +#include + +#ifdef HAVE_INET_ND_H +#include +#endif /* HAVE_INET_ND_H */ + +#ifdef HAVE_NETINET_IN_VAR_H +#include +#endif /* HAVE_NETINET_IN_VAR_H */ + +#ifdef HAVE_NETINET6_IN6_VAR_H +#include +#endif /* HAVE_NETINET6_IN6_VAR_H */ + +#ifdef HAVE_NETINET_IN6_VAR_H +#include +#endif /* HAVE_NETINET_IN6_VAR_H */ + +#ifdef HAVE_NETINET6_IN_H +#include +#endif /* HAVE_NETINET6_IN_H */ + + +#ifdef HAVE_NETINET6_IP6_H +#include +#endif /* HAVE_NETINET6_IP6_H */ + +#ifdef HAVE_NETINET_ICMP6_H +#include +#endif /* HAVE_NETINET_ICMP6_H */ + +#ifdef HAVE_NETINET6_ND6_H +#include +#endif /* HAVE_NETINET6_ND6_H */ + +/* Some systems do not define UINT32_MAX, etc.. from inttypes.h + * e.g. this makes life easier for FBSD 4.11 users. + */ +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#ifdef HAVE_GLIBC_BACKTRACE +#include +#endif /* HAVE_GLIBC_BACKTRACE */ + +/* Local includes: */ +#if !(defined(__GNUC__) || defined(VTYSH_EXTRACT_PL)) +#define __attribute__(x) +#endif /* !__GNUC__ || VTYSH_EXTRACT_PL */ + +#include "zassert.h" +#include "str.h" + + +#ifdef HAVE_BROKEN_CMSG_FIRSTHDR +/* This bug is present in Solaris 8 and pre-patch Solaris 9 ; + please refer to http://bugzilla.quagga.net/show_bug.cgi?id=142 */ + +/* Check that msg_controllen is large enough. */ +#define ZCMSG_FIRSTHDR(mhdr) \ + (((size_t)((mhdr)->msg_controllen) >= sizeof(struct cmsghdr)) ? \ + CMSG_FIRSTHDR(mhdr) : (struct cmsghdr *)NULL) + +#warning "CMSG_FIRSTHDR is broken on this platform, using a workaround" + +#else /* HAVE_BROKEN_CMSG_FIRSTHDR */ +#define ZCMSG_FIRSTHDR(M) CMSG_FIRSTHDR(M) +#endif /* HAVE_BROKEN_CMSG_FIRSTHDR */ + + + +/* + * RFC 3542 defines several macros for using struct cmsghdr. + * Here, we define those that are not present + */ + +/* + * Internal defines, for use only in this file. + * These are likely wrong on other than ILP32 machines, so warn. + */ +#ifndef _CMSG_DATA_ALIGN +#define _CMSG_DATA_ALIGN(n) (((n) + 3) & ~3) +#endif /* _CMSG_DATA_ALIGN */ + +#ifndef _CMSG_HDR_ALIGN +#define _CMSG_HDR_ALIGN(n) (((n) + 3) & ~3) +#endif /* _CMSG_HDR_ALIGN */ + +/* + * CMSG_SPACE and CMSG_LEN are required in RFC3542, but were new in that + * version. + */ +#ifndef CMSG_SPACE +#define CMSG_SPACE(l) (_CMSG_DATA_ALIGN(sizeof(struct cmsghdr)) + \ + _CMSG_HDR_ALIGN(l)) +#warning "assuming 4-byte alignment for CMSG_SPACE" +#endif /* CMSG_SPACE */ + + +#ifndef CMSG_LEN +#define CMSG_LEN(l) (_CMSG_DATA_ALIGN(sizeof(struct cmsghdr)) + (l)) +#warning "assuming 4-byte alignment for CMSG_LEN" +#endif /* CMSG_LEN */ + + +/* The definition of struct in_pktinfo is missing in old version of + GLIBC 2.1 (Redhat 6.1). */ +#if defined (GNU_LINUX) && ! defined (HAVE_STRUCT_IN_PKTINFO) +struct in_pktinfo +{ + int ipi_ifindex; + struct in_addr ipi_spec_dst; + struct in_addr ipi_addr; +}; +#endif + +/* + * OSPF Fragmentation / fragmented writes + * + * ospfd can support writing fragmented packets, for cases where + * kernel will not fragment IP_HDRINCL and/or multicast destined + * packets (ie TTBOMK all kernels, BSD, SunOS, Linux). However, + * SunOS, probably BSD too, clobber the user supplied IP ID and IP + * flags fields, hence user-space fragmentation will not work. + * Only Linux is known to leave IP header unmolested. + * Further, fragmentation really should be done the kernel, which already + * supports it, and which avoids nasty IP ID state problems. + * + * Fragmentation of OSPF packets can be required on networks with router + * with many many interfaces active in one area, or on networks with links + * with low MTUs. + */ +#ifdef GNU_LINUX +#define WANT_OSPF_WRITE_FRAGMENT +#endif + +/* + * IP_HDRINCL / struct ip byte order + * + * Linux: network byte order + * *BSD: network, except for length and offset. (cf Stevens) + * SunOS: nominally as per BSD. but bug: network order on LE. + * OpenBSD: network byte order, apart from older versions which are as per + * *BSD + */ +#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 + +/* Define BYTE_ORDER, if not defined. Useful for compiler conditional + * code, rather than preprocessor conditional. + * Not all the world has this BSD define. + */ +#ifndef BYTE_ORDER +#define BIG_ENDIAN 4321 /* least-significant byte first (vax, pc) */ +#define LITTLE_ENDIAN 1234 /* most-significant byte first (IBM, net) */ +#define PDP_ENDIAN 3412 /* LSB first in word, MSW first in long (pdp) */ + +#if defined(WORDS_BIGENDIAN) +#define BYTE_ORDER BIG_ENDIAN +#else /* !WORDS_BIGENDIAN */ +#define BYTE_ORDER LITTLE_ENDIAN +#endif /* WORDS_BIGENDIAN */ + +#endif /* ndef BYTE_ORDER */ + +/* MAX / MIN are not commonly defined, but useful */ +#ifndef MAX +#define MAX(a, b) \ + ({ typeof (a) _a = (a); \ + typeof (b) _b = (b); \ + _a > _b ? _a : _b; }) +#endif +#ifndef MIN +#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 +#endif /* IN6_ARE_ADDR_EQUAL */ + +/* default zebra TCP port for zclient */ +#define ZEBRA_PORT 2600 + +/* Zebra message types. */ +#define ZEBRA_INTERFACE_ADD 1 +#define ZEBRA_INTERFACE_DELETE 2 +#define ZEBRA_INTERFACE_ADDRESS_ADD 3 +#define ZEBRA_INTERFACE_ADDRESS_DELETE 4 +#define ZEBRA_INTERFACE_UP 5 +#define ZEBRA_INTERFACE_DOWN 6 +#define ZEBRA_IPV4_ROUTE_ADD 7 +#define ZEBRA_IPV4_ROUTE_DELETE 8 +#define ZEBRA_IPV6_ROUTE_ADD 9 +#define ZEBRA_IPV6_ROUTE_DELETE 10 +#define ZEBRA_REDISTRIBUTE_ADD 11 +#define ZEBRA_REDISTRIBUTE_DELETE 12 +#define ZEBRA_REDISTRIBUTE_DEFAULT_ADD 13 +#define ZEBRA_REDISTRIBUTE_DEFAULT_DELETE 14 +#define ZEBRA_IPV4_NEXTHOP_LOOKUP 15 +#define ZEBRA_IPV6_NEXTHOP_LOOKUP 16 +#define ZEBRA_IPV4_IMPORT_LOOKUP 17 +#define ZEBRA_IPV6_IMPORT_LOOKUP 18 +#define ZEBRA_INTERFACE_RENAME 19 +#define ZEBRA_ROUTER_ID_ADD 20 +#define ZEBRA_ROUTER_ID_DELETE 21 +#define ZEBRA_ROUTER_ID_UPDATE 22 +#define ZEBRA_HELLO 23 +#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 + * Zserv headers to be distinguished from each other. + */ +#define ZEBRA_HEADER_MARKER 255 + +/* Zebra route's types are defined in route_types.h */ +#include "route_types.h" + +/* Note: whenever a new route-type or zserv-command is added the + * corresponding {command,route}_types[] table in lib/log.c MUST be + * updated! */ + +/* Map a route type to a string. For example, ZEBRA_ROUTE_RIPNG -> "ripng". */ +extern const char *zebra_route_string(unsigned int route_type); +/* Map a route type to a char. For example, ZEBRA_ROUTE_RIPNG -> 'R'. */ +extern char zebra_route_char(unsigned int route_type); +/* Map a zserv command type to the same string, + * e.g. ZEBRA_INTERFACE_ADD -> "ZEBRA_INTERFACE_ADD" */ +/* Map a protocol name to its number. e.g. ZEBRA_ROUTE_BGP->9*/ +extern int proto_name2num(const char *s); +/* Map redistribute X argument to protocol number. + * unlike proto_name2num, this accepts shorthands and takes + * an AFI value to restrict input */ +extern int proto_redistnum(int afi, const char *s); + +extern const char *zserv_command_string (unsigned int command); + +/* Error codes of zebra. */ +#define ZEBRA_ERR_NOERROR 0 +#define ZEBRA_ERR_RTEXIST -1 +#define ZEBRA_ERR_RTUNREACH -2 +#define ZEBRA_ERR_EPERM -3 +#define ZEBRA_ERR_RTNOEXIST -4 +#define ZEBRA_ERR_KERNEL -5 + +/* Zebra message flags */ +#define ZEBRA_FLAG_INTERNAL 0x01 +#define ZEBRA_FLAG_SELFROUTE 0x02 +#define ZEBRA_FLAG_BLACKHOLE 0x04 +#define ZEBRA_FLAG_IBGP 0x08 +#define ZEBRA_FLAG_SELECTED 0x10 +#define ZEBRA_FLAG_FIB_OVERRIDE 0x20 +#define ZEBRA_FLAG_STATIC 0x40 +#define ZEBRA_FLAG_REJECT 0x80 + +/* Zebra nexthop flags. */ +#define ZEBRA_NEXTHOP_IFINDEX 1 +#define ZEBRA_NEXTHOP_IFNAME 2 +#define ZEBRA_NEXTHOP_IPV4 3 +#define ZEBRA_NEXTHOP_IPV4_IFINDEX 4 +#define ZEBRA_NEXTHOP_IPV4_IFNAME 5 +#define ZEBRA_NEXTHOP_IPV6 6 +#define ZEBRA_NEXTHOP_IPV6_IFINDEX 7 +#define ZEBRA_NEXTHOP_IPV6_IFNAME 8 +#define ZEBRA_NEXTHOP_BLACKHOLE 9 + +#ifndef INADDR_LOOPBACK +#define INADDR_LOOPBACK 0x7f000001 /* Internet address 127.0.0.1. */ +#endif + +/* Address family numbers from RFC1700. */ +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_ENCAP 7 /* per IANA */ +#define SAFI_MAX 8 + +/* Default Administrative Distance of each protocol. */ +#define ZEBRA_KERNEL_DISTANCE_DEFAULT 0 +#define ZEBRA_CONNECT_DISTANCE_DEFAULT 0 +#define ZEBRA_STATIC_DISTANCE_DEFAULT 1 +#define ZEBRA_RIP_DISTANCE_DEFAULT 120 +#define ZEBRA_RIPNG_DISTANCE_DEFAULT 120 +#define ZEBRA_OSPF_DISTANCE_DEFAULT 110 +#define ZEBRA_OSPF6_DISTANCE_DEFAULT 110 +#define ZEBRA_ISIS_DISTANCE_DEFAULT 115 +#define ZEBRA_IBGP_DISTANCE_DEFAULT 200 +#define ZEBRA_EBGP_DISTANCE_DEFAULT 20 + +/* Flag manipulation macros. */ +#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 + +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; + +/* VRF ID type. */ +typedef u_int16_t vrf_id_t; + +typedef uint32_t route_tag_t; +#define ROUTE_TAG_MAX UINT32_MAX + +#endif /* _ZEBRA_H */ diff --git a/m4/.gitignore b/m4/.gitignore new file mode 100644 index 0000000..3f3bd0a --- /dev/null +++ b/m4/.gitignore @@ -0,0 +1,7 @@ +Makefile +Makefile.in +.arch-inventory +.arch-ids +*~ +*.loT + diff --git a/m4/Makefile.am b/m4/Makefile.am new file mode 100644 index 0000000..49a29bd --- /dev/null +++ b/m4/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST=Makefile.am README.txt diff --git a/m4/README.txt b/m4/README.txt new file mode 100644 index 0000000..ce06853 --- /dev/null +++ b/m4/README.txt @@ -0,0 +1,18 @@ +This directory contains local additions to/overrides for the Quagga +autoconf build system. + +At this time additions are: + +- m4 files taken from libtool CVS circa august 2004. These have been +imported into Quagga as they are more robust with respect to configuring +libtool support for languages which Quagga does not use. As and when libtool +releases become commonly available with that capability, these can be +removed. The files are: + + argz.m4 + libtool.m4 + ltdl.m4 + ltoptions.m4 + ltsugar.m4 + ltversion.m4 + diff --git a/m4/ax_sys_weak_alias.m4 b/m4/ax_sys_weak_alias.m4 new file mode 100644 index 0000000..27b0f0c --- /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 0000000..a9f70e2 --- /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 0000000..3fe1f65 --- /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 0000000..af358fa --- /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 0000000..b1f49aa --- /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 0000000..75a16ea --- /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 0000000..32f21ed --- /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 0000000..f05596b --- /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 0000000..a418eca --- /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 0000000..93998dc --- /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 0000000..e5e3bbf --- /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 0000000..447a814 --- /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 0000000..34cb065 --- /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 0000000..4ea8076 --- /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 0000000..56ea271 --- /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 0000000..d77ec0b --- /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 0000000..5d2866a --- /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 0000000..5095d55 --- /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 0000000..a4bc9fa --- /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 0000000..a80f3e0 --- /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 0000000..60c6392 --- /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 0000000..f9e1ee0 --- /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 0000000..91269f2 --- /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 0000000..9222ad4 --- /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 0000000..0fbe8b0 --- /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 0000000..24b3199 --- /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 0000000..07bdb73 --- /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 0000000..5491bac --- /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 0000000..24b900b --- /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 0000000..ead7cfd --- /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 0000000..73d7073 --- /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 0000000..2216d97 --- /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 0000000..2cd630b --- /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/.gitignore b/ospf6d/.gitignore new file mode 100644 index 0000000..3fef0b7 --- /dev/null +++ b/ospf6d/.gitignore @@ -0,0 +1,18 @@ +Makefile.in +Makefile +*.o +*.patch +ospf6d +ospf6d.conf +tags +TAGS +.deps +.nfs* +*.lo +*.la +*.libs +.arch-inventory +.arch-ids +*~ +*.loT +*.a diff --git a/ospf6d/Makefile.am b/ospf6d/Makefile.am new file mode 100644 index 0000000..75f2257 --- /dev/null +++ b/ospf6d/Makefile.am @@ -0,0 +1,32 @@ +## Process this file with automake to produce Makefile.in. + +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 = $(WERROR) + +noinst_LIBRARIES = libospf6.a +sbin_PROGRAMS = ospf6d + +libospf6_a_SOURCES = \ + ospf6_network.c ospf6_message.c ospf6_lsa.c ospf6_lsdb.c \ + ospf6_top.c ospf6_area.c ospf6_interface.c ospf6_neighbor.c \ + ospf6_flood.c ospf6_route.c ospf6_intra.c ospf6_zebra.c \ + ospf6_spf.c ospf6_proto.c ospf6_asbr.c ospf6_abr.c ospf6_snmp.c \ + ospf6d.c + +noinst_HEADERS = \ + ospf6_network.h ospf6_message.h ospf6_lsa.h ospf6_lsdb.h \ + ospf6_top.h ospf6_area.h ospf6_interface.h ospf6_neighbor.h \ + ospf6_flood.h ospf6_route.h ospf6_intra.h ospf6_zebra.h \ + ospf6_spf.h ospf6_proto.h ospf6_asbr.h ospf6_abr.h ospf6_snmp.h \ + ospf6d.h + +ospf6d_SOURCES = \ + ospf6_main.c $(libospf6_a_SOURCES) + +ospf6d_LDADD = ../lib/libzebra.la @LIBCAP@ + +examplesdir = $(exampledir) +dist_examples_DATA = ospf6d.conf.sample diff --git a/ospf6d/OSPFv3-MIB.txt b/ospf6d/OSPFv3-MIB.txt new file mode 100644 index 0000000..258f533 --- /dev/null +++ b/ospf6d/OSPFv3-MIB.txt @@ -0,0 +1,3951 @@ + 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 new file mode 100644 index 0000000..f5a0046 --- /dev/null +++ b/ospf6d/README @@ -0,0 +1,102 @@ + + Zebra OSPF daemon for IPv6 network + + 2003/08/18 + +README for newer code is not yet. General usage should remain +the same. For further usage, see command helps by typing '?' +in vty, and then imagin ! ;p) Previous README contents follows. + + Zebra OSPF daemon for IPv6 network + + 2001/12/20 + +Zebra OSPF6d is OSPF version 3 daemon which is specified by +"OSPF for IPv6" (RFC 2740). + +*** NOTE *** + Zebra ospf6d is in development yet. It may lack some functionalities, + and may have some bugs. Use the latest version from the anoncvs + repository (http://www.zebra.org/cvs.html) ! + +This file README is like memo yet, so please feel free to ask + by E-mail. Patches will be appriciated. + +ospf6d's vty port was default to 2606/tcp. +Use commands below. + +VIEW NODE: + show ipv6 ospf6 + To see Router-ID, uptime of ospf6d, some statistics. + + show ipv6 ospf6 database ... + This command shows LSA database. You can specify + LS-type/LS-ID/Advertising-Router of LSAs. '*' is recognized. + + show ipv6 ospf6 interface ... + To see the status of the OSPF interface, and the configuration + like interface costs. + + show ipv6 ospf6 neighbor ... + Shows state of neighbors and choosed (Backup) DR on the I/F. + + show ipv6 ospf6 route (X::X) + This command shows internal routing table of the ospf6d. + Routes not calculated by OSPFv3 (like connected routes) + are not shown. If Address is specified (X::X), shows the route + that the address matches. + + show ipv6 ospf6 route redistribute (X::X) + Shows the routes advertised as AS-External routes by the router + itself. If Address is specified (X::X), shows the route + that the address matches. + +CONFIG NODE: + interface NAME + To enter INTERFACE NODE + + router ospf6 ... + To enter OSPF6 NODE + +INTERFACE NODE: + ipv6 ospf6 cost COST + 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 + + ipv6 ospf6 dead-interval DEADINTERVAL + Sets the interface's Router Dead Interval. default 40 + + ipv6 ospf6 retransmit-interval RETRANSMITINTERVAL + Sets the interface's Rxmt Interval. default 5 + + ipv6 ospf6 priority PRIORITY + Sets the interface's Router Priority. default 1 + + ipv6 ospf6 transmit-delay TRANSMITDELAY + Sets the interface's Inf-Trans-Delay. default 1 + +OSPF6 NODE: + router-id A.B.C.D + Sets the router's Router-ID + + interface NAME area AREA + 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. + +-- +Yasuhiro Ohara +Kunihiro Ishiguro + diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c new file mode 100644 index 0000000..7e94cef --- /dev/null +++ b/ospf6d/ospf6_abr.c @@ -0,0 +1,945 @@ +/* + * Area Border Router function. + * Copyright (C) 2004 Yasuhiro Ohara + * + * 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 "prefix.h" +#include "table.h" +#include "vty.h" +#include "linklist.h" +#include "command.h" +#include "thread.h" +#include "plist.h" +#include "filter.h" + +#include "ospf6_proto.h" +#include "ospf6_route.h" +#include "ospf6_lsa.h" +#include "ospf6_route.h" +#include "ospf6_lsdb.h" +#include "ospf6_message.h" + +#include "ospf6_top.h" +#include "ospf6_area.h" +#include "ospf6_interface.h" +#include "ospf6_neighbor.h" + +#include "ospf6_flood.h" +#include "ospf6_intra.h" +#include "ospf6_abr.h" +#include "ospf6d.h" + +unsigned char conf_debug_ospf6_abr; + +int +ospf6_is_router_abr (struct ospf6 *o) +{ + struct listnode *node; + struct ospf6_area *oa; + int area_count = 0; + + for (ALL_LIST_ELEMENTS_RO (o->area_list, node, oa)) + if (IS_AREA_ENABLED (oa)) + area_count++; + + if (area_count > 1) + return 1; + return 0; +} + +void +ospf6_abr_enable_area (struct ospf6_area *area) +{ + struct ospf6_area *oa; + struct ospf6_route *ro; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS (area->ospf6->area_list, node, nnode, oa)) + { + /* update B bit for each area */ + OSPF6_ROUTER_LSA_SCHEDULE (oa); + + /* install other area's configured address range */ + if (oa != area) + { + for (ro = ospf6_route_head (oa->range_table); ro; + ro = ospf6_route_next (ro)) + { + if (CHECK_FLAG (ro->flag, OSPF6_ROUTE_ACTIVE_SUMMARY)) + ospf6_abr_originate_summary_to_area (ro, area); + } + } + } + + /* install calculated routes to border routers */ + for (ro = ospf6_route_head (area->ospf6->brouter_table); ro; + ro = ospf6_route_next (ro)) + ospf6_abr_originate_summary_to_area (ro, area); + + /* install calculated routes to network (may be rejected by ranges) */ + for (ro = ospf6_route_head (area->ospf6->route_table); ro; + ro = ospf6_route_next (ro)) + ospf6_abr_originate_summary_to_area (ro, area); +} + +void +ospf6_abr_disable_area (struct ospf6_area *area) +{ + struct ospf6_area *oa; + 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 = 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) + ospf6_lsa_purge (old); + ospf6_route_remove (ro, area->summary_prefix); + } + + /* Withdraw all summary router-routes previously originated */ + 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) + ospf6_lsa_purge (old); + ospf6_route_remove (ro, area->summary_router); + } + + /* Schedule Router-LSA for each area (ABR status may change) */ + for (ALL_LIST_ELEMENTS (area->ospf6->area_list, node, nnode, oa)) + /* update B bit for each area */ + OSPF6_ROUTER_LSA_SCHEDULE (oa); +} + +/* RFC 2328 12.4.3. Summary-LSAs */ +void +ospf6_abr_originate_summary_to_area (struct ospf6_route *route, + struct ospf6_area *area) +{ + struct ospf6_lsa *lsa, *old = NULL; + struct ospf6_interface *oi; + struct ospf6_route *summary, *range = NULL; + struct ospf6_area *route_area; + char buffer[OSPF6_MAX_LSASIZE]; + struct ospf6_lsa_header *lsa_header; + caddr_t p; + struct ospf6_inter_prefix_lsa *prefix_lsa; + struct ospf6_inter_router_lsa *router_lsa; + struct ospf6_route_table *summary_table = NULL; + u_int16_t type; + char buf[64]; + int is_debug = 0; + + if (route->type == OSPF6_DEST_TYPE_ROUTER) + { + if (IS_OSPF6_DEBUG_ABR || IS_OSPF6_DEBUG_ORIGINATE (INTER_ROUTER)) + { + is_debug++; + inet_ntop (AF_INET, &(ADV_ROUTER_IN_PREFIX (&route->prefix)), + buf, sizeof (buf)); + zlog_debug ("Originating summary in area %s for ASBR %s", + area->name, buf); + } + summary_table = area->summary_router; + } + else + { + if (IS_OSPF6_DEBUG_ABR || IS_OSPF6_DEBUG_ORIGINATE (INTER_PREFIX)) + { + is_debug++; + prefix2str (&route->prefix, buf, sizeof (buf)); + zlog_debug ("Originating summary in area %s for %s", + area->name, buf); + } + summary_table = area->summary_prefix; + } + + summary = ospf6_route_lookup (&route->prefix, summary_table); + if (summary) + old = ospf6_lsdb_lookup (summary->path.origin.type, + summary->path.origin.id, + area->ospf6->router_id, area->lsdb); + + /* if this route has just removed, remove corresponding LSA */ + if (CHECK_FLAG (route->flag, OSPF6_ROUTE_REMOVE)) + { + if (is_debug) + zlog_debug ("The route has just removed, purge previous LSA"); + if (summary) + ospf6_route_remove (summary, summary_table); + if (old) + ospf6_lsa_purge (old); + return; + } + + /* Only destination type network, range or ASBR are considered */ + if (route->type != OSPF6_DEST_TYPE_NETWORK && + route->type != OSPF6_DEST_TYPE_RANGE && + (route->type != OSPF6_DEST_TYPE_ROUTER || + ! CHECK_FLAG (route->path.router_bits, OSPF6_ROUTER_BIT_E))) + { + if (is_debug) + zlog_debug ("Route type is none of network, range nor ASBR, withdraw"); + if (summary) + ospf6_route_remove (summary, summary_table); + if (old) + ospf6_lsa_purge (old); + return; + } + + /* AS External routes are never considered */ + if (route->path.type == OSPF6_PATH_TYPE_EXTERNAL1 || + route->path.type == OSPF6_PATH_TYPE_EXTERNAL2) + { + if (is_debug) + zlog_debug ("Path type is external, withdraw"); + if (summary) + ospf6_route_remove (summary, summary_table); + if (old) + ospf6_lsa_purge (old); + return; + } + + /* do not generate if the path's area is the same as target area */ + if (route->path.area_id == area->area_id) + { + if (is_debug) + zlog_debug ("The route is in the area itself, ignore"); + if (summary) + ospf6_route_remove (summary, summary_table); + if (old) + ospf6_lsa_purge (old); + return; + } + + /* do not generate if the nexthops belongs to the target area */ + oi = ospf6_interface_lookup_by_ifindex (route->nexthop[0].ifindex); + if (oi && oi->area && oi->area == area) + { + if (is_debug) + zlog_debug ("The route's nexthop is in the same area, ignore"); + if (summary) + ospf6_route_remove (summary, summary_table); + if (old) + ospf6_lsa_purge (old); + return; + } + + /* do not generate if the route cost is greater or equal to LSInfinity */ + if (route->path.cost >= OSPF_LS_INFINITY) + { + if (is_debug) + zlog_debug ("The cost exceeds LSInfinity, withdraw"); + if (summary) + ospf6_route_remove (summary, summary_table); + if (old) + ospf6_lsa_purge (old); + return; + } + + /* if this is a route to ASBR */ + if (route->type == OSPF6_DEST_TYPE_ROUTER) + { + /* Only the prefered best path is considered */ + if (! CHECK_FLAG (route->flag, OSPF6_ROUTE_BEST)) + { + if (is_debug) + zlog_debug ("This is the secondary path to the ASBR, ignore"); + if (summary) + ospf6_route_remove (summary, summary_table); + if (old) + ospf6_lsa_purge (old); + return; + } + + /* Do not generate if the area is stub */ + /* XXX */ + } + + /* if this is an intra-area route, this may be suppressed by aggregation */ + if (route->type == OSPF6_DEST_TYPE_NETWORK && + route->path.type == OSPF6_PATH_TYPE_INTRA) + { + /* search for configured address range for the route's area */ + route_area = ospf6_area_lookup (route->path.area_id, area->ospf6); + assert (route_area); + range = ospf6_route_lookup_bestmatch (&route->prefix, + route_area->range_table); + + /* 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 != OSPF_AREA_BACKBONE || + ! IS_AREA_TRANSIT (area))) + { + if (is_debug) + { + prefix2str (&range->prefix, buf, sizeof (buf)); + zlog_debug ("Suppressed by range %s of area %s", + buf, route_area->name); + } + + if (summary) + ospf6_route_remove (summary, summary_table); + if (old) + ospf6_lsa_purge (old); + return; + } + } + + /* If this is a configured address range */ + if (route->type == OSPF6_DEST_TYPE_RANGE) + { + /* If DoNotAdvertise is set */ + if (CHECK_FLAG (route->flag, OSPF6_ROUTE_DO_NOT_ADVERTISE)) + { + if (is_debug) + zlog_debug ("This is the range with DoNotAdvertise set. ignore"); + if (summary) + ospf6_route_remove (summary, summary_table); + if (old) + ospf6_lsa_purge (old); + return; + } + + /* Whether the route have active longer prefix */ + if (! CHECK_FLAG (route->flag, OSPF6_ROUTE_ACTIVE_SUMMARY)) + { + if (is_debug) + zlog_debug ("The range is not active. withdraw"); + if (summary) + ospf6_route_remove (summary, summary_table); + if (old) + ospf6_lsa_purge (old); + return; + } + } + + /* Check export list */ + if (EXPORT_NAME (area)) + { + if (EXPORT_LIST (area) == NULL) + EXPORT_LIST (area) = + access_list_lookup (AFI_IP6, EXPORT_NAME (area)); + + if (EXPORT_LIST (area)) + if (access_list_apply (EXPORT_LIST (area), + &route->prefix) == FILTER_DENY) + { + if (is_debug) + { + inet_ntop (AF_INET, &(ADV_ROUTER_IN_PREFIX (&route->prefix)), + buf, sizeof(buf)); + zlog_debug ("prefix %s was denied by export list", buf); + } + return; + } + } + + /* Check filter-list */ + if (PREFIX_NAME_OUT (area)) + { + if (PREFIX_LIST_OUT (area) == NULL) + PREFIX_LIST_OUT (area) = + prefix_list_lookup(AFI_IP6, PREFIX_NAME_OUT (area)); + + if (PREFIX_LIST_OUT (area)) + if (prefix_list_apply (PREFIX_LIST_OUT (area), + &route->prefix) != PREFIX_PERMIT) + { + if (is_debug) + { + inet_ntop (AF_INET, &(ADV_ROUTER_IN_PREFIX (&route->prefix)), + buf, sizeof (buf)); + zlog_debug ("prefix %s was denied by filter-list out", buf); + } + return; + } + } + + /* the route is going to be originated. store it in area's summary_table */ + if (summary == NULL) + { + summary = ospf6_route_copy (route); + if (route->type == OSPF6_DEST_TYPE_NETWORK || + route->type == OSPF6_DEST_TYPE_RANGE) + summary->path.origin.type = htons (OSPF6_LSTYPE_INTER_PREFIX); + else + summary->path.origin.type = htons (OSPF6_LSTYPE_INTER_ROUTER); + summary->path.origin.adv_router = area->ospf6->router_id; + summary->path.origin.id = + ospf6_new_ls_id (summary->path.origin.type, + summary->path.origin.adv_router, area->lsdb); + summary = ospf6_route_add (summary, summary_table); + } + else + { + summary->type = route->type; + quagga_gettime (QUAGGA_CLK_MONOTONIC, &summary->changed); + } + + summary->path.router_bits = route->path.router_bits; + summary->path.options[0] = route->path.options[0]; + summary->path.options[1] = route->path.options[1]; + summary->path.options[2] = route->path.options[2]; + summary->path.prefix_options = route->path.prefix_options; + summary->path.area_id = area->area_id; + summary->path.type = OSPF6_PATH_TYPE_INTER; + summary->path.cost = route->path.cost; + summary->nexthop[0] = route->nexthop[0]; + + /* prepare buffer */ + memset (buffer, 0, sizeof (buffer)); + lsa_header = (struct ospf6_lsa_header *) buffer; + + if (route->type == OSPF6_DEST_TYPE_ROUTER) + { + router_lsa = (struct ospf6_inter_router_lsa *) + ((caddr_t) lsa_header + sizeof (struct ospf6_lsa_header)); + p = (caddr_t) router_lsa + sizeof (struct ospf6_inter_router_lsa); + + /* Fill Inter-Area-Router-LSA */ + router_lsa->options[0] = route->path.options[0]; + router_lsa->options[1] = route->path.options[1]; + router_lsa->options[2] = route->path.options[2]; + OSPF6_ABR_SUMMARY_METRIC_SET (router_lsa, route->path.cost); + router_lsa->router_id = ADV_ROUTER_IN_PREFIX (&route->prefix); + type = htons (OSPF6_LSTYPE_INTER_ROUTER); + } + else + { + prefix_lsa = (struct ospf6_inter_prefix_lsa *) + ((caddr_t) lsa_header + sizeof (struct ospf6_lsa_header)); + p = (caddr_t) prefix_lsa + sizeof (struct ospf6_inter_prefix_lsa); + + /* Fill Inter-Area-Prefix-LSA */ + OSPF6_ABR_SUMMARY_METRIC_SET (prefix_lsa, route->path.cost); + prefix_lsa->prefix.prefix_length = route->prefix.prefixlen; + prefix_lsa->prefix.prefix_options = route->path.prefix_options; + + /* set Prefix */ + memcpy (p, &route->prefix.u.prefix6, + OSPF6_PREFIX_SPACE (route->prefix.prefixlen)); + ospf6_prefix_apply_mask (&prefix_lsa->prefix); + p += OSPF6_PREFIX_SPACE (route->prefix.prefixlen); + type = htons (OSPF6_LSTYPE_INTER_PREFIX); + } + + /* Fill LSA Header */ + lsa_header->age = 0; + lsa_header->type = type; + lsa_header->id = summary->path.origin.id; + lsa_header->adv_router = area->ospf6->router_id; + lsa_header->seqnum = + ospf6_new_ls_seqnum (lsa_header->type, lsa_header->id, + lsa_header->adv_router, area->lsdb); + lsa_header->length = htons ((caddr_t) p - (caddr_t) lsa_header); + + /* LSA checksum */ + ospf6_lsa_checksum (lsa_header); + + /* create LSA */ + lsa = ospf6_lsa_create (lsa_header); + + /* Originate */ + ospf6_lsa_originate_area (lsa, area); +} + +static void +ospf6_abr_range_update (struct ospf6_route *range) +{ + u_int32_t cost = 0; + struct ospf6_route *ro; + + assert (range->type == OSPF6_DEST_TYPE_RANGE); + + /* update range's cost and active flag */ + for (ro = ospf6_route_match_head (&range->prefix, ospf6->route_table); + ro; ro = ospf6_route_match_next (&range->prefix, ro)) + { + if (ro->path.area_id == range->path.area_id && + ! CHECK_FLAG (ro->flag, OSPF6_ROUTE_REMOVE)) + cost = MAX (cost, ro->path.cost); + } + + if (range->path.cost != cost) + { + range->path.cost = cost; + + if (range->path.cost) + SET_FLAG (range->flag, OSPF6_ROUTE_ACTIVE_SUMMARY); + else + UNSET_FLAG (range->flag, OSPF6_ROUTE_ACTIVE_SUMMARY); + + ospf6_abr_originate_summary (range); + } +} + +void +ospf6_abr_originate_summary (struct ospf6_route *route) +{ + struct listnode *node, *nnode; + struct ospf6_area *oa; + struct ospf6_route *range = NULL; + + if (route->type == OSPF6_DEST_TYPE_NETWORK) + { + oa = ospf6_area_lookup (route->path.area_id, ospf6); + range = ospf6_route_lookup_bestmatch (&route->prefix, oa->range_table); + if (range) + ospf6_abr_range_update (range); + } + + for (ALL_LIST_ELEMENTS (ospf6->area_list, node, nnode, oa)) + ospf6_abr_originate_summary_to_area (route, oa); +} + +/* RFC 2328 16.2. Calculating the inter-area routes */ +void +ospf6_abr_examin_summary (struct ospf6_lsa *lsa, struct ospf6_area *oa) +{ + struct prefix prefix, abr_prefix; + struct ospf6_route_table *table = NULL; + struct ospf6_route *range, *route, *old = NULL; + struct ospf6_route *abr_entry; + u_char type = 0; + char options[3] = {0, 0, 0}; + u_int8_t prefix_options = 0; + u_int32_t cost = 0; + u_char router_bits = 0; + 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)) + { + if (IS_OSPF6_DEBUG_EXAMIN (INTER_PREFIX)) + { + is_debug++; + zlog_debug ("Examin %s in area %s", lsa->name, oa->name); + } + + prefix_lsa = (struct ospf6_inter_prefix_lsa *) + OSPF6_LSA_HEADER_END (lsa->header); + prefix.family = AF_INET6; + prefix.prefixlen = prefix_lsa->prefix.prefix_length; + ospf6_prefix_in6_addr (&prefix.u.prefix6, &prefix_lsa->prefix); + if (is_debug) + prefix2str (&prefix, buf, sizeof (buf)); + table = oa->ospf6->route_table; + type = OSPF6_DEST_TYPE_NETWORK; + prefix_options = prefix_lsa->prefix.prefix_options; + cost = OSPF6_ABR_SUMMARY_METRIC (prefix_lsa); + } + else if (lsa->header->type == htons (OSPF6_LSTYPE_INTER_ROUTER)) + { + if (IS_OSPF6_DEBUG_EXAMIN (INTER_ROUTER)) + { + is_debug++; + zlog_debug ("Examin %s in area %s", lsa->name, oa->name); + } + + router_lsa = (struct ospf6_inter_router_lsa *) + OSPF6_LSA_HEADER_END (lsa->header); + ospf6_linkstate_prefix (router_lsa->router_id, htonl (0), &prefix); + if (is_debug) + inet_ntop (AF_INET, &router_lsa->router_id, buf, sizeof (buf)); + table = oa->ospf6->brouter_table; + type = OSPF6_DEST_TYPE_ROUTER; + options[0] = router_lsa->options[0]; + options[1] = router_lsa->options[1]; + options[2] = router_lsa->options[2]; + cost = OSPF6_ABR_SUMMARY_METRIC (router_lsa); + SET_FLAG (router_bits, OSPF6_ROUTER_BIT_E); + } + else + assert (0); + + /* Find existing route */ + route = ospf6_route_lookup (&prefix, table); + if (route) + ospf6_route_lock (route); + while (route && ospf6_route_is_prefix (&prefix, route)) + { + if (route->path.area_id == oa->area_id && + route->path.origin.type == lsa->header->type && + route->path.origin.id == lsa->header->id && + route->path.origin.adv_router == lsa->header->adv_router && + ! CHECK_FLAG (route->flag, OSPF6_ROUTE_WAS_REMOVED)) + old = route; + route = ospf6_route_next (route); + } + + /* (1) if cost == LSInfinity or if the LSA is MaxAge */ + if (cost == OSPF_LS_INFINITY) + { + if (is_debug) + zlog_debug ("cost is LS_INFINITY, ignore"); + if (old) + ospf6_route_remove (old, table); + return; + } + if (OSPF6_LSA_IS_MAXAGE (lsa)) + { + if (is_debug) + zlog_debug ("LSA is MaxAge, ignore"); + if (old) + ospf6_route_remove (old, table); + return; + } + + /* (2) if the LSA is self-originated, ignore */ + if (lsa->header->adv_router == oa->ospf6->router_id) + { + if (is_debug) + zlog_debug ("LSA is self-originated, ignore"); + if (old) + ospf6_route_remove (old, table); + return; + } + + /* (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); + if (range) + { + if (is_debug) + zlog_debug ("Prefix is equal to address range, ignore"); + if (old) + 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 */ + ospf6_linkstate_prefix (lsa->header->adv_router, htonl (0), &abr_prefix); + abr_entry = ospf6_route_lookup (&abr_prefix, oa->ospf6->brouter_table); + if (abr_entry == NULL || abr_entry->path.area_id != oa->area_id || + CHECK_FLAG (abr_entry->flag, OSPF6_ROUTE_REMOVE) || + ! CHECK_FLAG (abr_entry->path.router_bits, OSPF6_ROUTER_BIT_B)) + { + if (is_debug) + zlog_debug ("ABR router entry does not exist, ignore"); + if (old) + ospf6_route_remove (old, table); + return; + } + + /* Check import list */ + if (IMPORT_NAME (oa)) + { + if (IMPORT_LIST (oa) == NULL) + IMPORT_LIST (oa) = access_list_lookup (AFI_IP6, IMPORT_NAME (oa)); + + if (IMPORT_LIST (oa)) + if (access_list_apply (IMPORT_LIST (oa), &prefix) == FILTER_DENY) + { + if (is_debug) + zlog_debug ("Prefix was denied by import-list"); + if (old) + ospf6_route_remove (old, table); + return; + } + } + + /* Check input prefix-list */ + if (PREFIX_NAME_IN (oa)) + { + if (PREFIX_LIST_IN (oa) == NULL) + PREFIX_LIST_IN (oa) = prefix_list_lookup (AFI_IP6, PREFIX_NAME_IN (oa)); + + if (PREFIX_LIST_IN (oa)) + if (prefix_list_apply (PREFIX_LIST_IN (oa), &prefix) != PREFIX_PERMIT) + { + if (is_debug) + zlog_debug ("Prefix was denied by prefix-list"); + if (old) + ospf6_route_remove (old, table); + return; + } + } + + /* (5),(6),(7) the path preference is handled by the sorting + in the routing table. Always install the path by substituting + old route (if any). */ + if (old) + route = ospf6_route_copy (old); + else + route = ospf6_route_create (); + + route->type = type; + route->prefix = prefix; + route->path.origin.type = lsa->header->type; + route->path.origin.id = lsa->header->id; + route->path.origin.adv_router = lsa->header->adv_router; + route->path.router_bits = router_bits; + route->path.options[0] = options[0]; + route->path.options[1] = options[1]; + route->path.options[2] = options[2]; + route->path.prefix_options = prefix_options; + route->path.area_id = oa->area_id; + route->path.type = OSPF6_PATH_TYPE_INTER; + route->path.cost = abr_entry->path.cost + cost; + for (i = 0; i < OSPF6_MULTI_PATH_LIMIT; i++) + route->nexthop[i] = abr_entry->nexthop[i]; + + if (is_debug) + zlog_debug ("Install route: %s", buf); + ospf6_route_add (route, table); +} + +void +ospf6_abr_examin_brouter (u_int32_t router_id) +{ + struct ospf6_lsa *lsa; + struct ospf6_area *oa; + struct listnode *node, *nnode; + u_int16_t type; + + for (ALL_LIST_ELEMENTS (ospf6->area_list, node, nnode, oa)) + { + type = htons (OSPF6_LSTYPE_INTER_ROUTER); + for (lsa = ospf6_lsdb_type_router_head (type, router_id, oa->lsdb); lsa; + lsa = ospf6_lsdb_type_router_next (type, router_id, lsa)) + ospf6_abr_examin_summary (lsa, oa); + + type = htons (OSPF6_LSTYPE_INTER_PREFIX); + for (lsa = ospf6_lsdb_type_router_head (type, router_id, oa->lsdb); lsa; + lsa = ospf6_lsdb_type_router_next (type, router_id, lsa)) + ospf6_abr_examin_summary (lsa, oa); + } +} + +void +ospf6_abr_reimport (struct ospf6_area *oa) +{ + struct ospf6_lsa *lsa; + u_int16_t type; + + type = htons (OSPF6_LSTYPE_INTER_ROUTER); + for (lsa = ospf6_lsdb_type_head (type, oa->lsdb); lsa; + lsa = ospf6_lsdb_type_next (type, lsa)) + ospf6_abr_examin_summary (lsa, oa); + + type = htons (OSPF6_LSTYPE_INTER_PREFIX); + for (lsa = ospf6_lsdb_type_head (type, oa->lsdb); lsa; + lsa = ospf6_lsdb_type_next (type, lsa)) + ospf6_abr_examin_summary (lsa, 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; + char buf[INET6_ADDRSTRLEN]; + + prefix_lsa = (struct ospf6_inter_prefix_lsa *) + OSPF6_LSA_HEADER_END (lsa->header); + + vty_out (vty, " Metric: %lu%s", + (u_long) OSPF6_ABR_SUMMARY_METRIC (prefix_lsa), VNL); + + ospf6_prefix_options_printbuf (prefix_lsa->prefix.prefix_options, + buf, sizeof (buf)); + vty_out (vty, " Prefix Options: %s%s", buf, 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) +{ + struct ospf6_inter_router_lsa *router_lsa; + char buf[64]; + + router_lsa = (struct ospf6_inter_router_lsa *) + OSPF6_LSA_HEADER_END (lsa->header); + + ospf6_options_printbuf (router_lsa->options, buf, sizeof (buf)); + 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); + + return 0; +} + +/* Debug commands */ +DEFUN (debug_ospf6_abr, + debug_ospf6_abr_cmd, + "debug ospf6 abr", + DEBUG_STR + OSPF6_STR + "Debug OSPFv3 ABR function\n" + ) +{ + OSPF6_DEBUG_ABR_ON (); + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf6_abr, + no_debug_ospf6_abr_cmd, + "no debug ospf6 abr", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug OSPFv3 ABR function\n" + ) +{ + OSPF6_DEBUG_ABR_OFF (); + return CMD_SUCCESS; +} + +int +config_write_ospf6_debug_abr (struct vty *vty) +{ + if (IS_OSPF6_DEBUG_ABR) + vty_out (vty, "debug ospf6 abr%s", VNL); + return 0; +} + +void +install_element_ospf6_debug_abr (void) +{ + install_element (ENABLE_NODE, &debug_ospf6_abr_cmd); + install_element (ENABLE_NODE, &no_debug_ospf6_abr_cmd); + install_element (CONFIG_NODE, &debug_ospf6_abr_cmd); + install_element (CONFIG_NODE, &no_debug_ospf6_abr_cmd); +} + +struct ospf6_lsa_handler inter_prefix_handler = +{ + OSPF6_LSTYPE_INTER_PREFIX, + "Inter-Prefix", + "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", + "IAR", + ospf6_inter_area_router_lsa_show, + ospf6_inter_area_router_lsa_get_prefix_str, +}; + +void +ospf6_abr_init (void) +{ + ospf6_install_lsa_handler (&inter_prefix_handler); + ospf6_install_lsa_handler (&inter_router_handler); +} + + diff --git a/ospf6d/ospf6_abr.h b/ospf6d/ospf6_abr.h new file mode 100644 index 0000000..e5e2660 --- /dev/null +++ b/ospf6d/ospf6_abr.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2004 Yasuhiro Ohara + * + * 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 OSPF6_ABR_H +#define OSPF6_ABR_H + +/* 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; +#define OSPF6_DEBUG_ABR_ON() \ + (conf_debug_ospf6_abr = 1) +#define OSPF6_DEBUG_ABR_OFF() \ + (conf_debug_ospf6_abr = 0) +#define IS_OSPF6_DEBUG_ABR \ + (conf_debug_ospf6_abr) + +/* Inter-Area-Prefix-LSA */ +#define OSPF6_INTER_PREFIX_LSA_MIN_SIZE 4U /* w/o IPv6 prefix */ +struct ospf6_inter_prefix_lsa +{ + u_int32_t metric; + struct ospf6_prefix prefix; +}; + +/* Inter-Area-Router-LSA */ +#define OSPF6_INTER_ROUTER_LSA_FIX_SIZE 12U +struct ospf6_inter_router_lsa +{ + u_char mbz; + u_char options[3]; + u_int32_t metric; + u_int32_t router_id; +}; + +#define OSPF6_ABR_SUMMARY_METRIC(E) (ntohl ((E)->metric & htonl (0x00ffffff))) +#define OSPF6_ABR_SUMMARY_METRIC_SET(E,C) \ + { (E)->metric &= htonl (0x00000000); \ + (E)->metric |= htonl (0x00ffffff) & htonl (C); } + +extern int ospf6_is_router_abr (struct ospf6 *o); + +extern void ospf6_abr_enable_area (struct ospf6_area *oa); +extern void ospf6_abr_disable_area (struct ospf6_area *oa); + +extern void ospf6_abr_originate_summary_to_area (struct ospf6_route *route, + struct ospf6_area *area); +extern void ospf6_abr_originate_summary (struct ospf6_route *route); +extern void ospf6_abr_examin_summary (struct ospf6_lsa *lsa, struct ospf6_area *oa); +extern void ospf6_abr_examin_brouter (u_int32_t router_id); +extern void ospf6_abr_reimport (struct ospf6_area *oa); + +extern int config_write_ospf6_debug_abr (struct vty *vty); +extern void install_element_ospf6_debug_abr (void); +extern int ospf6_abr_config_write (struct vty *vty); + +extern void ospf6_abr_init (void); + +#endif /*OSPF6_ABR_H*/ diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c new file mode 100644 index 0000000..1861fe7 --- /dev/null +++ b/ospf6d/ospf6_area.c @@ -0,0 +1,816 @@ +/* + * Copyright (C) 2003 Yasuhiro Ohara + * + * 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 "memory.h" +#include "linklist.h" +#include "thread.h" +#include "vty.h" +#include "command.h" +#include "if.h" +#include "prefix.h" +#include "table.h" +#include "plist.h" +#include "filter.h" + +#include "ospf6_proto.h" +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" +#include "ospf6_route.h" +#include "ospf6_spf.h" +#include "ospf6_top.h" +#include "ospf6_area.h" +#include "ospf6_interface.h" +#include "ospf6_intra.h" +#include "ospf6_abr.h" +#include "ospf6d.h" + +int +ospf6_area_cmp (void *va, void *vb) +{ + struct ospf6_area *oa = (struct ospf6_area *) va; + struct ospf6_area *ob = (struct ospf6_area *) vb; + return (ntohl (oa->area_id) < ntohl (ob->area_id) ? -1 : 1); +} + +/* schedule routing table recalculation */ +static void +ospf6_area_lsdb_hook_add (struct ospf6_lsa *lsa) +{ + switch (ntohs (lsa->header->type)) + { + case OSPF6_LSTYPE_ROUTER: + case OSPF6_LSTYPE_NETWORK: + if (IS_OSPF6_DEBUG_EXAMIN_TYPE (lsa->header->type)) + { + zlog_debug ("Examin %s", lsa->name); + zlog_debug ("Schedule SPF Calculation for %s", + OSPF6_AREA (lsa->lsdb->data)->name); + } + ospf6_spf_schedule (OSPF6_PROCESS(OSPF6_AREA (lsa->lsdb->data)->ospf6), + ospf6_lsadd_to_spf_reason(lsa)); + break; + + case OSPF6_LSTYPE_INTRA_PREFIX: + ospf6_intra_prefix_lsa_add (lsa); + break; + + case OSPF6_LSTYPE_INTER_PREFIX: + case OSPF6_LSTYPE_INTER_ROUTER: + ospf6_abr_examin_summary (lsa, (struct ospf6_area *) lsa->lsdb->data); + break; + + default: + break; + } +} + +static void +ospf6_area_lsdb_hook_remove (struct ospf6_lsa *lsa) +{ + switch (ntohs (lsa->header->type)) + { + case OSPF6_LSTYPE_ROUTER: + case OSPF6_LSTYPE_NETWORK: + if (IS_OSPF6_DEBUG_EXAMIN_TYPE (lsa->header->type)) + { + zlog_debug ("LSA disappearing: %s", lsa->name); + zlog_debug ("Schedule SPF Calculation for %s", + OSPF6_AREA (lsa->lsdb->data)->name); + } + ospf6_spf_schedule (OSPF6_PROCESS(OSPF6_AREA (lsa->lsdb->data)->ospf6), + ospf6_lsremove_to_spf_reason(lsa)); + break; + + case OSPF6_LSTYPE_INTRA_PREFIX: + ospf6_intra_prefix_lsa_remove (lsa); + break; + + case OSPF6_LSTYPE_INTER_PREFIX: + case OSPF6_LSTYPE_INTER_ROUTER: + ospf6_abr_examin_summary (lsa, (struct ospf6_area *) lsa->lsdb->data); + break; + + default: + break; + } +} + +static void +ospf6_area_route_hook_add (struct ospf6_route *route) +{ + struct ospf6_route *copy = ospf6_route_copy (route); + ospf6_route_add (copy, ospf6->route_table); +} + +static void +ospf6_area_route_hook_remove (struct ospf6_route *route) +{ + struct ospf6_route *copy; + + copy = ospf6_route_lookup_identical (route, ospf6->route_table); + if (copy) + ospf6_route_remove (copy, ospf6->route_table); +} + +/* Make new area structure */ +struct ospf6_area * +ospf6_area_create (u_int32_t area_id, struct ospf6 *o) +{ + struct ospf6_area *oa; + struct ospf6_route *route; + + oa = XCALLOC (MTYPE_OSPF6_AREA, sizeof (struct ospf6_area)); + + inet_ntop (AF_INET, &area_id, oa->name, sizeof (oa->name)); + oa->area_id = area_id; + oa->if_list = list_new (); + + oa->lsdb = ospf6_lsdb_create (oa); + oa->lsdb->hook_add = ospf6_area_lsdb_hook_add; + oa->lsdb->hook_remove = ospf6_area_lsdb_hook_remove; + oa->lsdb_self = ospf6_lsdb_create (oa); + + oa->spf_table = OSPF6_ROUTE_TABLE_CREATE (AREA, SPF_RESULTS); + oa->spf_table->scope = oa; + oa->route_table = OSPF6_ROUTE_TABLE_CREATE (AREA, ROUTES); + oa->route_table->scope = oa; + oa->route_table->hook_add = ospf6_area_route_hook_add; + oa->route_table->hook_remove = ospf6_area_route_hook_remove; + + oa->range_table = OSPF6_ROUTE_TABLE_CREATE (AREA, PREFIX_RANGES); + oa->range_table->scope = oa; + oa->summary_prefix = OSPF6_ROUTE_TABLE_CREATE (AREA, SUMMARY_PREFIXES); + oa->summary_prefix->scope = oa; + oa->summary_router = OSPF6_ROUTE_TABLE_CREATE (AREA, SUMMARY_ROUTERS); + oa->summary_router->scope = oa; + + /* set default options */ + 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); + + oa->ospf6 = o; + listnode_add_sort (o->area_list, oa); + + /* import athoer area's routes as inter-area routes */ + for (route = ospf6_route_head (o->route_table); route; + route = ospf6_route_next (route)) + ospf6_abr_originate_summary_to_area (route, oa); + + return oa; +} + +void +ospf6_area_delete (struct ospf6_area *oa) +{ + 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); + + /* 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); + ospf6_lsdb_delete (oa->lsdb_self); + + ospf6_spf_table_finish (oa->spf_table); + ospf6_route_table_delete (oa->spf_table); + ospf6_route_table_delete (oa->route_table); + + THREAD_OFF (oa->thread_spf_calculation); + THREAD_OFF (oa->thread_route_calculation); + + listnode_delete (oa->ospf6->area_list, oa); + oa->ospf6 = NULL; + + /* free area */ + XFREE (MTYPE_OSPF6_AREA, oa); +} + +struct ospf6_area * +ospf6_area_lookup (u_int32_t area_id, struct ospf6 *ospf6) +{ + struct ospf6_area *oa; + struct listnode *n; + + for (ALL_LIST_ELEMENTS_RO (ospf6->area_list, n, oa)) + if (oa->area_id == area_id) + return oa; + + return (struct ospf6_area *) NULL; +} + +static struct ospf6_area * +ospf6_area_get (u_int32_t area_id, struct ospf6 *o) +{ + struct ospf6_area *oa; + oa = ospf6_area_lookup (area_id, o); + if (oa == NULL) + oa = ospf6_area_create (area_id, o); + return oa; +} + +void +ospf6_area_enable (struct ospf6_area *oa) +{ + struct listnode *node, *nnode; + struct ospf6_interface *oi; + + SET_FLAG (oa->flag, OSPF6_AREA_ENABLE); + + for (ALL_LIST_ELEMENTS (oa->if_list, node, nnode, oi)) + ospf6_interface_enable (oi); + ospf6_abr_enable_area (oa); +} + +void +ospf6_area_disable (struct ospf6_area *oa) +{ + struct listnode *node, *nnode; + struct ospf6_interface *oi; + + UNSET_FLAG (oa->flag, OSPF6_AREA_ENABLE); + + 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) +{ + struct listnode *i; + struct ospf6_interface *oi; + + vty_out (vty, " Area %s%s", oa->name, VNL); + vty_out (vty, " Number of Area scoped LSAs is %u%s", + oa->lsdb->count, VNL); + + vty_out (vty, " Interface attached to this area:"); + for (ALL_LIST_ELEMENTS_RO (oa->if_list, i, oi)) + vty_out (vty, " %s", oi->interface->name); + + vty_out (vty, "%s", VNL); +} + + +#define OSPF6_CMD_AREA_LOOKUP(str, oa) \ +{ \ + u_int32_t area_id = 0; \ + if (inet_pton (AF_INET, str, &area_id) != 1) \ + { \ + vty_out (vty, "Malformed Area-ID: %s%s", str, VNL); \ + return CMD_SUCCESS; \ + } \ + oa = ospf6_area_lookup (area_id, ospf6); \ + if (oa == NULL) \ + { \ + vty_out (vty, "No such Area: %s%s", str, VNL); \ + return CMD_SUCCESS; \ + } \ +} + +#define OSPF6_CMD_AREA_GET(str, oa) \ +{ \ + u_int32_t area_id = 0; \ + if (inet_pton (AF_INET, str, &area_id) != 1) \ + { \ + vty_out (vty, "Malformed Area-ID: %s%s", str, VNL); \ + return CMD_SUCCESS; \ + } \ + oa = ospf6_area_get (area_id, ospf6); \ +} + +DEFUN (area_range, + area_range_cmd, + "area A.B.C.D range X:X::X:X/M", + "OSPF area parameters\n" + OSPF6_AREA_ID_STR + "Configured address range\n" + "Specify IPv6 prefix\n" + ) +{ + int ret; + struct ospf6_area *oa; + struct prefix prefix; + struct ospf6_route *range; + + OSPF6_CMD_AREA_GET (argv[0], oa); + argc--; + argv++; + + ret = str2prefix (argv[0], &prefix); + if (ret != 1 || prefix.family != AF_INET6) + { + vty_out (vty, "Malformed argument: %s%s", argv[0], VNL); + return CMD_SUCCESS; + } + argc--; + argv++; + + range = ospf6_route_lookup (&prefix, oa->range_table); + if (range == NULL) + { + range = ospf6_route_create (); + range->type = OSPF6_DEST_TYPE_RANGE; + range->prefix = prefix; + } + + if (argc) + { + if (! strcmp (argv[0], "not-advertise")) + SET_FLAG (range->flag, OSPF6_ROUTE_DO_NOT_ADVERTISE); + else if (! strcmp (argv[0], "advertise")) + UNSET_FLAG (range->flag, OSPF6_ROUTE_DO_NOT_ADVERTISE); + } + + if (range->rnode) + { + vty_out (vty, "Range already defined: %s%s", argv[-1], VNL); + return CMD_WARNING; + } + + ospf6_route_add (range, oa->range_table); + return CMD_SUCCESS; +} + +ALIAS (area_range, + area_range_advertise_cmd, + "area A.B.C.D range X:X::X:X/M (advertise|not-advertise)", + "OSPF area parameters\n" + OSPF6_AREA_ID_STR + "Configured address range\n" + "Specify IPv6 prefix\n" + ) + +DEFUN (no_area_range, + no_area_range_cmd, + "no area A.B.C.D range X:X::X:X/M", + "OSPF area parameters\n" + OSPF6_AREA_ID_STR + "Configured address range\n" + "Specify IPv6 prefix\n" + ) +{ + int ret; + struct ospf6_area *oa; + struct prefix prefix; + struct ospf6_route *range; + + OSPF6_CMD_AREA_GET (argv[0], oa); + argc--; + argv++; + + ret = str2prefix (argv[0], &prefix); + if (ret != 1 || prefix.family != AF_INET6) + { + vty_out (vty, "Malformed argument: %s%s", argv[0], VNL); + return CMD_SUCCESS; + } + + range = ospf6_route_lookup (&prefix, oa->range_table); + if (range == NULL) + { + vty_out (vty, "Range %s does not exists.%s", argv[0], VNL); + return CMD_SUCCESS; + } + + ospf6_route_remove (range, oa->range_table); + + return CMD_SUCCESS; +} + +void +ospf6_area_config_write (struct vty *vty) +{ + struct listnode *node; + struct ospf6_area *oa; + struct ospf6_route *range; + char buf[128]; + + for (ALL_LIST_ELEMENTS_RO (ospf6->area_list, node, oa)) + { + for (range = ospf6_route_head (oa->range_table); range; + range = ospf6_route_next (range)) + { + prefix2str (&range->prefix, buf, sizeof (buf)); + vty_out (vty, " area %s range %s%s", oa->name, buf, VNL); + } + if (PREFIX_NAME_IN (oa)) + vty_out (vty, " area %s filter-list prefix %s in%s", + oa->name, PREFIX_NAME_IN (oa), VNL); + if (PREFIX_NAME_OUT (oa)) + vty_out (vty, " area %s filter-list prefix %s out%s", + oa->name, PREFIX_NAME_OUT (oa), VNL); + if (IMPORT_NAME (oa)) + vty_out (vty, " area %s import-list %s%s", + oa->name, IMPORT_NAME (oa), VNL); + if (EXPORT_NAME (oa)) + vty_out (vty, " area %s export-list %s%s", + oa->name, EXPORT_NAME (oa), VNL); + } +} + +DEFUN (area_filter_list, + area_filter_list_cmd, + "area A.B.C.D filter-list prefix WORD (in|out)", + "OSPFv6 area parameters\n" + "OSPFv6 area ID in IP address format\n" + "Filter networks between OSPFv6 areas\n" + "Filter prefixes between OSPFv6 areas\n" + "Name of an IPv6 prefix-list\n" + "Filter networks sent to this area\n" + "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) + { + PREFIX_LIST_IN (area) = plist; + if (PREFIX_NAME_IN (area)) + free (PREFIX_NAME_IN (area)); + + PREFIX_NAME_IN (area) = strdup (argv[0]); + ospf6_abr_reimport (area); + } + else + { + PREFIX_LIST_OUT (area) = plist; + if (PREFIX_NAME_OUT (area)) + free (PREFIX_NAME_OUT (area)); + + PREFIX_NAME_OUT (area) = strdup (argv[0]); + ospf6_abr_enable_area (area); + } + + return CMD_SUCCESS; +} + +DEFUN (no_area_filter_list, + no_area_filter_list_cmd, + "no area A.B.C.D filter-list prefix WORD (in|out)", + NO_STR + "OSPFv6 area parameters\n" + "OSPFv6 area ID in IP address format\n" + "Filter networks between OSPFv6 areas\n" + "Filter prefixes between OSPFv6 areas\n" + "Name of an IPv6 prefix-list\n" + "Filter networks sent to this area\n" + "Filter networks sent from this area\n") +{ + struct ospf6_area *area; + + OSPF6_CMD_AREA_GET (argv[0], area); + argc--; + argv++; + + if (strncmp (argv[1], "in", 2) == 0) + { + if (PREFIX_NAME_IN (area)) + if (strcmp (PREFIX_NAME_IN (area), argv[0]) != 0) + return CMD_SUCCESS; + + PREFIX_LIST_IN (area) = NULL; + if (PREFIX_NAME_IN (area)) + free (PREFIX_NAME_IN (area)); + + PREFIX_NAME_IN (area) = NULL; + ospf6_abr_reimport (area); + } + else + { + if (PREFIX_NAME_OUT (area)) + if (strcmp (PREFIX_NAME_OUT (area), argv[0]) != 0) + return CMD_SUCCESS; + + PREFIX_LIST_OUT (area) = NULL; + if (PREFIX_NAME_OUT (area)) + free (PREFIX_NAME_OUT (area)); + + PREFIX_NAME_OUT (area) = NULL; + ospf6_abr_enable_area (area); + } + + return CMD_SUCCESS; +} + +DEFUN (area_import_list, + area_import_list_cmd, + "area A.B.C.D import-list NAME", + "OSPFv6 area parameters\n" + "OSPFv6 area ID in IP address format\n" + "Set the filter for networks from other areas announced to the specified one\n" + "Name of the acess-list\n") +{ + struct ospf6_area *area; + struct access_list *list; + + OSPF6_CMD_AREA_GET(argv[0], area); + + list = access_list_lookup (AFI_IP6, argv[1]); + + IMPORT_LIST (area) = list; + + if (IMPORT_NAME (area)) + free (IMPORT_NAME (area)); + + IMPORT_NAME (area) = strdup (argv[1]); + ospf6_abr_reimport (area); + + return CMD_SUCCESS; +} + +DEFUN (no_area_import_list, + no_area_import_list_cmd, + "no area A.B.C.D import-list NAME", + "OSPFv6 area parameters\n" + "OSPFv6 area ID in IP address format\n" + "Unset the filter for networks announced to other areas\n" + "NAme of the access-list\n") +{ + struct ospf6_area *area; + + OSPF6_CMD_AREA_GET(argv[0], area); + + IMPORT_LIST (area) = 0; + + if (IMPORT_NAME (area)) + free (IMPORT_NAME (area)); + + IMPORT_NAME (area) = NULL; + ospf6_abr_reimport (area); + + return CMD_SUCCESS; +} + +DEFUN (area_export_list, + area_export_list_cmd, + "area A.B.C.D export-list NAME", + "OSPFv6 area parameters\n" + "OSPFv6 area ID in IP address format\n" + "Set the filter for networks announced to other areas\n" + "Name of the acess-list\n") +{ + struct ospf6_area *area; + struct access_list *list; + + OSPF6_CMD_AREA_GET(argv[0], area); + + list = access_list_lookup (AFI_IP6, argv[1]); + + EXPORT_LIST (area) = list; + + if (EXPORT_NAME (area)) + free (EXPORT_NAME (area)); + + EXPORT_NAME (area) = strdup (argv[1]); + ospf6_abr_enable_area (area); + + return CMD_SUCCESS; +} + +DEFUN (no_area_export_list, + no_area_export_list_cmd, + "no area A.B.C.D export-list NAME", + "OSPFv6 area parameters\n" + "OSPFv6 area ID in IP address format\n" + "Unset the filter for networks announced to other areas\n" + "Name of the access-list\n") +{ + struct ospf6_area *area; + + OSPF6_CMD_AREA_GET(argv[0], area); + + EXPORT_LIST (area) = 0; + + if (EXPORT_NAME (area)) + free (EXPORT_NAME (area)); + + EXPORT_NAME (area) = NULL; + ospf6_abr_enable_area (area); + + return CMD_SUCCESS; +} + +DEFUN (show_ipv6_ospf6_spf_tree, + show_ipv6_ospf6_spf_tree_cmd, + "show ipv6 ospf6 spf tree", + SHOW_STR + IP6_STR + OSPF6_STR + "Shortest Path First caculation\n" + "Show SPF tree\n") +{ + struct listnode *node; + struct ospf6_area *oa; + struct ospf6_vertex *root; + 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)) + { + route = ospf6_route_lookup (&prefix, oa->spf_table); + if (route == NULL) + { + vty_out (vty, "LS entry for root not found in area %s%s", + oa->name, VNL); + continue; + } + root = (struct ospf6_vertex *) route->route_option; + ospf6_spf_display_subtree (vty, "", 0, root); + } + + return CMD_SUCCESS; +} + +DEFUN (show_ipv6_ospf6_area_spf_tree, + show_ipv6_ospf6_area_spf_tree_cmd, + "show ipv6 ospf6 area A.B.C.D spf tree", + SHOW_STR + IP6_STR + OSPF6_STR + OSPF6_AREA_STR + OSPF6_AREA_ID_STR + "Shortest Path First caculation\n" + "Show SPF tree\n") +{ + u_int32_t area_id; + struct ospf6_area *oa; + struct ospf6_vertex *root; + 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) + { + vty_out (vty, "Malformed Area-ID: %s%s", argv[0], VNL); + return CMD_SUCCESS; + } + oa = ospf6_area_lookup (area_id, ospf6); + if (oa == NULL) + { + vty_out (vty, "No such Area: %s%s", argv[0], VNL); + return CMD_SUCCESS; + } + + route = ospf6_route_lookup (&prefix, oa->spf_table); + if (route == NULL) + { + vty_out (vty, "LS entry for root not found in area %s%s", + oa->name, VNL); + return CMD_SUCCESS; + } + root = (struct ospf6_vertex *) route->route_option; + ospf6_spf_display_subtree (vty, "", 0, root); + + return CMD_SUCCESS; +} + +DEFUN (show_ipv6_ospf6_simulate_spf_tree_root, + show_ipv6_ospf6_simulate_spf_tree_root_cmd, + "show ipv6 ospf6 simulate spf-tree A.B.C.D area A.B.C.D", + SHOW_STR + IP6_STR + OSPF6_STR + "Shortest Path First caculation\n" + "Show SPF tree\n" + "Specify root's router-id to calculate another router's SPF tree\n") +{ + u_int32_t area_id; + struct ospf6_area *oa; + struct ospf6_vertex *root; + struct ospf6_route *route; + struct prefix prefix; + u_int32_t router_id; + 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); + + if (inet_pton (AF_INET, argv[1], &area_id) != 1) + { + vty_out (vty, "Malformed Area-ID: %s%s", argv[1], VNL); + return CMD_SUCCESS; + } + oa = ospf6_area_lookup (area_id, ospf6); + if (oa == NULL) + { + vty_out (vty, "No such Area: %s%s", argv[1], VNL); + return CMD_SUCCESS; + } + + tmp_debug_ospf6_spf = conf_debug_ospf6_spf; + conf_debug_ospf6_spf = 0; + + spf_table = OSPF6_ROUTE_TABLE_CREATE (NONE, SPF_RESULTS); + ospf6_spf_calculation (router_id, spf_table, oa); + + conf_debug_ospf6_spf = tmp_debug_ospf6_spf; + + route = ospf6_route_lookup (&prefix, spf_table); + if (route == NULL) + { + ospf6_spf_table_finish (spf_table); + ospf6_route_table_delete (spf_table); + return CMD_SUCCESS; + } + root = (struct ospf6_vertex *) route->route_option; + ospf6_spf_display_subtree (vty, "", 0, root); + + ospf6_spf_table_finish (spf_table); + ospf6_route_table_delete (spf_table); + + return CMD_SUCCESS; +} + +void +ospf6_area_init (void) +{ + install_element (VIEW_NODE, &show_ipv6_ospf6_spf_tree_cmd); + 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 (OSPF6_NODE, &area_range_cmd); + install_element (OSPF6_NODE, &area_range_advertise_cmd); + install_element (OSPF6_NODE, &no_area_range_cmd); + + install_element (OSPF6_NODE, &area_import_list_cmd); + install_element (OSPF6_NODE, &no_area_import_list_cmd); + install_element (OSPF6_NODE, &area_export_list_cmd); + install_element (OSPF6_NODE, &no_area_export_list_cmd); + + install_element (OSPF6_NODE, &area_filter_list_cmd); + install_element (OSPF6_NODE, &no_area_filter_list_cmd); + +} + + diff --git a/ospf6d/ospf6_area.h b/ospf6d/ospf6_area.h new file mode 100644 index 0000000..655967a --- /dev/null +++ b/ospf6d/ospf6_area.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2003 Yasuhiro Ohara + * + * 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 OSPF_AREA_H +#define OSPF_AREA_H + +#include "ospf6_top.h" + +struct ospf6_area +{ + /* Reference to Top data structure */ + struct ospf6 *ospf6; + + /* Area-ID */ + u_int32_t area_id; + + /* Area-ID string */ + char name[16]; + + /* flag */ + u_char flag; + + /* OSPF Option */ + u_char options[3]; + + /* Summary routes to be originated (includes Configured Address Ranges) */ + struct ospf6_route_table *range_table; + struct ospf6_route_table *summary_prefix; + struct ospf6_route_table *summary_router; + + /* OSPF interface list */ + struct list *if_list; + + struct ospf6_lsdb *lsdb; + struct ospf6_lsdb *lsdb_self; + + struct ospf6_route_table *spf_table; + struct ospf6_route_table *route_table; + + 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; + u_int32_t router_lsa_size_limit; + + /* Area announce list */ + struct + { + char *name; + struct access_list *list; + } _export; +#define EXPORT_NAME(A) (A)->_export.name +#define EXPORT_LIST(A) (A)->_export.list + + /* Area acceptance list */ + struct + { + char *name; + struct access_list *list; + } import; +#define IMPORT_NAME(A) (A)->import.name +#define IMPORT_LIST(A) (A)->import.list + + /* Type 3 LSA Area prefix-list */ + struct + { + char *name; + struct prefix_list *list; + } plist_in; +#define PREFIX_NAME_IN(A) (A)->plist_in.name +#define PREFIX_LIST_IN(A) (A)->plist_in.list + + struct + { + char *name; + struct prefix_list *list; + } plist_out; +#define PREFIX_NAME_OUT(A) (A)->plist_out.name +#define PREFIX_LIST_OUT(A) (A)->plist_out.list + +}; + +#define OSPF6_AREA_ENABLE 0x01 +#define OSPF6_AREA_ACTIVE 0x02 +#define OSPF6_AREA_TRANSIT 0x04 /* TransitCapability */ +#define OSPF6_AREA_STUB 0x08 + +#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)) +#define IS_AREA_STUB(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_STUB)) + +/* prototypes */ +extern int ospf6_area_cmp (void *va, void *vb); + +extern struct ospf6_area *ospf6_area_create (u_int32_t, struct ospf6 *); +extern void ospf6_area_delete (struct ospf6_area *); +extern struct ospf6_area *ospf6_area_lookup (u_int32_t, struct ospf6 *); + +extern void ospf6_area_enable (struct ospf6_area *); +extern void ospf6_area_disable (struct ospf6_area *); + +extern void ospf6_area_show (struct vty *, struct ospf6_area *); + +extern void ospf6_area_config_write (struct vty *vty); +extern void ospf6_area_init (void); + +#endif /* OSPF_AREA_H */ diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c new file mode 100644 index 0000000..a442506 --- /dev/null +++ b/ospf6d/ospf6_asbr.c @@ -0,0 +1,1580 @@ +/* + * Copyright (C) 2003 Yasuhiro Ohara + * + * 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 "memory.h" +#include "prefix.h" +#include "command.h" +#include "vty.h" +#include "routemap.h" +#include "table.h" +#include "plist.h" +#include "thread.h" +#include "linklist.h" + +#include "ospf6_proto.h" +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" +#include "ospf6_route.h" +#include "ospf6_zebra.h" +#include "ospf6_message.h" + +#include "ospf6_top.h" +#include "ospf6_area.h" +#include "ospf6_interface.h" +#include "ospf6_neighbor.h" +#include "ospf6_asbr.h" +#include "ospf6_intra.h" +#include "ospf6_flood.h" +#include "ospf6d.h" + +unsigned char conf_debug_ospf6_asbr = 0; + +#define ZROUTE_NAME(x) zebra_route_string(x) + +/* AS External LSA origination */ +static void +ospf6_as_external_lsa_originate (struct ospf6_route *route) +{ + char buffer[OSPF6_MAX_LSASIZE]; + struct ospf6_lsa_header *lsa_header; + 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; + + if (IS_OSPF6_DEBUG_ASBR || IS_OSPF6_DEBUG_ORIGINATE (AS_EXTERNAL)) + { + prefix2str (&route->prefix, buf, sizeof (buf)); + zlog_debug ("Originate AS-External-LSA for %s", buf); + } + + /* prepare buffer */ + memset (buffer, 0, sizeof (buffer)); + lsa_header = (struct ospf6_lsa_header *) buffer; + as_external_lsa = (struct ospf6_as_external_lsa *) + ((caddr_t) lsa_header + sizeof (struct ospf6_lsa_header)); + p = (caddr_t) + ((caddr_t) as_external_lsa + sizeof (struct ospf6_as_external_lsa)); + + /* Fill AS-External-LSA */ + /* Metric type */ + if (route->path.metric_type == 2) + SET_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_E); + else + UNSET_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_E); + + /* forwarding address */ + if (! IN6_IS_ADDR_UNSPECIFIED (&info->forwarding)) + SET_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_F); + else + UNSET_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_F); + + /* external route tag */ + 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); + + /* prefixlen */ + as_external_lsa->prefix.prefix_length = route->prefix.prefixlen; + + /* PrefixOptions */ + as_external_lsa->prefix.prefix_options = route->path.prefix_options; + + /* don't use refer LS-type */ + as_external_lsa->prefix.prefix_refer_lstype = htons (0); + + /* set Prefix */ + memcpy (p, &route->prefix.u.prefix6, + OSPF6_PREFIX_SPACE (route->prefix.prefixlen)); + ospf6_prefix_apply_mask (&as_external_lsa->prefix); + p += OSPF6_PREFIX_SPACE (route->prefix.prefixlen); + + /* Forwarding address */ + if (CHECK_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_F)) + { + memcpy (p, &info->forwarding, sizeof (struct in6_addr)); + p += sizeof (struct in6_addr); + } + + /* External Route Tag */ + if (CHECK_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T)) + { + route_tag_t network_order = htonl(info->tag); + + memcpy (p, &network_order, sizeof(network_order)); + p += sizeof(network_order); + } + + /* Fill LSA Header */ + lsa_header->age = 0; + lsa_header->type = htons (OSPF6_LSTYPE_AS_EXTERNAL); + lsa_header->id = route->path.origin.id; + lsa_header->adv_router = ospf6->router_id; + lsa_header->seqnum = + ospf6_new_ls_seqnum (lsa_header->type, lsa_header->id, + lsa_header->adv_router, ospf6->lsdb); + lsa_header->length = htons ((caddr_t) p - (caddr_t) lsa_header); + + /* LSA checksum */ + ospf6_lsa_checksum (lsa_header); + + /* create LSA */ + lsa = ospf6_lsa_create (lsa_header); + + /* Originate */ + 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) +{ + struct ospf6_as_external_lsa *external; + struct prefix asbr_id; + struct ospf6_route *asbr_entry, *route; + char buf[64]; + int i; + + external = (struct ospf6_as_external_lsa *) + OSPF6_LSA_HEADER_END (lsa->header); + + if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL)) + zlog_debug ("Calculate AS-External route for %s", lsa->name); + + if (lsa->header->adv_router == ospf6->router_id) + { + if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL)) + zlog_debug ("Ignore self-originated AS-External-LSA"); + return; + } + + 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 || + ! CHECK_FLAG (asbr_entry->path.router_bits, OSPF6_ROUTER_BIT_E)) + { + if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL)) + { + prefix2str (&asbr_id, buf, sizeof (buf)); + zlog_debug ("ASBR entry not found: %s", buf); + } + return; + } + + route = ospf6_route_create (); + route->type = OSPF6_DEST_TYPE_NETWORK; + route->prefix.family = AF_INET6; + route->prefix.prefixlen = external->prefix.prefix_length; + ospf6_prefix_in6_addr (&route->prefix.u.prefix6, &external->prefix); + + route->path.area_id = asbr_entry->path.area_id; + route->path.origin.type = lsa->header->type; + route->path.origin.id = lsa->header->id; + route->path.origin.adv_router = lsa->header->adv_router; + + route->path.prefix_options = external->prefix.prefix_options; + if (CHECK_FLAG (external->bits_metric, OSPF6_ASBR_BIT_E)) + { + route->path.type = OSPF6_PATH_TYPE_EXTERNAL2; + route->path.metric_type = 2; + route->path.cost = asbr_entry->path.cost; + route->path.cost_e2 = OSPF6_ASBR_METRIC (external); + } + else + { + route->path.type = OSPF6_PATH_TYPE_EXTERNAL1; + route->path.metric_type = 1; + route->path.cost = asbr_entry->path.cost + OSPF6_ASBR_METRIC (external); + 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]); + + if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL)) + { + prefix2str (&route->prefix, buf, sizeof (buf)); + zlog_debug ("AS-External route add: %s", buf); + } + + ospf6_route_add (route, ospf6->route_table); +} + +void +ospf6_asbr_lsa_remove (struct ospf6_lsa *lsa) +{ + struct ospf6_as_external_lsa *external; + struct prefix prefix; + struct ospf6_route *route, *nroute; + char buf[64]; + + external = (struct ospf6_as_external_lsa *) + OSPF6_LSA_HEADER_END (lsa->header); + + if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL)) + zlog_debug ("Withdraw AS-External route for %s", lsa->name); + + if (lsa->header->adv_router == ospf6->router_id) + { + if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL)) + zlog_debug ("Ignore self-originated AS-External-LSA"); + return; + } + + memset (&prefix, 0, sizeof (struct prefix)); + prefix.family = AF_INET6; + prefix.prefixlen = external->prefix.prefix_length; + ospf6_prefix_in6_addr (&prefix.u.prefix6, &external->prefix); + + route = ospf6_route_lookup (&prefix, ospf6->route_table); + if (route == NULL) + { + if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL)) + { + prefix2str (&prefix, buf, sizeof (buf)); + zlog_debug ("AS-External route %s not found", buf); + } + return; + } + + for (ospf6_route_lock (route); + route && ospf6_route_is_prefix (&prefix, route); + route = nroute) + { + nroute = ospf6_route_next (route); + if (route->type != OSPF6_DEST_TYPE_NETWORK) + continue; + if (route->path.origin.type != lsa->header->type) + continue; + if (route->path.origin.id != lsa->header->id) + continue; + if (route->path.origin.adv_router != lsa->header->adv_router) + continue; + + if (IS_OSPF6_DEBUG_EXAMIN (AS_EXTERNAL)) + { + prefix2str (&route->prefix, buf, sizeof (buf)); + zlog_debug ("AS-External route remove: %s", buf); + } + ospf6_route_remove (route, ospf6->route_table); + } + if (route != NULL) + ospf6_route_unlock (route); +} + +void +ospf6_asbr_lsentry_add (struct ospf6_route *asbr_entry) +{ + struct ospf6_lsa *lsa; + u_int16_t type; + u_int32_t router; + + if (! CHECK_FLAG (asbr_entry->flag, OSPF6_ROUTE_BEST)) + { + char buf[16]; + inet_ntop (AF_INET, &ADV_ROUTER_IN_PREFIX (&asbr_entry->prefix), + buf, sizeof (buf)); + zlog_info ("ignore non-best path: lsentry %s add", buf); + return; + } + + type = htons (OSPF6_LSTYPE_AS_EXTERNAL); + router = ospf6_linkstate_prefix_adv_router (&asbr_entry->prefix); + for (lsa = ospf6_lsdb_type_router_head (type, router, ospf6->lsdb); lsa; + lsa = ospf6_lsdb_type_router_next (type, router, lsa)) + { + if (! OSPF6_LSA_IS_MAXAGE (lsa)) + ospf6_asbr_lsa_add (lsa); + } +} + +void +ospf6_asbr_lsentry_remove (struct ospf6_route *asbr_entry) +{ + struct ospf6_lsa *lsa; + u_int16_t type; + u_int32_t router; + + type = htons (OSPF6_LSTYPE_AS_EXTERNAL); + router = ospf6_linkstate_prefix_adv_router (&asbr_entry->prefix); + for (lsa = ospf6_lsdb_type_router_head (type, router, ospf6->lsdb); + lsa; lsa = ospf6_lsdb_type_router_next (type, router, lsa)) + ospf6_asbr_lsa_remove (lsa); +} + + + +/* redistribute function */ + +static void +ospf6_asbr_routemap_set (int type, const char *mapname) +{ + if (ospf6->rmap[type].name) + free (ospf6->rmap[type].name); + ospf6->rmap[type].name = strdup (mapname); + ospf6->rmap[type].map = route_map_lookup_by_name (mapname); +} + +static void +ospf6_asbr_routemap_unset (int type) +{ + if (ospf6->rmap[type].name) + free (ospf6->rmap[type].name); + ospf6->rmap[type].name = NULL; + ospf6->rmap[type].map = NULL; +} + +static void +ospf6_asbr_routemap_update (const char *mapname) +{ + int type; + + if (ospf6 == NULL) + return; + + for (type = 0; type < ZEBRA_ROUTE_MAX; type++) + { + if (ospf6->rmap[type].name) + ospf6->rmap[type].map = + route_map_lookup_by_name (ospf6->rmap[type].name); + else + ospf6->rmap[type].map = NULL; + } +} + +int +ospf6_asbr_is_asbr (struct ospf6 *o) +{ + return o->external_table->count; +} + +static void +ospf6_asbr_redistribute_set (int type) +{ + ospf6_zebra_redistribute (type); +} + +static void +ospf6_asbr_redistribute_unset (int type) +{ + struct ospf6_route *route; + struct ospf6_external_info *info; + + ospf6_zebra_no_redistribute (type); + + for (route = ospf6_route_head (ospf6->external_table); route; + route = ospf6_route_next (route)) + { + info = route->route_option; + if (info->type != type) + continue; + + ospf6_asbr_redistribute_remove (info->type, route->nexthop[0].ifindex, + &route->prefix); + } + + ospf6_asbr_routemap_unset (type); +} + +void +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; + struct ospf6_external_info tinfo; + struct ospf6_route *route, *match; + struct ospf6_external_info *info; + struct prefix prefix_id; + struct route_node *node; + char pbuf[64], ibuf[16]; + struct listnode *lnode, *lnnode; + struct ospf6_area *oa; + + if (! ospf6_zebra_is_redistribute (type)) + return; + + if (IS_OSPF6_DEBUG_ASBR) + { + prefix2str (prefix, pbuf, sizeof (pbuf)); + zlog_debug ("Redistribute %s (%s)", pbuf, ZROUTE_NAME (type)); + } + + /* if route-map was specified but not found, do not advertise */ + if (ospf6->rmap[type].name) + { + if (ospf6->rmap[type].map == NULL) + ospf6_asbr_routemap_update (NULL); + if (ospf6->rmap[type].map == NULL) + { + zlog_warn ("route-map \"%s\" not found, suppress redistributing", + ospf6->rmap[type].name); + return; + } + } + + /* apply route-map */ + if (ospf6->rmap[type].map) + { + memset (&troute, 0, sizeof (troute)); + 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); + if (ret == RMAP_DENYMATCH) + { + if (IS_OSPF6_DEBUG_ASBR) + zlog_debug ("Denied by route-map \"%s\"", ospf6->rmap[type].name); + return; + } + } + + match = ospf6_route_lookup (prefix, ospf6->external_table); + if (match) + { + info = match->route_option; + + /* copy result of route-map */ + if (ospf6->rmap[type].map) + { + if (troute.path.metric_type) + match->path.metric_type = troute.path.metric_type; + if (troute.path.cost) + match->path.cost = troute.path.cost; + 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; + match->nexthop[0].ifindex = ifindex; + if (nexthop_num && nexthop) + memcpy (&match->nexthop[0].address, nexthop, sizeof (struct in6_addr)); + + /* create/update binding in external_id_table */ + prefix_id.family = AF_INET; + prefix_id.prefixlen = 32; + prefix_id.u.prefix4.s_addr = htonl (info->id); + node = route_node_get (ospf6->external_id_table, &prefix_id); + node->info = match; + + if (IS_OSPF6_DEBUG_ASBR) + { + inet_ntop (AF_INET, &prefix_id.u.prefix4, ibuf, sizeof (ibuf)); + zlog_debug ("Advertise as AS-External Id:%s", ibuf); + } + + match->path.origin.id = htonl (info->id); + ospf6_as_external_lsa_originate (match); + return; + } + + /* create new entry */ + route = ospf6_route_create (); + route->type = OSPF6_DEST_TYPE_NETWORK; + memcpy (&route->prefix, prefix, sizeof (struct prefix)); + + info = (struct ospf6_external_info *) + XCALLOC (MTYPE_OSPF6_EXTERNAL_INFO, sizeof (struct ospf6_external_info)); + route->route_option = info; + info->id = ospf6->external_id++; + + /* copy result of route-map */ + if (ospf6->rmap[type].map) + { + if (troute.path.metric_type) + route->path.metric_type = troute.path.metric_type; + if (troute.path.cost) + route->path.cost = troute.path.cost; + 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; + route->nexthop[0].ifindex = ifindex; + if (nexthop_num && nexthop) + memcpy (&route->nexthop[0].address, nexthop, sizeof (struct in6_addr)); + + /* create/update binding in external_id_table */ + prefix_id.family = AF_INET; + prefix_id.prefixlen = 32; + prefix_id.u.prefix4.s_addr = htonl (info->id); + node = route_node_get (ospf6->external_id_table, &prefix_id); + node->info = route; + + route = ospf6_route_add (route, ospf6->external_table); + route->route_option = info; + + if (IS_OSPF6_DEBUG_ASBR) + { + inet_ntop (AF_INET, &prefix_id.u.prefix4, ibuf, sizeof (ibuf)); + zlog_debug ("Advertise as AS-External Id:%s", ibuf); + } + + route->path.origin.id = htonl (info->id); + ospf6_as_external_lsa_originate (route); + + /* Router-Bit (ASBR Flag) may have to be updated */ + for (ALL_LIST_ELEMENTS (ospf6->area_list, lnode, lnnode, oa)) + OSPF6_ROUTER_LSA_SCHEDULE (oa); +} + +void +ospf6_asbr_redistribute_remove (int type, ifindex_t ifindex, + struct prefix *prefix) +{ + struct ospf6_route *match; + struct ospf6_external_info *info = NULL; + struct route_node *node; + struct ospf6_lsa *lsa; + struct prefix prefix_id; + char pbuf[64], ibuf[16]; + struct listnode *lnode, *lnnode; + struct ospf6_area *oa; + + match = ospf6_route_lookup (prefix, ospf6->external_table); + if (match == NULL) + { + if (IS_OSPF6_DEBUG_ASBR) + { + prefix2str (prefix, pbuf, sizeof (pbuf)); + zlog_debug ("No such route %s to withdraw", pbuf); + } + return; + } + + info = match->route_option; + assert (info); + + if (info->type != type) + { + if (IS_OSPF6_DEBUG_ASBR) + { + prefix2str (prefix, pbuf, sizeof (pbuf)); + zlog_debug ("Original protocol mismatch: %s", pbuf); + } + return; + } + + if (IS_OSPF6_DEBUG_ASBR) + { + prefix2str (prefix, pbuf, sizeof (pbuf)); + inet_ntop (AF_INET, &prefix_id.u.prefix4, ibuf, sizeof (ibuf)); + zlog_debug ("Withdraw %s (AS-External Id:%s)", pbuf, ibuf); + } + + lsa = ospf6_lsdb_lookup (htons (OSPF6_LSTYPE_AS_EXTERNAL), + htonl (info->id), ospf6->router_id, ospf6->lsdb); + if (lsa) + ospf6_lsa_purge (lsa); + + /* remove binding in external_id_table */ + prefix_id.family = AF_INET; + prefix_id.prefixlen = 32; + prefix_id.u.prefix4.s_addr = htonl (info->id); + node = route_node_lookup (ospf6->external_id_table, &prefix_id); + assert (node); + node->info = NULL; + route_unlock_node (node); + + ospf6_route_remove (match, ospf6->external_table); + XFREE (MTYPE_OSPF6_EXTERNAL_INFO, info); + + /* Router-Bit (ASBR Flag) may have to be updated */ + for (ALL_LIST_ELEMENTS (ospf6->area_list, lnode, lnnode, oa)) + OSPF6_ROUTER_LSA_SCHEDULE (oa); +} + +DEFUN (ospf6_redistribute, + ospf6_redistribute_cmd, + "redistribute " QUAGGA_REDIST_STR_OSPF6D, + "Redistribute\n" + QUAGGA_REDIST_HELP_STR_OSPF6D + ) +{ + int type; + + type = proto_redistnum(AFI_IP6, argv[0]); + if (type < 0 || type == ZEBRA_ROUTE_OSPF6) + return CMD_WARNING; + + ospf6_asbr_redistribute_unset (type); + ospf6_asbr_redistribute_set (type); + return CMD_SUCCESS; +} + +DEFUN (ospf6_redistribute_routemap, + ospf6_redistribute_routemap_cmd, + "redistribute " QUAGGA_REDIST_STR_OSPF6D " route-map WORD", + "Redistribute\n" + QUAGGA_REDIST_HELP_STR_OSPF6D + "Route map reference\n" + "Route map name\n" + ) +{ + int type; + + type = proto_redistnum(AFI_IP6, argv[0]); + if (type < 0 || type == ZEBRA_ROUTE_OSPF6) + return CMD_WARNING; + + ospf6_asbr_redistribute_unset (type); + ospf6_asbr_routemap_set (type, argv[1]); + ospf6_asbr_redistribute_set (type); + return CMD_SUCCESS; +} + +DEFUN (no_ospf6_redistribute, + no_ospf6_redistribute_cmd, + "no redistribute " QUAGGA_REDIST_STR_OSPF6D, + NO_STR + "Redistribute\n" + QUAGGA_REDIST_HELP_STR_OSPF6D + ) +{ + int type; + + type = proto_redistnum(AFI_IP6, argv[0]); + if (type < 0 || type == ZEBRA_ROUTE_OSPF6) + return CMD_WARNING; + + ospf6_asbr_redistribute_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) +{ + int type; + + for (type = 0; type < ZEBRA_ROUTE_MAX; type++) + { + if (type == ZEBRA_ROUTE_OSPF6) + continue; + if (! ospf6_zebra_is_redistribute (type)) + continue; + + if (ospf6->rmap[type].name) + vty_out (vty, " redistribute %s route-map %s%s", + ZROUTE_NAME (type), ospf6->rmap[type].name, VNL); + else + vty_out (vty, " redistribute %s%s", + ZROUTE_NAME (type), VNL); + } + + return 0; +} + +static void +ospf6_redistribute_show_config (struct vty *vty) +{ + int type; + int nroute[ZEBRA_ROUTE_MAX]; + int total; + struct ospf6_route *route; + struct ospf6_external_info *info; + + total = 0; + for (type = 0; type < ZEBRA_ROUTE_MAX; type++) + nroute[type] = 0; + for (route = ospf6_route_head (ospf6->external_table); route; + route = ospf6_route_next (route)) + { + info = route->route_option; + nroute[info->type]++; + total++; + } + + vty_out (vty, "Redistributing External Routes from:%s", VNL); + for (type = 0; type < ZEBRA_ROUTE_MAX; type++) + { + if (type == ZEBRA_ROUTE_OSPF6) + continue; + if (! ospf6_zebra_is_redistribute (type)) + continue; + + if (ospf6->rmap[type].name) + vty_out (vty, " %d: %s with route-map \"%s\"%s%s", nroute[type], + ZROUTE_NAME (type), ospf6->rmap[type].name, + (ospf6->rmap[type].map ? "" : " (not found !)"), + VNL); + else + vty_out (vty, " %d: %s%s", nroute[type], + ZROUTE_NAME (type), VNL); + } + vty_out (vty, "Total %d routes%s", total, VNL); +} + + + +/* Routemap Functions */ +static route_map_result_t +ospf6_routemap_rule_match_address_prefixlist (void *rule, + struct prefix *prefix, + route_map_object_t type, + void *object) +{ + struct prefix_list *plist; + + if (type != RMAP_OSPF6) + return RMAP_NOMATCH; + + plist = prefix_list_lookup (AFI_IP6, (char *) rule); + if (plist == NULL) + return RMAP_NOMATCH; + + return (prefix_list_apply (plist, prefix) == PREFIX_DENY ? + RMAP_NOMATCH : RMAP_MATCH); +} + +static void * +ospf6_routemap_rule_match_address_prefixlist_compile (const char *arg) +{ + return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); +} + +static void +ospf6_routemap_rule_match_address_prefixlist_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +struct route_map_rule_cmd +ospf6_routemap_rule_match_address_prefixlist_cmd = +{ + "ipv6 address prefix-list", + ospf6_routemap_rule_match_address_prefixlist, + ospf6_routemap_rule_match_address_prefixlist_compile, + ospf6_routemap_rule_match_address_prefixlist_free, +}; + +/* `match interface IFNAME' */ +/* Match function should return 1 if match is success else return + zero. */ +static route_map_result_t +ospf6_routemap_rule_match_interface (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct interface *ifp; + struct ospf6_external_info *ei; + + if (type == RMAP_OSPF6) + { + ei = ((struct ospf6_route *) object)->route_option; + ifp = if_lookup_by_name ((char *)rule); + + if (ifp != NULL + && ei->ifindex == ifp->ifindex) + return RMAP_MATCH; + } + + return RMAP_NOMATCH; +} + +/* Route map `interface' match statement. `arg' should be + interface name. */ +static void * +ospf6_routemap_rule_match_interface_compile (const char *arg) +{ + return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); +} + +/* Free route map's compiled `interface' value. */ +static void +ospf6_routemap_rule_match_interface_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for interface matching. */ +struct route_map_rule_cmd +ospf6_routemap_rule_match_interface_cmd = +{ + "interface", + ospf6_routemap_rule_match_interface, + ospf6_routemap_rule_match_interface_compile, + 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) +{ + char *metric_type = rule; + struct ospf6_route *route = object; + + if (type != RMAP_OSPF6) + return RMAP_OKAY; + + if (strcmp (metric_type, "type-2") == 0) + route->path.metric_type = 2; + else + route->path.metric_type = 1; + + return RMAP_OKAY; +} + +static void * +ospf6_routemap_rule_set_metric_type_compile (const char *arg) +{ + if (strcmp (arg, "type-2") && strcmp (arg, "type-1")) + return NULL; + return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); +} + +static void +ospf6_routemap_rule_set_metric_type_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +struct route_map_rule_cmd +ospf6_routemap_rule_set_metric_type_cmd = +{ + "metric-type", + ospf6_routemap_rule_set_metric_type, + ospf6_routemap_rule_set_metric_type_compile, + ospf6_routemap_rule_set_metric_type_free, +}; + +static route_map_result_t +ospf6_routemap_rule_set_metric (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + char *metric = rule; + struct ospf6_route *route = object; + + if (type != RMAP_OSPF6) + return RMAP_OKAY; + + route->path.cost = atoi (metric); + return RMAP_OKAY; +} + +static void * +ospf6_routemap_rule_set_metric_compile (const char *arg) +{ + u_int32_t metric; + char *endp; + metric = strtoul (arg, &endp, 0); + if (metric > OSPF_LS_INFINITY || *endp != '\0') + return NULL; + return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); +} + +static void +ospf6_routemap_rule_set_metric_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +struct route_map_rule_cmd +ospf6_routemap_rule_set_metric_cmd = +{ + "metric", + ospf6_routemap_rule_set_metric, + ospf6_routemap_rule_set_metric_compile, + ospf6_routemap_rule_set_metric_free, +}; + +static route_map_result_t +ospf6_routemap_rule_set_forwarding (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + char *forwarding = rule; + struct ospf6_route *route = object; + struct ospf6_external_info *info = route->route_option; + + if (type != RMAP_OSPF6) + return RMAP_OKAY; + + if (inet_pton (AF_INET6, forwarding, &info->forwarding) != 1) + { + memset (&info->forwarding, 0, sizeof (struct in6_addr)); + return RMAP_ERROR; + } + + return RMAP_OKAY; +} + +static void * +ospf6_routemap_rule_set_forwarding_compile (const char *arg) +{ + struct in6_addr a; + if (inet_pton (AF_INET6, arg, &a) != 1) + return NULL; + return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); +} + +static void +ospf6_routemap_rule_set_forwarding_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +struct route_map_rule_cmd +ospf6_routemap_rule_set_forwarding_cmd = +{ + "forwarding-address", + ospf6_routemap_rule_set_forwarding, + ospf6_routemap_rule_set_forwarding_compile, + 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) +{ + if (! ret) + return CMD_SUCCESS; + + switch (ret) + { + case RMAP_RULE_MISSING: + vty_out (vty, "OSPF6 Can't find rule.%s", VNL); + break; + case RMAP_COMPILE_ERROR: + vty_out (vty, "OSPF6 Argument is malformed.%s", VNL); + break; + default: + vty_out (vty, "OSPF6 route-map add set failed.%s", VNL); + break; + } + return CMD_WARNING; +} + +/* add "match address" */ +DEFUN (ospf6_routemap_match_address_prefixlist, + ospf6_routemap_match_address_prefixlist_cmd, + "match ipv6 address prefix-list WORD", + "Match values\n" + IPV6_STR + "Match address of route\n" + "Match entries of prefix-lists\n" + "IPv6 prefix-list name\n") +{ + int ret = route_map_add_match ((struct route_map_index *) vty->index, + "ipv6 address prefix-list", argv[0]); + return route_map_command_status (vty, ret); +} + +/* delete "match address" */ +DEFUN (ospf6_routemap_no_match_address_prefixlist, + ospf6_routemap_no_match_address_prefixlist_cmd, + "no match ipv6 address prefix-list WORD", + NO_STR + "Match values\n" + IPV6_STR + "Match address of route\n" + "Match entries of prefix-lists\n" + "IPv6 prefix-list name\n") +{ + int ret = route_map_delete_match ((struct route_map_index *) vty->index, + "ipv6 address prefix-list", argv[0]); + return route_map_command_status (vty, ret); +} + +/* "match interface" */ +DEFUN (ospf6_routemap_match_interface, + ospf6_routemap_match_interface_cmd, + "match interface WORD", + MATCH_STR + "Match first hop interface of route\n" + "Interface name\n") +{ + return route_map_add_match ((struct route_map_index *) vty->index, + "interface", argv[0]); +} + +/* "no match interface WORD" */ +DEFUN (ospf6_routemap_no_match_interface, + ospf6_routemap_no_match_interface_cmd, + "no match interface", + NO_STR + MATCH_STR + "Match first hop interface of route\n") +{ + int ret = route_map_delete_match ((struct route_map_index *) vty->index, + "interface", (argc == 0) ? NULL : argv[0]); + return route_map_command_status (vty, ret); +} + +ALIAS (ospf6_routemap_no_match_interface, + ospf6_routemap_no_match_interface_val_cmd, + "no match interface WORD", + 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, + "set metric-type (type-1|type-2)", + "Set value\n" + "Type of metric\n" + "OSPF6 external type 1 metric\n" + "OSPF6 external type 2 metric\n") +{ + int ret = route_map_add_set ((struct route_map_index *) vty->index, + "metric-type", argv[0]); + return route_map_command_status (vty, ret); +} + +/* delete "set metric-type" */ +DEFUN (ospf6_routemap_no_set_metric_type, + ospf6_routemap_no_set_metric_type_cmd, + "no set metric-type (type-1|type-2)", + NO_STR + "Set value\n" + "Type of metric\n" + "OSPF6 external type 1 metric\n" + "OSPF6 external type 2 metric\n") +{ + int ret = route_map_delete_set ((struct route_map_index *) vty->index, + "metric-type", argv[0]); + return route_map_command_status (vty, ret); +} + +/* add "set metric" */ +DEFUN (set_metric, + set_metric_cmd, + "set metric <0-4294967295>", + "Set value\n" + "Metric value\n" + "Metric value\n") +{ + int ret = route_map_add_set ((struct route_map_index *) vty->index, + "metric", argv[0]); + return route_map_command_status (vty, ret); +} + +/* delete "set metric" */ +DEFUN (no_set_metric, + no_set_metric_cmd, + "no set metric", + NO_STR + SET_STR + "Metric value for destination routing protocol\n") +{ + 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, + "set forwarding-address X:X::X:X", + "Set value\n" + "Forwarding Address\n" + "IPv6 Address\n") +{ + int ret = route_map_add_set ((struct route_map_index *) vty->index, + "forwarding-address", argv[0]); + return route_map_command_status (vty, ret); +} + +/* delete "set forwarding-address" */ +DEFUN (ospf6_routemap_no_set_forwarding, + ospf6_routemap_no_set_forwarding_cmd, + "no set forwarding-address X:X::X:X", + NO_STR + "Set value\n" + "Forwarding Address\n" + "IPv6 Address\n") +{ + int ret = route_map_delete_set ((struct route_map_index *) vty->index, + "forwarding-address", argv[0]); + 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) +{ + route_map_init (); + route_map_init_vty (); + route_map_add_hook (ospf6_asbr_routemap_update); + route_map_delete_hook (ospf6_asbr_routemap_update); + + 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); + install_element (RMAP_NODE, &ospf6_routemap_no_match_address_prefixlist_cmd); + + /* Match interface */ + install_element (RMAP_NODE, &ospf6_routemap_match_interface_cmd); + 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); + + /* 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); + + /* 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]; + + assert (lsa->header); + external = (struct ospf6_as_external_lsa *) + OSPF6_LSA_HEADER_END (lsa->header); + + /* bits */ + snprintf (buf, sizeof (buf), "%c%c%c", + (CHECK_FLAG (external->bits_metric, OSPF6_ASBR_BIT_E) ? 'E' : '-'), + (CHECK_FLAG (external->bits_metric, OSPF6_ASBR_BIT_F) ? 'F' : '-'), + (CHECK_FLAG (external->bits_metric, OSPF6_ASBR_BIT_T) ? 'T' : '-')); + + vty_out (vty, " Bits: %s%s", buf, VNL); + vty_out (vty, " Metric: %5lu%s", (u_long) OSPF6_ASBR_METRIC (external), + VNL); + + ospf6_prefix_options_printbuf (external->prefix.prefix_options, + buf, sizeof (buf)); + vty_out (vty, " Prefix Options: %s%s", buf, + VNL); + + vty_out (vty, " Referenced LSType: %d%s", + ntohs (external->prefix.prefix_refer_lstype), + 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)) + { + 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; +} + +static void +ospf6_asbr_external_route_show (struct vty *vty, struct ospf6_route *route) +{ + struct ospf6_external_info *info = route->route_option; + char prefix[64], id[16], forwarding[64]; + u_int32_t tmp_id; + + prefix2str (&route->prefix, prefix, sizeof (prefix)); + tmp_id = ntohl (info->id); + inet_ntop (AF_INET, &tmp_id, id, sizeof (id)); + if (! IN6_IS_ADDR_UNSPECIFIED (&info->forwarding)) + inet_ntop (AF_INET6, &info->forwarding, forwarding, sizeof (forwarding)); + else + snprintf (forwarding, sizeof (forwarding), ":: (ifindex %d)", + route->nexthop[0].ifindex); + + vty_out (vty, "%c %-32s %-15s type-%d %5lu %s%s", + zebra_route_char(info->type), + prefix, id, route->path.metric_type, + (u_long) (route->path.metric_type == 2 ? + route->path.cost_e2 : route->path.cost), + forwarding, VNL); +} + +DEFUN (show_ipv6_ospf6_redistribute, + show_ipv6_ospf6_redistribute_cmd, + "show ipv6 ospf6 redistribute", + SHOW_STR + IP6_STR + OSPF6_STR + "redistributing External information\n" + ) +{ + struct ospf6_route *route; + + OSPF6_CMD_CHECK_RUNNING (); + + ospf6_redistribute_show_config (vty); + + for (route = ospf6_route_head (ospf6->external_table); route; + route = ospf6_route_next (route)) + ospf6_asbr_external_route_show (vty, route); + + return CMD_SUCCESS; +} + +struct ospf6_lsa_handler as_external_handler = +{ + OSPF6_LSTYPE_AS_EXTERNAL, + "AS-External", + "ASE", + ospf6_as_external_lsa_show, + ospf6_as_external_lsa_get_prefix_str +}; + +void +ospf6_asbr_init (void) +{ + ospf6_routemap_init (); + + ospf6_install_lsa_handler (&as_external_handler); + + install_element (VIEW_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 +ospf6_asbr_terminate (void) +{ + route_map_finish (); +} + +DEFUN (debug_ospf6_asbr, + debug_ospf6_asbr_cmd, + "debug ospf6 asbr", + DEBUG_STR + OSPF6_STR + "Debug OSPFv3 ASBR function\n" + ) +{ + OSPF6_DEBUG_ASBR_ON (); + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf6_asbr, + no_debug_ospf6_asbr_cmd, + "no debug ospf6 asbr", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug OSPFv3 ASBR function\n" + ) +{ + OSPF6_DEBUG_ASBR_OFF (); + return CMD_SUCCESS; +} + +int +config_write_ospf6_debug_asbr (struct vty *vty) +{ + if (IS_OSPF6_DEBUG_ASBR) + vty_out (vty, "debug ospf6 asbr%s", VNL); + return 0; +} + +void +install_element_ospf6_debug_asbr () +{ + install_element (ENABLE_NODE, &debug_ospf6_asbr_cmd); + install_element (ENABLE_NODE, &no_debug_ospf6_asbr_cmd); + install_element (CONFIG_NODE, &debug_ospf6_asbr_cmd); + install_element (CONFIG_NODE, &no_debug_ospf6_asbr_cmd); +} + + diff --git a/ospf6d/ospf6_asbr.h b/ospf6d/ospf6_asbr.h new file mode 100644 index 0000000..14113b0 --- /dev/null +++ b/ospf6d/ospf6_asbr.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2001 Yasuhiro Ohara + * + * 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 OSPF6_ASBR_H +#define OSPF6_ASBR_H + +/* for struct ospf6_prefix */ +#include "ospf6_proto.h" +/* for struct ospf6_lsa */ +#include "ospf6_lsa.h" +/* for struct ospf6_route */ +#include "ospf6_route.h" + +/* Debug option */ +extern unsigned char conf_debug_ospf6_asbr; +#define OSPF6_DEBUG_ASBR_ON() \ + (conf_debug_ospf6_asbr = 1) +#define OSPF6_DEBUG_ASBR_OFF() \ + (conf_debug_ospf6_asbr = 0) +#define IS_OSPF6_DEBUG_ASBR \ + (conf_debug_ospf6_asbr) + +struct ospf6_external_info +{ + /* External route type */ + int type; + + /* Originating Link State ID */ + u_int32_t id; + + struct in6_addr forwarding; + + route_tag_t tag; + + ifindex_t ifindex; +}; + +/* AS-External-LSA */ +#define OSPF6_AS_EXTERNAL_LSA_MIN_SIZE 4U /* w/o IPv6 prefix */ +struct ospf6_as_external_lsa +{ + u_int32_t bits_metric; + + struct ospf6_prefix prefix; + /* followed by none or one forwarding address */ + /* followed by none or one external route tag */ + /* followed by none or one referenced LS-ID */ +}; + +#define OSPF6_ASBR_BIT_T ntohl (0x01000000) +#define OSPF6_ASBR_BIT_F ntohl (0x02000000) +#define OSPF6_ASBR_BIT_E ntohl (0x04000000) + +#define OSPF6_ASBR_METRIC(E) (ntohl ((E)->bits_metric & htonl (0x00ffffff))) +#define OSPF6_ASBR_METRIC_SET(E,C) \ + { (E)->bits_metric &= htonl (0xff000000); \ + (E)->bits_metric |= htonl (0x00ffffff) & htonl (C); } + +extern void ospf6_asbr_lsa_add (struct ospf6_lsa *lsa); +extern void ospf6_asbr_lsa_remove (struct ospf6_lsa *lsa); +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, ifindex_t ifindex, + struct prefix *prefix, + u_int nexthop_num, + 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); +extern void install_element_ospf6_debug_asbr (void); + +#endif /* OSPF6_ASBR_H */ diff --git a/ospf6d/ospf6_flood.c b/ospf6d/ospf6_flood.c new file mode 100644 index 0000000..815db7b --- /dev/null +++ b/ospf6d/ospf6_flood.c @@ -0,0 +1,1051 @@ +/* + * Copyright (C) 2003 Yasuhiro Ohara + * + * 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 "thread.h" +#include "linklist.h" +#include "vty.h" +#include "command.h" + +#include "ospf6d.h" +#include "ospf6_proto.h" +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" +#include "ospf6_message.h" +#include "ospf6_route.h" +#include "ospf6_spf.h" + +#include "ospf6_top.h" +#include "ospf6_area.h" +#include "ospf6_interface.h" +#include "ospf6_neighbor.h" + +#include "ospf6_flood.h" + +unsigned char conf_debug_ospf6_flooding; + +struct ospf6_lsdb * +ospf6_get_scoped_lsdb (struct ospf6_lsa *lsa) +{ + struct ospf6_lsdb *lsdb = NULL; + switch (OSPF6_LSA_SCOPE (lsa->header->type)) + { + case OSPF6_SCOPE_LINKLOCAL: + lsdb = OSPF6_INTERFACE (lsa->lsdb->data)->lsdb; + break; + case OSPF6_SCOPE_AREA: + lsdb = OSPF6_AREA (lsa->lsdb->data)->lsdb; + break; + case OSPF6_SCOPE_AS: + lsdb = OSPF6_PROCESS (lsa->lsdb->data)->lsdb; + break; + default: + assert (0); + break; + } + return lsdb; +} + +struct ospf6_lsdb * +ospf6_get_scoped_lsdb_self (struct ospf6_lsa *lsa) +{ + struct ospf6_lsdb *lsdb_self = NULL; + switch (OSPF6_LSA_SCOPE (lsa->header->type)) + { + case OSPF6_SCOPE_LINKLOCAL: + lsdb_self = OSPF6_INTERFACE (lsa->lsdb->data)->lsdb_self; + break; + case OSPF6_SCOPE_AREA: + lsdb_self = OSPF6_AREA (lsa->lsdb->data)->lsdb_self; + break; + case OSPF6_SCOPE_AS: + lsdb_self = OSPF6_PROCESS (lsa->lsdb->data)->lsdb_self; + break; + default: + assert (0); + break; + } + return lsdb_self; +} + +void +ospf6_lsa_originate (struct ospf6_lsa *lsa) +{ + struct ospf6_lsa *old; + struct ospf6_lsdb *lsdb_self; + + /* find previous LSA */ + old = ospf6_lsdb_lookup (lsa->header->type, lsa->header->id, + lsa->header->adv_router, lsa->lsdb); + + /* if the new LSA does not differ from previous, + suppress this update of the LSA */ + if (old && ! OSPF6_LSA_IS_DIFFER (lsa, old)) + { + if (IS_OSPF6_DEBUG_ORIGINATE_TYPE (lsa->header->type)) + zlog_debug ("Suppress updating LSA: %s", lsa->name); + ospf6_lsa_delete (lsa); + return; + } + + /* store it in the LSDB for self-originated LSAs */ + lsdb_self = ospf6_get_scoped_lsdb_self (lsa); + ospf6_lsdb_add (ospf6_lsa_copy (lsa), lsdb_self); + + lsa->refresh = thread_add_timer (master, ospf6_lsa_refresh, lsa, + OSPF_LS_REFRESH_TIME); + + if (IS_OSPF6_DEBUG_LSA_TYPE (lsa->header->type) || + IS_OSPF6_DEBUG_ORIGINATE_TYPE (lsa->header->type)) + { + zlog_debug ("LSA Originate:"); + ospf6_lsa_header_print (lsa); + } + + ospf6_install_lsa (lsa); + ospf6_flood (NULL, lsa); +} + +void +ospf6_lsa_originate_process (struct ospf6_lsa *lsa, + struct ospf6 *process) +{ + lsa->lsdb = process->lsdb; + ospf6_lsa_originate (lsa); +} + +void +ospf6_lsa_originate_area (struct ospf6_lsa *lsa, + struct ospf6_area *oa) +{ + lsa->lsdb = oa->lsdb; + ospf6_lsa_originate (lsa); +} + +void +ospf6_lsa_originate_interface (struct ospf6_lsa *lsa, + struct ospf6_interface *oi) +{ + lsa->lsdb = oi->lsdb; + ospf6_lsa_originate (lsa); +} + +void +ospf6_lsa_purge (struct ospf6_lsa *lsa) +{ + struct ospf6_lsa *self; + struct ospf6_lsdb *lsdb_self; + + /* remove it from the LSDB for self-originated LSAs */ + lsdb_self = ospf6_get_scoped_lsdb_self (lsa); + self = ospf6_lsdb_lookup (lsa->header->type, lsa->header->id, + lsa->header->adv_router, lsdb_self); + if (self) + { + THREAD_OFF (self->expire); + THREAD_OFF (self->refresh); + ospf6_lsdb_remove (self, lsdb_self); + } + + ospf6_lsa_premature_aging (lsa); +} + + +void +ospf6_increment_retrans_count (struct ospf6_lsa *lsa) +{ + /* The LSA must be the original one (see the description + in ospf6_decrement_retrans_count () below) */ + lsa->retrans_count++; +} + +void +ospf6_decrement_retrans_count (struct ospf6_lsa *lsa) +{ + struct ospf6_lsdb *lsdb; + struct ospf6_lsa *orig; + + /* The LSA must be on the retrans-list of a neighbor. It means + the "lsa" is a copied one, and we have to decrement the + retransmission count of the original one (instead of this "lsa"'s). + In order to find the original LSA, first we have to find + appropriate LSDB that have the original LSA. */ + lsdb = ospf6_get_scoped_lsdb (lsa); + + /* Find the original LSA of which the retrans_count should be decremented */ + orig = ospf6_lsdb_lookup (lsa->header->type, lsa->header->id, + lsa->header->adv_router, lsdb); + if (orig) + { + orig->retrans_count--; + assert (orig->retrans_count >= 0); + } +} + +/* RFC2328 section 13.2 Installing LSAs in the database */ +void +ospf6_install_lsa (struct ospf6_lsa *lsa) +{ + struct timeval now; + struct ospf6_lsa *old; + + if (IS_OSPF6_DEBUG_LSA_TYPE (lsa->header->type) || + IS_OSPF6_DEBUG_EXAMIN_TYPE (lsa->header->type)) + zlog_debug ("Install LSA: %s", lsa->name); + + /* Remove the old instance from all neighbors' Link state + retransmission list (RFC2328 13.2 last paragraph) */ + old = ospf6_lsdb_lookup (lsa->header->type, lsa->header->id, + lsa->header->adv_router, lsa->lsdb); + 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, + 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); + + return; +} + +/* RFC2740 section 3.5.2. Sending Link State Update packets */ +/* RFC2328 section 13.3 Next step in the flooding procedure */ +static void +ospf6_flood_interface (struct ospf6_neighbor *from, + struct ospf6_lsa *lsa, struct ospf6_interface *oi) +{ + struct listnode *node, *nnode; + struct ospf6_neighbor *on; + struct ospf6_lsa *req; + int retrans_added = 0; + int is_debug = 0; + + if (IS_OSPF6_DEBUG_FLOODING || + IS_OSPF6_DEBUG_FLOOD_TYPE (lsa->header->type)) + { + is_debug++; + zlog_debug ("Flooding on %s: %s", oi->interface->name, lsa->name); + } + + /* (1) For each neighbor */ + for (ALL_LIST_ELEMENTS (oi->neighbor_list, node, nnode, on)) + { + if (is_debug) + zlog_debug ("To neighbor %s", on->name); + + /* (a) if neighbor state < Exchange, examin next */ + if (on->state < OSPF6_NEIGHBOR_EXCHANGE) + { + if (is_debug) + zlog_debug ("Neighbor state less than ExChange, next neighbor"); + continue; + } + + /* (b) if neighbor not yet Full, check request-list */ + if (on->state != OSPF6_NEIGHBOR_FULL) + { + if (is_debug) + zlog_debug ("Neighbor not yet Full"); + + req = ospf6_lsdb_lookup (lsa->header->type, lsa->header->id, + lsa->header->adv_router, on->request_list); + if (req == NULL) + { + if (is_debug) + zlog_debug ("Not on request-list for this neighbor"); + /* fall through */ + } + else + { + /* If new LSA less recent, examin next neighbor */ + if (ospf6_lsa_compare (lsa, req) > 0) + { + if (is_debug) + zlog_debug ("Requesting is older, next neighbor"); + continue; + } + + /* If the same instance, delete from request-list and + examin next neighbor */ + if (ospf6_lsa_compare (lsa, req) == 0) + { + 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 (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 */ + } + } + } + + /* (c) If the new LSA was received from this neighbor, + examin next neighbor */ + if (from == on) + { + if (is_debug) + zlog_debug ("Received is from the neighbor, next neighbor"); + continue; + } + + /* (d) add retrans-list, schedule retransmission */ + if (is_debug) + zlog_debug ("Add retrans-list of this neighbor"); + ospf6_increment_retrans_count (lsa); + ospf6_lsdb_add (ospf6_lsa_copy (lsa), on->retrans_list); + if (on->thread_send_lsupdate == NULL) + on->thread_send_lsupdate = + thread_add_timer (master, ospf6_lsupdate_send_neighbor, + on, on->ospf6_if->rxmt_interval); + retrans_added++; + } + + /* (2) examin next interface if not added to retrans-list */ + if (retrans_added == 0) + { + if (is_debug) + zlog_debug ("No retransmission scheduled, next interface"); + return; + } + + /* (3) If the new LSA was received on this interface, + and it was from DR or BDR, examin next interface */ + if (from && from->ospf6_if == oi && + (from->router_id == oi->drouter || from->router_id == oi->bdrouter)) + { + if (is_debug) + zlog_debug ("Received is from the I/F's DR or BDR, next interface"); + return; + } + + /* (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) + { + 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 ((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) + oi->thread_send_lsupdate = + thread_add_event (master, ospf6_lsupdate_send_interface, oi, 0); + } + else + { + /* reschedule retransmissions to all neighbors */ + for (ALL_LIST_ELEMENTS (oi->neighbor_list, node, nnode, on)) + { + THREAD_OFF (on->thread_send_lsupdate); + on->thread_send_lsupdate = + thread_add_event (master, ospf6_lsupdate_send_neighbor, on, 0); + } + } +} + +static void +ospf6_flood_area (struct ospf6_neighbor *from, + struct ospf6_lsa *lsa, struct ospf6_area *oa) +{ + struct listnode *node, *nnode; + struct ospf6_interface *oi; + + for (ALL_LIST_ELEMENTS (oa->if_list, node, nnode, oi)) + { + if (OSPF6_LSA_SCOPE (lsa->header->type) == OSPF6_SCOPE_LINKLOCAL && + oi != OSPF6_INTERFACE (lsa->lsdb->data)) + continue; + +#if 0 + if (OSPF6_LSA_SCOPE (lsa->header->type) == OSPF6_SCOPE_AS && + ospf6_is_interface_virtual_link (oi)) + continue; +#endif/*0*/ + + ospf6_flood_interface (from, lsa, oi); + } +} + +static void +ospf6_flood_process (struct ospf6_neighbor *from, + struct ospf6_lsa *lsa, struct ospf6 *process) +{ + struct listnode *node, *nnode; + struct ospf6_area *oa; + + for (ALL_LIST_ELEMENTS (process->area_list, node, nnode, oa)) + { + if (OSPF6_LSA_SCOPE (lsa->header->type) == OSPF6_SCOPE_AREA && + oa != OSPF6_AREA (lsa->lsdb->data)) + continue; + if (OSPF6_LSA_SCOPE (lsa->header->type) == OSPF6_SCOPE_LINKLOCAL && + oa != OSPF6_INTERFACE (lsa->lsdb->data)->area) + continue; + + if (ntohs (lsa->header->type) == OSPF6_LSTYPE_AS_EXTERNAL && + IS_AREA_STUB (oa)) + continue; + + ospf6_flood_area (from, lsa, oa); + } +} + +void +ospf6_flood (struct ospf6_neighbor *from, struct ospf6_lsa *lsa) +{ + ospf6_flood_process (from, lsa, ospf6); +} + +static void +ospf6_flood_clear_interface (struct ospf6_lsa *lsa, struct ospf6_interface *oi) +{ + struct listnode *node, *nnode; + struct ospf6_neighbor *on; + struct ospf6_lsa *rem; + + for (ALL_LIST_ELEMENTS (oi->neighbor_list, node, nnode, on)) + { + rem = ospf6_lsdb_lookup (lsa->header->type, lsa->header->id, + lsa->header->adv_router, on->retrans_list); + if (rem && ! ospf6_lsa_compare (rem, lsa)) + { + if (IS_OSPF6_DEBUG_FLOODING || + IS_OSPF6_DEBUG_FLOOD_TYPE (lsa->header->type)) + zlog_debug ("Remove %s from retrans_list of %s", + rem->name, on->name); + ospf6_decrement_retrans_count (rem); + ospf6_lsdb_remove (rem, on->retrans_list); + } + } +} + +static void +ospf6_flood_clear_area (struct ospf6_lsa *lsa, struct ospf6_area *oa) +{ + struct listnode *node, *nnode; + struct ospf6_interface *oi; + + for (ALL_LIST_ELEMENTS (oa->if_list, node, nnode, oi)) + { + if (OSPF6_LSA_SCOPE (lsa->header->type) == OSPF6_SCOPE_LINKLOCAL && + oi != OSPF6_INTERFACE (lsa->lsdb->data)) + continue; + +#if 0 + if (OSPF6_LSA_SCOPE (lsa->header->type) == OSPF6_SCOPE_AS && + ospf6_is_interface_virtual_link (oi)) + continue; +#endif/*0*/ + + ospf6_flood_clear_interface (lsa, oi); + } +} + +static void +ospf6_flood_clear_process (struct ospf6_lsa *lsa, struct ospf6 *process) +{ + struct listnode *node, *nnode; + struct ospf6_area *oa; + + for (ALL_LIST_ELEMENTS (process->area_list, node, nnode, oa)) + { + if (OSPF6_LSA_SCOPE (lsa->header->type) == OSPF6_SCOPE_AREA && + oa != OSPF6_AREA (lsa->lsdb->data)) + continue; + if (OSPF6_LSA_SCOPE (lsa->header->type) == OSPF6_SCOPE_LINKLOCAL && + oa != OSPF6_INTERFACE (lsa->lsdb->data)->area) + continue; + + if (ntohs (lsa->header->type) == OSPF6_LSTYPE_AS_EXTERNAL && + IS_AREA_STUB (oa)) + continue; + + ospf6_flood_clear_area (lsa, oa); + } +} + +void +ospf6_flood_clear (struct ospf6_lsa *lsa) +{ + ospf6_flood_clear_process (lsa, ospf6); +} + + +/* RFC2328 13.5 (Table 19): Sending link state acknowledgements. */ +static void +ospf6_acknowledge_lsa_bdrouter (struct ospf6_lsa *lsa, int ismore_recent, + struct ospf6_neighbor *from) +{ + struct ospf6_interface *oi; + int is_debug = 0; + + if (IS_OSPF6_DEBUG_FLOODING || + IS_OSPF6_DEBUG_FLOOD_TYPE (lsa->header->type)) + is_debug++; + + assert (from && from->ospf6_if); + oi = from->ospf6_if; + + /* LSA is more recent than database copy, but was not flooded + back out receiving interface. Delayed acknowledgement sent + if advertisement received from Designated Router, + otherwide do nothing. */ + if (ismore_recent < 0) + { + if (oi->drouter == from->router_id) + { + if (is_debug) + zlog_debug ("Delayed acknowledgement (BDR & MoreRecent & from DR)"); + /* Delayed acknowledgement */ + ospf6_lsdb_add (ospf6_lsa_copy (lsa), oi->lsack_list); + if (oi->thread_send_lsack == NULL) + oi->thread_send_lsack = + thread_add_timer (master, ospf6_lsack_send_interface, oi, 3); + } + else + { + if (is_debug) + zlog_debug ("No acknowledgement (BDR & MoreRecent & ! from DR)"); + } + return; + } + + /* LSA is a duplicate, and was treated as an implied acknowledgement. + Delayed acknowledgement sent if advertisement received from + Designated Router, otherwise do nothing */ + if (CHECK_FLAG (lsa->flag, OSPF6_LSA_DUPLICATE) && + CHECK_FLAG (lsa->flag, OSPF6_LSA_IMPLIEDACK)) + { + if (oi->drouter == from->router_id) + { + if (is_debug) + zlog_debug ("Delayed acknowledgement (BDR & Duplicate & ImpliedAck & from DR)"); + /* Delayed acknowledgement */ + ospf6_lsdb_add (ospf6_lsa_copy (lsa), oi->lsack_list); + if (oi->thread_send_lsack == NULL) + oi->thread_send_lsack = + thread_add_timer (master, ospf6_lsack_send_interface, oi, 3); + } + else + { + if (is_debug) + zlog_debug ("No acknowledgement (BDR & Duplicate & ImpliedAck & ! from DR)"); + } + return; + } + + /* LSA is a duplicate, and was not treated as an implied acknowledgement. + Direct acknowledgement sent */ + if (CHECK_FLAG (lsa->flag, OSPF6_LSA_DUPLICATE) && + ! CHECK_FLAG (lsa->flag, OSPF6_LSA_IMPLIEDACK)) + { + if (is_debug) + zlog_debug ("Direct acknowledgement (BDR & Duplicate)"); + ospf6_lsdb_add (ospf6_lsa_copy (lsa), from->lsack_list); + if (from->thread_send_lsack == NULL) + from->thread_send_lsack = + thread_add_event (master, ospf6_lsack_send_neighbor, from, 0); + return; + } + + /* LSA's LS age is equal to Maxage, and there is no current instance + of the LSA in the link state database, and none of router's + neighbors are in states Exchange or Loading */ + /* Direct acknowledgement sent, but this case is handled in + early of ospf6_receive_lsa () */ +} + +static void +ospf6_acknowledge_lsa_allother (struct ospf6_lsa *lsa, int ismore_recent, + struct ospf6_neighbor *from) +{ + struct ospf6_interface *oi; + int is_debug = 0; + + if (IS_OSPF6_DEBUG_FLOODING || + IS_OSPF6_DEBUG_FLOOD_TYPE (lsa->header->type)) + is_debug++; + + 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 (AllOther & FloodBack)"); + return; + } + + /* LSA is more recent than database copy, but was not flooded + back out receiving interface. Delayed acknowledgement sent. */ + if (ismore_recent < 0) + { + if (is_debug) + zlog_debug ("Delayed acknowledgement (AllOther & MoreRecent)"); + /* Delayed acknowledgement */ + ospf6_lsdb_add (ospf6_lsa_copy (lsa), oi->lsack_list); + if (oi->thread_send_lsack == NULL) + oi->thread_send_lsack = + thread_add_timer (master, ospf6_lsack_send_interface, oi, 3); + return; + } + + /* LSA is a duplicate, and was treated as an implied acknowledgement. + No acknowledgement sent. */ + if (CHECK_FLAG (lsa->flag, OSPF6_LSA_DUPLICATE) && + CHECK_FLAG (lsa->flag, OSPF6_LSA_IMPLIEDACK)) + { + if (is_debug) + zlog_debug ("No acknowledgement (AllOther & Duplicate & ImpliedAck)"); + return; + } + + /* LSA is a duplicate, and was not treated as an implied acknowledgement. + Direct acknowledgement sent */ + if (CHECK_FLAG (lsa->flag, OSPF6_LSA_DUPLICATE) && + ! CHECK_FLAG (lsa->flag, OSPF6_LSA_IMPLIEDACK)) + { + if (is_debug) + zlog_debug ("Direct acknowledgement (AllOther & Duplicate)"); + ospf6_lsdb_add (ospf6_lsa_copy (lsa), from->lsack_list); + if (from->thread_send_lsack == NULL) + from->thread_send_lsack = + thread_add_event (master, ospf6_lsack_send_neighbor, from, 0); + return; + } + + /* LSA's LS age is equal to Maxage, and there is no current instance + of the LSA in the link state database, and none of router's + neighbors are in states Exchange or Loading */ + /* Direct acknowledgement sent, but this case is handled in + early of ospf6_receive_lsa () */ +} + +static void +ospf6_acknowledge_lsa (struct ospf6_lsa *lsa, int ismore_recent, + struct ospf6_neighbor *from) +{ + struct ospf6_interface *oi; + + assert (from && from->ospf6_if); + oi = from->ospf6_if; + + if (oi->state == OSPF6_INTERFACE_BDR) + ospf6_acknowledge_lsa_bdrouter (lsa, ismore_recent, from); + else + ospf6_acknowledge_lsa_allother (lsa, ismore_recent, from); +} + +/* RFC2328 section 13 (4): + if MaxAge LSA and if we have no instance, and no neighbor + is in states Exchange or Loading + returns 1 if match this case, else returns 0 */ +static int +ospf6_is_maxage_lsa_drop (struct ospf6_lsa *lsa, struct ospf6_neighbor *from) +{ + struct ospf6_neighbor *on; + struct ospf6_interface *oi; + struct ospf6_area *oa; + struct ospf6 *process = NULL; + struct listnode *i, *j, *k; + int count = 0; + + if (! OSPF6_LSA_IS_MAXAGE (lsa)) + return 0; + + if (ospf6_lsdb_lookup (lsa->header->type, lsa->header->id, + lsa->header->adv_router, lsa->lsdb)) + return 0; + + process = from->ospf6_if->area->ospf6; + + for (ALL_LIST_ELEMENTS_RO (process->area_list, i, oa)) + for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi)) + for (ALL_LIST_ELEMENTS_RO (oi->neighbor_list, k, on)) + if (on->state == OSPF6_NEIGHBOR_EXCHANGE || + on->state == OSPF6_NEIGHBOR_LOADING) + count++; + + if (count == 0) + return 1; + return 0; +} + +/* RFC2328 section 13 The Flooding Procedure */ +void +ospf6_receive_lsa (struct ospf6_neighbor *from, + struct ospf6_lsa_header *lsa_header) +{ + struct ospf6_lsa *new = NULL, *old = NULL, *rem = NULL; + int ismore_recent; + int is_debug = 0; + + ismore_recent = 1; + assert (from); + + /* make lsa structure for received lsa */ + new = ospf6_lsa_create (lsa_header); + + if (IS_OSPF6_DEBUG_FLOODING || + IS_OSPF6_DEBUG_FLOOD_TYPE (new->header->type)) + { + is_debug++; + zlog_debug ("LSA Receive from %s", from->name); + ospf6_lsa_header_print (new); + } + + /* (1) LSA Checksum */ + if (! ospf6_lsa_checksum_valid (new->header)) + { + if (is_debug) + zlog_debug ("Wrong LSA Checksum, discard"); + ospf6_lsa_delete (new); + return; + } + + /* (2) Examine the LSA's LS type. + RFC2470 3.5.1. Receiving Link State Update packets */ + if (IS_AREA_STUB (from->ospf6_if->area) && + OSPF6_LSA_SCOPE (new->header->type) == OSPF6_SCOPE_AS) + { + if (is_debug) + zlog_debug ("AS-External-LSA (or AS-scope LSA) in stub area, discard"); + ospf6_lsa_delete (new); + return; + } + + /* (3) LSA which have reserved scope is discarded + RFC2470 3.5.1. Receiving Link State Update packets */ + /* Flooding scope check. LSAs with unknown scope are discarded here. + Set appropriate LSDB for the LSA */ + switch (OSPF6_LSA_SCOPE (new->header->type)) + { + case OSPF6_SCOPE_LINKLOCAL: + new->lsdb = from->ospf6_if->lsdb; + break; + case OSPF6_SCOPE_AREA: + new->lsdb = from->ospf6_if->area->lsdb; + break; + case OSPF6_SCOPE_AS: + new->lsdb = from->ospf6_if->area->ospf6->lsdb; + break; + default: + if (is_debug) + zlog_debug ("LSA has reserved scope, discard"); + ospf6_lsa_delete (new); + return; + } + + /* (4) if MaxAge LSA and if we have no instance, and no neighbor + is in states Exchange or Loading */ + if (ospf6_is_maxage_lsa_drop (new, from)) + { + /* log */ + if (is_debug) + 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); + if (from->thread_send_lsack == NULL) + from->thread_send_lsack = + thread_add_event (master, ospf6_lsack_send_neighbor, from, 0); + + /* b) Discard */ + ospf6_lsa_delete (new); + return; + } + + /* (5) */ + /* lookup the same database copy in lsdb */ + old = ospf6_lsdb_lookup (new->header->type, new->header->id, + new->header->adv_router, new->lsdb); + if (old) + { + ismore_recent = ospf6_lsa_compare (new, old); + if (ntohl (new->header->seqnum) == ntohl (old->header->seqnum)) + { + if (is_debug) + zlog_debug ("Received is duplicated LSA"); + SET_FLAG (new->flag, OSPF6_LSA_DUPLICATE); + } + } + + /* if no database copy or received is more recent */ + if (old == NULL || ismore_recent < 0) + { + /* in case we have no database copy */ + ismore_recent = -1; + + /* (a) MinLSArrival check */ + if (old) + { + struct timeval now, res; + quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); + timersub (&now, &old->installed, &res); + if (res.tv_sec < (OSPF_MIN_LS_ARRIVAL / 1000)) + { + if (is_debug) + zlog_debug ("LSA can't be updated within MinLSArrival, discard"); + ospf6_lsa_delete (new); + return; /* examin next lsa */ + } + } + + quagga_gettime (QUAGGA_CLK_MONOTONIC, &new->received); + + if (is_debug) + 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 + reoriginated instance of the LSA not to be rejected by other routers + due to MinLSArrival. */ + if (new->header->adv_router != from->ospf6_if->area->ospf6->router_id) + ospf6_flood (from, new); + + /* (d), installing lsdb, which may cause routing + table calculation (replacing database copy) */ + ospf6_install_lsa (new); + + /* (e) possibly acknowledge */ + ospf6_acknowledge_lsa (new, ismore_recent, from); + + /* (f) Self Originated LSA, section 13.4 */ + if (new->header->adv_router == from->ospf6_if->area->ospf6->router_id) + { + /* Self-originated LSA (newer than ours) is received from + another router. We have to make a new instance of the LSA + or have to flush this LSA. */ + if (is_debug) + { + zlog_debug ("Newer instance of the self-originated LSA"); + zlog_debug ("Schedule reorigination"); + } + new->refresh = thread_add_event (master, ospf6_lsa_refresh, new, 0); + } + + return; + } + + /* (6) if there is instance on sending neighbor's request list */ + if (ospf6_lsdb_lookup (new->header->type, new->header->id, + new->header->adv_router, from->request_list)) + { + /* if no database copy, should go above state (5) */ + assert (old); + + if (is_debug) + { + zlog_debug ("Received is not newer, on the neighbor's request-list"); + zlog_debug ("BadLSReq, discard the received LSA"); + } + + /* BadLSReq */ + thread_add_event (master, bad_lsreq, from, 0); + + ospf6_lsa_delete (new); + return; + } + + /* (7) if neither one is more recent */ + if (ismore_recent == 0) + { + if (is_debug) + zlog_debug ("The same instance as database copy (neither recent)"); + + /* (a) if on retrans-list, Treat this LSA as an Ack: Implied Ack */ + rem = ospf6_lsdb_lookup (new->header->type, new->header->id, + new->header->adv_router, from->retrans_list); + if (rem) + { + if (is_debug) + { + zlog_debug ("It is on the neighbor's retrans-list."); + zlog_debug ("Treat as an Implied acknowledgement"); + } + SET_FLAG (new->flag, OSPF6_LSA_IMPLIEDACK); + ospf6_decrement_retrans_count (rem); + ospf6_lsdb_remove (rem, from->retrans_list); + } + + if (is_debug) + zlog_debug ("Possibly acknowledge and then discard"); + + /* (b) possibly acknowledge */ + ospf6_acknowledge_lsa (new, ismore_recent, from); + + ospf6_lsa_delete (new); + return; + } + + /* (8) previous database copy is more recent */ + { + assert (old); + + /* If database copy is in 'Seqnumber Wrapping', + simply discard the received LSA */ + if (OSPF6_LSA_IS_MAXAGE (old) && + 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; + } + + /* Otherwise, Send database copy of this LSA to this neighbor */ + { + if (is_debug) + { + zlog_debug ("Database copy is more recent."); + zlog_debug ("Send back directly and then discard"); + } + + /* XXX, MinLSArrival check !? RFC 2328 13 (8) */ + + ospf6_lsdb_add (ospf6_lsa_copy (old), from->lsupdate_list); + 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; + } + return; + } +} + + +DEFUN (debug_ospf6_flooding, + debug_ospf6_flooding_cmd, + "debug ospf6 flooding", + DEBUG_STR + OSPF6_STR + "Debug OSPFv3 flooding function\n" + ) +{ + OSPF6_DEBUG_FLOODING_ON (); + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf6_flooding, + no_debug_ospf6_flooding_cmd, + "no debug ospf6 flooding", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug OSPFv3 flooding function\n" + ) +{ + OSPF6_DEBUG_FLOODING_OFF (); + return CMD_SUCCESS; +} + +int +config_write_ospf6_debug_flood (struct vty *vty) +{ + if (IS_OSPF6_DEBUG_FLOODING) + vty_out (vty, "debug ospf6 flooding%s", VNL); + return 0; +} + +void +install_element_ospf6_debug_flood (void) +{ + install_element (ENABLE_NODE, &debug_ospf6_flooding_cmd); + install_element (ENABLE_NODE, &no_debug_ospf6_flooding_cmd); + install_element (CONFIG_NODE, &debug_ospf6_flooding_cmd); + install_element (CONFIG_NODE, &no_debug_ospf6_flooding_cmd); +} + + + + + diff --git a/ospf6d/ospf6_flood.h b/ospf6d/ospf6_flood.h new file mode 100644 index 0000000..3a6f300 --- /dev/null +++ b/ospf6d/ospf6_flood.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2003 Yasuhiro Ohara + * + * 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 OSPF6_FLOOD_H +#define OSPF6_FLOOD_H + +/* Debug option */ +extern unsigned char conf_debug_ospf6_flooding; +#define OSPF6_DEBUG_FLOODING_ON() \ + (conf_debug_ospf6_flooding = 1) +#define OSPF6_DEBUG_FLOODING_OFF() \ + (conf_debug_ospf6_flooding = 0) +#define IS_OSPF6_DEBUG_FLOODING \ + (conf_debug_ospf6_flooding) + +/* Function Prototypes */ +extern struct ospf6_lsdb *ospf6_get_scoped_lsdb (struct ospf6_lsa *lsa); +extern struct ospf6_lsdb *ospf6_get_scoped_lsdb_self (struct ospf6_lsa *lsa); + +/* origination & purging */ +extern void ospf6_lsa_originate (struct ospf6_lsa *lsa); +extern void ospf6_lsa_originate_process (struct ospf6_lsa *lsa, + struct ospf6 *process); +extern void ospf6_lsa_originate_area (struct ospf6_lsa *lsa, + struct ospf6_area *oa); +extern void ospf6_lsa_originate_interface (struct ospf6_lsa *lsa, + struct ospf6_interface *oi); +extern void ospf6_lsa_purge (struct ospf6_lsa *lsa); + +/* access method to retrans_count */ +extern void ospf6_increment_retrans_count (struct ospf6_lsa *lsa); +extern void ospf6_decrement_retrans_count (struct ospf6_lsa *lsa); + +/* flooding & clear flooding */ +extern void ospf6_flood_clear (struct ospf6_lsa *lsa); +extern void ospf6_flood (struct ospf6_neighbor *from, struct ospf6_lsa *lsa); + +/* receive & install */ +extern void ospf6_receive_lsa (struct ospf6_neighbor *from, + struct ospf6_lsa_header *header); +extern void ospf6_install_lsa (struct ospf6_lsa *lsa); + +extern int config_write_ospf6_debug_flood (struct vty *vty); +extern void install_element_ospf6_debug_flood (void); + +#endif /* OSPF6_FLOOD_H */ + + diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c new file mode 100644 index 0000000..fa6509f --- /dev/null +++ b/ospf6d/ospf6_interface.c @@ -0,0 +1,2000 @@ +/* + * Copyright (C) 2003 Yasuhiro Ohara + * + * 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 "memory.h" +#include "if.h" +#include "log.h" +#include "command.h" +#include "thread.h" +#include "prefix.h" +#include "plist.h" + +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" +#include "ospf6_network.h" +#include "ospf6_message.h" +#include "ospf6_route.h" +#include "ospf6_top.h" +#include "ospf6_area.h" +#include "ospf6_interface.h" +#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; + +const char *ospf6_interface_state_str[] = +{ + "None", + "Down", + "Loopback", + "Waiting", + "PointToPoint", + "DROther", + "BDR", + "DR", + NULL +}; + +struct ospf6_interface * +ospf6_interface_lookup_by_ifindex (ifindex_t ifindex) +{ + struct ospf6_interface *oi; + struct interface *ifp; + + ifp = if_lookup_by_index (ifindex); + if (ifp == NULL) + return (struct ospf6_interface *) NULL; + + oi = (struct ospf6_interface *) ifp->info; + return oi; +} + +/* schedule routing table recalculation */ +static void +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 (oi->state == OSPF6_INTERFACE_DR) + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT (oi); + ospf6_spf_schedule (oi->area->ospf6, reason); + break; + + default: + break; + } +} + +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) +{ + struct ospf6_interface *oi; + unsigned int iobuflen; + + oi = (struct ospf6_interface *) + XCALLOC (MTYPE_OSPF6_IF, sizeof (struct ospf6_interface)); + + if (!oi) + { + zlog_err ("Can't malloc ospf6_interface for ifindex %d", ifp->ifindex); + return (struct ospf6_interface *) NULL; + } + + oi->area = (struct ospf6_area *) NULL; + oi->neighbor_list = list_new (); + oi->neighbor_list->cmp = ospf6_neighbor_cmp; + oi->linklocal_addr = (struct in6_addr *) NULL; + oi->instance_id = OSPF6_INTERFACE_INSTANCE_ID; + oi->transdelay = OSPF6_INTERFACE_TRANSDELAY; + oi->priority = OSPF6_INTERFACE_PRIORITY; + + 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; + + /* Try to adjust I/O buffer size with IfMtu */ + oi->ifmtu = ifp->mtu6; + iobuflen = ospf6_iobuf_size (ifp->mtu6); + if (oi->ifmtu > iobuflen) + { + if (IS_OSPF6_DEBUG_INTERFACE) + zlog_debug ("Interface %s: IfMtu is adjusted to I/O buffer size: %d.", + ifp->name, iobuflen); + oi->ifmtu = iobuflen; + } + + 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_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); + oi->route_connected->scope = oi; + + /* link both */ + oi->interface = ifp; + ifp->info = oi; + + /* Compute cost. */ + oi->cost = ospf6_interface_get_cost(oi); + + return oi; +} + +void +ospf6_interface_delete (struct ospf6_interface *oi) +{ + struct listnode *node, *nnode; + struct ospf6_neighbor *on; + + for (ALL_LIST_ELEMENTS (oi->neighbor_list, node, nnode, on)) + ospf6_neighbor_delete (on); + + list_delete (oi->neighbor_list); + + THREAD_OFF (oi->thread_send_hello); + THREAD_OFF (oi->thread_send_lsupdate); + THREAD_OFF (oi->thread_send_lsack); + + ospf6_lsdb_remove_all (oi->lsdb); + ospf6_lsdb_remove_all (oi->lsupdate_list); + ospf6_lsdb_remove_all (oi->lsack_list); + + ospf6_lsdb_delete (oi->lsdb); + ospf6_lsdb_delete (oi->lsdb_self); + + ospf6_lsdb_delete (oi->lsupdate_list); + ospf6_lsdb_delete (oi->lsack_list); + + ospf6_route_table_delete (oi->route_connected); + + /* cut link */ + oi->interface->info = NULL; + + /* plist_name */ + if (oi->plist_name) + XFREE (MTYPE_PREFIX_LIST_STR, oi->plist_name); + + XFREE (MTYPE_OSPF6_IF, oi); +} + +void +ospf6_interface_enable (struct ospf6_interface *oi) +{ + UNSET_FLAG (oi->flag, OSPF6_INTERFACE_DISABLE); + ospf6_interface_state_update (oi->interface); +} + +void +ospf6_interface_disable (struct ospf6_interface *oi) +{ + SET_FLAG (oi->flag, OSPF6_INTERFACE_DISABLE); + + 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 * +ospf6_interface_get_linklocal_address (struct interface *ifp) +{ + struct listnode *n; + struct connected *c; + struct in6_addr *l = (struct in6_addr *) NULL; + + /* for each connected address */ + for (ALL_LIST_ELEMENTS_RO (ifp->connected, n, c)) + { + /* if family not AF_INET6, ignore */ + if (c->address->family != AF_INET6) + continue; + + /* linklocal scope check */ + if (IN6_IS_ADDR_LINKLOCAL (&c->address->u.prefix6)) + l = &c->address->u.prefix6; + } + return l; +} + +void +ospf6_interface_if_add (struct interface *ifp) +{ + struct ospf6_interface *oi; + unsigned int iobuflen; + + oi = (struct ospf6_interface *) ifp->info; + if (oi == NULL) + return; + + /* Try to adjust I/O buffer size with IfMtu */ + if (oi->ifmtu == 0) + oi->ifmtu = ifp->mtu6; + iobuflen = ospf6_iobuf_size (ifp->mtu6); + if (oi->ifmtu > iobuflen) + { + if (IS_OSPF6_DEBUG_INTERFACE) + zlog_debug ("Interface %s: IfMtu is adjusted to I/O buffer size: %d.", + ifp->name, iobuflen); + oi->ifmtu = iobuflen; + } + + /* interface start */ + ospf6_interface_state_update(oi->interface); +} + +void +ospf6_interface_state_update (struct interface *ifp) +{ + struct ospf6_interface *oi; + + oi = (struct ospf6_interface *) ifp->info; + if (oi == NULL) + return; + if (oi->area == NULL) + return; + if (CHECK_FLAG (oi->flag, OSPF6_INTERFACE_DISABLE)) + return; + + 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); + + return; +} + +void +ospf6_interface_connected_route_update (struct interface *ifp) +{ + struct ospf6_interface *oi; + struct ospf6_route *route; + struct connected *c; + struct listnode *node, *nnode; + + oi = (struct ospf6_interface *) ifp->info; + if (oi == NULL) + return; + + /* reset linklocal pointer */ + oi->linklocal_addr = ospf6_interface_get_linklocal_address (ifp); + + /* if area is null, do not make connected-route list */ + 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); + + for (ALL_LIST_ELEMENTS (oi->interface->connected, node, nnode, c)) + { + if (c->address->family != AF_INET6) + continue; + + CONTINUE_IF_ADDRESS_LINKLOCAL (IS_OSPF6_DEBUG_INTERFACE, c->address); + CONTINUE_IF_ADDRESS_UNSPECIFIED (IS_OSPF6_DEBUG_INTERFACE, c->address); + CONTINUE_IF_ADDRESS_LOOPBACK (IS_OSPF6_DEBUG_INTERFACE, c->address); + CONTINUE_IF_ADDRESS_V4COMPAT (IS_OSPF6_DEBUG_INTERFACE, c->address); + CONTINUE_IF_ADDRESS_V4MAPPED (IS_OSPF6_DEBUG_INTERFACE, c->address); + + /* apply filter */ + if (oi->plist_name) + { + struct prefix_list *plist; + enum prefix_list_type ret; + char buf[128]; + + prefix2str (c->address, buf, sizeof (buf)); + plist = prefix_list_lookup (AFI_IP6, oi->plist_name); + ret = prefix_list_apply (plist, (void *) c->address); + if (ret == PREFIX_DENY) + { + if (IS_OSPF6_DEBUG_INTERFACE) + zlog_debug ("%s on %s filtered by prefix-list %s ", + buf, oi->interface->name, oi->plist_name); + continue; + } + } + + route = ospf6_route_create (); + memcpy (&route->prefix, c->address, sizeof (struct prefix)); + apply_mask (&route->prefix); + route->type = OSPF6_DEST_TYPE_NETWORK; + route->path.area_id = oi->area->area_id; + route->path.type = OSPF6_PATH_TYPE_INTRA; + route->path.cost = oi->cost; + route->nexthop[0].ifindex = oi->interface->ifindex; + inet_pton (AF_INET6, "::1", &route->nexthop[0].address); + ospf6_route_add (route, oi->route_connected); + } + + /* create new Link-LSA */ + OSPF6_LINK_LSA_SCHEDULE (oi); + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT (oi); + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB (oi->area); +} + +static void +ospf6_interface_state_change (u_char next_state, struct ospf6_interface *oi) +{ + u_char prev_state; + + prev_state = oi->state; + oi->state = next_state; + + if (prev_state == next_state) + return; + + /* log */ + if (IS_OSPF6_DEBUG_INTERFACE) + { + zlog_debug ("Interface state change %s: %s -> %s", oi->interface->name, + 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 || + next_state == OSPF6_INTERFACE_BDR)) + ospf6_sso (oi->interface->ifindex, &alldrouters6, IPV6_JOIN_GROUP); + + OSPF6_ROUTER_LSA_SCHEDULE (oi->area); + if (next_state == OSPF6_INTERFACE_DOWN) + { + OSPF6_NETWORK_LSA_EXECUTE (oi); + OSPF6_INTRA_PREFIX_LSA_EXECUTE_TRANSIT (oi); + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB (oi->area); + } + else if (prev_state == OSPF6_INTERFACE_DR || + next_state == OSPF6_INTERFACE_DR) + { + OSPF6_NETWORK_LSA_SCHEDULE (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) \ + ((n)->state >= OSPF6_NEIGHBOR_TWOWAY && (n)->priority != 0) + +static struct ospf6_neighbor * +better_bdrouter (struct ospf6_neighbor *a, struct ospf6_neighbor *b) +{ + if ((a == NULL || ! IS_ELIGIBLE (a) || a->drouter == a->router_id) && + (b == NULL || ! IS_ELIGIBLE (b) || b->drouter == b->router_id)) + return NULL; + else if (a == NULL || ! IS_ELIGIBLE (a) || a->drouter == a->router_id) + return b; + else if (b == NULL || ! IS_ELIGIBLE (b) || b->drouter == b->router_id) + return a; + + if (a->bdrouter == a->router_id && b->bdrouter != b->router_id) + return a; + if (a->bdrouter != a->router_id && b->bdrouter == b->router_id) + return b; + + if (a->priority > b->priority) + return a; + if (a->priority < b->priority) + return b; + + if (ntohl (a->router_id) > ntohl (b->router_id)) + return a; + if (ntohl (a->router_id) < ntohl (b->router_id)) + return b; + + zlog_warn ("Router-ID duplicate ?"); + return a; +} + +static struct ospf6_neighbor * +better_drouter (struct ospf6_neighbor *a, struct ospf6_neighbor *b) +{ + if ((a == NULL || ! IS_ELIGIBLE (a) || a->drouter != a->router_id) && + (b == NULL || ! IS_ELIGIBLE (b) || b->drouter != b->router_id)) + return NULL; + else if (a == NULL || ! IS_ELIGIBLE (a) || a->drouter != a->router_id) + return b; + else if (b == NULL || ! IS_ELIGIBLE (b) || b->drouter != b->router_id) + return a; + + if (a->drouter == a->router_id && b->drouter != b->router_id) + return a; + if (a->drouter != a->router_id && b->drouter == b->router_id) + return b; + + if (a->priority > b->priority) + return a; + if (a->priority < b->priority) + return b; + + if (ntohl (a->router_id) > ntohl (b->router_id)) + return a; + if (ntohl (a->router_id) < ntohl (b->router_id)) + return b; + + zlog_warn ("Router-ID duplicate ?"); + return a; +} + +static u_char +dr_election (struct ospf6_interface *oi) +{ + struct listnode *node, *nnode; + struct ospf6_neighbor *on, *drouter, *bdrouter, myself; + struct ospf6_neighbor *best_drouter, *best_bdrouter; + u_char next_state = 0; + + drouter = bdrouter = NULL; + best_drouter = best_bdrouter = NULL; + + /* pseudo neighbor myself, including noting current DR/BDR (1) */ + memset (&myself, 0, sizeof (myself)); + inet_ntop (AF_INET, &oi->area->ospf6->router_id, myself.name, + sizeof (myself.name)); + myself.state = OSPF6_NEIGHBOR_TWOWAY; + myself.drouter = oi->drouter; + myself.bdrouter = oi->bdrouter; + myself.priority = oi->priority; + myself.router_id = oi->area->ospf6->router_id; + + /* Electing BDR (2) */ + for (ALL_LIST_ELEMENTS (oi->neighbor_list, node, nnode, on)) + bdrouter = better_bdrouter (bdrouter, on); + + best_bdrouter = bdrouter; + bdrouter = better_bdrouter (best_bdrouter, &myself); + + /* Electing DR (3) */ + for (ALL_LIST_ELEMENTS (oi->neighbor_list, node, nnode, on)) + drouter = better_drouter (drouter, on); + + best_drouter = drouter; + drouter = better_drouter (best_drouter, &myself); + if (drouter == NULL) + drouter = bdrouter; + + /* the router itself is newly/no longer DR/BDR (4) */ + if ((drouter == &myself && myself.drouter != myself.router_id) || + (drouter != &myself && myself.drouter == myself.router_id) || + (bdrouter == &myself && myself.bdrouter != myself.router_id) || + (bdrouter != &myself && myself.bdrouter == myself.router_id)) + { + myself.drouter = (drouter ? drouter->router_id : htonl (0)); + myself.bdrouter = (bdrouter ? bdrouter->router_id : htonl (0)); + + /* compatible to Electing BDR (2) */ + bdrouter = better_bdrouter (best_bdrouter, &myself); + + /* compatible to Electing DR (3) */ + drouter = better_drouter (best_drouter, &myself); + if (drouter == NULL) + drouter = bdrouter; + } + + /* Set interface state accordingly (5) */ + if (drouter && drouter == &myself) + next_state = OSPF6_INTERFACE_DR; + else if (bdrouter && bdrouter == &myself) + next_state = OSPF6_INTERFACE_BDR; + else + next_state = OSPF6_INTERFACE_DROTHER; + + /* If NBMA, schedule Start for each neighbor having priority of 0 (6) */ + /* XXX */ + + /* If DR or BDR change, invoke AdjOK? for each neighbor (7) */ + /* RFC 2328 section 12.4. Originating LSAs (3) will be handled + accordingly after AdjOK */ + if (oi->drouter != (drouter ? drouter->router_id : htonl (0)) || + oi->bdrouter != (bdrouter ? bdrouter->router_id : htonl (0))) + { + if (IS_OSPF6_DEBUG_INTERFACE) + zlog_debug ("DR Election on %s: DR: %s BDR: %s", oi->interface->name, + (drouter ? drouter->name : "0.0.0.0"), + (bdrouter ? bdrouter->name : "0.0.0.0")); + + for (ALL_LIST_ELEMENTS_RO (oi->neighbor_list, node, on)) + { + if (on->state < OSPF6_NEIGHBOR_TWOWAY) + continue; + /* Schedule AdjOK. */ + thread_add_event (master, adj_ok, on, 0); + } + } + + oi->drouter = (drouter ? drouter->router_id : htonl (0)); + oi->bdrouter = (bdrouter ? bdrouter->router_id : htonl (0)); + return next_state; +} + + +/* Interface State Machine */ +int +interface_up (struct thread *thread) +{ + struct ospf6_interface *oi; + + oi = (struct ospf6_interface *) THREAD_ARG (thread); + assert (oi && oi->interface); + + if (IS_OSPF6_DEBUG_INTERFACE) + zlog_debug ("Interface Event %s: [InterfaceUp]", + oi->interface->name); + + /* check physical interface is up */ + if (! if_is_operative (oi->interface)) + { + if (IS_OSPF6_DEBUG_INTERFACE) + zlog_debug ("Interface %s is down, can't execute [InterfaceUp]", + oi->interface->name); + 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) + { + if (IS_OSPF6_DEBUG_INTERFACE) + zlog_debug ("Interface %s already enabled", + oi->interface->name); + 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 */ + 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)) + oi->thread_send_hello = thread_add_event (master, ospf6_hello_send, oi, 0); + + /* decide next interface state */ + 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 + { + ospf6_interface_state_change (OSPF6_INTERFACE_WAITING, oi); + thread_add_timer (master, wait_timer, oi, oi->dead_interval); + } + + return 0; +} + +int +wait_timer (struct thread *thread) +{ + struct ospf6_interface *oi; + + oi = (struct ospf6_interface *) THREAD_ARG (thread); + assert (oi && oi->interface); + + if (IS_OSPF6_DEBUG_INTERFACE) + zlog_debug ("Interface Event %s: [WaitTimer]", + oi->interface->name); + + if (oi->state == OSPF6_INTERFACE_WAITING) + ospf6_interface_state_change (dr_election (oi), oi); + + return 0; +} + +int +backup_seen (struct thread *thread) +{ + struct ospf6_interface *oi; + + oi = (struct ospf6_interface *) THREAD_ARG (thread); + assert (oi && oi->interface); + + if (IS_OSPF6_DEBUG_INTERFACE) + zlog_debug ("Interface Event %s: [BackupSeen]", + oi->interface->name); + + if (oi->state == OSPF6_INTERFACE_WAITING) + ospf6_interface_state_change (dr_election (oi), oi); + + return 0; +} + +int +neighbor_change (struct thread *thread) +{ + struct ospf6_interface *oi; + + oi = (struct ospf6_interface *) THREAD_ARG (thread); + assert (oi && oi->interface); + + if (IS_OSPF6_DEBUG_INTERFACE) + zlog_debug ("Interface Event %s: [NeighborChange]", + oi->interface->name); + + if (oi->state == OSPF6_INTERFACE_DROTHER || + oi->state == OSPF6_INTERFACE_BDR || + oi->state == OSPF6_INTERFACE_DR) + ospf6_interface_state_change (dr_election (oi), oi); + + return 0; +} + +int +interface_down (struct thread *thread) +{ + struct ospf6_interface *oi; + struct listnode *node, *nnode; + struct ospf6_neighbor *on; + + oi = (struct ospf6_interface *) THREAD_ARG (thread); + assert (oi && oi->interface); + + if (IS_OSPF6_DEBUG_INTERFACE) + 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); + + ospf6_interface_state_change (OSPF6_INTERFACE_DOWN, oi); + + for (ALL_LIST_ELEMENTS (oi->neighbor_list, node, nnode, on)) + ospf6_neighbor_delete (on); + + 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) +{ + struct ospf6_interface *oi; + struct connected *c; + struct prefix *p; + struct listnode *i; + char strbuf[64], drouter[32], bdrouter[32]; + const char *updown[3] = {"down", "up", NULL}; + const char *type; + struct timeval res, now; + char duration[32]; + struct ospf6_lsa *lsa; + + /* check physical interface type */ + if (if_is_loopback (ifp)) + type = "LOOPBACK"; + else if (if_is_broadcast (ifp)) + type = "BROADCAST"; + else if (if_is_pointopoint (ifp)) + type = "POINTOPOINT"; + else + type = "UNKNOWN"; + + vty_out (vty, "%s is %s, type %s%s", + ifp->name, updown[if_is_operative (ifp)], type, + VNL); + vty_out (vty, " Interface ID: %d%s", ifp->ifindex, VNL); + + if (ifp->info == NULL) + { + vty_out (vty, " OSPF not enabled on this interface%s", VNL); + return 0; + } + else + oi = (struct ospf6_interface *) ifp->info; + + vty_out (vty, " Internet Address:%s", VNL); + + for (ALL_LIST_ELEMENTS_RO (ifp->connected, i, c)) + { + p = c->address; + prefix2str (p, strbuf, sizeof (strbuf)); + switch (p->family) + { + case AF_INET: + vty_out (vty, " inet : %s%s", strbuf, + VNL); + break; + case AF_INET6: + vty_out (vty, " inet6: %s%s", strbuf, + VNL); + break; + default: + vty_out (vty, " ??? : %s%s", strbuf, + VNL); + break; + } + } + + if (oi->area) + { + vty_out (vty, " Instance ID %d, Interface MTU %d (autodetect: %d)%s", + oi->instance_id, oi->ifmtu, ifp->mtu6, VNL); + vty_out (vty, " MTU mismatch detection: %s%s", oi->mtu_ignore ? + "disabled" : "enabled", VNL); + inet_ntop (AF_INET, &oi->area->area_id, + strbuf, sizeof (strbuf)); + vty_out (vty, " Area ID %s, Cost %u%s", strbuf, oi->cost, + VNL); + } + else + vty_out (vty, " Not Attached to Area%s", VNL); + + vty_out (vty, " State %s, Transmit Delay %d sec, Priority %d%s", + ospf6_interface_state_str[oi->state], + oi->transdelay, oi->priority, + VNL); + vty_out (vty, " Timer intervals configured:%s", VNL); + vty_out (vty, " Hello %d, Dead %d, Retransmit %d%s", + oi->hello_interval, oi->dead_interval, oi->rxmt_interval, + VNL); + + inet_ntop (AF_INET, &oi->drouter, drouter, sizeof (drouter)); + inet_ntop (AF_INET, &oi->bdrouter, bdrouter, sizeof (bdrouter)); + vty_out (vty, " DR: %s BDR: %s%s", drouter, bdrouter, VNL); + + vty_out (vty, " Number of I/F scoped LSAs is %u%s", + oi->lsdb->count, VNL); + + quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); + + timerclear (&res); + if (oi->thread_send_lsupdate) + timersub (&oi->thread_send_lsupdate->u.sands, &now, &res); + timerstring (&res, duration, sizeof (duration)); + vty_out (vty, " %d Pending LSAs for LSUpdate in Time %s [thread %s]%s", + oi->lsupdate_list->count, duration, + (oi->thread_send_lsupdate ? "on" : "off"), + VNL); + for (lsa = ospf6_lsdb_head (oi->lsupdate_list); lsa; + lsa = ospf6_lsdb_next (lsa)) + vty_out (vty, " %s%s", lsa->name, VNL); + + timerclear (&res); + if (oi->thread_send_lsack) + timersub (&oi->thread_send_lsack->u.sands, &now, &res); + timerstring (&res, duration, sizeof (duration)); + vty_out (vty, " %d Pending LSAs for LSAck in Time %s [thread %s]%s", + oi->lsack_list->count, duration, + (oi->thread_send_lsack ? "on" : "off"), + VNL); + for (lsa = ospf6_lsdb_head (oi->lsack_list); lsa; + lsa = ospf6_lsdb_next (lsa)) + vty_out (vty, " %s%s", lsa->name, VNL); + + return 0; +} + +/* show interface */ +DEFUN (show_ipv6_ospf6_interface, + show_ipv6_ospf6_interface_ifname_cmd, + "show ipv6 ospf6 interface IFNAME", + SHOW_STR + IP6_STR + OSPF6_STR + INTERFACE_STR + IFNAME_STR + ) +{ + struct interface *ifp; + struct listnode *i; + + if (argc) + { + ifp = if_lookup_by_name (argv[0]); + if (ifp == NULL) + { + vty_out (vty, "No such Interface: %s%s", argv[0], + VNL); + return CMD_WARNING; + } + ospf6_interface_show (vty, ifp); + } + else + { + for (ALL_LIST_ELEMENTS_RO (iflist, i, ifp)) + ospf6_interface_show (vty, ifp); + } + + return CMD_SUCCESS; +} + +ALIAS (show_ipv6_ospf6_interface, + show_ipv6_ospf6_interface_cmd, + "show ipv6 ospf6 interface", + SHOW_STR + IP6_STR + OSPF6_STR + INTERFACE_STR + ) + +DEFUN (show_ipv6_ospf6_interface_ifname_prefix, + show_ipv6_ospf6_interface_ifname_prefix_cmd, + "show ipv6 ospf6 interface IFNAME prefix", + SHOW_STR + IP6_STR + OSPF6_STR + INTERFACE_STR + IFNAME_STR + "Display connected prefixes to advertise\n" + ) +{ + struct interface *ifp; + struct ospf6_interface *oi; + + ifp = if_lookup_by_name (argv[0]); + if (ifp == NULL) + { + vty_out (vty, "No such Interface: %s%s", argv[0], VNL); + return CMD_WARNING; + } + + oi = ifp->info; + if (oi == NULL) + { + vty_out (vty, "OSPFv3 is not enabled on %s%s", argv[0], VNL); + return CMD_WARNING; + } + + argc--; + argv++; + ospf6_route_table_show (vty, argc, argv, oi->route_connected); + + return CMD_SUCCESS; +} + +ALIAS (show_ipv6_ospf6_interface_ifname_prefix, + show_ipv6_ospf6_interface_ifname_prefix_detail_cmd, + "show ipv6 ospf6 interface IFNAME prefix (X:X::X:X|X:X::X:X/M|detail)", + SHOW_STR + IP6_STR + OSPF6_STR + INTERFACE_STR + IFNAME_STR + "Display connected prefixes to advertise\n" + OSPF6_ROUTE_ADDRESS_STR + OSPF6_ROUTE_PREFIX_STR + "Display details of the prefixes\n" + ) + +ALIAS (show_ipv6_ospf6_interface_ifname_prefix, + show_ipv6_ospf6_interface_ifname_prefix_match_cmd, + "show ipv6 ospf6 interface IFNAME prefix X:X::X:X/M (match|detail)", + SHOW_STR + IP6_STR + OSPF6_STR + INTERFACE_STR + IFNAME_STR + "Display connected prefixes to advertise\n" + OSPF6_ROUTE_PREFIX_STR + OSPF6_ROUTE_MATCH_STR + "Display details of the prefixes\n" + ) + +DEFUN (show_ipv6_ospf6_interface_prefix, + show_ipv6_ospf6_interface_prefix_cmd, + "show ipv6 ospf6 interface prefix", + SHOW_STR + IP6_STR + OSPF6_STR + INTERFACE_STR + "Display connected prefixes to advertise\n" + ) +{ + struct listnode *i; + struct ospf6_interface *oi; + struct interface *ifp; + + for (ALL_LIST_ELEMENTS_RO (iflist, i, ifp)) + { + oi = (struct ospf6_interface *) ifp->info; + if (oi == NULL) + continue; + + ospf6_route_table_show (vty, argc, argv, oi->route_connected); + } + + return CMD_SUCCESS; +} + +ALIAS (show_ipv6_ospf6_interface_prefix, + show_ipv6_ospf6_interface_prefix_detail_cmd, + "show ipv6 ospf6 interface prefix (X:X::X:X|X:X::X:X/M|detail)", + SHOW_STR + IP6_STR + OSPF6_STR + INTERFACE_STR + "Display connected prefixes to advertise\n" + OSPF6_ROUTE_ADDRESS_STR + OSPF6_ROUTE_PREFIX_STR + "Display details of the prefixes\n" + ) + +ALIAS (show_ipv6_ospf6_interface_prefix, + show_ipv6_ospf6_interface_prefix_match_cmd, + "show ipv6 ospf6 interface prefix X:X::X:X/M (match|detail)", + SHOW_STR + IP6_STR + OSPF6_STR + INTERFACE_STR + "Display connected prefixes to advertise\n" + OSPF6_ROUTE_PREFIX_STR + OSPF6_ROUTE_MATCH_STR + "Display details of the prefixes\n" + ) + + +/* interface variable set command */ +DEFUN (ipv6_ospf6_ifmtu, + ipv6_ospf6_ifmtu_cmd, + "ipv6 ospf6 ifmtu <1-65535>", + IP6_STR + OSPF6_STR + "Interface MTU\n" + "OSPFv3 Interface MTU\n" + ) +{ + struct ospf6_interface *oi; + struct interface *ifp; + unsigned int ifmtu, iobuflen; + struct listnode *node, *nnode; + struct ospf6_neighbor *on; + + ifp = (struct interface *) vty->index; + assert (ifp); + + oi = (struct ospf6_interface *) ifp->info; + if (oi == NULL) + oi = ospf6_interface_create (ifp); + assert (oi); + + ifmtu = strtol (argv[0], NULL, 10); + + if (oi->ifmtu == ifmtu) + return CMD_SUCCESS; + + if (ifp->mtu6 != 0 && ifp->mtu6 < ifmtu) + { + vty_out (vty, "%s's ospf6 ifmtu cannot go beyond physical mtu (%d)%s", + ifp->name, ifp->mtu6, VNL); + return CMD_WARNING; + } + + if (oi->ifmtu < ifmtu) + { + iobuflen = ospf6_iobuf_size (ifmtu); + if (iobuflen < ifmtu) + { + vty_out (vty, "%s's ifmtu is adjusted to I/O buffer size (%d).%s", + ifp->name, iobuflen, VNL); + oi->ifmtu = iobuflen; + } + else + oi->ifmtu = ifmtu; + } + else + oi->ifmtu = ifmtu; + + /* re-establish adjacencies */ + for (ALL_LIST_ELEMENTS (oi->neighbor_list, node, nnode, on)) + { + THREAD_OFF (on->inactivity_timer); + thread_add_event (master, inactivity_timer, on, 0); + } + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_ospf6_ifmtu, + no_ipv6_ospf6_ifmtu_cmd, + "no ipv6 ospf6 ifmtu", + NO_STR + IP6_STR + OSPF6_STR + "Interface MTU\n" + ) +{ + struct ospf6_interface *oi; + struct interface *ifp; + unsigned int iobuflen; + struct listnode *node, *nnode; + struct ospf6_neighbor *on; + + ifp = (struct interface *) vty->index; + assert (ifp); + + oi = (struct ospf6_interface *) ifp->info; + if (oi == NULL) + oi = ospf6_interface_create (ifp); + assert (oi); + + if (oi->ifmtu < ifp->mtu) + { + iobuflen = ospf6_iobuf_size (ifp->mtu); + if (iobuflen < ifp->mtu) + { + vty_out (vty, "%s's ifmtu is adjusted to I/O buffer size (%d).%s", + ifp->name, iobuflen, VNL); + oi->ifmtu = iobuflen; + } + else + oi->ifmtu = ifp->mtu; + } + else + oi->ifmtu = ifp->mtu; + + /* re-establish adjacencies */ + for (ALL_LIST_ELEMENTS (oi->neighbor_list, node, nnode, on)) + { + THREAD_OFF (on->inactivity_timer); + thread_add_event (master, inactivity_timer, on, 0); + } + + return CMD_SUCCESS; +} + +DEFUN (ipv6_ospf6_cost, + ipv6_ospf6_cost_cmd, + "ipv6 ospf6 cost <1-65535>", + IP6_STR + OSPF6_STR + "Interface cost\n" + "Outgoing metric of this interface\n" + ) +{ + struct ospf6_interface *oi; + struct interface *ifp; + unsigned long int lcost; + + ifp = (struct interface *) vty->index; + assert (ifp); + + oi = (struct ospf6_interface *) ifp->info; + if (oi == NULL) + oi = ospf6_interface_create (ifp); + assert (oi); + + lcost = strtol (argv[0], NULL, 10); + + if (lcost > UINT32_MAX) + { + vty_out (vty, "Cost %ld is out of range%s", lcost, VNL); + return CMD_WARNING; + } + + if (oi->cost == lcost) + return CMD_SUCCESS; + + oi->cost = lcost; + SET_FLAG (oi->flag, OSPF6_INTERFACE_NOAUTOCOST); + + 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) + { + 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; +} + +DEFUN (ipv6_ospf6_hellointerval, + ipv6_ospf6_hellointerval_cmd, + "ipv6 ospf6 hello-interval <1-65535>", + IP6_STR + OSPF6_STR + "Interval time of Hello packets\n" + SECONDS_STR + ) +{ + 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); + + oi->hello_interval = strtol (argv[0], NULL, 10); + return CMD_SUCCESS; +} + +/* interface variable set command */ +DEFUN (ipv6_ospf6_deadinterval, + ipv6_ospf6_deadinterval_cmd, + "ipv6 ospf6 dead-interval <1-65535>", + IP6_STR + OSPF6_STR + "Interval time after which a neighbor is declared down\n" + SECONDS_STR + ) +{ + 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); + + oi->dead_interval = strtol (argv[0], NULL, 10); + return CMD_SUCCESS; +} + +/* interface variable set command */ +DEFUN (ipv6_ospf6_transmitdelay, + ipv6_ospf6_transmitdelay_cmd, + "ipv6 ospf6 transmit-delay <1-3600>", + IP6_STR + OSPF6_STR + "Transmit delay of this interface\n" + SECONDS_STR + ) +{ + 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); + + oi->transdelay = strtol (argv[0], NULL, 10); + return CMD_SUCCESS; +} + +/* interface variable set command */ +DEFUN (ipv6_ospf6_retransmitinterval, + ipv6_ospf6_retransmitinterval_cmd, + "ipv6 ospf6 retransmit-interval <1-65535>", + IP6_STR + OSPF6_STR + "Time between retransmitting lost link state advertisements\n" + SECONDS_STR + ) +{ + 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); + + oi->rxmt_interval = strtol (argv[0], NULL, 10); + return CMD_SUCCESS; +} + +/* interface variable set command */ +DEFUN (ipv6_ospf6_priority, + ipv6_ospf6_priority_cmd, + "ipv6 ospf6 priority <0-255>", + IP6_STR + OSPF6_STR + "Router priority\n" + "Priority value\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); + + oi->priority = strtol (argv[0], NULL, 10); + + 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; +} + +DEFUN (ipv6_ospf6_instance, + ipv6_ospf6_instance_cmd, + "ipv6 ospf6 instance-id <0-255>", + IP6_STR + OSPF6_STR + "Instance ID for this interface\n" + "Instance ID value\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); + + oi->instance_id = strtol (argv[0], NULL, 10); + return CMD_SUCCESS; +} + +DEFUN (ipv6_ospf6_passive, + ipv6_ospf6_passive_cmd, + "ipv6 ospf6 passive", + IP6_STR + OSPF6_STR + "passive interface, No adjacency will be formed on this interface\n" + ) +{ + struct ospf6_interface *oi; + struct interface *ifp; + struct listnode *node, *nnode; + struct ospf6_neighbor *on; + + ifp = (struct interface *) vty->index; + assert (ifp); + + oi = (struct ospf6_interface *) ifp->info; + if (oi == NULL) + oi = ospf6_interface_create (ifp); + assert (oi); + + SET_FLAG (oi->flag, OSPF6_INTERFACE_PASSIVE); + THREAD_OFF (oi->thread_send_hello); + + for (ALL_LIST_ELEMENTS (oi->neighbor_list, node, nnode, on)) + { + THREAD_OFF (on->inactivity_timer); + thread_add_event (master, inactivity_timer, on, 0); + } + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_ospf6_passive, + no_ipv6_ospf6_passive_cmd, + "no ipv6 ospf6 passive", + NO_STR + IP6_STR + OSPF6_STR + "passive interface: No Adjacency will be formed on this I/F\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_PASSIVE); + THREAD_OFF (oi->thread_send_hello); + oi->thread_send_hello = + thread_add_event (master, ospf6_hello_send, oi, 0); + + return CMD_SUCCESS; +} + +DEFUN (ipv6_ospf6_mtu_ignore, + ipv6_ospf6_mtu_ignore_cmd, + "ipv6 ospf6 mtu-ignore", + IP6_STR + OSPF6_STR + "Ignore MTU mismatch on this interface\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); + + oi->mtu_ignore = 1; + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_ospf6_mtu_ignore, + no_ipv6_ospf6_mtu_ignore_cmd, + "no ipv6 ospf6 mtu-ignore", + NO_STR + IP6_STR + OSPF6_STR + "Ignore MTU mismatch on this interface\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); + + oi->mtu_ignore = 0; + + return CMD_SUCCESS; +} + +DEFUN (ipv6_ospf6_advertise_prefix_list, + ipv6_ospf6_advertise_prefix_list_cmd, + "ipv6 ospf6 advertise prefix-list WORD", + IP6_STR + OSPF6_STR + "Advertising options\n" + "Filter prefix using prefix-list\n" + "Prefix list name\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 (oi->plist_name) + XFREE (MTYPE_PREFIX_LIST_STR, oi->plist_name); + oi->plist_name = XSTRDUP (MTYPE_PREFIX_LIST_STR, argv[0]); + + ospf6_interface_connected_route_update (oi->interface); + + if (oi->area) + { + OSPF6_LINK_LSA_SCHEDULE (oi); + if (oi->state == OSPF6_INTERFACE_DR) + { + OSPF6_NETWORK_LSA_SCHEDULE (oi); + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT (oi); + } + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB (oi->area); + } + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_ospf6_advertise_prefix_list, + no_ipv6_ospf6_advertise_prefix_list_cmd, + "no ipv6 ospf6 advertise prefix-list", + NO_STR + IP6_STR + OSPF6_STR + "Advertising options\n" + "Filter prefix using prefix-list\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 (oi->plist_name) + { + XFREE (MTYPE_PREFIX_LIST_STR, oi->plist_name); + oi->plist_name = NULL; + } + + ospf6_interface_connected_route_update (oi->interface); + + if (oi->area) + { + OSPF6_LINK_LSA_SCHEDULE (oi); + if (oi->state == OSPF6_INTERFACE_DR) + { + OSPF6_NETWORK_LSA_SCHEDULE (oi); + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT (oi); + } + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB (oi->area); + } + + 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) +{ + struct listnode *i; + struct ospf6_interface *oi; + struct interface *ifp; + + for (ALL_LIST_ELEMENTS_RO (iflist, i, ifp)) + { + oi = (struct ospf6_interface *) ifp->info; + if (oi == NULL) + continue; + + vty_out (vty, "interface %s%s", + oi->interface->name, VNL); + + if (ifp->desc) + vty_out (vty, " description %s%s", ifp->desc, VNL); + if (ifp->mtu6 != oi->ifmtu) + vty_out (vty, " ipv6 ospf6 ifmtu %d%s", oi->ifmtu, VNL); + + if (CHECK_FLAG (oi->flag, OSPF6_INTERFACE_NOAUTOCOST)) + vty_out (vty, " ipv6 ospf6 cost %d%s", + oi->cost, VNL); + + if (oi->hello_interval != OSPF6_INTERFACE_HELLO_INTERVAL) + vty_out (vty, " ipv6 ospf6 hello-interval %d%s", + oi->hello_interval, VNL); + + if (oi->dead_interval != OSPF6_INTERFACE_DEAD_INTERVAL) + vty_out (vty, " ipv6 ospf6 dead-interval %d%s", + oi->dead_interval, VNL); + + if (oi->rxmt_interval != OSPF6_INTERFACE_RXMT_INTERVAL) + vty_out (vty, " ipv6 ospf6 retransmit-interval %d%s", + oi->rxmt_interval, VNL); + + if (oi->priority != OSPF6_INTERFACE_PRIORITY) + vty_out (vty, " ipv6 ospf6 priority %d%s", + oi->priority, VNL); + + if (oi->transdelay != OSPF6_INTERFACE_TRANSDELAY) + vty_out (vty, " ipv6 ospf6 transmit-delay %d%s", + oi->transdelay, VNL); + + if (oi->instance_id != OSPF6_INTERFACE_INSTANCE_ID) + vty_out (vty, " ipv6 ospf6 instance-id %d%s", + oi->instance_id, VNL); + + if (oi->plist_name) + vty_out (vty, " ipv6 ospf6 advertise prefix-list %s%s", + oi->plist_name, VNL); + + if (CHECK_FLAG (oi->flag, OSPF6_INTERFACE_PASSIVE)) + vty_out (vty, " ipv6 ospf6 passive%s", VNL); + + 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; +} + +static struct cmd_node interface_node = +{ + INTERFACE_NODE, + "%s(config-if)# ", + 1 /* VTYSH */ +}; + +void +ospf6_interface_init (void) +{ + /* Install interface node. */ + install_node (&interface_node, config_write_ospf6_interface); + + install_element (VIEW_NODE, &show_ipv6_ospf6_interface_cmd); + install_element (VIEW_NODE, &show_ipv6_ospf6_interface_prefix_cmd); + install_element (VIEW_NODE, &show_ipv6_ospf6_interface_prefix_detail_cmd); + install_element (VIEW_NODE, &show_ipv6_ospf6_interface_prefix_match_cmd); + install_element (VIEW_NODE, &show_ipv6_ospf6_interface_ifname_cmd); + 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 (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); + install_element (INTERFACE_NODE, &ipv6_ospf6_hellointerval_cmd); + install_element (INTERFACE_NODE, &ipv6_ospf6_priority_cmd); + install_element (INTERFACE_NODE, &ipv6_ospf6_retransmitinterval_cmd); + install_element (INTERFACE_NODE, &ipv6_ospf6_transmitdelay_cmd); + install_element (INTERFACE_NODE, &ipv6_ospf6_instance_cmd); + + install_element (INTERFACE_NODE, &ipv6_ospf6_passive_cmd); + install_element (INTERFACE_NODE, &no_ipv6_ospf6_passive_cmd); + + install_element (INTERFACE_NODE, &ipv6_ospf6_mtu_ignore_cmd); + install_element (INTERFACE_NODE, &no_ipv6_ospf6_mtu_ignore_cmd); + + 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, + debug_ospf6_interface_cmd, + "debug ospf6 interface", + DEBUG_STR + OSPF6_STR + "Debug OSPFv3 Interface\n" + ) +{ + OSPF6_DEBUG_INTERFACE_ON (); + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf6_interface, + no_debug_ospf6_interface_cmd, + "no debug ospf6 interface", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug OSPFv3 Interface\n" + ) +{ + OSPF6_DEBUG_INTERFACE_OFF (); + return CMD_SUCCESS; +} + +int +config_write_ospf6_debug_interface (struct vty *vty) +{ + if (IS_OSPF6_DEBUG_INTERFACE) + vty_out (vty, "debug ospf6 interface%s", VNL); + return 0; +} + +void +install_element_ospf6_debug_interface (void) +{ + install_element (ENABLE_NODE, &debug_ospf6_interface_cmd); + install_element (ENABLE_NODE, &no_debug_ospf6_interface_cmd); + install_element (CONFIG_NODE, &debug_ospf6_interface_cmd); + install_element (CONFIG_NODE, &no_debug_ospf6_interface_cmd); +} + + diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h new file mode 100644 index 0000000..2fbb83c --- /dev/null +++ b/ospf6d/ospf6_interface.h @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2003 Yasuhiro Ohara + * + * 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 OSPF6_INTERFACE_H +#define OSPF6_INTERFACE_H + +#include "if.h" + +/* Debug option */ +extern unsigned char conf_debug_ospf6_interface; +#define OSPF6_DEBUG_INTERFACE_ON() \ + (conf_debug_ospf6_interface = 1) +#define OSPF6_DEBUG_INTERFACE_OFF() \ + (conf_debug_ospf6_interface = 0) +#define IS_OSPF6_DEBUG_INTERFACE \ + (conf_debug_ospf6_interface) + +/* Interface structure */ +struct ospf6_interface +{ + /* IF info from zebra */ + struct interface *interface; + + /* back pointer */ + struct ospf6_area *area; + + /* list of ospf6 neighbor */ + struct list *neighbor_list; + + /* linklocal address of this I/F */ + struct in6_addr *linklocal_addr; + + /* Interface ID; use interface->ifindex */ + + /* ospf6 instance id */ + u_char instance_id; + + /* I/F transmission delay */ + u_int32_t transdelay; + + /* Network Type */ + u_char type; + + /* Router Priority */ + u_char priority; + + /* Time Interval */ + u_int16_t hello_interval; + u_int16_t dead_interval; + u_int32_t rxmt_interval; + + u_int32_t state_change; + + /* Cost */ + u_int32_t cost; + + /* I/F MTU */ + u_int32_t ifmtu; + + /* Interface State */ + u_char state; + + /* Interface socket setting trial counter, resets on success */ + u_char sso_try_cnt; + + /* OSPF6 Interface flag */ + char flag; + + /* MTU mismatch check */ + u_char mtu_ignore; + + /* Decision of DR Election */ + u_int32_t drouter; + u_int32_t bdrouter; + u_int32_t prev_drouter; + u_int32_t prev_bdrouter; + + /* Linklocal LSA Database: includes Link-LSA */ + struct ospf6_lsdb *lsdb; + struct ospf6_lsdb *lsdb_self; + + struct ospf6_lsdb *lsupdate_list; + struct ospf6_lsdb *lsack_list; + + /* Ongoing Tasks */ + struct thread *thread_send_hello; + struct thread *thread_send_lsupdate; + struct thread *thread_send_lsack; + + struct thread *thread_network_lsa; + struct thread *thread_link_lsa; + struct thread *thread_intra_prefix_lsa; + + struct ospf6_route_table *route_connected; + + /* prefix-list name to filter connected prefix */ + char *plist_name; +}; + +/* interface state */ +#define OSPF6_INTERFACE_NONE 0 +#define OSPF6_INTERFACE_DOWN 1 +#define OSPF6_INTERFACE_LOOPBACK 2 +#define OSPF6_INTERFACE_WAITING 3 +#define OSPF6_INTERFACE_POINTTOPOINT 4 +#define OSPF6_INTERFACE_DROTHER 5 +#define OSPF6_INTERFACE_BDR 6 +#define OSPF6_INTERFACE_DR 7 +#define OSPF6_INTERFACE_MAX 8 + +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 +#define OSPF6_INTERFACE_DEAD_INTERVAL 40 +#define OSPF6_INTERFACE_RXMT_INTERVAL 5 +#define OSPF6_INTERFACE_COST 1 +#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); +extern struct ospf6_interface *ospf6_interface_create (struct interface *); +extern void ospf6_interface_delete (struct ospf6_interface *); + +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_state_update (struct interface *); +extern void ospf6_interface_connected_route_update (struct interface *); + +/* interface event */ +extern int interface_up (struct thread *); +extern int interface_down (struct thread *); +extern int wait_timer (struct thread *); +extern int backup_seen (struct thread *); +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); + +#endif /* OSPF6_INTERFACE_H */ diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c new file mode 100644 index 0000000..591dab3 --- /dev/null +++ b/ospf6d/ospf6_intra.c @@ -0,0 +1,1789 @@ +/* + * Copyright (C) 2003 Yasuhiro Ohara + * + * 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 "linklist.h" +#include "thread.h" +#include "memory.h" +#include "if.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" +#include "command.h" + +#include "ospf6_proto.h" +#include "ospf6_message.h" +#include "ospf6_route.h" +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" + +#include "ospf6_top.h" +#include "ospf6_area.h" +#include "ospf6_interface.h" +#include "ospf6_neighbor.h" +#include "ospf6_intra.h" +#include "ospf6_asbr.h" +#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; +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) +{ + char *start, *end, *current; + char buf[32], name[32], bits[16], options[32]; + struct ospf6_router_lsa *router_lsa; + struct ospf6_router_lsdesc *lsdesc; + + router_lsa = (struct ospf6_router_lsa *) + ((char *) lsa->header + sizeof (struct ospf6_lsa_header)); + + ospf6_capability_printbuf (router_lsa->bits, bits, sizeof (bits)); + ospf6_options_printbuf (router_lsa->options, options, sizeof (options)); + vty_out (vty, " Bits: %s Options: %s%s", bits, options, VNL); + + start = (char *) router_lsa + sizeof (struct ospf6_router_lsa); + end = (char *) lsa->header + ntohs (lsa->header->length); + for (current = start; current + sizeof (struct ospf6_router_lsdesc) <= end; + current += sizeof (struct ospf6_router_lsdesc)) + { + lsdesc = (struct ospf6_router_lsdesc *) current; + + if (lsdesc->type == OSPF6_ROUTER_LSDESC_POINTTOPOINT) + snprintf (name, sizeof (name), "Point-To-Point"); + else if (lsdesc->type == OSPF6_ROUTER_LSDESC_TRANSIT_NETWORK) + snprintf (name, sizeof (name), "Transit-Network"); + else if (lsdesc->type == OSPF6_ROUTER_LSDESC_STUB_NETWORK) + snprintf (name, sizeof (name), "Stub-Network"); + else if (lsdesc->type == OSPF6_ROUTER_LSDESC_VIRTUAL_LINK) + snprintf (name, sizeof (name), "Virtual-Link"); + else + snprintf (name, sizeof (name), "Unknown (%#x)", lsdesc->type); + + vty_out (vty, " Type: %s Metric: %d%s", + name, ntohs (lsdesc->metric), VNL); + vty_out (vty, " Interface ID: %s%s", + inet_ntop (AF_INET, &lsdesc->interface_id, + buf, sizeof (buf)), VNL); + vty_out (vty, " Neighbor Interface ID: %s%s", + inet_ntop (AF_INET, &lsdesc->neighbor_interface_id, + buf, sizeof (buf)), VNL); + vty_out (vty, " Neighbor Router ID: %s%s", + inet_ntop (AF_INET, &lsdesc->neighbor_router_id, + buf, sizeof (buf)), VNL); + } + 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) +{ + struct ospf6_area *oa; + + char buffer [OSPF6_MAX_LSASIZE]; + struct ospf6_lsa_header *lsa_header; + struct ospf6_lsa *lsa; + + u_int32_t link_state_id = 0; + struct listnode *node, *nnode; + struct listnode *j; + struct ospf6_interface *oi; + struct ospf6_neighbor *on, *drouter = NULL; + struct ospf6_router_lsa *router_lsa; + struct ospf6_router_lsdesc *lsdesc; + u_int16_t type; + u_int32_t router; + int count; + + oa = (struct ospf6_area *) THREAD_ARG (thread); + oa->thread_router_lsa = NULL; + + if (IS_OSPF6_DEBUG_ORIGINATE (ROUTER)) + zlog_debug ("Originate Router-LSA for Area %s", oa->name); + + memset (buffer, 0, sizeof (buffer)); + lsa_header = (struct ospf6_lsa_header *) buffer; + router_lsa = (struct ospf6_router_lsa *) + ((caddr_t) lsa_header + sizeof (struct ospf6_lsa_header)); + + OSPF6_OPT_SET (router_lsa->options, OSPF6_OPT_V6); + OSPF6_OPT_SET (router_lsa->options, OSPF6_OPT_E); + OSPF6_OPT_CLEAR (router_lsa->options, OSPF6_OPT_MC); + OSPF6_OPT_CLEAR (router_lsa->options, OSPF6_OPT_N); + OSPF6_OPT_SET (router_lsa->options, OSPF6_OPT_R); + OSPF6_OPT_CLEAR (router_lsa->options, OSPF6_OPT_DC); + + if (ospf6_is_router_abr (ospf6)) + SET_FLAG (router_lsa->bits, OSPF6_ROUTER_BIT_B); + else + UNSET_FLAG (router_lsa->bits, OSPF6_ROUTER_BIT_B); + if (ospf6_asbr_is_asbr (ospf6)) + SET_FLAG (router_lsa->bits, OSPF6_ROUTER_BIT_E); + else + UNSET_FLAG (router_lsa->bits, OSPF6_ROUTER_BIT_E); + UNSET_FLAG (router_lsa->bits, OSPF6_ROUTER_BIT_V); + UNSET_FLAG (router_lsa->bits, OSPF6_ROUTER_BIT_W); + + /* describe links for each interfaces */ + lsdesc = (struct ospf6_router_lsdesc *) + ((caddr_t) router_lsa + sizeof (struct ospf6_router_lsa)); + + for (ALL_LIST_ELEMENTS (oa->if_list, node, nnode, oi)) + { + /* Interfaces in state Down or Loopback are not described */ + if (oi->state == OSPF6_INTERFACE_DOWN || + oi->state == OSPF6_INTERFACE_LOOPBACK) + continue; + + /* Nor are interfaces without any full adjacencies described */ + count = 0; + for (ALL_LIST_ELEMENTS_RO (oi->neighbor_list, j, on)) + if (on->state == OSPF6_NEIGHBOR_FULL) + count++; + + if (count == 0) + continue; + + /* Multiple Router-LSA instance according to size limit setting */ + if ( (oa->router_lsa_size_limit != 0) + && ((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)) + { + if (IS_OSPF6_DEBUG_ORIGINATE (ROUTER)) + zlog_debug ("Size limit setting for Router-LSA too short"); + return 0; + } + + link_state_id ++; + } + + /* Point-to-Point interfaces */ + if (oi->type == OSPF_IFTYPE_POINTOPOINT) + { + for (ALL_LIST_ELEMENTS_RO (oi->neighbor_list, j, on)) + { + if (on->state != OSPF6_NEIGHBOR_FULL) + continue; + + lsdesc->type = OSPF6_ROUTER_LSDESC_POINTTOPOINT; + lsdesc->metric = htons (oi->cost); + lsdesc->interface_id = htonl (oi->interface->ifindex); + lsdesc->neighbor_interface_id = htonl (on->ifindex); + lsdesc->neighbor_router_id = on->router_id; + + lsdesc++; + } + } + + /* Broadcast and NBMA interfaces */ + else if (oi->type == OSPF_IFTYPE_BROADCAST) + { + /* If this router is not DR, + and If this router not fully adjacent with DR, + this interface is not transit yet: ignore. */ + if (oi->state != OSPF6_INTERFACE_DR) + { + drouter = ospf6_neighbor_lookup (oi->drouter, oi); + if (drouter == NULL || drouter->state != OSPF6_NEIGHBOR_FULL) + continue; + } + + lsdesc->type = OSPF6_ROUTER_LSDESC_TRANSIT_NETWORK; + lsdesc->metric = htons (oi->cost); + lsdesc->interface_id = htonl (oi->interface->ifindex); + if (oi->state != OSPF6_INTERFACE_DR) + { + lsdesc->neighbor_interface_id = htonl (drouter->ifindex); + lsdesc->neighbor_router_id = drouter->router_id; + } + else + { + lsdesc->neighbor_interface_id = htonl (oi->interface->ifindex); + lsdesc->neighbor_router_id = oi->area->ospf6->router_id; + } + + lsdesc++; + } + else + { + assert (0); /* Unknown interface type */ + } + + /* Virtual links */ + /* xxx */ + /* Point-to-Multipoint interfaces */ + /* xxx */ + } + + /* 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); + router = oa->ospf6->router_id; + for (lsa = ospf6_lsdb_type_router_head (type, router, oa->lsdb); lsa; + lsa = ospf6_lsdb_type_router_next (type, router, lsa)) + { + if (ntohl (lsa->header->id) < link_state_id) + continue; + ospf6_lsa_purge (lsa); + } + + return 0; +} + +/*******************************/ +/* 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) +{ + char *start, *end, *current; + struct ospf6_network_lsa *network_lsa; + struct ospf6_network_lsdesc *lsdesc; + char buf[128], options[32]; + + network_lsa = (struct ospf6_network_lsa *) + ((caddr_t) lsa->header + sizeof (struct ospf6_lsa_header)); + + ospf6_options_printbuf (network_lsa->options, options, sizeof (options)); + vty_out (vty, " Options: %s%s", options, VNL); + + start = (char *) network_lsa + sizeof (struct ospf6_network_lsa); + end = (char *) lsa->header + ntohs (lsa->header->length); + for (current = start; current + sizeof (struct ospf6_network_lsdesc) <= end; + current += sizeof (struct ospf6_network_lsdesc)) + { + lsdesc = (struct ospf6_network_lsdesc *) current; + inet_ntop (AF_INET, &lsdesc->router_id, buf, sizeof (buf)); + vty_out (vty, " Attached Router: %s%s", buf, VNL); + } + return 0; +} + +int +ospf6_network_lsa_originate (struct thread *thread) +{ + struct ospf6_interface *oi; + + char buffer [OSPF6_MAX_LSASIZE]; + struct ospf6_lsa_header *lsa_header; + + int count; + struct ospf6_lsa *old, *lsa; + struct ospf6_network_lsa *network_lsa; + struct ospf6_network_lsdesc *lsdesc; + struct ospf6_neighbor *on; + struct ospf6_link_lsa *link_lsa; + struct listnode *i; + u_int16_t type; + + oi = (struct ospf6_interface *) THREAD_ARG (thread); + oi->thread_network_lsa = NULL; + + /* The interface must be enabled until here. A Network-LSA of a + disabled interface (but was once enabled) should be flushed + by ospf6_lsa_refresh (), and does not come here. */ + assert (oi->area); + + old = ospf6_lsdb_lookup (htons (OSPF6_LSTYPE_NETWORK), + htonl (oi->interface->ifindex), + oi->area->ospf6->router_id, oi->area->lsdb); + + /* Do not originate Network-LSA if not DR */ + if (oi->state != OSPF6_INTERFACE_DR) + { + if (old) + ospf6_lsa_purge (old); + return 0; + } + + if (IS_OSPF6_DEBUG_ORIGINATE (NETWORK)) + zlog_debug ("Originate Network-LSA for Interface %s", oi->interface->name); + + /* If none of neighbor is adjacent to us */ + count = 0; + + for (ALL_LIST_ELEMENTS_RO (oi->neighbor_list, i, on)) + if (on->state == OSPF6_NEIGHBOR_FULL) + count++; + + if (count == 0) + { + if (IS_OSPF6_DEBUG_ORIGINATE (NETWORK)) + zlog_debug ("Interface stub, ignore"); + if (old) + ospf6_lsa_purge (old); + return 0; + } + + /* prepare buffer */ + memset (buffer, 0, sizeof (buffer)); + lsa_header = (struct ospf6_lsa_header *) buffer; + network_lsa = (struct ospf6_network_lsa *) + ((caddr_t) lsa_header + sizeof (struct ospf6_lsa_header)); + + /* Collect the interface's Link-LSAs to describe + network's optional capabilities */ + type = htons (OSPF6_LSTYPE_LINK); + for (lsa = ospf6_lsdb_type_head (type, oi->lsdb); lsa; + lsa = ospf6_lsdb_type_next (type, lsa)) + { + link_lsa = (struct ospf6_link_lsa *) + ((caddr_t) lsa->header + sizeof (struct ospf6_lsa_header)); + network_lsa->options[0] |= link_lsa->options[0]; + network_lsa->options[1] |= link_lsa->options[1]; + network_lsa->options[2] |= link_lsa->options[2]; + } + + lsdesc = (struct ospf6_network_lsdesc *) + ((caddr_t) network_lsa + sizeof (struct ospf6_network_lsa)); + + /* set Link Description to the router itself */ + lsdesc->router_id = oi->area->ospf6->router_id; + lsdesc++; + + /* Walk through the neighbors */ + for (ALL_LIST_ELEMENTS_RO (oi->neighbor_list, i, on)) + { + if (on->state != OSPF6_NEIGHBOR_FULL) + continue; + + /* set this neighbor's Router-ID to LSA */ + lsdesc->router_id = on->router_id; + lsdesc++; + } + + /* Fill LSA Header */ + lsa_header->age = 0; + lsa_header->type = htons (OSPF6_LSTYPE_NETWORK); + lsa_header->id = htonl (oi->interface->ifindex); + lsa_header->adv_router = oi->area->ospf6->router_id; + lsa_header->seqnum = + ospf6_new_ls_seqnum (lsa_header->type, lsa_header->id, + lsa_header->adv_router, oi->area->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, oi->area); + + return 0; +} + + +/****************************/ +/* 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) +{ + char *start, *end, *current; + struct ospf6_link_lsa *link_lsa; + int prefixnum; + char buf[128], options[32]; + struct ospf6_prefix *prefix; + const char *p, *mc, *la, *nu; + struct in6_addr in6; + + link_lsa = (struct ospf6_link_lsa *) + ((caddr_t) lsa->header + sizeof (struct ospf6_lsa_header)); + + ospf6_options_printbuf (link_lsa->options, options, sizeof (options)); + inet_ntop (AF_INET6, &link_lsa->linklocal_addr, buf, sizeof (buf)); + prefixnum = ntohl (link_lsa->prefix_num); + + vty_out (vty, " Priority: %d Options: %s%s", + link_lsa->priority, options, VNL); + vty_out (vty, " LinkLocal Address: %s%s", buf, VNL); + vty_out (vty, " Number of Prefix: %d%s", prefixnum, VNL); + + start = (char *) link_lsa + sizeof (struct ospf6_link_lsa); + end = (char *) lsa->header + ntohs (lsa->header->length); + for (current = start; current < end; current += OSPF6_PREFIX_SIZE (prefix)) + { + prefix = (struct ospf6_prefix *) current; + if (prefix->prefix_length == 0 || + current + OSPF6_PREFIX_SIZE (prefix) > end) + break; + + p = (CHECK_FLAG (prefix->prefix_options, OSPF6_PREFIX_OPTION_P) ? + "P" : "--"); + mc = (CHECK_FLAG (prefix->prefix_options, OSPF6_PREFIX_OPTION_MC) ? + "MC" : "--"); + la = (CHECK_FLAG (prefix->prefix_options, OSPF6_PREFIX_OPTION_LA) ? + "LA" : "--"); + nu = (CHECK_FLAG (prefix->prefix_options, OSPF6_PREFIX_OPTION_NU) ? + "NU" : "--"); + vty_out (vty, " Prefix Options: %s|%s|%s|%s%s", + p, mc, la, nu, VNL); + + memset (&in6, 0, sizeof (in6)); + memcpy (&in6, OSPF6_PREFIX_BODY (prefix), + OSPF6_PREFIX_SPACE (prefix->prefix_length)); + inet_ntop (AF_INET6, &in6, buf, sizeof (buf)); + vty_out (vty, " Prefix: %s/%d%s", + buf, prefix->prefix_length, VNL); + } + + return 0; +} + +int +ospf6_link_lsa_originate (struct thread *thread) +{ + struct ospf6_interface *oi; + + char buffer[OSPF6_MAX_LSASIZE]; + struct ospf6_lsa_header *lsa_header; + struct ospf6_lsa *old, *lsa; + + struct ospf6_link_lsa *link_lsa; + struct ospf6_route *route; + struct ospf6_prefix *op; + + oi = (struct ospf6_interface *) THREAD_ARG (thread); + oi->thread_link_lsa = NULL; + + assert (oi->area); + + /* find previous LSA */ + old = ospf6_lsdb_lookup (htons (OSPF6_LSTYPE_LINK), + htonl (oi->interface->ifindex), + oi->area->ospf6->router_id, oi->lsdb); + + if (CHECK_FLAG (oi->flag, OSPF6_INTERFACE_DISABLE)) + { + if (old) + ospf6_lsa_purge (old); + return 0; + } + + if (IS_OSPF6_DEBUG_ORIGINATE (LINK)) + zlog_debug ("Originate Link-LSA for Interface %s", oi->interface->name); + + /* can't make Link-LSA if linklocal address not set */ + if (oi->linklocal_addr == NULL) + { + if (IS_OSPF6_DEBUG_ORIGINATE (LINK)) + zlog_debug ("No Linklocal address on %s, defer originating", + oi->interface->name); + if (old) + ospf6_lsa_purge (old); + return 0; + } + + /* prepare buffer */ + memset (buffer, 0, sizeof (buffer)); + lsa_header = (struct ospf6_lsa_header *) buffer; + link_lsa = (struct ospf6_link_lsa *) + ((caddr_t) lsa_header + sizeof (struct ospf6_lsa_header)); + + /* Fill Link-LSA */ + link_lsa->priority = oi->priority; + memcpy (link_lsa->options, oi->area->options, 3); + memcpy (&link_lsa->linklocal_addr, oi->linklocal_addr, + sizeof (struct in6_addr)); + link_lsa->prefix_num = htonl (oi->route_connected->count); + + op = (struct ospf6_prefix *) + ((caddr_t) link_lsa + sizeof (struct ospf6_link_lsa)); + + /* connected prefix to advertise */ + for (route = ospf6_route_head (oi->route_connected); route; + route = ospf6_route_next (route)) + { + op->prefix_length = route->prefix.prefixlen; + op->prefix_options = route->path.prefix_options; + op->prefix_metric = htons (0); + memcpy (OSPF6_PREFIX_BODY (op), &route->prefix.u.prefix6, + OSPF6_PREFIX_SPACE (op->prefix_length)); + op = OSPF6_PREFIX_NEXT (op); + } + + /* Fill LSA Header */ + lsa_header->age = 0; + lsa_header->type = htons (OSPF6_LSTYPE_LINK); + lsa_header->id = htonl (oi->interface->ifindex); + lsa_header->adv_router = oi->area->ospf6->router_id; + lsa_header->seqnum = + ospf6_new_ls_seqnum (lsa_header->type, lsa_header->id, + lsa_header->adv_router, oi->lsdb); + lsa_header->length = htons ((caddr_t) op - (caddr_t) buffer); + + /* LSA checksum */ + ospf6_lsa_checksum (lsa_header); + + /* create LSA */ + lsa = ospf6_lsa_create (lsa_header); + + /* Originate */ + ospf6_lsa_originate_interface (lsa, oi); + + return 0; +} + + +/*****************************************/ +/* 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) +{ + char *start, *end, *current; + struct ospf6_intra_prefix_lsa *intra_prefix_lsa; + int prefixnum; + char buf[128]; + struct ospf6_prefix *prefix; + char id[16], adv_router[16]; + const char *p, *mc, *la, *nu; + struct in6_addr in6; + + intra_prefix_lsa = (struct ospf6_intra_prefix_lsa *) + ((caddr_t) lsa->header + sizeof (struct ospf6_lsa_header)); + + prefixnum = ntohs (intra_prefix_lsa->prefix_num); + + vty_out (vty, " Number of Prefix: %d%s", prefixnum, VNL); + + inet_ntop (AF_INET, &intra_prefix_lsa->ref_id, id, sizeof (id)); + inet_ntop (AF_INET, &intra_prefix_lsa->ref_adv_router, + adv_router, sizeof (adv_router)); + vty_out (vty, " Reference: %s Id: %s Adv: %s%s", + ospf6_lstype_name (intra_prefix_lsa->ref_type), id, adv_router, + VNL); + + start = (char *) intra_prefix_lsa + sizeof (struct ospf6_intra_prefix_lsa); + end = (char *) lsa->header + ntohs (lsa->header->length); + for (current = start; current < end; current += OSPF6_PREFIX_SIZE (prefix)) + { + prefix = (struct ospf6_prefix *) current; + if (prefix->prefix_length == 0 || + current + OSPF6_PREFIX_SIZE (prefix) > end) + break; + + p = (CHECK_FLAG (prefix->prefix_options, OSPF6_PREFIX_OPTION_P) ? + "P" : "--"); + mc = (CHECK_FLAG (prefix->prefix_options, OSPF6_PREFIX_OPTION_MC) ? + "MC" : "--"); + la = (CHECK_FLAG (prefix->prefix_options, OSPF6_PREFIX_OPTION_LA) ? + "LA" : "--"); + nu = (CHECK_FLAG (prefix->prefix_options, OSPF6_PREFIX_OPTION_NU) ? + "NU" : "--"); + vty_out (vty, " Prefix Options: %s|%s|%s|%s%s", + p, mc, la, nu, VNL); + + memset (&in6, 0, sizeof (in6)); + memcpy (&in6, OSPF6_PREFIX_BODY (prefix), + OSPF6_PREFIX_SPACE (prefix->prefix_length)); + inet_ntop (AF_INET6, &in6, buf, sizeof (buf)); + vty_out (vty, " Prefix: %s/%d%s", + buf, prefix->prefix_length, VNL); + } + + return 0; +} + +int +ospf6_intra_prefix_lsa_originate_stub (struct thread *thread) +{ + struct ospf6_area *oa; + + char buffer[OSPF6_MAX_LSASIZE]; + struct ospf6_lsa_header *lsa_header; + struct ospf6_lsa *old, *lsa; + + struct ospf6_intra_prefix_lsa *intra_prefix_lsa; + struct ospf6_interface *oi; + struct ospf6_neighbor *on; + struct ospf6_route *route; + struct ospf6_prefix *op; + struct listnode *i, *j; + int full_count = 0; + unsigned short prefix_num = 0; + char buf[BUFSIZ]; + struct ospf6_route_table *route_advertise; + + oa = (struct ospf6_area *) THREAD_ARG (thread); + oa->thread_intra_prefix_lsa = NULL; + + /* find previous LSA */ + old = ospf6_lsdb_lookup (htons (OSPF6_LSTYPE_INTRA_PREFIX), + htonl (0), oa->ospf6->router_id, oa->lsdb); + + if (! IS_AREA_ENABLED (oa)) + { + if (old) + ospf6_lsa_purge (old); + return 0; + } + + if (IS_OSPF6_DEBUG_ORIGINATE (INTRA_PREFIX)) + zlog_debug ("Originate Intra-Area-Prefix-LSA for area %s's stub prefix", + oa->name); + + /* prepare buffer */ + memset (buffer, 0, sizeof (buffer)); + lsa_header = (struct ospf6_lsa_header *) buffer; + intra_prefix_lsa = (struct ospf6_intra_prefix_lsa *) + ((caddr_t) lsa_header + sizeof (struct ospf6_lsa_header)); + + /* Fill Intra-Area-Prefix-LSA */ + intra_prefix_lsa->ref_type = htons (OSPF6_LSTYPE_ROUTER); + intra_prefix_lsa->ref_id = htonl (0); + intra_prefix_lsa->ref_adv_router = oa->ospf6->router_id; + + route_advertise = ospf6_route_table_create (0, 0); + + for (ALL_LIST_ELEMENTS_RO (oa->if_list, i, oi)) + { + if (oi->state == OSPF6_INTERFACE_DOWN) + { + if (IS_OSPF6_DEBUG_ORIGINATE (INTRA_PREFIX)) + zlog_debug (" Interface %s is down, ignore", oi->interface->name); + continue; + } + + full_count = 0; + + for (ALL_LIST_ELEMENTS_RO (oi->neighbor_list, j, on)) + if (on->state == OSPF6_NEIGHBOR_FULL) + full_count++; + + if (oi->state != OSPF6_INTERFACE_LOOPBACK && + oi->state != OSPF6_INTERFACE_POINTTOPOINT && + full_count != 0) + { + if (IS_OSPF6_DEBUG_ORIGINATE (INTRA_PREFIX)) + zlog_debug (" Interface %s is not stub, ignore", + oi->interface->name); + continue; + } + + if (IS_OSPF6_DEBUG_ORIGINATE (INTRA_PREFIX)) + zlog_debug (" Interface %s:", oi->interface->name); + + /* connected prefix to advertise */ + for (route = ospf6_route_head (oi->route_connected); route; + route = ospf6_route_best_next (route)) + { + if (IS_OSPF6_DEBUG_ORIGINATE (INTRA_PREFIX)) + { + prefix2str (&route->prefix, buf, sizeof (buf)); + zlog_debug (" include %s", buf); + } + ospf6_route_add (ospf6_route_copy (route), route_advertise); + } + } + + if (route_advertise->count == 0) + { + if (old) + ospf6_lsa_purge (old); + ospf6_route_table_delete (route_advertise); + return 0; + } + + /* put prefixes to advertise */ + prefix_num = 0; + op = (struct ospf6_prefix *) + ((caddr_t) intra_prefix_lsa + sizeof (struct ospf6_intra_prefix_lsa)); + for (route = ospf6_route_head (route_advertise); route; + route = ospf6_route_best_next (route)) + { + op->prefix_length = route->prefix.prefixlen; + op->prefix_options = route->path.prefix_options; + op->prefix_metric = htons (route->path.cost); + memcpy (OSPF6_PREFIX_BODY (op), &route->prefix.u.prefix6, + OSPF6_PREFIX_SPACE (op->prefix_length)); + op = OSPF6_PREFIX_NEXT (op); + prefix_num++; + } + + ospf6_route_table_delete (route_advertise); + + if (prefix_num == 0) + { + if (IS_OSPF6_DEBUG_ORIGINATE (INTRA_PREFIX)) + zlog_debug ("Quit to Advertise Intra-Prefix: no route to advertise"); + return 0; + } + + intra_prefix_lsa->prefix_num = htons (prefix_num); + + /* Fill LSA Header */ + lsa_header->age = 0; + lsa_header->type = htons (OSPF6_LSTYPE_INTRA_PREFIX); + lsa_header->id = htonl (0); + 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) op - (caddr_t) lsa_header); + + /* LSA checksum */ + ospf6_lsa_checksum (lsa_header); + + /* create LSA */ + lsa = ospf6_lsa_create (lsa_header); + + /* Originate */ + ospf6_lsa_originate_area (lsa, oa); + + return 0; +} + + +int +ospf6_intra_prefix_lsa_originate_transit (struct thread *thread) +{ + struct ospf6_interface *oi; + + char buffer[OSPF6_MAX_LSASIZE]; + struct ospf6_lsa_header *lsa_header; + struct ospf6_lsa *old, *lsa; + + struct ospf6_intra_prefix_lsa *intra_prefix_lsa; + struct ospf6_neighbor *on; + struct ospf6_route *route; + struct ospf6_prefix *op; + struct listnode *i; + int full_count = 0; + unsigned short prefix_num = 0; + struct ospf6_route_table *route_advertise; + struct ospf6_link_lsa *link_lsa; + char *start, *end, *current; + u_int16_t type; + char buf[BUFSIZ]; + + oi = (struct ospf6_interface *) THREAD_ARG (thread); + oi->thread_intra_prefix_lsa = NULL; + + assert (oi->area); + + /* find previous LSA */ + old = ospf6_lsdb_lookup (htons (OSPF6_LSTYPE_INTRA_PREFIX), + htonl (oi->interface->ifindex), + oi->area->ospf6->router_id, oi->area->lsdb); + + if (CHECK_FLAG (oi->flag, OSPF6_INTERFACE_DISABLE)) + { + if (old) + ospf6_lsa_purge (old); + return 0; + } + + if (IS_OSPF6_DEBUG_ORIGINATE (INTRA_PREFIX)) + zlog_debug ("Originate Intra-Area-Prefix-LSA for interface %s's prefix", + oi->interface->name); + + /* prepare buffer */ + memset (buffer, 0, sizeof (buffer)); + lsa_header = (struct ospf6_lsa_header *) buffer; + intra_prefix_lsa = (struct ospf6_intra_prefix_lsa *) + ((caddr_t) lsa_header + sizeof (struct ospf6_lsa_header)); + + /* Fill Intra-Area-Prefix-LSA */ + intra_prefix_lsa->ref_type = htons (OSPF6_LSTYPE_NETWORK); + intra_prefix_lsa->ref_id = htonl (oi->interface->ifindex); + intra_prefix_lsa->ref_adv_router = oi->area->ospf6->router_id; + + if (oi->state != OSPF6_INTERFACE_DR) + { + if (IS_OSPF6_DEBUG_ORIGINATE (INTRA_PREFIX)) + zlog_debug (" Interface is not DR"); + if (old) + ospf6_lsa_purge (old); + return 0; + } + + full_count = 0; + for (ALL_LIST_ELEMENTS_RO (oi->neighbor_list, i, on)) + if (on->state == OSPF6_NEIGHBOR_FULL) + full_count++; + + if (full_count == 0) + { + if (IS_OSPF6_DEBUG_ORIGINATE (INTRA_PREFIX)) + zlog_debug (" Interface is stub"); + if (old) + ospf6_lsa_purge (old); + return 0; + } + + /* connected prefix to advertise */ + route_advertise = ospf6_route_table_create (0, 0); + + type = ntohs (OSPF6_LSTYPE_LINK); + for (lsa = ospf6_lsdb_type_head (type, oi->lsdb); lsa; + lsa = ospf6_lsdb_type_next (type, lsa)) + { + if (OSPF6_LSA_IS_MAXAGE (lsa)) + continue; + + if (IS_OSPF6_DEBUG_ORIGINATE (INTRA_PREFIX)) + zlog_debug (" include prefix from %s", lsa->name); + + if (lsa->header->adv_router != oi->area->ospf6->router_id) + { + on = ospf6_neighbor_lookup (lsa->header->adv_router, oi); + if (on == NULL || on->state != OSPF6_NEIGHBOR_FULL) + { + if (IS_OSPF6_DEBUG_ORIGINATE (INTRA_PREFIX)) + zlog_debug (" Neighbor not found or not Full, ignore"); + continue; + } + } + + link_lsa = (struct ospf6_link_lsa *) + ((caddr_t) lsa->header + sizeof (struct ospf6_lsa_header)); + + prefix_num = (unsigned short) ntohl (link_lsa->prefix_num); + start = (char *) link_lsa + sizeof (struct ospf6_link_lsa); + end = (char *) lsa->header + ntohs (lsa->header->length); + for (current = start; current < end && prefix_num; + current += OSPF6_PREFIX_SIZE (op)) + { + op = (struct ospf6_prefix *) current; + if (op->prefix_length == 0 || + current + OSPF6_PREFIX_SIZE (op) > end) + break; + + route = ospf6_route_create (); + + route->type = OSPF6_DEST_TYPE_NETWORK; + route->prefix.family = AF_INET6; + route->prefix.prefixlen = op->prefix_length; + memset (&route->prefix.u.prefix6, 0, sizeof (struct in6_addr)); + memcpy (&route->prefix.u.prefix6, OSPF6_PREFIX_BODY (op), + OSPF6_PREFIX_SPACE (op->prefix_length)); + + route->path.origin.type = lsa->header->type; + route->path.origin.id = lsa->header->id; + route->path.origin.adv_router = lsa->header->adv_router; + route->path.options[0] = link_lsa->options[0]; + route->path.options[1] = link_lsa->options[1]; + route->path.options[2] = link_lsa->options[2]; + route->path.prefix_options = op->prefix_options; + route->path.area_id = oi->area->area_id; + route->path.type = OSPF6_PATH_TYPE_INTRA; + + if (IS_OSPF6_DEBUG_ORIGINATE (INTRA_PREFIX)) + { + prefix2str (&route->prefix, buf, sizeof (buf)); + zlog_debug (" include %s", buf); + } + + ospf6_route_add (route, route_advertise); + prefix_num--; + } + if (current != end && IS_OSPF6_DEBUG_ORIGINATE (INTRA_PREFIX)) + zlog_debug ("Trailing garbage in %s", lsa->name); + } + + op = (struct ospf6_prefix *) + ((caddr_t) intra_prefix_lsa + sizeof (struct ospf6_intra_prefix_lsa)); + + prefix_num = 0; + for (route = ospf6_route_head (route_advertise); route; + route = ospf6_route_best_next (route)) + { + op->prefix_length = route->prefix.prefixlen; + op->prefix_options = route->path.prefix_options; + op->prefix_metric = htons (0); + memcpy (OSPF6_PREFIX_BODY (op), &route->prefix.u.prefix6, + OSPF6_PREFIX_SPACE (op->prefix_length)); + op = OSPF6_PREFIX_NEXT (op); + prefix_num++; + } + + ospf6_route_table_delete (route_advertise); + + if (prefix_num == 0) + { + if (IS_OSPF6_DEBUG_ORIGINATE (INTRA_PREFIX)) + zlog_debug ("Quit to Advertise Intra-Prefix: no route to advertise"); + return 0; + } + + intra_prefix_lsa->prefix_num = htons (prefix_num); + + /* Fill LSA Header */ + lsa_header->age = 0; + lsa_header->type = htons (OSPF6_LSTYPE_INTRA_PREFIX); + lsa_header->id = htonl (oi->interface->ifindex); + lsa_header->adv_router = oi->area->ospf6->router_id; + lsa_header->seqnum = + ospf6_new_ls_seqnum (lsa_header->type, lsa_header->id, + lsa_header->adv_router, oi->area->lsdb); + lsa_header->length = htons ((caddr_t) op - (caddr_t) lsa_header); + + /* LSA checksum */ + ospf6_lsa_checksum (lsa_header); + + /* create LSA */ + lsa = ospf6_lsa_create (lsa_header); + + /* Originate */ + ospf6_lsa_originate_area (lsa, oi->area); + + return 0; +} + +void +ospf6_intra_prefix_lsa_add (struct ospf6_lsa *lsa) +{ + struct ospf6_area *oa; + struct ospf6_intra_prefix_lsa *intra_prefix_lsa; + struct prefix ls_prefix; + struct ospf6_route *route, *ls_entry; + int i, prefix_num; + 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; + + if (IS_OSPF6_DEBUG_EXAMIN (INTRA_PREFIX)) + zlog_debug ("%s found", lsa->name); + + oa = OSPF6_AREA (lsa->lsdb->data); + + intra_prefix_lsa = (struct ospf6_intra_prefix_lsa *) + OSPF6_LSA_HEADER_END (lsa->header); + if (intra_prefix_lsa->ref_type == htons (OSPF6_LSTYPE_ROUTER)) + ospf6_linkstate_prefix (intra_prefix_lsa->ref_adv_router, + htonl (0), &ls_prefix); + else if (intra_prefix_lsa->ref_type == htons (OSPF6_LSTYPE_NETWORK)) + ospf6_linkstate_prefix (intra_prefix_lsa->ref_adv_router, + intra_prefix_lsa->ref_id, &ls_prefix); + else + { + if (IS_OSPF6_DEBUG_EXAMIN (INTRA_PREFIX)) + zlog_debug ("Unknown reference LS-type: %#hx", + ntohs (intra_prefix_lsa->ref_type)); + return; + } + + ls_entry = ospf6_route_lookup (&ls_prefix, oa->spf_table); + if (ls_entry == NULL) + { + if (IS_OSPF6_DEBUG_EXAMIN (INTRA_PREFIX)) + { + ospf6_linkstate_prefix2str (&ls_prefix, buf, sizeof (buf)); + zlog_debug ("LS entry does not exist: %s", buf); + } + 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); + end = OSPF6_LSA_END (lsa->header); + for (current = start; current < end; current += OSPF6_PREFIX_SIZE (op)) + { + op = (struct ospf6_prefix *) current; + if (prefix_num == 0) + break; + 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)); + route->prefix.family = AF_INET6; + route->prefix.prefixlen = op->prefix_length; + ospf6_prefix_in6_addr (&route->prefix.u.prefix6, op); + + route->type = OSPF6_DEST_TYPE_NETWORK; + route->path.origin.type = lsa->header->type; + route->path.origin.id = lsa->header->id; + route->path.origin.adv_router = lsa->header->adv_router; + route->path.prefix_options = op->prefix_options; + route->path.area_id = oa->area_id; + route->path.type = OSPF6_PATH_TYPE_INTRA; + route->path.metric_type = 1; + route->path.cost = ls_entry->path.cost + + ntohs (op->prefix_metric); + + 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)) + { + prefix2str (&route->prefix, buf, sizeof (buf)); + zlog_debug (" add %s", buf); + } + + ospf6_route_add (route, oa->route_table); + prefix_num--; + } + + if (current != end && IS_OSPF6_DEBUG_EXAMIN (INTRA_PREFIX)) + zlog_debug ("Trailing garbage ignored"); +} + +void +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, *nroute; + int prefix_num; + struct ospf6_prefix *op; + char *start, *current, *end; + char buf[64]; + + if (IS_OSPF6_DEBUG_EXAMIN (INTRA_PREFIX)) + zlog_debug ("%s disappearing", lsa->name); + + oa = OSPF6_AREA (lsa->lsdb->data); + + intra_prefix_lsa = (struct ospf6_intra_prefix_lsa *) + OSPF6_LSA_HEADER_END (lsa->header); + + prefix_num = ntohs (intra_prefix_lsa->prefix_num); + start = (caddr_t) intra_prefix_lsa + + sizeof (struct ospf6_intra_prefix_lsa); + end = OSPF6_LSA_END (lsa->header); + for (current = start; current < end; current += OSPF6_PREFIX_SIZE (op)) + { + op = (struct ospf6_prefix *) current; + if (prefix_num == 0) + break; + if (end < current + OSPF6_PREFIX_SIZE (op)) + break; + prefix_num--; + + memset (&prefix, 0, sizeof (struct prefix)); + prefix.family = AF_INET6; + prefix.prefixlen = op->prefix_length; + ospf6_prefix_in6_addr (&prefix.u.prefix6, op); + + route = ospf6_route_lookup (&prefix, oa->route_table); + if (route == NULL) + continue; + + for (ospf6_route_lock (route); + route && ospf6_route_is_prefix (&prefix, route); + route = nroute) + { + nroute = ospf6_route_next (route); + if (route->type != OSPF6_DEST_TYPE_NETWORK) + continue; + if (route->path.area_id != oa->area_id) + continue; + if (route->path.type != OSPF6_PATH_TYPE_INTRA) + continue; + if (route->path.origin.type != lsa->header->type || + route->path.origin.id != lsa->header->id || + route->path.origin.adv_router != lsa->header->adv_router) + continue; + + if (IS_OSPF6_DEBUG_EXAMIN (INTRA_PREFIX)) + { + prefix2str (&route->prefix, buf, sizeof (buf)); + zlog_debug ("remove %s", buf); + } + ospf6_route_remove (route, oa->route_table); + } + if (route) + ospf6_route_unlock (route); + } + + if (current != end && IS_OSPF6_DEBUG_EXAMIN (INTRA_PREFIX)) + zlog_debug ("Trailing garbage ignored"); +} + +void +ospf6_intra_route_calculation (struct ospf6_area *oa) +{ + struct ospf6_route *route, *nroute; + u_int16_t type; + struct ospf6_lsa *lsa; + void (*hook_add) (struct ospf6_route *) = NULL; + void (*hook_remove) (struct ospf6_route *) = NULL; + + if (IS_OSPF6_DEBUG_EXAMIN (INTRA_PREFIX)) + zlog_debug ("Re-examin intra-routes for area %s", oa->name); + + hook_add = oa->route_table->hook_add; + hook_remove = oa->route_table->hook_remove; + oa->route_table->hook_add = NULL; + oa->route_table->hook_remove = NULL; + + for (route = ospf6_route_head (oa->route_table); route; + route = ospf6_route_next (route)) + route->flag = OSPF6_ROUTE_REMOVE; + + type = htons (OSPF6_LSTYPE_INTRA_PREFIX); + for (lsa = ospf6_lsdb_type_head (type, oa->lsdb); lsa; + lsa = ospf6_lsdb_type_next (type, lsa)) + ospf6_intra_prefix_lsa_add (lsa); + + oa->route_table->hook_add = hook_add; + oa->route_table->hook_remove = hook_remove; + + for (route = ospf6_route_head (oa->route_table); route; + route = nroute) + { + nroute = ospf6_route_next (route); + if (CHECK_FLAG (route->flag, OSPF6_ROUTE_REMOVE) && + CHECK_FLAG (route->flag, OSPF6_ROUTE_ADD)) + { + UNSET_FLAG (route->flag, OSPF6_ROUTE_REMOVE); + UNSET_FLAG (route->flag, OSPF6_ROUTE_ADD); + } + + if (CHECK_FLAG (route->flag, OSPF6_ROUTE_REMOVE)) + ospf6_route_remove (route, oa->route_table); + else if (CHECK_FLAG (route->flag, OSPF6_ROUTE_ADD) || + CHECK_FLAG (route->flag, OSPF6_ROUTE_CHANGE)) + { + if (hook_add) + (*hook_add) (route); + } + + route->flag = 0; + } + + if (IS_OSPF6_DEBUG_EXAMIN (INTRA_PREFIX)) + zlog_debug ("Re-examin intra-routes for area %s: Done", oa->name); +} + +static void +ospf6_brouter_debug_print (struct ospf6_route *brouter) +{ + u_int32_t brouter_id; + char brouter_name[16]; + char area_name[16]; + char destination[64]; + char installed[16], changed[16]; + struct timeval now, res; + char id[16], adv_router[16]; + char capa[16], options[16]; + + brouter_id = ADV_ROUTER_IN_PREFIX (&brouter->prefix); + inet_ntop (AF_INET, &brouter_id, brouter_name, sizeof (brouter_name)); + inet_ntop (AF_INET, &brouter->path.area_id, area_name, sizeof (area_name)); + ospf6_linkstate_prefix2str (&brouter->prefix, destination, + sizeof (destination)); + + quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); + timersub (&now, &brouter->installed, &res); + timerstring (&res, installed, sizeof (installed)); + + quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); + timersub (&now, &brouter->changed, &res); + timerstring (&res, changed, sizeof (changed)); + + inet_ntop (AF_INET, &brouter->path.origin.id, id, sizeof (id)); + inet_ntop (AF_INET, &brouter->path.origin.adv_router, adv_router, + sizeof (adv_router)); + + ospf6_options_printbuf (brouter->path.options, options, sizeof (options)); + ospf6_capability_printbuf (brouter->path.router_bits, capa, sizeof (capa)); + + zlog_info ("Brouter: %s via area %s", brouter_name, area_name); + zlog_info (" memory: prev: %p this: %p next: %p parent rnode: %p", + (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, + (CHECK_FLAG (brouter->flag, OSPF6_ROUTE_BEST) ? "B" : "-"), + (CHECK_FLAG (brouter->flag, OSPF6_ROUTE_ADD) ? "A" : "-"), + (CHECK_FLAG (brouter->flag, OSPF6_ROUTE_REMOVE) ? "R" : "-"), + (CHECK_FLAG (brouter->flag, OSPF6_ROUTE_CHANGE) ? "C" : "-")); + zlog_info (" path type: %s ls-origin %s id: %s adv-router %s", + OSPF6_PATH_TYPE_NAME (brouter->path.type), + ospf6_lstype_name (brouter->path.origin.type), + id, adv_router); + zlog_info (" options: %s router-bits: %s metric-type: %d metric: %d/%d", + options, capa, brouter->path.metric_type, + brouter->path.cost, brouter->path.cost_e2); +} + +void +ospf6_intra_brouter_calculation (struct ospf6_area *oa) +{ + 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; + char brouter_name[16]; + + if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID (oa->area_id)) + zlog_info ("border-router calculation for area %s", oa->name); + + hook_add = oa->ospf6->brouter_table->hook_add; + hook_remove = oa->ospf6->brouter_table->hook_remove; + oa->ospf6->brouter_table->hook_add = NULL; + oa->ospf6->brouter_table->hook_remove = NULL; + + /* withdraw the previous router entries for the area */ + for (brouter = ospf6_route_head (oa->ospf6->brouter_table); brouter; + brouter = ospf6_route_next (brouter)) + { + brouter_id = ADV_ROUTER_IN_PREFIX (&brouter->prefix); + inet_ntop (AF_INET, &brouter_id, brouter_name, sizeof (brouter_name)); + if (brouter->path.area_id != oa->area_id) + continue; + brouter->flag = OSPF6_ROUTE_REMOVE; + + if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ID (brouter_id) || + IS_OSPF6_DEBUG_ROUTE (MEMORY)) + { + zlog_info ("%p: mark as removing: area %s brouter %s", + (void *)brouter, oa->name, brouter_name); + ospf6_brouter_debug_print (brouter); + } + } + + for (brouter = ospf6_route_head (oa->spf_table); brouter; + brouter = ospf6_route_next (brouter)) + { + brouter_id = ADV_ROUTER_IN_PREFIX (&brouter->prefix); + inet_ntop (AF_INET, &brouter_id, brouter_name, sizeof (brouter_name)); + + if (brouter->type != OSPF6_DEST_TYPE_LINKSTATE) + continue; + if (ospf6_linkstate_prefix_id (&brouter->prefix) != htonl (0)) + continue; + if (! CHECK_FLAG (brouter->path.router_bits, OSPF6_ROUTER_BIT_E) && + ! 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; + ospf6_route_add (copy, oa->ospf6->brouter_table); + + if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ID (brouter_id) || + IS_OSPF6_DEBUG_ROUTE (MEMORY)) + { + zlog_info ("%p: transfer: area %s brouter %s", + (void *)brouter, oa->name, brouter_name); + ospf6_brouter_debug_print (brouter); + } + } + + oa->ospf6->brouter_table->hook_add = hook_add; + oa->ospf6->brouter_table->hook_remove = hook_remove; + + for (brouter = ospf6_route_head (oa->ospf6->brouter_table); 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)); + + if (brouter->path.area_id != oa->area_id) + continue; + + if (CHECK_FLAG (brouter->flag, OSPF6_ROUTE_WAS_REMOVED)) + continue; + + if (CHECK_FLAG (brouter->flag, OSPF6_ROUTE_REMOVE) && + CHECK_FLAG (brouter->flag, OSPF6_ROUTE_ADD)) + { + UNSET_FLAG (brouter->flag, OSPF6_ROUTE_REMOVE); + UNSET_FLAG (brouter->flag, OSPF6_ROUTE_ADD); + } + + if (CHECK_FLAG (brouter->flag, OSPF6_ROUTE_REMOVE)) + { + if (IS_OSPF6_DEBUG_BROUTER || + IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ID (brouter_id) || + IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID (oa->area_id)) + zlog_info ("brouter %s disappears via area %s", + brouter_name, oa->name); + ospf6_route_remove (brouter, oa->ospf6->brouter_table); + } + else if (CHECK_FLAG (brouter->flag, OSPF6_ROUTE_ADD) || + CHECK_FLAG (brouter->flag, OSPF6_ROUTE_CHANGE)) + { + if (IS_OSPF6_DEBUG_BROUTER || + IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ID (brouter_id) || + IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID (oa->area_id)) + zlog_info ("brouter %s appears via area %s", + brouter_name, oa->name); + + /* newly added */ + if (hook_add) + (*hook_add) (brouter); + } + else + { + if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ID (brouter_id) || + IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID (oa->area_id)) + zlog_info ("brouter %s still exists via area %s", + brouter_name, oa->name); + } + + brouter->flag = 0; + } + + if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID (oa->area_id)) + zlog_info ("border-router calculation for area %s: done", oa->name); +} + +struct ospf6_lsa_handler router_handler = +{ + OSPF6_LSTYPE_ROUTER, + "Router", + "Rtr", + ospf6_router_lsa_show, + ospf6_router_lsa_get_nbr_id +}; + +struct ospf6_lsa_handler network_handler = +{ + OSPF6_LSTYPE_NETWORK, + "Network", + "Net", + ospf6_network_lsa_show, + ospf6_network_lsa_get_ar_id +}; + +struct ospf6_lsa_handler link_handler = +{ + OSPF6_LSTYPE_LINK, + "Link", + "Lnk", + ospf6_link_lsa_show, + ospf6_link_lsa_get_prefix_str +}; + +struct ospf6_lsa_handler intra_prefix_handler = +{ + OSPF6_LSTYPE_INTRA_PREFIX, + "Intra-Prefix", + "INP", + ospf6_intra_prefix_lsa_show, + ospf6_intra_prefix_lsa_get_prefix_str +}; + +void +ospf6_intra_init (void) +{ + ospf6_install_lsa_handler (&router_handler); + ospf6_install_lsa_handler (&network_handler); + ospf6_install_lsa_handler (&link_handler); + ospf6_install_lsa_handler (&intra_prefix_handler); +} + +DEFUN (debug_ospf6_brouter, + debug_ospf6_brouter_cmd, + "debug ospf6 border-routers", + DEBUG_STR + OSPF6_STR + "Debug border router\n" + ) +{ + OSPF6_DEBUG_BROUTER_ON (); + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf6_brouter, + no_debug_ospf6_brouter_cmd, + "no debug ospf6 border-routers", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug border router\n" + ) +{ + OSPF6_DEBUG_BROUTER_OFF (); + return CMD_SUCCESS; +} + +DEFUN (debug_ospf6_brouter_router, + debug_ospf6_brouter_router_cmd, + "debug ospf6 border-routers router-id A.B.C.D", + DEBUG_STR + OSPF6_STR + "Debug border router\n" + "Debug specific border router\n" + "Specify border-router's router-id\n" + ) +{ + u_int32_t router_id; + inet_pton (AF_INET, argv[0], &router_id); + OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ON (router_id); + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf6_brouter_router, + no_debug_ospf6_brouter_router_cmd, + "no debug ospf6 border-routers router-id", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug border router\n" + "Debug specific border router\n" + ) +{ + OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_OFF (); + return CMD_SUCCESS; +} + +DEFUN (debug_ospf6_brouter_area, + debug_ospf6_brouter_area_cmd, + "debug ospf6 border-routers area-id A.B.C.D", + DEBUG_STR + OSPF6_STR + "Debug border router\n" + "Debug border routers in specific Area\n" + "Specify Area-ID\n" + ) +{ + u_int32_t area_id; + inet_pton (AF_INET, argv[0], &area_id); + OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ON (area_id); + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf6_brouter_area, + no_debug_ospf6_brouter_area_cmd, + "no debug ospf6 border-routers area-id", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug border router\n" + "Debug border routers in specific Area\n" + ) +{ + OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_OFF (); + return CMD_SUCCESS; +} + +int +config_write_ospf6_debug_brouter (struct vty *vty) +{ + char buf[16]; + if (IS_OSPF6_DEBUG_BROUTER) + vty_out (vty, "debug ospf6 border-routers%s", VNL); + if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER) + { + inet_ntop (AF_INET, &conf_debug_ospf6_brouter_specific_router_id, + buf, sizeof (buf)); + vty_out (vty, "debug ospf6 border-routers router-id %s%s", buf, VNL); + } + if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA) + { + inet_ntop (AF_INET, &conf_debug_ospf6_brouter_specific_area_id, + buf, sizeof (buf)); + vty_out (vty, "debug ospf6 border-routers area-id %s%s", buf, VNL); + } + return 0; +} + +void +install_element_ospf6_debug_brouter (void) +{ + install_element (ENABLE_NODE, &debug_ospf6_brouter_cmd); + install_element (ENABLE_NODE, &debug_ospf6_brouter_router_cmd); + install_element (ENABLE_NODE, &debug_ospf6_brouter_area_cmd); + install_element (ENABLE_NODE, &no_debug_ospf6_brouter_cmd); + install_element (ENABLE_NODE, &no_debug_ospf6_brouter_router_cmd); + install_element (ENABLE_NODE, &no_debug_ospf6_brouter_area_cmd); + install_element (CONFIG_NODE, &debug_ospf6_brouter_cmd); + install_element (CONFIG_NODE, &debug_ospf6_brouter_router_cmd); + install_element (CONFIG_NODE, &debug_ospf6_brouter_area_cmd); + install_element (CONFIG_NODE, &no_debug_ospf6_brouter_cmd); + install_element (CONFIG_NODE, &no_debug_ospf6_brouter_router_cmd); + install_element (CONFIG_NODE, &no_debug_ospf6_brouter_area_cmd); +} + + diff --git a/ospf6d/ospf6_intra.h b/ospf6d/ospf6_intra.h new file mode 100644 index 0000000..c9660b6 --- /dev/null +++ b/ospf6d/ospf6_intra.h @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2003 Yasuhiro Ohara + * + * 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 OSPF6_INTRA_H +#define OSPF6_INTRA_H + +/* Debug option */ +extern unsigned char conf_debug_ospf6_brouter; +extern u_int32_t conf_debug_ospf6_brouter_specific_router_id; +extern u_int32_t conf_debug_ospf6_brouter_specific_area_id; +#define OSPF6_DEBUG_BROUTER_SUMMARY 0x01 +#define OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER 0x02 +#define OSPF6_DEBUG_BROUTER_SPECIFIC_AREA 0x04 +#define OSPF6_DEBUG_BROUTER_ON() \ + (conf_debug_ospf6_brouter |= OSPF6_DEBUG_BROUTER_SUMMARY) +#define OSPF6_DEBUG_BROUTER_OFF() \ + (conf_debug_ospf6_brouter &= ~OSPF6_DEBUG_BROUTER_SUMMARY) +#define IS_OSPF6_DEBUG_BROUTER \ + (conf_debug_ospf6_brouter & OSPF6_DEBUG_BROUTER_SUMMARY) + +#define OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ON(router_id) \ + do { \ + conf_debug_ospf6_brouter_specific_router_id = (router_id); \ + conf_debug_ospf6_brouter |= OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER; \ + } while (0) +#define OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_OFF() \ + do { \ + conf_debug_ospf6_brouter_specific_router_id = 0; \ + conf_debug_ospf6_brouter &= ~OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER; \ + } while (0) +#define IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER \ + (conf_debug_ospf6_brouter & OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER) +#define IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ID(router_id) \ + (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER && \ + conf_debug_ospf6_brouter_specific_router_id == (router_id)) + +#define OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ON(area_id) \ + do { \ + conf_debug_ospf6_brouter_specific_area_id = (area_id); \ + conf_debug_ospf6_brouter |= OSPF6_DEBUG_BROUTER_SPECIFIC_AREA; \ + } while (0) +#define OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_OFF() \ + do { \ + conf_debug_ospf6_brouter_specific_area_id = 0; \ + conf_debug_ospf6_brouter &= ~OSPF6_DEBUG_BROUTER_SPECIFIC_AREA; \ + } while (0) +#define IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA \ + (conf_debug_ospf6_brouter & OSPF6_DEBUG_BROUTER_SPECIFIC_AREA) +#define IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(area_id) \ + (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA && \ + conf_debug_ospf6_brouter_specific_area_id == (area_id)) + +/* Router-LSA */ +#define OSPF6_ROUTER_LSA_MIN_SIZE 4U +struct ospf6_router_lsa +{ + u_char bits; + u_char options[3]; + /* followed by ospf6_router_lsdesc(s) */ +}; + +/* Link State Description in Router-LSA */ +#define OSPF6_ROUTER_LSDESC_FIX_SIZE 16U +struct ospf6_router_lsdesc +{ + u_char type; + u_char reserved; + u_int16_t metric; /* output cost */ + u_int32_t interface_id; + u_int32_t neighbor_interface_id; + u_int32_t neighbor_router_id; +}; + +#define OSPF6_ROUTER_LSDESC_POINTTOPOINT 1 +#define OSPF6_ROUTER_LSDESC_TRANSIT_NETWORK 2 +#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) +#define ROUTER_LSDESC_GET_METRIC(x) \ + (ntohs (((struct ospf6_router_lsdesc *)(x))->metric)) +#define ROUTER_LSDESC_GET_IFID(x) \ + (ntohl (((struct ospf6_router_lsdesc *)(x))->interface_id)) +#define ROUTER_LSDESC_GET_NBR_IFID(x) \ + (ntohl (((struct ospf6_router_lsdesc *)(x))->neighbor_interface_id)) +#define ROUTER_LSDESC_GET_NBR_ROUTERID(x) \ + (((struct ospf6_router_lsdesc *)(x))->neighbor_router_id) + +/* Network-LSA */ +#define OSPF6_NETWORK_LSA_MIN_SIZE 4U +struct ospf6_network_lsa +{ + u_char reserved; + u_char options[3]; + /* followed by ospf6_netowrk_lsd(s) */ +}; + +/* Link State Description in Router-LSA */ +#define OSPF6_NETWORK_LSDESC_FIX_SIZE 4U +struct ospf6_network_lsdesc +{ + u_int32_t router_id; +}; +#define NETWORK_LSDESC_GET_NBR_ROUTERID(x) \ + (((struct ospf6_network_lsdesc *)(x))->router_id) + +/* Link-LSA */ +#define OSPF6_LINK_LSA_MIN_SIZE 24U /* w/o 1st IPv6 prefix */ +struct ospf6_link_lsa +{ + u_char priority; + u_char options[3]; + struct in6_addr linklocal_addr; + u_int32_t prefix_num; + /* followed by ospf6 prefix(es) */ +}; + +/* Intra-Area-Prefix-LSA */ +#define OSPF6_INTRA_PREFIX_LSA_MIN_SIZE 12U /* w/o 1st IPv6 prefix */ +struct ospf6_intra_prefix_lsa +{ + u_int16_t prefix_num; + u_int16_t ref_type; + u_int32_t ref_id; + u_int32_t ref_adv_router; + /* followed by ospf6 prefix(es) */ +}; + + +#define OSPF6_ROUTER_LSA_SCHEDULE(oa) \ + do { \ + 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 \ + && ! 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 \ + && ! 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 \ + && 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 \ + && ! CHECK_FLAG((oi)->flag, OSPF6_INTERFACE_DISABLE)) \ + (oi)->thread_intra_prefix_lsa = \ + thread_add_event (master, ospf6_intra_prefix_lsa_originate_transit, \ + oi, 0); \ + } while (0) + +#define OSPF6_NETWORK_LSA_EXECUTE(oi) \ + do { \ + THREAD_OFF ((oi)->thread_network_lsa); \ + thread_execute (master, ospf6_network_lsa_originate, oi, 0); \ + } while (0) +#define OSPF6_INTRA_PREFIX_LSA_EXECUTE_TRANSIT(oi) \ + do { \ + THREAD_OFF ((oi)->thread_intra_prefix_lsa); \ + thread_execute (master, ospf6_intra_prefix_lsa_originate_transit, oi, 0); \ + } while (0) + + +/* Function Prototypes */ +extern char *ospf6_router_lsdesc_lookup (u_char type, u_int32_t interface_id, + u_int32_t neighbor_interface_id, + u_int32_t neighbor_router_id, + struct ospf6_lsa *lsa); +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 *); +extern int ospf6_intra_prefix_lsa_originate_transit (struct thread *); +extern int ospf6_intra_prefix_lsa_originate_stub (struct thread *); +extern void ospf6_intra_prefix_lsa_add (struct ospf6_lsa *lsa); +extern void ospf6_intra_prefix_lsa_remove (struct ospf6_lsa *lsa); + +extern void ospf6_intra_route_calculation (struct ospf6_area *oa); +extern void ospf6_intra_brouter_calculation (struct ospf6_area *oa); + +extern void ospf6_intra_init (void); + +extern int config_write_ospf6_debug_brouter (struct vty *vty); +extern void install_element_ospf6_debug_brouter (void); + +#endif /* OSPF6_LSA_H */ + diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c new file mode 100644 index 0000000..3f008d3 --- /dev/null +++ b/ospf6d/ospf6_lsa.c @@ -0,0 +1,967 @@ +/* + * Copyright (C) 2003 Yasuhiro Ohara + * + * 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 other stuffs */ +#include "log.h" +#include "linklist.h" +#include "vector.h" +#include "vty.h" +#include "command.h" +#include "memory.h" +#include "thread.h" +#include "checksum.h" + +#include "ospf6_proto.h" +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" +#include "ospf6_message.h" + +#include "ospf6_top.h" +#include "ospf6_area.h" +#include "ospf6_interface.h" +#include "ospf6_neighbor.h" + +#include "ospf6_flood.h" +#include "ospf6d.h" + +vector ospf6_lsa_handler_vector; + +static int +ospf6_unknown_lsa_show (struct vty *vty, struct ospf6_lsa *lsa) +{ + u_char *start, *end, *current; + char byte[4]; + + start = (u_char *) lsa->header + sizeof (struct ospf6_lsa_header); + end = (u_char *) lsa->header + ntohs (lsa->header->length); + + vty_out (vty, " Unknown contents:%s", VNL); + for (current = start; current < end; current ++) + { + if ((current - start) % 16 == 0) + vty_out (vty, "%s ", VNL); + else if ((current - start) % 4 == 0) + vty_out (vty, " "); + + snprintf (byte, sizeof (byte), "%02x", *current); + vty_out (vty, "%s", byte); + } + + vty_out (vty, "%s%s", VNL, VNL); + return 0; +} + +struct ospf6_lsa_handler unknown_handler = +{ + OSPF6_LSTYPE_UNKNOWN, + "Unknown", + "Unk", + ospf6_unknown_lsa_show, + NULL, + OSPF6_LSA_DEBUG, +}; + +void +ospf6_install_lsa_handler (struct ospf6_lsa_handler *handler) +{ + /* type in handler is host byte order */ + int index = handler->type & OSPF6_LSTYPE_FCODE_MASK; + vector_set_index (ospf6_lsa_handler_vector, index, handler); +} + +struct ospf6_lsa_handler * +ospf6_get_lsa_handler (u_int16_t type) +{ + struct ospf6_lsa_handler *handler = NULL; + unsigned int index = ntohs (type) & OSPF6_LSTYPE_FCODE_MASK; + + if (index >= vector_active (ospf6_lsa_handler_vector)) + handler = &unknown_handler; + else + handler = vector_slot (ospf6_lsa_handler_vector, index); + + if (handler == NULL) + handler = &unknown_handler; + + return handler; +} + +const char * +ospf6_lstype_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->name; + + snprintf (buf, sizeof (buf), "0x%04hx", ntohs (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) +{ + struct ospf6_lsa_handler *handler; + handler = ospf6_get_lsa_handler (type); + return handler->debug; +} + +/* RFC2328: Section 13.2 */ +int +ospf6_lsa_is_differ (struct ospf6_lsa *lsa1, + struct ospf6_lsa *lsa2) +{ + int len; + + assert (OSPF6_LSA_IS_SAME (lsa1, lsa2)); + + /* XXX, Options ??? */ + + ospf6_lsa_age_current (lsa1); + ospf6_lsa_age_current (lsa2); + if (ntohs (lsa1->header->age) == OSPF_LSA_MAXAGE && + ntohs (lsa2->header->age) != OSPF_LSA_MAXAGE) + return 1; + if (ntohs (lsa1->header->age) != OSPF_LSA_MAXAGE && + ntohs (lsa2->header->age) == OSPF_LSA_MAXAGE) + return 1; + + /* compare body */ + if (ntohs (lsa1->header->length) != ntohs (lsa2->header->length)) + return 1; + + len = ntohs (lsa1->header->length) - sizeof (struct ospf6_lsa_header); + return memcmp (lsa1->header + 1, lsa2->header + 1, len); +} + +int +ospf6_lsa_is_changed (struct ospf6_lsa *lsa1, + struct ospf6_lsa *lsa2) +{ + int length; + + if (OSPF6_LSA_IS_MAXAGE (lsa1) ^ OSPF6_LSA_IS_MAXAGE (lsa2)) + return 1; + if (ntohs (lsa1->header->length) != ntohs (lsa2->header->length)) + return 1; + /* Going beyond LSA headers to compare the payload only makes sense, when both LSAs aren't header-only. */ + if (CHECK_FLAG (lsa1->flag, OSPF6_LSA_HEADERONLY) != CHECK_FLAG (lsa2->flag, OSPF6_LSA_HEADERONLY)) + { + zlog_warn ("%s: only one of two (%s, %s) LSAs compared is header-only", __func__, lsa1->name, lsa2->name); + return 1; + } + if (CHECK_FLAG (lsa1->flag, OSPF6_LSA_HEADERONLY)) + return 0; + + length = OSPF6_LSA_SIZE (lsa1->header) - sizeof (struct ospf6_lsa_header); + /* Once upper layer verifies LSAs received, length underrun should become a warning. */ + if (length <= 0) + return 0; + + return memcmp (OSPF6_LSA_HEADER_END (lsa1->header), + OSPF6_LSA_HEADER_END (lsa2->header), length); +} + +/* ospf6 age functions */ +/* calculate birth */ +static void +ospf6_lsa_age_set (struct ospf6_lsa *lsa) +{ + struct timeval now; + + assert (lsa && lsa->header); + + if (quagga_gettime (QUAGGA_CLK_MONOTONIC, &now) < 0) + zlog_warn ("LSA: quagga_gettime failed, may fail LSA AGEs: %s", + safe_strerror (errno)); + + lsa->birth.tv_sec = now.tv_sec - ntohs (lsa->header->age); + lsa->birth.tv_usec = now.tv_usec; + + return; +} + +/* this function calculates current age from its birth, + then update age field of LSA header. return value is current age */ +u_int16_t +ospf6_lsa_age_current (struct ospf6_lsa *lsa) +{ + struct timeval now; + u_int32_t ulage; + u_int16_t age; + + assert (lsa); + assert (lsa->header); + + /* current time */ + if (quagga_gettime (QUAGGA_CLK_MONOTONIC, &now) < 0) + zlog_warn ("LSA: quagga_gettime failed, may fail LSA AGEs: %s", + safe_strerror (errno)); + + 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 (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 > OSPF_LSA_MAXAGE ? OSPF_LSA_MAXAGE : ulage); + + lsa->header->age = htons (age); + return age; +} + +/* update age field of LSA header with adding InfTransDelay */ +void +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 > OSPF_LSA_MAXAGE) + age = OSPF_LSA_MAXAGE; + lsa->header->age = htons (age); +} + +void +ospf6_lsa_premature_aging (struct ospf6_lsa *lsa) +{ + /* log */ + if (IS_OSPF6_DEBUG_LSA_TYPE (lsa->header->type)) + zlog_debug ("LSA: Premature aging: %s", lsa->name); + + THREAD_OFF (lsa->expire); + THREAD_OFF (lsa->refresh); + + /* + * 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); +} + +/* check which is more recent. if a is more recent, return -1; + if the same, return 0; otherwise(b is more recent), return 1 */ +int +ospf6_lsa_compare (struct ospf6_lsa *a, struct ospf6_lsa *b) +{ + int32_t seqnuma, seqnumb; + u_int16_t cksuma, cksumb; + u_int16_t agea, ageb; + + assert (a && a->header); + assert (b && b->header); + assert (OSPF6_LSA_IS_SAME (a, b)); + + seqnuma = (int32_t) ntohl (a->header->seqnum); + seqnumb = (int32_t) ntohl (b->header->seqnum); + + /* compare by sequence number */ + if (seqnuma > seqnumb) + return -1; + if (seqnuma < seqnumb) + return 1; + + /* Checksum */ + cksuma = ntohs (a->header->checksum); + cksumb = ntohs (b->header->checksum); + if (cksuma > cksumb) + return -1; + if (cksuma < cksumb) + return 0; + + /* Update Age */ + agea = ospf6_lsa_age_current (a); + ageb = ospf6_lsa_age_current (b); + + /* MaxAge check */ + if (agea == OSPF_LSA_MAXAGE && ageb != OSPF_LSA_MAXAGE) + return -1; + else if (agea != OSPF_LSA_MAXAGE && ageb == OSPF_LSA_MAXAGE) + return 1; + + /* Age check */ + if (agea > ageb && agea - ageb >= OSPF_LSA_MAXAGE_DIFF) + return 1; + else if (agea < ageb && ageb - agea >= OSPF_LSA_MAXAGE_DIFF) + return -1; + + /* neither recent */ + return 0; +} + +char * +ospf6_lsa_printbuf (struct ospf6_lsa *lsa, char *buf, int size) +{ + char id[16], adv_router[16]; + inet_ntop (AF_INET, &lsa->header->id, id, sizeof (id)); + inet_ntop (AF_INET, &lsa->header->adv_router, adv_router, + sizeof (adv_router)); + snprintf (buf, size, "[%s Id:%s Adv:%s]", + ospf6_lstype_name (lsa->header->type), id, adv_router); + return buf; +} + +void +ospf6_lsa_header_print_raw (struct ospf6_lsa_header *header) +{ + char id[16], adv_router[16]; + inet_ntop (AF_INET, &header->id, id, sizeof (id)); + inet_ntop (AF_INET, &header->adv_router, adv_router, + sizeof (adv_router)); + zlog_debug (" [%s Id:%s Adv:%s]", + ospf6_lstype_name (header->type), id, adv_router); + zlog_debug (" Age: %4hu SeqNum: %#08lx Cksum: %04hx Len: %d", + ntohs (header->age), (u_long) ntohl (header->seqnum), + ntohs (header->checksum), ntohs (header->length)); +} + +void +ospf6_lsa_header_print (struct ospf6_lsa *lsa) +{ + ospf6_lsa_age_current (lsa); + ospf6_lsa_header_print_raw (lsa->header); +} + +void +ospf6_lsa_show_summary_header (struct vty *vty) +{ + vty_out (vty, "%-4s %-15s%-15s%4s %8s %30s%s", + "Type", "LSId", "AdvRouter", "Age", "SeqNum", + "Payload", VNL); +} + +void +ospf6_lsa_show_summary (struct vty *vty, struct ospf6_lsa *lsa) +{ + char adv_router[16], id[16]; + int type; + struct ospf6_lsa_handler *handler; + char buf[64], tmpbuf[80]; + int cnt = 0; + + assert (lsa); + assert (lsa->header); + + inet_ntop (AF_INET, &lsa->header->id, id, sizeof (id)); + inet_ntop (AF_INET, &lsa->header->adv_router, adv_router, + sizeof (adv_router)); + + 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 +ospf6_lsa_show_dump (struct vty *vty, struct ospf6_lsa *lsa) +{ + u_char *start, *end, *current; + char byte[4]; + + start = (u_char *) lsa->header; + end = (u_char *) lsa->header + ntohs (lsa->header->length); + + vty_out (vty, "%s", VNL); + vty_out (vty, "%s:%s", lsa->name, VNL); + + for (current = start; current < end; current ++) + { + if ((current - start) % 16 == 0) + vty_out (vty, "%s ", VNL); + else if ((current - start) % 4 == 0) + vty_out (vty, " "); + + snprintf (byte, sizeof (byte), "%02x", *current); + vty_out (vty, "%s", byte); + } + + vty_out (vty, "%s%s", VNL, VNL); + return; +} + +void +ospf6_lsa_show_internal (struct vty *vty, struct ospf6_lsa *lsa) +{ + char adv_router[64], id[64]; + + assert (lsa && lsa->header); + + inet_ntop (AF_INET, &lsa->header->id, id, sizeof (id)); + inet_ntop (AF_INET, &lsa->header->adv_router, + adv_router, sizeof (adv_router)); + + vty_out (vty, "%s", VNL); + 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); + vty_out (vty, "Advertising Router: %s%s", adv_router, VNL); + vty_out (vty, "LS Sequence Number: %#010lx%s", + (u_long) ntohl (lsa->header->seqnum), VNL); + vty_out (vty, "CheckSum: %#06hx Length: %hu%s", + ntohs (lsa->header->checksum), + ntohs (lsa->header->length), 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; +} + +void +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); + + inet_ntop (AF_INET, &lsa->header->id, id, sizeof (id)); + 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); + vty_out (vty, "Advertising Router: %s%s", adv_router, VNL); + vty_out (vty, "LS Sequence Number: %#010lx%s", + (u_long) ntohl (lsa->header->seqnum), VNL); + 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) + handler = &unknown_handler; + (*handler->show) (vty, lsa); + + vty_out (vty, "%s", VNL); +} + +/* OSPFv3 LSA creation/deletion function */ +struct ospf6_lsa * +ospf6_lsa_create (struct ospf6_lsa_header *header) +{ + struct ospf6_lsa *lsa = NULL; + struct ospf6_lsa_header *new_header = NULL; + u_int16_t lsa_size = 0; + + /* size of the entire LSA */ + lsa_size = ntohs (header->length); /* XXX vulnerable */ + + /* allocate memory for this LSA */ + new_header = (struct ospf6_lsa_header *) + XMALLOC (MTYPE_OSPF6_LSA, lsa_size); + + /* copy LSA from original header */ + memcpy (new_header, header, lsa_size); + + /* LSA information structure */ + /* allocate memory */ + lsa = (struct ospf6_lsa *) + XCALLOC (MTYPE_OSPF6_LSA, sizeof (struct ospf6_lsa)); + + lsa->header = (struct ospf6_lsa_header *) new_header; + + /* dump string */ + ospf6_lsa_printbuf (lsa, lsa->name, sizeof (lsa->name)); + + /* calculate birth of this lsa */ + ospf6_lsa_age_set (lsa); + + return lsa; +} + +struct ospf6_lsa * +ospf6_lsa_create_headeronly (struct ospf6_lsa_header *header) +{ + struct ospf6_lsa *lsa = NULL; + struct ospf6_lsa_header *new_header = NULL; + + /* allocate memory for this LSA */ + new_header = (struct ospf6_lsa_header *) + XMALLOC (MTYPE_OSPF6_LSA, sizeof (struct ospf6_lsa_header)); + + /* copy LSA from original header */ + memcpy (new_header, header, sizeof (struct ospf6_lsa_header)); + + /* LSA information structure */ + /* allocate memory */ + lsa = (struct ospf6_lsa *) + XCALLOC (MTYPE_OSPF6_LSA, sizeof (struct ospf6_lsa)); + + lsa->header = (struct ospf6_lsa_header *) new_header; + SET_FLAG (lsa->flag, OSPF6_LSA_HEADERONLY); + + /* dump string */ + ospf6_lsa_printbuf (lsa, lsa->name, sizeof (lsa->name)); + + /* calculate birth of this lsa */ + ospf6_lsa_age_set (lsa); + + return lsa; +} + +void +ospf6_lsa_delete (struct ospf6_lsa *lsa) +{ + assert (lsa->lock == 0); + + /* cancel threads */ + THREAD_OFF (lsa->expire); + THREAD_OFF (lsa->refresh); + + /* do free */ + XFREE (MTYPE_OSPF6_LSA, lsa->header); + XFREE (MTYPE_OSPF6_LSA, lsa); +} + +struct ospf6_lsa * +ospf6_lsa_copy (struct ospf6_lsa *lsa) +{ + struct ospf6_lsa *copy = NULL; + + ospf6_lsa_age_current (lsa); + if (CHECK_FLAG (lsa->flag, OSPF6_LSA_HEADERONLY)) + copy = ospf6_lsa_create_headeronly (lsa->header); + else + copy = ospf6_lsa_create (lsa->header); + assert (copy->lock == 0); + + copy->birth = lsa->birth; + copy->originated = lsa->originated; + copy->received = lsa->received; + copy->installed = lsa->installed; + copy->lsdb = lsa->lsdb; + copy->rn = NULL; + + return copy; +} + +/* increment reference counter of struct ospf6_lsa */ +void +ospf6_lsa_lock (struct ospf6_lsa *lsa) +{ + lsa->lock++; + return; +} + +/* decrement reference counter of struct ospf6_lsa */ +void +ospf6_lsa_unlock (struct ospf6_lsa *lsa) +{ + /* decrement reference counter */ + assert (lsa->lock > 0); + lsa->lock--; + + if (lsa->lock != 0) + return; + + ospf6_lsa_delete (lsa); +} + + +/* ospf6 lsa expiry */ +int +ospf6_lsa_expire (struct thread *thread) +{ + struct ospf6_lsa *lsa; + + lsa = (struct ospf6_lsa *) THREAD_ARG (thread); + + assert (lsa && lsa->header); + assert (OSPF6_LSA_IS_MAXAGE (lsa)); + assert (! lsa->refresh); + + lsa->expire = (struct thread *) NULL; + + if (IS_OSPF6_DEBUG_LSA_TYPE (lsa->header->type)) + { + zlog_debug ("LSA Expire:"); + ospf6_lsa_header_print (lsa); + } + + if (CHECK_FLAG (lsa->flag, OSPF6_LSA_HEADERONLY)) + return 0; /* dbexchange will do something ... */ + + /* reinstall lsa */ + ospf6_install_lsa (lsa); + + /* reflood lsa */ + ospf6_flood (NULL, lsa); + + /* schedule maxage remover */ + ospf6_maxage_remove (ospf6); + + return 0; +} + +int +ospf6_lsa_refresh (struct thread *thread) +{ + struct ospf6_lsa *old, *self, *new; + struct ospf6_lsdb *lsdb_self; + + assert (thread); + old = (struct ospf6_lsa *) THREAD_ARG (thread); + assert (old && old->header); + + old->refresh = (struct thread *) NULL; + + lsdb_self = ospf6_get_scoped_lsdb_self (old); + self = ospf6_lsdb_lookup (old->header->type, old->header->id, + old->header->adv_router, lsdb_self); + if (self == NULL) + { + if (IS_OSPF6_DEBUG_LSA_TYPE (old->header->type)) + zlog_debug ("Refresh: could not find self LSA, flush %s", old->name); + ospf6_lsa_premature_aging (old); + return 0; + } + + /* Reset age, increment LS sequence number. */ + self->header->age = htons (0); + self->header->seqnum = + ospf6_new_ls_seqnum (self->header->type, self->header->id, + self->header->adv_router, old->lsdb); + ospf6_lsa_checksum (self->header); + + new = ospf6_lsa_create (self->header); + new->lsdb = old->lsdb; + new->refresh = thread_add_timer (master, ospf6_lsa_refresh, new, + OSPF_LS_REFRESH_TIME); + + /* store it in the LSDB for self-originated LSAs */ + ospf6_lsdb_add (ospf6_lsa_copy (new), lsdb_self); + + if (IS_OSPF6_DEBUG_LSA_TYPE (new->header->type)) + { + zlog_debug ("LSA Refresh:"); + ospf6_lsa_header_print (new); + } + + ospf6_install_lsa (new); + ospf6_flood (NULL, new); + + return 0; +} + + + +/* 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 *buffer = (u_char *) &lsa_header->type; + int type_offset = buffer - (u_char *) &lsa_header->age; /* should be 2 */ + + /* Skip the AGE field */ + u_int16_t len = ntohs(lsa_header->length) - type_offset; + + /* 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); +} + +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 */ + + /* Skip the AGE field */ + u_int16_t len = ntohs(lsa_header->length) - type_offset; + + return (fletcher_checksum(buffer, len, FLETCHER_CHECKSUM_VALIDATE) == 0); +} + +void +ospf6_lsa_init (void) +{ + ospf6_lsa_handler_vector = vector_init (0); + ospf6_install_lsa_handler (&unknown_handler); +} + +void +ospf6_lsa_terminate (void) +{ + vector_free (ospf6_lsa_handler_vector); +} + +static char * +ospf6_lsa_handler_name (struct ospf6_lsa_handler *h) +{ + static char buf[64]; + unsigned int i; + unsigned int size = strlen (h->name); + + if (!strcmp(h->name, "unknown") && + h->type != OSPF6_LSTYPE_UNKNOWN) + { + snprintf (buf, sizeof (buf), "%#04hx", h->type); + return buf; + } + + for (i = 0; i < MIN (size, sizeof (buf)); i++) + { + if (! islower ((unsigned char)h->name[i])) + buf[i] = tolower ((unsigned char)h->name[i]); + else + buf[i] = h->name[i]; + } + buf[size] = '\0'; + return buf; +} + +DEFUN (debug_ospf6_lsa_type, + debug_ospf6_lsa_hex_cmd, + "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" + "Specify LS type as Hexadecimal\n" + ) +{ + unsigned int i; + struct ospf6_lsa_handler *handler = NULL; + + assert (argc); + + for (i = 0; i < vector_active (ospf6_lsa_handler_vector); i++) + { + handler = vector_slot (ospf6_lsa_handler_vector, i); + if (handler == NULL) + continue; + if (strncmp (argv[0], ospf6_lsa_handler_name(handler), strlen(argv[0])) == 0) + break; + if (! strcasecmp (argv[0], handler->name)) + break; + handler = NULL; + } + + if (handler == NULL) + handler = &unknown_handler; + + if (argc >= 2) + { + if (! strcmp (argv[1], "originate")) + SET_FLAG (handler->debug, OSPF6_LSA_DEBUG_ORIGINATE); + 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); + } + else + SET_FLAG (handler->debug, OSPF6_LSA_DEBUG); + + 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 (router|network|inter-prefix|inter-router|as-ext|grp-mbr|type7|link|intra-prefix|unknown)", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug Link State Advertisements (LSAs)\n" + "Specify LS type as Hexadecimal\n" + ) +{ + u_int i; + struct ospf6_lsa_handler *handler = NULL; + + assert (argc); + + for (i = 0; i < vector_active (ospf6_lsa_handler_vector); i++) + { + handler = vector_slot (ospf6_lsa_handler_vector, i); + if (handler == NULL) + continue; + if (strncmp (argv[0], ospf6_lsa_handler_name(handler), strlen(argv[0])) == 0) + break; + if (! strcasecmp (argv[0], handler->name)) + break; + } + + if (handler == NULL) + return CMD_SUCCESS; + + if (argc >= 2) + { + if (! strcmp (argv[1], "originate")) + UNSET_FLAG (handler->debug, OSPF6_LSA_DEBUG_ORIGINATE); + 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); + } + else + UNSET_FLAG (handler->debug, OSPF6_LSA_DEBUG); + + return CMD_SUCCESS; +} + +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) +{ + install_element (ENABLE_NODE, &debug_ospf6_lsa_hex_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_hex_detail_cmd); + install_element (CONFIG_NODE, &debug_ospf6_lsa_hex_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_hex_detail_cmd); +} + +int +config_write_ospf6_debug_lsa (struct vty *vty) +{ + u_int i; + struct ospf6_lsa_handler *handler; + + for (i = 0; i < vector_active (ospf6_lsa_handler_vector); i++) + { + handler = vector_slot (ospf6_lsa_handler_vector, i); + if (handler == NULL) + continue; + if (CHECK_FLAG (handler->debug, OSPF6_LSA_DEBUG)) + vty_out (vty, "debug ospf6 lsa %s%s", + ospf6_lsa_handler_name (handler), VNL); + if (CHECK_FLAG (handler->debug, OSPF6_LSA_DEBUG_ORIGINATE)) + 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 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", + ospf6_lsa_handler_name (handler), VNL); + } + + return 0; +} + + diff --git a/ospf6d/ospf6_lsa.h b/ospf6d/ospf6_lsa.h new file mode 100644 index 0000000..a85ca66 --- /dev/null +++ b/ospf6d/ospf6_lsa.h @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2003 Yasuhiro Ohara + * + * 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 OSPF6_LSA_H +#define OSPF6_LSA_H + +/* Debug option */ +#define OSPF6_LSA_DEBUG 0x01 +#define OSPF6_LSA_DEBUG_ORIGINATE 0x02 +#define OSPF6_LSA_DEBUG_EXAMIN 0x04 +#define OSPF6_LSA_DEBUG_FLOOD 0x08 + +#define IS_OSPF6_DEBUG_LSA(name) \ + (ospf6_lstype_debug (htons (OSPF6_LSTYPE_ ## name)) & \ + OSPF6_LSA_DEBUG) +#define IS_OSPF6_DEBUG_ORIGINATE(name) \ + (ospf6_lstype_debug (htons (OSPF6_LSTYPE_ ## name)) & \ + OSPF6_LSA_DEBUG_ORIGINATE) +#define IS_OSPF6_DEBUG_EXAMIN(name) \ + (ospf6_lstype_debug (htons (OSPF6_LSTYPE_ ## name)) & \ + OSPF6_LSA_DEBUG_EXAMIN) +#define IS_OSPF6_DEBUG_LSA_TYPE(type) \ + (ospf6_lstype_debug (type) & OSPF6_LSA_DEBUG) +#define IS_OSPF6_DEBUG_ORIGINATE_TYPE(type) \ + (ospf6_lstype_debug (type) & OSPF6_LSA_DEBUG_ORIGINATE) +#define IS_OSPF6_DEBUG_EXAMIN_TYPE(type) \ + (ospf6_lstype_debug (type) & OSPF6_LSA_DEBUG_EXAMIN) +#define IS_OSPF6_DEBUG_FLOOD_TYPE(type) \ + (ospf6_lstype_debug (type) & OSPF6_LSA_DEBUG_FLOOD) + +/* LSA definition */ + +#define OSPF6_MAX_LSASIZE 4096 + +/* Type */ +#define OSPF6_LSTYPE_UNKNOWN 0x0000 +#define OSPF6_LSTYPE_ROUTER 0x2001 +#define OSPF6_LSTYPE_NETWORK 0x2002 +#define OSPF6_LSTYPE_INTER_PREFIX 0x2003 +#define OSPF6_LSTYPE_INTER_ROUTER 0x2004 +#define OSPF6_LSTYPE_AS_EXTERNAL 0x4005 +#define OSPF6_LSTYPE_GROUP_MEMBERSHIP 0x2006 +#define OSPF6_LSTYPE_TYPE_7 0x2007 +#define OSPF6_LSTYPE_LINK 0x0008 +#define OSPF6_LSTYPE_INTRA_PREFIX 0x2009 +#define OSPF6_LSTYPE_SIZE 0x000a + +/* Masks for LS Type : RFC 2740 A.4.2.1 "LS type" */ +#define OSPF6_LSTYPE_UBIT_MASK 0x8000 +#define OSPF6_LSTYPE_SCOPE_MASK 0x6000 +#define OSPF6_LSTYPE_FCODE_MASK 0x1fff + +/* LSA scope */ +#define OSPF6_SCOPE_LINKLOCAL 0x0000 +#define OSPF6_SCOPE_AREA 0x2000 +#define OSPF6_SCOPE_AS 0x4000 +#define OSPF6_SCOPE_RESERVED 0x6000 + +/* XXX U-bit handling should be treated here */ +#define OSPF6_LSA_SCOPE(type) \ + (ntohs (type) & OSPF6_LSTYPE_SCOPE_MASK) + +/* LSA Header */ +#define OSPF6_LSA_HEADER_SIZE 20U +struct ospf6_lsa_header +{ + u_int16_t age; /* LS age */ + u_int16_t type; /* LS type */ + u_int32_t id; /* Link State ID */ + u_int32_t adv_router; /* Advertising Router */ + u_int32_t seqnum; /* LS sequence number */ + u_int16_t checksum; /* LS checksum */ + u_int16_t length; /* LSA length */ +}; + +#define OSPF6_LSA_HEADER_END(h) \ + ((caddr_t)(h) + sizeof (struct ospf6_lsa_header)) +#define OSPF6_LSA_SIZE(h) \ + (ntohs (((struct ospf6_lsa_header *) (h))->length)) +#define OSPF6_LSA_END(h) \ + ((caddr_t)(h) + ntohs (((struct ospf6_lsa_header *) (h))->length)) +#define OSPF6_LSA_IS_TYPE(t, L) \ + ((L)->header->type == htons (OSPF6_LSTYPE_ ## t) ? 1 : 0) +#define OSPF6_LSA_IS_SAME(L1, L2) \ + ((L1)->header->adv_router == (L2)->header->adv_router && \ + (L1)->header->id == (L2)->header->id && \ + (L1)->header->type == (L2)->header->type) +#define OSPF6_LSA_IS_MATCH(t, i, a, L) \ + ((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) == 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 route_node *rn; + + unsigned char lock; /* reference counter */ + unsigned char flag; /* special meaning (e.g. floodback) */ + + struct timeval birth; /* tv_sec when LS age 0 */ + struct timeval originated; /* used by MinLSInterval check */ + struct timeval received; /* used by MinLSArrival check */ + struct timeval installed; + + struct thread *expire; + struct thread *refresh; /* For self-originated LSA */ + + int retrans_count; + + struct ospf6_lsdb *lsdb; + + /* lsa instance */ + struct ospf6_lsa_header *header; +}; + +#define OSPF6_LSA_HEADERONLY 0x01 +#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; +}; + +extern struct ospf6_lsa_handler unknown_handler; +#define OSPF6_LSA_IS_KNOWN(type) \ + (ospf6_get_lsa_handler (type) != &unknown_handler ? 1 : 0) + +/* Macro for LSA Origination */ +/* addr is (struct prefix *) */ +#define CONTINUE_IF_ADDRESS_LINKLOCAL(debug,addr) \ + if (IN6_IS_ADDR_LINKLOCAL (&(addr)->u.prefix6)) \ + { \ + char buf[64]; \ + prefix2str (addr, buf, sizeof (buf)); \ + if (debug) \ + zlog_debug ("Filter out Linklocal: %s", buf); \ + continue; \ + } + +#define CONTINUE_IF_ADDRESS_UNSPECIFIED(debug,addr) \ + if (IN6_IS_ADDR_UNSPECIFIED (&(addr)->u.prefix6)) \ + { \ + char buf[64]; \ + prefix2str (addr, buf, sizeof (buf)); \ + if (debug) \ + zlog_debug ("Filter out Unspecified: %s", buf);\ + continue; \ + } + +#define CONTINUE_IF_ADDRESS_LOOPBACK(debug,addr) \ + if (IN6_IS_ADDR_LOOPBACK (&(addr)->u.prefix6)) \ + { \ + char buf[64]; \ + prefix2str (addr, buf, sizeof (buf)); \ + if (debug) \ + zlog_debug ("Filter out Loopback: %s", buf); \ + continue; \ + } + +#define CONTINUE_IF_ADDRESS_V4COMPAT(debug,addr) \ + if (IN6_IS_ADDR_V4COMPAT (&(addr)->u.prefix6)) \ + { \ + char buf[64]; \ + prefix2str (addr, buf, sizeof (buf)); \ + if (debug) \ + zlog_debug ("Filter out V4Compat: %s", buf); \ + continue; \ + } + +#define CONTINUE_IF_ADDRESS_V4MAPPED(debug,addr) \ + if (IN6_IS_ADDR_V4MAPPED (&(addr)->u.prefix6)) \ + { \ + char buf[64]; \ + prefix2str (addr, buf, sizeof (buf)); \ + if (debug) \ + zlog_debug ("Filter out V4Mapped: %s", buf); \ + 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); +extern u_int16_t ospf6_lsa_age_current (struct ospf6_lsa *); +extern void ospf6_lsa_age_update_to_send (struct ospf6_lsa *, u_int32_t); +extern void ospf6_lsa_premature_aging (struct ospf6_lsa *); +extern int ospf6_lsa_compare (struct ospf6_lsa *, struct ospf6_lsa *); + +extern char *ospf6_lsa_printbuf (struct ospf6_lsa *lsa, char *buf, int size); +extern void ospf6_lsa_header_print_raw (struct ospf6_lsa_header *header); +extern void ospf6_lsa_header_print (struct ospf6_lsa *lsa); +extern void ospf6_lsa_show_summary_header (struct vty *vty); +extern void ospf6_lsa_show_summary (struct vty *vty, struct ospf6_lsa *lsa); +extern void ospf6_lsa_show_dump (struct vty *vty, struct ospf6_lsa *lsa); +extern void ospf6_lsa_show_internal (struct vty *vty, struct ospf6_lsa *lsa); +extern void ospf6_lsa_show (struct vty *vty, struct ospf6_lsa *lsa); + +extern struct ospf6_lsa *ospf6_lsa_create (struct ospf6_lsa_header *header); +extern struct ospf6_lsa *ospf6_lsa_create_headeronly (struct ospf6_lsa_header *header); +extern void ospf6_lsa_delete (struct ospf6_lsa *lsa); +extern struct ospf6_lsa *ospf6_lsa_copy (struct ospf6_lsa *); + +extern void ospf6_lsa_lock (struct ospf6_lsa *); +extern void ospf6_lsa_unlock (struct ospf6_lsa *); + +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); + +extern void ospf6_install_lsa_handler (struct ospf6_lsa_handler *handler); +extern struct ospf6_lsa_handler *ospf6_get_lsa_handler (u_int16_t type); + +extern void ospf6_lsa_init (void); +extern void ospf6_lsa_terminate (void); + +extern int config_write_ospf6_debug_lsa (struct vty *vty); +extern void install_element_ospf6_debug_lsa (void); + +#endif /* OSPF6_LSA_H */ + diff --git a/ospf6d/ospf6_lsdb.c b/ospf6d/ospf6_lsdb.c new file mode 100644 index 0000000..b5a4587 --- /dev/null +++ b/ospf6d/ospf6_lsdb.c @@ -0,0 +1,592 @@ +/* + * Copyright (C) 2003 Yasuhiro Ohara + * + * 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 "memory.h" +#include "log.h" +#include "command.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" + +#include "ospf6_proto.h" +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" +#include "ospf6d.h" + +struct ospf6_lsdb * +ospf6_lsdb_create (void *data) +{ + struct ospf6_lsdb *lsdb; + + lsdb = XCALLOC (MTYPE_OSPF6_LSDB, sizeof (struct ospf6_lsdb)); + if (lsdb == NULL) + { + zlog_warn ("Can't malloc lsdb"); + return NULL; + } + memset (lsdb, 0, sizeof (struct ospf6_lsdb)); + + lsdb->data = data; + lsdb->table = route_table_init (); + return lsdb; +} + +void +ospf6_lsdb_delete (struct ospf6_lsdb *lsdb) +{ + if (lsdb != NULL) + { + ospf6_lsdb_remove_all (lsdb); + route_table_finish (lsdb->table); + XFREE (MTYPE_OSPF6_LSDB, lsdb); + } +} + +static void +ospf6_lsdb_set_key (struct prefix_ipv6 *key, void *value, int len) +{ + assert (key->prefixlen % 8 == 0); + + memcpy ((caddr_t) &key->prefix + key->prefixlen / 8, + (caddr_t) value, len); + key->family = AF_INET6; + key->prefixlen += len * 8; +} + +#ifdef DEBUG +static void +_lsdb_count_assert (struct ospf6_lsdb *lsdb) +{ + struct ospf6_lsa *debug; + unsigned int num = 0; + for (debug = ospf6_lsdb_head (lsdb); debug; + debug = ospf6_lsdb_next (debug)) + num++; + + if (num == lsdb->count) + return; + + zlog_debug ("PANIC !! lsdb[%p]->count = %d, real = %d", + lsdb, lsdb->count, num); + for (debug = ospf6_lsdb_head (lsdb); debug; + debug = ospf6_lsdb_next (debug)) + zlog_debug ("%p %p %s lsdb[%p]", debug->prev, debug->next, debug->name, + debug->lsdb); + zlog_debug ("DUMP END"); + + assert (num == lsdb->count); +} +#define ospf6_lsdb_count_assert(t) (_lsdb_count_assert (t)) +#else /*DEBUG*/ +#define ospf6_lsdb_count_assert(t) ((void) 0) +#endif /*DEBUG*/ + +void +ospf6_lsdb_add (struct ospf6_lsa *lsa, struct ospf6_lsdb *lsdb) +{ + struct prefix_ipv6 key; + 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)); + ospf6_lsdb_set_key (&key, &lsa->header->adv_router, + sizeof (lsa->header->adv_router)); + ospf6_lsdb_set_key (&key, &lsa->header->id, sizeof (lsa->header->id)); + + current = route_node_get (lsdb->table, (struct prefix *) &key); + old = current->info; + current->info = lsa; + lsa->rn = current; + ospf6_lsa_lock (lsa); + + if (!old) + { + lsdb->count++; + + if (OSPF6_LSA_IS_MAXAGE (lsa)) + { + if (lsdb->hook_remove) + (*lsdb->hook_remove) (lsa); + } + else + { + if (lsdb->hook_add) + (*lsdb->hook_add) (lsa); + } + } + else + { + if (OSPF6_LSA_IS_CHANGED (old, lsa)) + { + if (OSPF6_LSA_IS_MAXAGE (lsa)) + { + if (lsdb->hook_remove) + { + (*lsdb->hook_remove) (old); + (*lsdb->hook_remove) (lsa); + } + } + else if (OSPF6_LSA_IS_MAXAGE (old)) + { + if (lsdb->hook_add) + (*lsdb->hook_add) (lsa); + } + else + { + if (lsdb->hook_remove) + (*lsdb->hook_remove) (old); + if (lsdb->hook_add) + (*lsdb->hook_add) (lsa); + } + } + ospf6_lsa_unlock (old); + } + + ospf6_lsdb_count_assert (lsdb); +} + +void +ospf6_lsdb_remove (struct ospf6_lsa *lsa, struct ospf6_lsdb *lsdb) +{ + struct route_node *node; + struct prefix_ipv6 key; + + memset (&key, 0, sizeof (key)); + ospf6_lsdb_set_key (&key, &lsa->header->type, sizeof (lsa->header->type)); + ospf6_lsdb_set_key (&key, &lsa->header->adv_router, + sizeof (lsa->header->adv_router)); + ospf6_lsdb_set_key (&key, &lsa->header->id, sizeof (lsa->header->id)); + + node = route_node_lookup (lsdb->table, (struct prefix *) &key); + assert (node && node->info == lsa); + + 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); + + ospf6_lsdb_count_assert (lsdb); +} + +struct ospf6_lsa * +ospf6_lsdb_lookup (u_int16_t type, u_int32_t id, u_int32_t adv_router, + struct ospf6_lsdb *lsdb) +{ + struct route_node *node; + struct prefix_ipv6 key; + + if (lsdb == NULL) + return NULL; + + memset (&key, 0, sizeof (key)); + ospf6_lsdb_set_key (&key, &type, sizeof (type)); + ospf6_lsdb_set_key (&key, &adv_router, sizeof (adv_router)); + ospf6_lsdb_set_key (&key, &id, sizeof (id)); + + 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; +} + +struct ospf6_lsa * +ospf6_lsdb_lookup_next (u_int16_t type, u_int32_t id, u_int32_t adv_router, + struct ospf6_lsdb *lsdb) +{ + struct route_node *node; + struct route_node *matched = NULL; + struct prefix_ipv6 key; + struct prefix *p; + + if (lsdb == NULL) + return NULL; + + memset (&key, 0, sizeof (key)); + ospf6_lsdb_set_key (&key, &type, sizeof (type)); + ospf6_lsdb_set_key (&key, &adv_router, sizeof (adv_router)); + ospf6_lsdb_set_key (&key, &id, sizeof (id)); + p = (struct prefix *) &key; + + { + char buf[64]; + prefix2str (p, buf, sizeof (buf)); + zlog_debug ("lsdb_lookup_next: key: %s", buf); + } + + node = lsdb->table->top; + /* walk down tree. */ + while (node && node->p.prefixlen <= p->prefixlen && + prefix_match (&node->p, p)) + { + matched = node; + node = node->link[prefix_bit(&p->u.prefix, node->p.prefixlen)]; + } + + if (matched) + node = matched; + else + node = lsdb->table->top; + route_lock_node (node); + + /* skip to real existing entry */ + while (node && node->info == NULL) + node = route_next (node); + + if (! node) + return NULL; + + if (prefix_same (&node->p, p)) + { + node = route_next (node); + while (node && node->info == NULL) + node = route_next (node); + } + + if (! node) + return NULL; + + route_unlock_node (node); + return (struct ospf6_lsa *) node->info; +} + +/* Iteration function */ +struct ospf6_lsa * +ospf6_lsdb_head (struct ospf6_lsdb *lsdb) +{ + struct route_node *node; + + node = route_top (lsdb->table); + if (node == NULL) + return NULL; + + /* skip to the existing lsdb entry */ + while (node && node->info == NULL) + node = route_next (node); + if (node == NULL) + return NULL; + + if (node->info) + ospf6_lsa_lock ((struct ospf6_lsa *) node->info); + return (struct ospf6_lsa *) node->info; +} + +struct ospf6_lsa * +ospf6_lsdb_next (struct ospf6_lsa *lsa) +{ + struct route_node *node = lsa->rn; + struct ospf6_lsa *next = NULL; + + 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; +} + +struct ospf6_lsa * +ospf6_lsdb_type_router_head (u_int16_t type, u_int32_t adv_router, + struct ospf6_lsdb *lsdb) +{ + struct route_node *node; + struct prefix_ipv6 key; + struct ospf6_lsa *lsa; + + memset (&key, 0, sizeof (key)); + ospf6_lsdb_set_key (&key, &type, sizeof (type)); + ospf6_lsdb_set_key (&key, &adv_router, sizeof (adv_router)); + + node = lsdb->table->top; + + /* Walk down tree. */ + while (node && node->p.prefixlen <= key.prefixlen && + prefix_match (&node->p, (struct prefix *) &key)) + node = node->link[prefix6_bit(&key.prefix, node->p.prefixlen)]; + + if (node) + route_lock_node (node); + while (node && node->info == NULL) + node = route_next (node); + + if (node == NULL) + return NULL; + + if (! prefix_match ((struct prefix *) &key, &node->p)) + return NULL; + + lsa = node->info; + ospf6_lsa_lock (lsa); + + return lsa; +} + +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 = ospf6_lsdb_next(lsa); + + if (next) + { + if (next->header->type != type || + next->header->adv_router != adv_router) + { + route_unlock_node (next->rn); + ospf6_lsa_unlock (next); + next = NULL; + } + } + + return next; +} + +struct ospf6_lsa * +ospf6_lsdb_type_head (u_int16_t type, struct ospf6_lsdb *lsdb) +{ + struct route_node *node; + struct prefix_ipv6 key; + struct ospf6_lsa *lsa; + + memset (&key, 0, sizeof (key)); + ospf6_lsdb_set_key (&key, &type, sizeof (type)); + + /* Walk down tree. */ + node = lsdb->table->top; + while (node && node->p.prefixlen <= key.prefixlen && + prefix_match (&node->p, (struct prefix *) &key)) + node = node->link[prefix6_bit(&key.prefix, node->p.prefixlen)]; + + if (node) + route_lock_node (node); + while (node && node->info == NULL) + node = route_next (node); + + if (node == NULL) + return NULL; + + if (! prefix_match ((struct prefix *) &key, &node->p)) + return NULL; + + lsa = node->info; + ospf6_lsa_lock (lsa); + + return lsa; +} + +struct ospf6_lsa * +ospf6_lsdb_type_next (u_int16_t type, struct ospf6_lsa *lsa) +{ + struct ospf6_lsa *next = ospf6_lsdb_next (lsa); + + if (next) + { + if (next->header->type != type) + { + route_unlock_node (next->rn); + ospf6_lsa_unlock (next); + next = NULL; + } + } + + return next; +} + +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_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; + + 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); + if (lsa) + { + if (level == OSPF6_LSDB_SHOW_LEVEL_NORMAL) + ospf6_lsa_show (vty, lsa); + else + (*showfunc) (vty, lsa); + } + return; + } + + if (level == OSPF6_LSDB_SHOW_LEVEL_NORMAL) + ospf6_lsa_show_summary_header (vty); + + if (type && adv_router) + lsa = ospf6_lsdb_type_router_head (*type, *adv_router, lsdb); + else if (type) + lsa = ospf6_lsdb_type_head (*type, lsdb); + else + lsa = ospf6_lsdb_head (lsdb); + while (lsa) + { + if ((! adv_router || lsa->header->adv_router == *adv_router) && + (! id || lsa->header->id == *id)) + (*showfunc) (vty, lsa); + + if (type && adv_router) + lsa = ospf6_lsdb_type_router_next (*type, *adv_router, lsa); + else if (type) + lsa = ospf6_lsdb_type_next (*type, lsa); + else + lsa = ospf6_lsdb_next (lsa); + } +} + +/* Decide new Link State ID to originate. + note return value is network byte order */ +u_int32_t +ospf6_new_ls_id (u_int16_t type, u_int32_t adv_router, + struct ospf6_lsdb *lsdb) +{ + struct ospf6_lsa *lsa; + u_int32_t id = 1; + + for (lsa = ospf6_lsdb_type_router_head (type, adv_router, lsdb); lsa; + lsa = ospf6_lsdb_type_router_next (type, adv_router, lsa)) + { + if (ntohl (lsa->header->id) < id) + continue; + if (ntohl (lsa->header->id) > id) + { + ospf6_lsdb_lsa_unlock (lsa); + break; + } + id++; + } + + return ((u_int32_t) htonl (id)); +} + +/* Decide new LS sequence number to originate. + note return value is network byte order */ +u_int32_t +ospf6_new_ls_seqnum (u_int16_t type, u_int32_t id, u_int32_t adv_router, + struct ospf6_lsdb *lsdb) +{ + struct ospf6_lsa *lsa; + signed long seqnum = 0; + + /* if current database copy not found, return InitialSequenceNumber */ + lsa = ospf6_lsdb_lookup (type, id, adv_router, lsdb); + if (lsa == NULL) + seqnum = OSPF_INITIAL_SEQUENCE_NUMBER; + else + seqnum = (signed long) ntohl (lsa->header->seqnum) + 1; + + return ((u_int32_t) htonl (seqnum)); +} + + diff --git a/ospf6d/ospf6_lsdb.h b/ospf6d/ospf6_lsdb.h new file mode 100644 index 0000000..425f615 --- /dev/null +++ b/ospf6d/ospf6_lsdb.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2003 Yasuhiro Ohara + * + * 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 OSPF6_LSDB_H +#define OSPF6_LSDB_H + +#include "prefix.h" +#include "table.h" + +struct ospf6_lsdb +{ + void *data; /* data structure that holds this lsdb */ + struct route_table *table; + u_int32_t count; + void (*hook_add) (struct ospf6_lsa *); + void (*hook_remove) (struct ospf6_lsa *); +}; + +/* Function Prototypes */ +extern struct ospf6_lsdb *ospf6_lsdb_create (void *data); +extern void ospf6_lsdb_delete (struct ospf6_lsdb *lsdb); + +extern struct ospf6_lsa *ospf6_lsdb_lookup (u_int16_t type, u_int32_t id, + u_int32_t adv_router, + struct ospf6_lsdb *lsdb); +extern struct ospf6_lsa *ospf6_lsdb_lookup_next (u_int16_t type, u_int32_t id, + u_int32_t adv_router, + struct ospf6_lsdb *lsdb); + +extern void ospf6_lsdb_add (struct ospf6_lsa *lsa, struct ospf6_lsdb *lsdb); +extern void ospf6_lsdb_remove (struct ospf6_lsa *lsa, struct ospf6_lsdb *lsdb); + +extern struct ospf6_lsa *ospf6_lsdb_head (struct ospf6_lsdb *lsdb); +extern struct ospf6_lsa *ospf6_lsdb_next (struct ospf6_lsa *lsa); + +extern struct ospf6_lsa *ospf6_lsdb_type_router_head (u_int16_t type, + u_int32_t adv_router, + struct ospf6_lsdb *lsdb); +extern struct ospf6_lsa *ospf6_lsdb_type_router_next (u_int16_t type, + u_int32_t adv_router, + struct ospf6_lsa *lsa); + +extern struct ospf6_lsa *ospf6_lsdb_type_head (u_int16_t type, + struct ospf6_lsdb *lsdb); +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); + +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, + enum ospf_lsdb_show_level level, u_int16_t *type, + u_int32_t *id, u_int32_t *adv_router, + struct ospf6_lsdb *lsdb); + +extern u_int32_t ospf6_new_ls_id (u_int16_t type, u_int32_t adv_router, + struct ospf6_lsdb *lsdb); +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 new file mode 100644 index 0000000..1afe84a --- /dev/null +++ b/ospf6d/ospf6_main.c @@ -0,0 +1,365 @@ +/* + * Copyright (C) 1999 Yasuhiro Ohara + * + * 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 "getopt.h" +#include "thread.h" +#include "log.h" +#include "command.h" +#include "vty.h" +#include "memory.h" +#include "if.h" +#include "filter.h" +#include "prefix.h" +#include "plist.h" +#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" + +/* Default port values. */ +#define OSPF6_VTY_PORT 2606 + +/* ospf6d privileges */ +zebra_capabilities_t _caps_p [] = +{ + ZCAP_NET_RAW, + ZCAP_BIND +}; + +struct zebra_privs_t ospf6d_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 +}; + +/* ospf6d options, we use GNU getopt library. */ +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'}, + { "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'}, + { "dryrun", no_argument, NULL, 'C'}, + { "help", no_argument, NULL, 'h'}, + { 0 } +}; + +/* Configuration file and directory. */ +char config_default[] = SYSCONFDIR OSPF6_DEFAULT_CONFIG; + +/* ospf6d program name. */ +char *progname; + +/* is daemon? */ +int daemon_mode = 0; + +/* Master of threads. */ +struct thread_master *master; + +/* Process ID saved for use by init system */ +const char *pid_file = PATH_OSPF6D_PID; + +/* Help information display. */ +static void +usage (char *progname, int status) +{ + if (status != 0) + fprintf (stderr, "Try `%s --help' for more information.\n", progname); + else + { + printf ("Usage : %s [OPTION...]\n\n\ +Daemon which manages OSPF version 3.\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\ +-C, --dryrun Check configuration for validity and exit\n\ +-h, --help Display this help and exit\n\ +\n\ +Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); + } + + exit (status); +} + +static void __attribute__ ((noreturn)) +ospf6_exit (int status) +{ + 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 (); + + vrf_terminate (); + vty_terminate (); + cmd_terminate (); + + if (zclient) + zclient_free (zclient); + + if (master) + thread_master_free (master); + + if (zlog_default) + closezlog (zlog_default); + + exit (status); +} + +/* SIGHUP handler. */ +static void +sighup (void) +{ + zlog_info ("SIGHUP received"); +} + +/* SIGINT handler. */ +static void +sigint (void) +{ + zlog_notice ("Terminating on signal SIGINT"); + ospf6_exit (0); +} + +/* SIGTERM handler. */ +static void +sigterm (void) +{ + zlog_notice ("Terminating on signal SIGTERM"); + ospf6_clean(); + ospf6_exit (0); +} + +/* SIGUSR1 handler. */ +static void +sigusr1 (void) +{ + zlog_info ("SIGUSR1 received"); + zlog_rotate (NULL); +} + +struct quagga_signal_t ospf6_signals[] = +{ + { + .signal = SIGHUP, + .handler = &sighup, + }, + { + .signal = SIGINT, + .handler = &sigint, + }, + { + .signal = SIGTERM, + .handler = &sigterm, + }, + { + .signal = SIGUSR1, + .handler = &sigusr1, + }, +}; + +/* Main routine of ospf6d. Treatment of argument and starting ospf finite + state machine is handled here. */ +int +main (int argc, char *argv[], char *envp[]) +{ + char *p; + int opt; + char *vty_addr = NULL; + int vty_port = 0; + char *config_file = NULL; + struct thread thread; + int dryrun = 0; + + /* Set umask before anything for security */ + umask (0027); + + /* Preserve name of myself. */ + progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]); + + /* Command line argument treatment. */ + while (1) + { + opt = getopt_long (argc, argv, "df:i:z:hp:A:P:u:g:vC", longopts, 0); + + if (opt == EOF) + break; + + switch (opt) + { + case 0: + break; + case 'd': + daemon_mode = 1; + break; + case 'f': + config_file = optarg; + break; + case 'A': + vty_addr = optarg; + break; + case 'i': + pid_file = optarg; + break; + case 'z': + zclient_serv_path_set (optarg); + break; + case 'P': + /* Deal with atoi() returning 0 on failure, and ospf6d not + listening on ospf6d port... */ + if (strcmp(optarg, "0") == 0) + { + vty_port = 0; + break; + } + vty_port = atoi (optarg); + if (vty_port <= 0 || vty_port > 0xffff) + vty_port = OSPF6_VTY_PORT; + break; + case 'u': + ospf6d_privs.user = optarg; + break; + case 'g': + ospf6d_privs.group = optarg; + break; + case 'v': + print_version (progname); + exit (0); + break; + case 'C': + dryrun = 1; + break; + case 'h': + usage (progname, 0); + break; + default: + usage (progname, 1); + break; + } + } + + if (geteuid () != 0) + { + errno = EPERM; + perror (progname); + exit (1); + } + + /* thread master */ + master = thread_master_create (); + + /* Initializations. */ + zlog_default = openzlog (progname, ZLOG_OSPF6, + LOG_CONS|LOG_NDELAY|LOG_PID, + LOG_DAEMON); + zprivs_init (&ospf6d_privs); + /* initialize zebra libraries */ + signal_init (master, array_size(ospf6_signals), ospf6_signals); + cmd_init (1); + vty_init (master); + memory_init (); + vrf_init (); + access_list_init (); + prefix_list_init (); + + /* initialize ospf6 */ + ospf6_init (); + + /* parse config file */ + vty_read_config (config_file, config_default); + + /* Start execution only if not in dry-run mode */ + if (dryrun) + return(0); + + if (daemon_mode && daemon (0, 0) < 0) + { + zlog_err("OSPF6d daemon failed: %s", strerror(errno)); + exit (1); + } + + /* pid file create */ + pid_output (pid_file); + + /* Make ospf6 vty socket. */ + if (!vty_port) + vty_port = OSPF6_VTY_PORT; + vty_serv_sock (vty_addr, vty_port, OSPF6_VTYSH_PATH); + + /* Print start message */ + zlog_notice ("OSPF6d (Quagga-%s ospf6d-%s) starts: vty@%d", + QUAGGA_VERSION, OSPF6_DAEMON_VERSION,vty_port); + + /* Start finite state machine, here we go! */ + while (thread_fetch (master, &thread)) + thread_call (&thread); + + /* Log in case thread failed */ + zlog_warn ("Thread failed"); + + /* Not reached. */ + ospf6_exit (0); +} + + diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c new file mode 100644 index 0000000..d382f03 --- /dev/null +++ b/ospf6d/ospf6_message.c @@ -0,0 +1,2550 @@ +/* + * Copyright (C) 2003 Yasuhiro Ohara + * + * 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 "memory.h" +#include "log.h" +#include "vty.h" +#include "command.h" +#include "thread.h" +#include "linklist.h" + +#include "ospf6_proto.h" +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" +#include "ospf6_network.h" +#include "ospf6_message.h" + +#include "ospf6_top.h" +#include "ospf6_area.h" +#include "ospf6_neighbor.h" +#include "ospf6_interface.h" + +/* for structures and macros ospf6_lsa_examin() needs */ +#include "ospf6_abr.h" +#include "ospf6_asbr.h" +#include "ospf6_intra.h" + +#include "ospf6_flood.h" +#include "ospf6d.h" + +#include + +unsigned char conf_debug_ospf6_message[6] = {0x03, 0, 0, 0, 0, 0}; +static const struct message ospf6_message_type_str [] = +{ + { OSPF6_MESSAGE_TYPE_HELLO, "Hello" }, + { OSPF6_MESSAGE_TYPE_DBDESC, "DbDesc" }, + { OSPF6_MESSAGE_TYPE_LSREQ, "LSReq" }, + { OSPF6_MESSAGE_TYPE_LSUPDATE, "LSUpdate" }, + { OSPF6_MESSAGE_TYPE_LSACK, "LSAck" }, +}; +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. */ +const u_int16_t ospf6_packet_minlen[OSPF6_MESSAGE_TYPE_ALL] = +{ + 0, + OSPF6_HELLO_MIN_SIZE, + OSPF6_DB_DESC_MIN_SIZE, + OSPF6_LS_REQ_MIN_SIZE, + OSPF6_LS_UPD_MIN_SIZE, + OSPF6_LS_ACK_MIN_SIZE +}; + +/* Minimum (besides the standard LSA header) lengths for LSAs of particular + types, offset is the "LSA function code" portion of "LSA type" field. */ +const u_int16_t ospf6_lsa_minlen[OSPF6_LSTYPE_SIZE] = +{ + 0, + /* 0x2001 */ OSPF6_ROUTER_LSA_MIN_SIZE, + /* 0x2002 */ OSPF6_NETWORK_LSA_MIN_SIZE, + /* 0x2003 */ OSPF6_INTER_PREFIX_LSA_MIN_SIZE, + /* 0x2004 */ OSPF6_INTER_ROUTER_LSA_FIX_SIZE, + /* 0x4005 */ OSPF6_AS_EXTERNAL_LSA_MIN_SIZE, + /* 0x2006 */ 0, + /* 0x2007 */ OSPF6_AS_EXTERNAL_LSA_MIN_SIZE, + /* 0x0008 */ OSPF6_LINK_LSA_MIN_SIZE, + /* 0x2009 */ OSPF6_INTRA_PREFIX_LSA_MIN_SIZE +}; + +/* print functions */ + +static void +ospf6_header_print (struct ospf6_header *oh) +{ + char router_id[16], area_id[16]; + inet_ntop (AF_INET, &oh->router_id, router_id, sizeof (router_id)); + inet_ntop (AF_INET, &oh->area_id, area_id, sizeof (area_id)); + + zlog_debug (" OSPFv%d Type:%d Len:%hu Router-ID:%s", + oh->version, oh->type, ntohs (oh->length), router_id); + zlog_debug (" Area-ID:%s Cksum:%hx Instance-ID:%d", + area_id, ntohs (oh->checksum), oh->instance_id); +} + +void +ospf6_hello_print (struct ospf6_header *oh) +{ + struct ospf6_hello *hello; + char options[16]; + char drouter[16], bdrouter[16], neighbor[16]; + char *p; + + ospf6_header_print (oh); + assert (oh->type == OSPF6_MESSAGE_TYPE_HELLO); + + hello = (struct ospf6_hello *) + ((caddr_t) oh + sizeof (struct ospf6_header)); + + inet_ntop (AF_INET, &hello->drouter, drouter, sizeof (drouter)); + inet_ntop (AF_INET, &hello->bdrouter, bdrouter, sizeof (bdrouter)); + ospf6_options_printbuf (hello->options, options, sizeof (options)); + + zlog_debug (" I/F-Id:%ld Priority:%d Option:%s", + (u_long) ntohl (hello->interface_id), hello->priority, options); + zlog_debug (" HelloInterval:%hu DeadInterval:%hu", + ntohs (hello->hello_interval), ntohs (hello->dead_interval)); + zlog_debug (" DR:%s BDR:%s", drouter, bdrouter); + + for (p = (char *) ((caddr_t) hello + sizeof (struct ospf6_hello)); + p + sizeof (u_int32_t) <= OSPF6_MESSAGE_END (oh); + p += sizeof (u_int32_t)) + { + inet_ntop (AF_INET, (void *) p, neighbor, sizeof (neighbor)); + zlog_debug (" Neighbor: %s", neighbor); + } + + assert (p == OSPF6_MESSAGE_END (oh)); +} + +void +ospf6_dbdesc_print (struct ospf6_header *oh) +{ + struct ospf6_dbdesc *dbdesc; + char options[16]; + char *p; + + ospf6_header_print (oh); + assert (oh->type == OSPF6_MESSAGE_TYPE_DBDESC); + + dbdesc = (struct ospf6_dbdesc *) + ((caddr_t) oh + sizeof (struct ospf6_header)); + + ospf6_options_printbuf (dbdesc->options, options, sizeof (options)); + + zlog_debug (" MBZ: %#x Option: %s IfMTU: %hu", + dbdesc->reserved1, options, ntohs (dbdesc->ifmtu)); + zlog_debug (" MBZ: %#x Bits: %s%s%s SeqNum: %#lx", + dbdesc->reserved2, + (CHECK_FLAG (dbdesc->bits, OSPF6_DBDESC_IBIT) ? "I" : "-"), + (CHECK_FLAG (dbdesc->bits, OSPF6_DBDESC_MBIT) ? "M" : "-"), + (CHECK_FLAG (dbdesc->bits, OSPF6_DBDESC_MSBIT) ? "m" : "s"), + (u_long) ntohl (dbdesc->seqnum)); + + for (p = (char *) ((caddr_t) dbdesc + sizeof (struct ospf6_dbdesc)); + p + sizeof (struct ospf6_lsa_header) <= OSPF6_MESSAGE_END (oh); + p += sizeof (struct ospf6_lsa_header)) + ospf6_lsa_header_print_raw ((struct ospf6_lsa_header *) p); + + assert (p == OSPF6_MESSAGE_END (oh)); +} + +void +ospf6_lsreq_print (struct ospf6_header *oh) +{ + char id[16], adv_router[16]; + char *p; + + ospf6_header_print (oh); + assert (oh->type == OSPF6_MESSAGE_TYPE_LSREQ); + + for (p = (char *) ((caddr_t) oh + sizeof (struct ospf6_header)); + p + sizeof (struct ospf6_lsreq_entry) <= OSPF6_MESSAGE_END (oh); + p += sizeof (struct ospf6_lsreq_entry)) + { + struct ospf6_lsreq_entry *e = (struct ospf6_lsreq_entry *) p; + inet_ntop (AF_INET, &e->adv_router, adv_router, sizeof (adv_router)); + inet_ntop (AF_INET, &e->id, id, sizeof (id)); + zlog_debug (" [%s Id:%s Adv:%s]", + ospf6_lstype_name (e->type), id, adv_router); + } + + assert (p == OSPF6_MESSAGE_END (oh)); +} + +void +ospf6_lsupdate_print (struct ospf6_header *oh) +{ + struct ospf6_lsupdate *lsupdate; + u_long num; + char *p; + + ospf6_header_print (oh); + assert (oh->type == OSPF6_MESSAGE_TYPE_LSUPDATE); + + lsupdate = (struct ospf6_lsupdate *) + ((caddr_t) oh + sizeof (struct ospf6_header)); + + num = ntohl (lsupdate->lsa_number); + zlog_debug (" Number of LSA: %ld", num); + + for (p = (char *) ((caddr_t) lsupdate + sizeof (struct ospf6_lsupdate)); + p < OSPF6_MESSAGE_END (oh) && + p + OSPF6_LSA_SIZE (p) <= OSPF6_MESSAGE_END (oh); + p += OSPF6_LSA_SIZE (p)) + { + ospf6_lsa_header_print_raw ((struct ospf6_lsa_header *) p); + } + + assert (p == OSPF6_MESSAGE_END (oh)); +} + +void +ospf6_lsack_print (struct ospf6_header *oh) +{ + char *p; + + ospf6_header_print (oh); + assert (oh->type == OSPF6_MESSAGE_TYPE_LSACK); + + for (p = (char *) ((caddr_t) oh + sizeof (struct ospf6_header)); + p + sizeof (struct ospf6_lsa_header) <= OSPF6_MESSAGE_END (oh); + p += sizeof (struct ospf6_lsa_header)) + ospf6_lsa_header_print_raw ((struct ospf6_lsa_header *) p); + + assert (p == OSPF6_MESSAGE_END (oh)); +} + +static void +ospf6_hello_recv (struct in6_addr *src, struct in6_addr *dst, + struct ospf6_interface *oi, struct ospf6_header *oh) +{ + struct ospf6_hello *hello; + struct ospf6_neighbor *on; + char *p; + int twoway = 0; + int neighborchange = 0; + int backupseen = 0; + + hello = (struct ospf6_hello *) + ((caddr_t) oh + sizeof (struct ospf6_header)); + + /* HelloInterval check */ + if (ntohs (hello->hello_interval) != oi->hello_interval) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("HelloInterval mismatch"); + return; + } + + /* RouterDeadInterval check */ + if (ntohs (hello->dead_interval) != oi->dead_interval) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("RouterDeadInterval mismatch"); + return; + } + + /* E-bit check */ + if (OSPF6_OPT_ISSET (hello->options, OSPF6_OPT_E) != + OSPF6_OPT_ISSET (oi->area->options, OSPF6_OPT_E)) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("E-bit mismatch"); + return; + } + + /* Find neighbor, create if not exist */ + on = ospf6_neighbor_lookup (oh->router_id, oi); + if (on == NULL) + { + on = ospf6_neighbor_create (oh->router_id, oi); + on->prev_drouter = on->drouter = hello->drouter; + on->prev_bdrouter = on->bdrouter = hello->bdrouter; + on->priority = hello->priority; + } + + /* always override neighbor's source address and ifindex */ + on->ifindex = ntohl (hello->interface_id); + memcpy (&on->linklocal_addr, src, sizeof (struct in6_addr)); + + /* TwoWay check */ + for (p = (char *) ((caddr_t) hello + sizeof (struct ospf6_hello)); + p + sizeof (u_int32_t) <= OSPF6_MESSAGE_END (oh); + p += sizeof (u_int32_t)) + { + u_int32_t *router_id = (u_int32_t *) p; + + if (*router_id == oi->area->ospf6->router_id) + twoway++; + } + + assert (p == OSPF6_MESSAGE_END (oh)); + + /* RouterPriority check */ + if (on->priority != hello->priority) + { + on->priority = hello->priority; + neighborchange++; + } + + /* DR check */ + if (on->drouter != hello->drouter) + { + on->prev_drouter = on->drouter; + on->drouter = hello->drouter; + if (on->prev_drouter == on->router_id || on->drouter == on->router_id) + neighborchange++; + } + + /* BDR check */ + if (on->bdrouter != hello->bdrouter) + { + on->prev_bdrouter = on->bdrouter; + on->bdrouter = hello->bdrouter; + if (on->prev_bdrouter == on->router_id || on->bdrouter == on->router_id) + neighborchange++; + } + + /* BackupSeen check */ + if (oi->state == OSPF6_INTERFACE_WAITING) + { + if (hello->bdrouter == on->router_id) + backupseen++; + else if (hello->drouter == on->router_id && hello->bdrouter == htonl (0)) + backupseen++; + } + + /* Execute neighbor events */ + thread_execute (master, hello_received, on, 0); + if (twoway) + thread_execute (master, twoway_received, on, 0); + else + thread_execute (master, oneway_received, on, 0); + + /* Schedule interface events */ + if (backupseen) + thread_add_event (master, backup_seen, oi, 0); + if (neighborchange) + thread_add_event (master, neighbor_change, oi, 0); +} + +static void +ospf6_dbdesc_recv_master (struct ospf6_header *oh, + struct ospf6_neighbor *on) +{ + struct ospf6_dbdesc *dbdesc; + char *p; + + dbdesc = (struct ospf6_dbdesc *) + ((caddr_t) oh + sizeof (struct ospf6_header)); + + if (on->state < OSPF6_NEIGHBOR_INIT) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Neighbor state less than Init, ignore"); + return; + } + + switch (on->state) + { + case OSPF6_NEIGHBOR_TWOWAY: + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Neighbor state is 2-Way, ignore"); + return; + + case OSPF6_NEIGHBOR_INIT: + thread_execute (master, twoway_received, on, 0); + if (on->state != OSPF6_NEIGHBOR_EXSTART) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Neighbor state is not ExStart, ignore"); + return; + } + /* else fall through to ExStart */ + + case OSPF6_NEIGHBOR_EXSTART: + /* if neighbor obeys us as our slave, schedule negotiation_done + and process LSA Headers. Otherwise, ignore this message */ + if (! CHECK_FLAG (dbdesc->bits, OSPF6_DBDESC_MSBIT) && + ! CHECK_FLAG (dbdesc->bits, OSPF6_DBDESC_IBIT) && + ntohl (dbdesc->seqnum) == on->dbdesc_seqnum) + { + /* execute NegotiationDone */ + thread_execute (master, negotiation_done, on, 0); + + /* Record neighbor options */ + memcpy (on->options, dbdesc->options, sizeof (on->options)); + } + else + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Negotiation failed"); + return; + } + /* fall through to exchange */ + + case OSPF6_NEIGHBOR_EXCHANGE: + if (! memcmp (dbdesc, &on->dbdesc_last, sizeof (struct ospf6_dbdesc))) + { + /* Duplicated DatabaseDescription is dropped by master */ + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Duplicated dbdesc discarded by Master, ignore"); + return; + } + + if (CHECK_FLAG (dbdesc->bits, OSPF6_DBDESC_MSBIT)) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Master/Slave bit mismatch"); + thread_add_event (master, seqnumber_mismatch, on, 0); + return; + } + + if (CHECK_FLAG (dbdesc->bits, OSPF6_DBDESC_IBIT)) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Initialize bit mismatch"); + thread_add_event (master, seqnumber_mismatch, on, 0); + return; + } + + if (memcmp (on->options, dbdesc->options, sizeof (on->options))) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Option field mismatch"); + thread_add_event (master, seqnumber_mismatch, on, 0); + return; + } + + if (ntohl (dbdesc->seqnum) != on->dbdesc_seqnum) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Sequence number mismatch (%#lx expected)", + (u_long) on->dbdesc_seqnum); + thread_add_event (master, seqnumber_mismatch, on, 0); + return; + } + break; + + case OSPF6_NEIGHBOR_LOADING: + case OSPF6_NEIGHBOR_FULL: + if (! memcmp (dbdesc, &on->dbdesc_last, sizeof (struct ospf6_dbdesc))) + { + /* Duplicated DatabaseDescription is dropped by master */ + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Duplicated dbdesc discarded by Master, ignore"); + return; + } + + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Not duplicate dbdesc in state %s", + ospf6_neighbor_state_str[on->state]); + thread_add_event (master, seqnumber_mismatch, on, 0); + return; + + default: + assert (0); + break; + } + + /* Process LSA headers */ + for (p = (char *) ((caddr_t) dbdesc + sizeof (struct ospf6_dbdesc)); + p + sizeof (struct ospf6_lsa_header) <= OSPF6_MESSAGE_END (oh); + p += sizeof (struct ospf6_lsa_header)) + { + struct ospf6_lsa *his, *mine; + struct ospf6_lsdb *lsdb = NULL; + + his = ospf6_lsa_create_headeronly ((struct ospf6_lsa_header *) p); + + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("%s", his->name); + + switch (OSPF6_LSA_SCOPE (his->header->type)) + { + case OSPF6_SCOPE_LINKLOCAL: + lsdb = on->ospf6_if->lsdb; + break; + case OSPF6_SCOPE_AREA: + lsdb = on->ospf6_if->area->lsdb; + break; + case OSPF6_SCOPE_AS: + lsdb = on->ospf6_if->area->ospf6->lsdb; + break; + case OSPF6_SCOPE_RESERVED: + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Ignoring LSA of reserved scope"); + ospf6_lsa_delete (his); + continue; + break; + } + + if (ntohs (his->header->type) == OSPF6_LSTYPE_AS_EXTERNAL && + IS_AREA_STUB (on->ospf6_if->area)) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("SeqNumMismatch (E-bit mismatch), discard"); + ospf6_lsa_delete (his); + thread_add_event (master, seqnumber_mismatch, on, 0); + return; + } + + mine = ospf6_lsdb_lookup (his->header->type, his->header->id, + his->header->adv_router, lsdb); + if (mine == NULL) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Add request (No database copy)"); + 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 (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); + } + + assert (p == OSPF6_MESSAGE_END (oh)); + + /* Increment sequence number */ + on->dbdesc_seqnum ++; + + /* schedule send lsreq */ + if (on->request_list->count && (on->thread_send_lsreq == NULL)) + on->thread_send_lsreq = + thread_add_event (master, ospf6_lsreq_send, on, 0); + + THREAD_OFF (on->thread_send_dbdesc); + + /* More bit check */ + if (! CHECK_FLAG (dbdesc->bits, OSPF6_DBDESC_MBIT) && + ! CHECK_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MBIT)) + thread_add_event (master, exchange_done, on, 0); + else + on->thread_send_dbdesc = + thread_add_event (master, ospf6_dbdesc_send_newone, on, 0); + + /* save last received dbdesc */ + memcpy (&on->dbdesc_last, dbdesc, sizeof (struct ospf6_dbdesc)); +} + +static void +ospf6_dbdesc_recv_slave (struct ospf6_header *oh, + struct ospf6_neighbor *on) +{ + struct ospf6_dbdesc *dbdesc; + char *p; + + dbdesc = (struct ospf6_dbdesc *) + ((caddr_t) oh + sizeof (struct ospf6_header)); + + if (on->state < OSPF6_NEIGHBOR_INIT) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Neighbor state less than Init, ignore"); + return; + } + + switch (on->state) + { + case OSPF6_NEIGHBOR_TWOWAY: + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Neighbor state is 2-Way, ignore"); + return; + + case OSPF6_NEIGHBOR_INIT: + thread_execute (master, twoway_received, on, 0); + if (on->state != OSPF6_NEIGHBOR_EXSTART) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Neighbor state is not ExStart, ignore"); + return; + } + /* else fall through to ExStart */ + + case OSPF6_NEIGHBOR_EXSTART: + /* If the neighbor is Master, act as Slave. Schedule negotiation_done + and process LSA Headers. Otherwise, ignore this message */ + if (CHECK_FLAG (dbdesc->bits, OSPF6_DBDESC_IBIT) && + CHECK_FLAG (dbdesc->bits, OSPF6_DBDESC_MBIT) && + CHECK_FLAG (dbdesc->bits, OSPF6_DBDESC_MSBIT) && + ntohs (oh->length) == sizeof (struct ospf6_header) + + sizeof (struct ospf6_dbdesc)) + { + /* set the master/slave bit to slave */ + UNSET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MSBIT); + + /* set the DD sequence number to one specified by master */ + on->dbdesc_seqnum = ntohl (dbdesc->seqnum); + + /* schedule NegotiationDone */ + thread_execute (master, negotiation_done, on, 0); + + /* Record neighbor options */ + memcpy (on->options, dbdesc->options, sizeof (on->options)); + } + else + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Negotiation failed"); + return; + } + break; + + case OSPF6_NEIGHBOR_EXCHANGE: + if (! memcmp (dbdesc, &on->dbdesc_last, sizeof (struct ospf6_dbdesc))) + { + /* Duplicated DatabaseDescription causes slave to retransmit */ + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Duplicated dbdesc causes retransmit"); + THREAD_OFF (on->thread_send_dbdesc); + on->thread_send_dbdesc = + thread_add_event (master, ospf6_dbdesc_send, on, 0); + return; + } + + if (! CHECK_FLAG (dbdesc->bits, OSPF6_DBDESC_MSBIT)) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Master/Slave bit mismatch"); + thread_add_event (master, seqnumber_mismatch, on, 0); + return; + } + + if (CHECK_FLAG (dbdesc->bits, OSPF6_DBDESC_IBIT)) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Initialize bit mismatch"); + thread_add_event (master, seqnumber_mismatch, on, 0); + return; + } + + if (memcmp (on->options, dbdesc->options, sizeof (on->options))) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Option field mismatch"); + thread_add_event (master, seqnumber_mismatch, on, 0); + return; + } + + if (ntohl (dbdesc->seqnum) != on->dbdesc_seqnum + 1) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Sequence number mismatch (%#lx expected)", + (u_long) on->dbdesc_seqnum + 1); + thread_add_event (master, seqnumber_mismatch, on, 0); + return; + } + break; + + case OSPF6_NEIGHBOR_LOADING: + case OSPF6_NEIGHBOR_FULL: + if (! memcmp (dbdesc, &on->dbdesc_last, sizeof (struct ospf6_dbdesc))) + { + /* Duplicated DatabaseDescription causes slave to retransmit */ + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Duplicated dbdesc causes retransmit"); + THREAD_OFF (on->thread_send_dbdesc); + on->thread_send_dbdesc = + thread_add_event (master, ospf6_dbdesc_send, on, 0); + return; + } + + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Not duplicate dbdesc in state %s", + ospf6_neighbor_state_str[on->state]); + thread_add_event (master, seqnumber_mismatch, on, 0); + return; + + default: + assert (0); + break; + } + + /* Process LSA headers */ + for (p = (char *) ((caddr_t) dbdesc + sizeof (struct ospf6_dbdesc)); + p + sizeof (struct ospf6_lsa_header) <= OSPF6_MESSAGE_END (oh); + p += sizeof (struct ospf6_lsa_header)) + { + struct ospf6_lsa *his, *mine; + struct ospf6_lsdb *lsdb = NULL; + + his = ospf6_lsa_create_headeronly ((struct ospf6_lsa_header *) p); + + switch (OSPF6_LSA_SCOPE (his->header->type)) + { + case OSPF6_SCOPE_LINKLOCAL: + lsdb = on->ospf6_if->lsdb; + break; + case OSPF6_SCOPE_AREA: + lsdb = on->ospf6_if->area->lsdb; + break; + case OSPF6_SCOPE_AS: + lsdb = on->ospf6_if->area->ospf6->lsdb; + break; + case OSPF6_SCOPE_RESERVED: + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Ignoring LSA of reserved scope"); + ospf6_lsa_delete (his); + continue; + break; + } + + if (OSPF6_LSA_SCOPE (his->header->type) == OSPF6_SCOPE_AS && + IS_AREA_STUB (on->ospf6_if->area)) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("E-bit mismatch with LSA Headers"); + ospf6_lsa_delete (his); + thread_add_event (master, seqnumber_mismatch, on, 0); + return; + } + + mine = ospf6_lsdb_lookup (his->header->type, his->header->id, + his->header->adv_router, lsdb); + if (mine == NULL || ospf6_lsa_compare (his, mine) < 0) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Add request-list: %s", his->name); + ospf6_lsdb_add (ospf6_lsa_copy(his), on->request_list); + } + ospf6_lsa_delete (his); + } + + assert (p == OSPF6_MESSAGE_END (oh)); + + /* Set sequence number to Master's */ + on->dbdesc_seqnum = ntohl (dbdesc->seqnum); + + /* schedule send lsreq */ + if ((on->thread_send_lsreq == NULL) && + (on->request_list->count)) + on->thread_send_lsreq = + thread_add_event (master, ospf6_lsreq_send, on, 0); + + THREAD_OFF (on->thread_send_dbdesc); + on->thread_send_dbdesc = + thread_add_event (master, ospf6_dbdesc_send_newone, on, 0); + + /* save last received dbdesc */ + memcpy (&on->dbdesc_last, dbdesc, sizeof (struct ospf6_dbdesc)); +} + +static void +ospf6_dbdesc_recv (struct in6_addr *src, struct in6_addr *dst, + struct ospf6_interface *oi, struct ospf6_header *oh) +{ + struct ospf6_neighbor *on; + struct ospf6_dbdesc *dbdesc; + + on = ospf6_neighbor_lookup (oh->router_id, oi); + if (on == NULL) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Neighbor not found, ignore"); + return; + } + + dbdesc = (struct ospf6_dbdesc *) + ((caddr_t) oh + sizeof (struct ospf6_header)); + + /* Interface MTU check */ + if (!oi->mtu_ignore && ntohs (dbdesc->ifmtu) != oi->ifmtu) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("I/F MTU mismatch"); + return; + } + + if (dbdesc->reserved1 || dbdesc->reserved2) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Non-0 reserved field in %s's DbDesc, correct", + on->name); + dbdesc->reserved1 = 0; + dbdesc->reserved2 = 0; + } + + if (ntohl (oh->router_id) < ntohl (ospf6->router_id)) + ospf6_dbdesc_recv_master (oh, on); + else if (ntohl (ospf6->router_id) < ntohl (oh->router_id)) + ospf6_dbdesc_recv_slave (oh, on); + else + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Can't decide which is master, ignore"); + } +} + +static void +ospf6_lsreq_recv (struct in6_addr *src, struct in6_addr *dst, + struct ospf6_interface *oi, struct ospf6_header *oh) +{ + struct ospf6_neighbor *on; + char *p; + struct ospf6_lsreq_entry *e; + struct ospf6_lsdb *lsdb = NULL; + struct ospf6_lsa *lsa; + + on = ospf6_neighbor_lookup (oh->router_id, oi); + if (on == NULL) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Neighbor not found, ignore"); + return; + } + + if (on->state != OSPF6_NEIGHBOR_EXCHANGE && + on->state != OSPF6_NEIGHBOR_LOADING && + on->state != OSPF6_NEIGHBOR_FULL) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Neighbor state less than Exchange, ignore"); + return; + } + + /* Process each request */ + for (p = (char *) ((caddr_t) oh + sizeof (struct ospf6_header)); + p + sizeof (struct ospf6_lsreq_entry) <= OSPF6_MESSAGE_END (oh); + p += sizeof (struct ospf6_lsreq_entry)) + { + e = (struct ospf6_lsreq_entry *) p; + + switch (OSPF6_LSA_SCOPE (e->type)) + { + case OSPF6_SCOPE_LINKLOCAL: + lsdb = on->ospf6_if->lsdb; + break; + case OSPF6_SCOPE_AREA: + lsdb = on->ospf6_if->area->lsdb; + break; + case OSPF6_SCOPE_AS: + lsdb = on->ospf6_if->area->ospf6->lsdb; + break; + default: + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Ignoring LSA of reserved scope"); + continue; + break; + } + + /* Find database copy */ + lsa = ospf6_lsdb_lookup (e->type, e->id, e->adv_router, lsdb); + if (lsa == NULL) + { + char id[16], adv_router[16]; + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + { + inet_ntop (AF_INET, &e->id, id, sizeof (id)); + inet_ntop (AF_INET, &e->adv_router, adv_router, + sizeof (adv_router)); + zlog_debug ("Can't find requested [%s Id:%s Adv:%s]", + ospf6_lstype_name (e->type), id, adv_router); + } + thread_add_event (master, bad_lsreq, on, 0); + return; + } + + ospf6_lsdb_add (ospf6_lsa_copy (lsa), on->lsupdate_list); + } + + assert (p == OSPF6_MESSAGE_END (oh)); + + /* schedule send lsupdate */ + THREAD_OFF (on->thread_send_lsupdate); + on->thread_send_lsupdate = + thread_add_event (master, ospf6_lsupdate_send_neighbor, on, 0); +} + +/* Verify, that the specified memory area contains exactly N valid IPv6 + prefixes as specified by RFC5340, A.4.1. */ +static unsigned +ospf6_prefixes_examin +( + struct ospf6_prefix *current, /* start of buffer */ + unsigned length, + const u_int32_t req_num_pfxs /* always compared with the actual number of prefixes */ +) +{ + u_char requested_pfx_bytes; + u_int32_t real_num_pfxs = 0; + + while (length) + { + if (length < OSPF6_PREFIX_MIN_SIZE) + { + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) + zlog_debug ("%s: undersized IPv6 prefix header", __func__); + return MSG_NG; + } + /* safe to look deeper */ + if (current->prefix_length > IPV6_MAX_BITLEN) + { + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) + zlog_debug ("%s: invalid PrefixLength (%u bits)", __func__, current->prefix_length); + return MSG_NG; + } + /* covers both fixed- and variable-sized fields */ + requested_pfx_bytes = OSPF6_PREFIX_MIN_SIZE + OSPF6_PREFIX_SPACE (current->prefix_length); + if (requested_pfx_bytes > length) + { + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) + zlog_debug ("%s: undersized IPv6 prefix", __func__); + return MSG_NG; + } + /* next prefix */ + length -= requested_pfx_bytes; + current = (struct ospf6_prefix *) ((caddr_t) current + requested_pfx_bytes); + real_num_pfxs++; + } + if (real_num_pfxs != req_num_pfxs) + { + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) + zlog_debug ("%s: IPv6 prefix number mismatch (%u required, %u real)", + __func__, req_num_pfxs, real_num_pfxs); + return MSG_NG; + } + return MSG_OK; +} + +/* Verify an LSA to have a valid length and dispatch further (where + appropriate) to check if the contents, including nested IPv6 prefixes, + is properly sized/aligned within the LSA. Note that this function gets + LSA type in network byte order, uses in host byte order and passes to + ospf6_lstype_name() in network byte order again. */ +static unsigned +ospf6_lsa_examin (struct ospf6_lsa_header *lsah, const u_int16_t lsalen, const u_char headeronly) +{ + struct ospf6_intra_prefix_lsa *intra_prefix_lsa; + struct ospf6_as_external_lsa *as_external_lsa; + struct ospf6_link_lsa *link_lsa; + unsigned exp_length; + u_int8_t ltindex; + u_int16_t lsatype; + + /* In case an additional minimum length constraint is defined for current + LSA type, make sure that this constraint is met. */ + lsatype = ntohs (lsah->type); + ltindex = lsatype & OSPF6_LSTYPE_FCODE_MASK; + if + ( + ltindex < OSPF6_LSTYPE_SIZE && + ospf6_lsa_minlen[ltindex] && + lsalen < ospf6_lsa_minlen[ltindex] + OSPF6_LSA_HEADER_SIZE + ) + { + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) + zlog_debug ("%s: undersized (%u B) LSA", __func__, lsalen); + return MSG_NG; + } + switch (lsatype) + { + case OSPF6_LSTYPE_ROUTER: + /* RFC5340 A.4.3, LSA header + OSPF6_ROUTER_LSA_MIN_SIZE bytes followed + by N>=0 interface descriptions. */ + if ((lsalen - OSPF6_LSA_HEADER_SIZE - OSPF6_ROUTER_LSA_MIN_SIZE) % OSPF6_ROUTER_LSDESC_FIX_SIZE) + { + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) + zlog_debug ("%s: interface description alignment error", __func__); + return MSG_NG; + } + break; + case OSPF6_LSTYPE_NETWORK: + /* RFC5340 A.4.4, LSA header + OSPF6_NETWORK_LSA_MIN_SIZE bytes + followed by N>=0 attached router descriptions. */ + if ((lsalen - OSPF6_LSA_HEADER_SIZE - OSPF6_NETWORK_LSA_MIN_SIZE) % OSPF6_NETWORK_LSDESC_FIX_SIZE) + { + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) + zlog_debug ("%s: router description alignment error", __func__); + return MSG_NG; + } + break; + case OSPF6_LSTYPE_INTER_PREFIX: + /* RFC5340 A.4.5, LSA header + OSPF6_INTER_PREFIX_LSA_MIN_SIZE bytes + followed by 3-4 fields of a single IPv6 prefix. */ + if (headeronly) + break; + return ospf6_prefixes_examin + ( + (struct ospf6_prefix *) ((caddr_t) lsah + OSPF6_LSA_HEADER_SIZE + OSPF6_INTER_PREFIX_LSA_MIN_SIZE), + lsalen - OSPF6_LSA_HEADER_SIZE - OSPF6_INTER_PREFIX_LSA_MIN_SIZE, + 1 + ); + case OSPF6_LSTYPE_INTER_ROUTER: + /* RFC5340 A.4.6, fixed-size LSA. */ + if (lsalen > OSPF6_LSA_HEADER_SIZE + OSPF6_INTER_ROUTER_LSA_FIX_SIZE) + { + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) + zlog_debug ("%s: oversized (%u B) LSA", __func__, lsalen); + return MSG_NG; + } + break; + case OSPF6_LSTYPE_AS_EXTERNAL: /* RFC5340 A.4.7, same as A.4.8. */ + case OSPF6_LSTYPE_TYPE_7: + /* RFC5340 A.4.8, LSA header + OSPF6_AS_EXTERNAL_LSA_MIN_SIZE bytes + followed by 3-4 fields of IPv6 prefix and 3 conditional LSA fields: + 16 bytes of forwarding address, 4 bytes of external route tag, + 4 bytes of referenced link state ID. */ + if (headeronly) + break; + as_external_lsa = (struct ospf6_as_external_lsa *) ((caddr_t) lsah + OSPF6_LSA_HEADER_SIZE); + exp_length = OSPF6_LSA_HEADER_SIZE + OSPF6_AS_EXTERNAL_LSA_MIN_SIZE; + /* To find out if the last optional field (Referenced Link State ID) is + assumed in this LSA, we need to access fixed fields of the IPv6 + prefix before ospf6_prefix_examin() confirms its sizing. */ + if (exp_length + OSPF6_PREFIX_MIN_SIZE > lsalen) + { + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) + zlog_debug ("%s: undersized (%u B) LSA header", __func__, lsalen); + return MSG_NG; + } + /* forwarding address */ + if (CHECK_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_F)) + exp_length += 16; + /* external route tag */ + if (CHECK_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T)) + exp_length += 4; + /* referenced link state ID */ + if (as_external_lsa->prefix.u._prefix_referenced_lstype) + exp_length += 4; + /* All the fixed-size fields (mandatory and optional) must fit. I.e., + this check does not include any IPv6 prefix fields. */ + if (exp_length > lsalen) + { + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) + zlog_debug ("%s: undersized (%u B) LSA header", __func__, lsalen); + return MSG_NG; + } + /* The last call completely covers the remainder (IPv6 prefix). */ + return ospf6_prefixes_examin + ( + (struct ospf6_prefix *) ((caddr_t) as_external_lsa + OSPF6_AS_EXTERNAL_LSA_MIN_SIZE), + lsalen - exp_length, + 1 + ); + case OSPF6_LSTYPE_LINK: + /* RFC5340 A.4.9, LSA header + OSPF6_LINK_LSA_MIN_SIZE bytes followed + by N>=0 IPv6 prefix blocks (with N declared beforehand). */ + if (headeronly) + break; + link_lsa = (struct ospf6_link_lsa *) ((caddr_t) lsah + OSPF6_LSA_HEADER_SIZE); + return ospf6_prefixes_examin + ( + (struct ospf6_prefix *) ((caddr_t) link_lsa + OSPF6_LINK_LSA_MIN_SIZE), + lsalen - OSPF6_LSA_HEADER_SIZE - OSPF6_LINK_LSA_MIN_SIZE, + ntohl (link_lsa->prefix_num) /* 32 bits */ + ); + case OSPF6_LSTYPE_INTRA_PREFIX: + /* RFC5340 A.4.10, LSA header + OSPF6_INTRA_PREFIX_LSA_MIN_SIZE bytes + followed by N>=0 IPv6 prefixes (with N declared beforehand). */ + if (headeronly) + break; + intra_prefix_lsa = (struct ospf6_intra_prefix_lsa *) ((caddr_t) lsah + OSPF6_LSA_HEADER_SIZE); + return ospf6_prefixes_examin + ( + (struct ospf6_prefix *) ((caddr_t) intra_prefix_lsa + OSPF6_INTRA_PREFIX_LSA_MIN_SIZE), + lsalen - OSPF6_LSA_HEADER_SIZE - OSPF6_INTRA_PREFIX_LSA_MIN_SIZE, + ntohs (intra_prefix_lsa->prefix_num) /* 16 bits */ + ); + } + /* No additional validation is possible for unknown LSA types, which are + themselves valid in OPSFv3, hence the default decision is to accept. */ + return MSG_OK; +} + +/* Verify if the provided input buffer is a valid sequence of LSAs. This + includes verification of LSA blocks length/alignment and dispatching + of deeper-level checks. */ +static unsigned +ospf6_lsaseq_examin +( + struct ospf6_lsa_header *lsah, /* start of buffered data */ + size_t length, + const u_char headeronly, + /* When declared_num_lsas is not 0, compare it to the real number of LSAs + and treat the difference as an error. */ + const u_int32_t declared_num_lsas +) +{ + u_int32_t counted_lsas = 0; + + while (length) + { + u_int16_t lsalen; + if (length < OSPF6_LSA_HEADER_SIZE) + { + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) + zlog_debug ("%s: undersized (%zu B) trailing (#%u) LSA header", + __func__, length, counted_lsas); + return MSG_NG; + } + /* save on ntohs() calls here and in the LSA validator */ + lsalen = OSPF6_LSA_SIZE (lsah); + if (lsalen < OSPF6_LSA_HEADER_SIZE) + { + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) + zlog_debug ("%s: malformed LSA header #%u, declared length is %u B", + __func__, counted_lsas, lsalen); + return MSG_NG; + } + if (headeronly) + { + /* less checks here and in ospf6_lsa_examin() */ + if (MSG_OK != ospf6_lsa_examin (lsah, lsalen, 1)) + { + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) + zlog_debug ("%s: anomaly in header-only %s LSA #%u", __func__, + ospf6_lstype_name (lsah->type), counted_lsas); + return MSG_NG; + } + lsah = (struct ospf6_lsa_header *) ((caddr_t) lsah + OSPF6_LSA_HEADER_SIZE); + length -= OSPF6_LSA_HEADER_SIZE; + } + else + { + /* make sure the input buffer is deep enough before further checks */ + if (lsalen > length) + { + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) + zlog_debug ("%s: anomaly in %s LSA #%u: declared length is %u B, buffered length is %zu B", + __func__, ospf6_lstype_name (lsah->type), counted_lsas, lsalen, length); + return MSG_NG; + } + if (MSG_OK != ospf6_lsa_examin (lsah, lsalen, 0)) + { + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) + zlog_debug ("%s: anomaly in %s LSA #%u", __func__, + ospf6_lstype_name (lsah->type), counted_lsas); + return MSG_NG; + } + lsah = (struct ospf6_lsa_header *) ((caddr_t) lsah + lsalen); + length -= lsalen; + } + counted_lsas++; + } + + if (declared_num_lsas && counted_lsas != declared_num_lsas) + { + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) + zlog_debug ("%s: #LSAs declared (%u) does not match actual (%u)", + __func__, declared_num_lsas, counted_lsas); + return MSG_NG; + } + return MSG_OK; +} + +/* Verify a complete OSPF packet for proper sizing/alignment. */ +static unsigned +ospf6_packet_examin (struct ospf6_header *oh, const unsigned bytesonwire) +{ + struct ospf6_lsupdate *lsupd; + unsigned test; + + /* length, 1st approximation */ + if (bytesonwire < OSPF6_HEADER_SIZE) + { + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) + zlog_debug ("%s: undersized (%u B) packet", __func__, bytesonwire); + return MSG_NG; + } + /* Now it is safe to access header fields. */ + if (bytesonwire != ntohs (oh->length)) + { + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) + zlog_debug ("%s: packet length error (%u real, %u declared)", + __func__, bytesonwire, ntohs (oh->length)); + return MSG_NG; + } + /* version check */ + if (oh->version != OSPFV3_VERSION) + { + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) + zlog_debug ("%s: invalid (%u) protocol version", __func__, oh->version); + return MSG_NG; + } + /* length, 2nd approximation */ + if + ( + oh->type < OSPF6_MESSAGE_TYPE_ALL && + ospf6_packet_minlen[oh->type] && + bytesonwire < OSPF6_HEADER_SIZE + ospf6_packet_minlen[oh->type] + ) + { + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) + zlog_debug ("%s: undersized (%u B) %s packet", __func__, + bytesonwire, LOOKUP (ospf6_message_type_str, oh->type)); + return MSG_NG; + } + /* type-specific deeper validation */ + switch (oh->type) + { + case OSPF6_MESSAGE_TYPE_HELLO: + /* RFC5340 A.3.2, packet header + OSPF6_HELLO_MIN_SIZE bytes followed + by N>=0 router-IDs. */ + if (0 == (bytesonwire - OSPF6_HEADER_SIZE - OSPF6_HELLO_MIN_SIZE) % 4) + return MSG_OK; + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) + zlog_debug ("%s: alignment error in %s packet", + __func__, LOOKUP (ospf6_message_type_str, oh->type)); + return MSG_NG; + case OSPF6_MESSAGE_TYPE_DBDESC: + /* RFC5340 A.3.3, packet header + OSPF6_DB_DESC_MIN_SIZE bytes followed + by N>=0 header-only LSAs. */ + test = ospf6_lsaseq_examin + ( + (struct ospf6_lsa_header *) ((caddr_t) oh + OSPF6_HEADER_SIZE + OSPF6_DB_DESC_MIN_SIZE), + bytesonwire - OSPF6_HEADER_SIZE - OSPF6_DB_DESC_MIN_SIZE, + 1, + 0 + ); + break; + case OSPF6_MESSAGE_TYPE_LSREQ: + /* RFC5340 A.3.4, packet header + N>=0 LS description blocks. */ + if (0 == (bytesonwire - OSPF6_HEADER_SIZE - OSPF6_LS_REQ_MIN_SIZE) % OSPF6_LSREQ_LSDESC_FIX_SIZE) + return MSG_OK; + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) + zlog_debug ("%s: alignment error in %s packet", + __func__, LOOKUP (ospf6_message_type_str, oh->type)); + return MSG_NG; + case OSPF6_MESSAGE_TYPE_LSUPDATE: + /* RFC5340 A.3.5, packet header + OSPF6_LS_UPD_MIN_SIZE bytes followed + by N>=0 full LSAs (with N declared beforehand). */ + lsupd = (struct ospf6_lsupdate *) ((caddr_t) oh + OSPF6_HEADER_SIZE); + test = ospf6_lsaseq_examin + ( + (struct ospf6_lsa_header *) ((caddr_t) lsupd + OSPF6_LS_UPD_MIN_SIZE), + bytesonwire - OSPF6_HEADER_SIZE - OSPF6_LS_UPD_MIN_SIZE, + 0, + ntohl (lsupd->lsa_number) /* 32 bits */ + ); + break; + case OSPF6_MESSAGE_TYPE_LSACK: + /* RFC5340 A.3.6, packet header + N>=0 header-only LSAs. */ + test = ospf6_lsaseq_examin + ( + (struct ospf6_lsa_header *) ((caddr_t) oh + OSPF6_HEADER_SIZE + OSPF6_LS_ACK_MIN_SIZE), + bytesonwire - OSPF6_HEADER_SIZE - OSPF6_LS_ACK_MIN_SIZE, + 1, + 0 + ); + break; + default: + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) + zlog_debug ("%s: invalid (%u) message type", __func__, oh->type); + return MSG_NG; + } + if (test != MSG_OK && IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) + zlog_debug ("%s: anomaly in %s packet", __func__, LOOKUP (ospf6_message_type_str, oh->type)); + return test; +} + +/* Verify particular fields of otherwise correct received OSPF packet to + meet the requirements of RFC. */ +static int +ospf6_rxpacket_examin (struct ospf6_interface *oi, struct ospf6_header *oh, const unsigned bytesonwire) +{ + char buf[2][INET_ADDRSTRLEN]; + + if (MSG_OK != ospf6_packet_examin (oh, bytesonwire)) + return MSG_NG; + + /* Area-ID check */ + if (oh->area_id != oi->area->area_id) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + { + if (oh->area_id == OSPF_AREA_BACKBONE) + zlog_debug ("%s: Message may be via Virtual Link: not supported", __func__); + else + zlog_debug + ( + "%s: Area-ID mismatch (my %s, rcvd %s)", __func__, + inet_ntop (AF_INET, &oi->area->area_id, buf[0], INET_ADDRSTRLEN), + inet_ntop (AF_INET, &oh->area_id, buf[1], INET_ADDRSTRLEN) + ); + } + return MSG_NG; + } + + /* Instance-ID check */ + if (oh->instance_id != oi->instance_id) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("%s: Instance-ID mismatch (my %u, rcvd %u)", __func__, oi->instance_id, oh->instance_id); + return MSG_NG; + } + + /* Router-ID check */ + if (oh->router_id == oi->area->ospf6->router_id) + { + zlog_warn ("%s: Duplicate Router-ID (%s)", __func__, inet_ntop (AF_INET, &oh->router_id, buf[0], INET_ADDRSTRLEN)); + return MSG_NG; + } + return MSG_OK; +} + +static void +ospf6_lsupdate_recv (struct in6_addr *src, struct in6_addr *dst, + struct ospf6_interface *oi, struct ospf6_header *oh) +{ + struct ospf6_neighbor *on; + struct ospf6_lsupdate *lsupdate; + char *p; + + on = ospf6_neighbor_lookup (oh->router_id, oi); + if (on == NULL) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Neighbor not found, ignore"); + return; + } + + if (on->state != OSPF6_NEIGHBOR_EXCHANGE && + on->state != OSPF6_NEIGHBOR_LOADING && + on->state != OSPF6_NEIGHBOR_FULL) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Neighbor state less than Exchange, ignore"); + return; + } + + lsupdate = (struct ospf6_lsupdate *) + ((caddr_t) oh + sizeof (struct ospf6_header)); + + /* Process LSAs */ + for (p = (char *) ((caddr_t) lsupdate + sizeof (struct ospf6_lsupdate)); + p < OSPF6_MESSAGE_END (oh) && + p + OSPF6_LSA_SIZE (p) <= OSPF6_MESSAGE_END (oh); + p += OSPF6_LSA_SIZE (p)) + { + ospf6_receive_lsa (on, (struct ospf6_lsa_header *) p); + } + + assert (p == OSPF6_MESSAGE_END (oh)); + +} + +static void +ospf6_lsack_recv (struct in6_addr *src, struct in6_addr *dst, + struct ospf6_interface *oi, struct ospf6_header *oh) +{ + struct ospf6_neighbor *on; + char *p; + struct ospf6_lsa *his, *mine; + struct ospf6_lsdb *lsdb = NULL; + + assert (oh->type == OSPF6_MESSAGE_TYPE_LSACK); + + on = ospf6_neighbor_lookup (oh->router_id, oi); + if (on == NULL) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Neighbor not found, ignore"); + return; + } + + if (on->state != OSPF6_NEIGHBOR_EXCHANGE && + on->state != OSPF6_NEIGHBOR_LOADING && + on->state != OSPF6_NEIGHBOR_FULL) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Neighbor state less than Exchange, ignore"); + return; + } + + for (p = (char *) ((caddr_t) oh + sizeof (struct ospf6_header)); + p + sizeof (struct ospf6_lsa_header) <= OSPF6_MESSAGE_END (oh); + p += sizeof (struct ospf6_lsa_header)) + { + his = ospf6_lsa_create_headeronly ((struct ospf6_lsa_header *) p); + + switch (OSPF6_LSA_SCOPE (his->header->type)) + { + case OSPF6_SCOPE_LINKLOCAL: + lsdb = on->ospf6_if->lsdb; + break; + case OSPF6_SCOPE_AREA: + lsdb = on->ospf6_if->area->lsdb; + break; + case OSPF6_SCOPE_AS: + lsdb = on->ospf6_if->area->ospf6->lsdb; + break; + case OSPF6_SCOPE_RESERVED: + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Ignoring LSA of reserved scope"); + ospf6_lsa_delete (his); + continue; + break; + } + + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("%s acknowledged by %s", his->name, on->name); + + /* Find database copy */ + mine = ospf6_lsdb_lookup (his->header->type, his->header->id, + his->header->adv_router, lsdb); + if (mine == NULL) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("No database copy"); + ospf6_lsa_delete (his); + continue; + } + + /* Check if the LSA is on his retrans-list */ + mine = ospf6_lsdb_lookup (his->header->type, his->header->id, + his->header->adv_router, on->retrans_list); + if (mine == NULL) + { + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Not on %s's retrans-list", on->name); + ospf6_lsa_delete (his); + continue; + } + + if (ospf6_lsa_compare (his, mine) != 0) + { + /* Log this questionable acknowledgement, + and examine the next one. */ + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Questionable acknowledgement"); + ospf6_lsa_delete (his); + continue; + } + + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + zlog_debug ("Acknowledged, remove from %s's retrans-list", + on->name); + + ospf6_decrement_retrans_count (mine); + if (OSPF6_LSA_IS_MAXAGE (mine)) + ospf6_maxage_remove (on->ospf6_if->area->ospf6); + ospf6_lsdb_remove (mine, on->retrans_list); + ospf6_lsa_delete (his); + } + + assert (p == OSPF6_MESSAGE_END (oh)); +} + +static u_char *recvbuf = NULL; +static u_char *sendbuf = NULL; +static unsigned int iobuflen = 0; + +int +ospf6_iobuf_size (unsigned int size) +{ + u_char *recvnew, *sendnew; + + if (size <= iobuflen) + return iobuflen; + + recvnew = XMALLOC (MTYPE_OSPF6_MESSAGE, size); + sendnew = XMALLOC (MTYPE_OSPF6_MESSAGE, size); + if (recvnew == NULL || sendnew == NULL) + { + if (recvnew) + XFREE (MTYPE_OSPF6_MESSAGE, recvnew); + if (sendnew) + XFREE (MTYPE_OSPF6_MESSAGE, sendnew); + zlog_debug ("Could not allocate I/O buffer of size %d.", size); + return iobuflen; + } + + if (recvbuf) + XFREE (MTYPE_OSPF6_MESSAGE, recvbuf); + if (sendbuf) + XFREE (MTYPE_OSPF6_MESSAGE, sendbuf); + recvbuf = recvnew; + sendbuf = sendnew; + iobuflen = size; + + return iobuflen; +} + +void +ospf6_message_terminate (void) +{ + if (recvbuf) + { + XFREE (MTYPE_OSPF6_MESSAGE, recvbuf); + recvbuf = NULL; + } + + if (sendbuf) + { + XFREE (MTYPE_OSPF6_MESSAGE, sendbuf); + sendbuf = NULL; + } + + iobuflen = 0; +} + +int +ospf6_receive (struct thread *thread) +{ + int sockfd; + unsigned int len; + char srcname[64], dstname[64]; + struct in6_addr src, dst; + ifindex_t ifindex; + struct iovec iovector[2]; + struct ospf6_interface *oi; + struct ospf6_header *oh; + + /* add next read thread */ + sockfd = THREAD_FD (thread); + thread_add_read (master, ospf6_receive, NULL, sockfd); + + /* initialize */ + memset (&src, 0, sizeof (src)); + memset (&dst, 0, sizeof (dst)); + ifindex = 0; + memset (recvbuf, 0, iobuflen); + iovector[0].iov_base = recvbuf; + iovector[0].iov_len = iobuflen; + iovector[1].iov_base = NULL; + iovector[1].iov_len = 0; + + /* receive message */ + len = ospf6_recvmsg (&src, &dst, &ifindex, iovector); + if (len > iobuflen) + { + zlog_err ("Excess message read"); + return 0; + } + + oi = ospf6_interface_lookup_by_ifindex (ifindex); + if (oi == NULL || oi->area == NULL || CHECK_FLAG(oi->flag, OSPF6_INTERFACE_DISABLE)) + { + zlog_debug ("Message received on disabled interface"); + return 0; + } + if (CHECK_FLAG (oi->flag, OSPF6_INTERFACE_PASSIVE)) + { + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) + zlog_debug ("%s: Ignore message on passive interface %s", + __func__, oi->interface->name); + return 0; + } + + oh = (struct ospf6_header *) recvbuf; + if (ospf6_rxpacket_examin (oi, oh, len) != MSG_OK) + return 0; + + /* Being here means, that no sizing/alignment issues were detected in + the input packet. This renders the additional checks performed below + and also in the type-specific dispatching functions a dead code, + which can be dismissed in a cleanup-focused review round later. */ + + /* Log */ + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) + { + inet_ntop (AF_INET6, &src, srcname, sizeof (srcname)); + inet_ntop (AF_INET6, &dst, dstname, sizeof (dstname)); + zlog_debug ("%s received on %s", + LOOKUP (ospf6_message_type_str, oh->type), oi->interface->name); + zlog_debug (" src: %s", srcname); + zlog_debug (" dst: %s", dstname); + + switch (oh->type) + { + case OSPF6_MESSAGE_TYPE_HELLO: + ospf6_hello_print (oh); + break; + case OSPF6_MESSAGE_TYPE_DBDESC: + ospf6_dbdesc_print (oh); + break; + case OSPF6_MESSAGE_TYPE_LSREQ: + ospf6_lsreq_print (oh); + break; + case OSPF6_MESSAGE_TYPE_LSUPDATE: + ospf6_lsupdate_print (oh); + break; + case OSPF6_MESSAGE_TYPE_LSACK: + ospf6_lsack_print (oh); + break; + default: + assert (0); + } + } + + switch (oh->type) + { + case OSPF6_MESSAGE_TYPE_HELLO: + ospf6_hello_recv (&src, &dst, oi, oh); + break; + + case OSPF6_MESSAGE_TYPE_DBDESC: + ospf6_dbdesc_recv (&src, &dst, oi, oh); + break; + + case OSPF6_MESSAGE_TYPE_LSREQ: + ospf6_lsreq_recv (&src, &dst, oi, oh); + break; + + case OSPF6_MESSAGE_TYPE_LSUPDATE: + ospf6_lsupdate_recv (&src, &dst, oi, oh); + break; + + case OSPF6_MESSAGE_TYPE_LSACK: + ospf6_lsack_recv (&src, &dst, oi, oh); + break; + + default: + assert (0); + } + + return 0; +} + +static void +ospf6_send (struct in6_addr *src, struct in6_addr *dst, + struct ospf6_interface *oi, struct ospf6_header *oh) +{ + unsigned int len; + char srcname[64], dstname[64]; + struct iovec iovector[2]; + + /* initialize */ + iovector[0].iov_base = (caddr_t) oh; + iovector[0].iov_len = ntohs (oh->length); + iovector[1].iov_base = NULL; + iovector[1].iov_len = 0; + + /* fill OSPF header */ + oh->version = OSPFV3_VERSION; + /* message type must be set before */ + /* message length must be set before */ + oh->router_id = oi->area->ospf6->router_id; + oh->area_id = oi->area->area_id; + /* checksum is calculated by kernel */ + oh->instance_id = oi->instance_id; + oh->reserved = 0; + + /* Log */ + if (IS_OSPF6_DEBUG_MESSAGE (oh->type, SEND)) + { + inet_ntop (AF_INET6, dst, dstname, sizeof (dstname)); + if (src) + inet_ntop (AF_INET6, src, srcname, sizeof (srcname)); + else + memset (srcname, 0, sizeof (srcname)); + zlog_debug ("%s send on %s", + LOOKUP (ospf6_message_type_str, oh->type), oi->interface->name); + zlog_debug (" src: %s", srcname); + zlog_debug (" dst: %s", dstname); + + switch (oh->type) + { + case OSPF6_MESSAGE_TYPE_HELLO: + ospf6_hello_print (oh); + break; + case OSPF6_MESSAGE_TYPE_DBDESC: + ospf6_dbdesc_print (oh); + break; + case OSPF6_MESSAGE_TYPE_LSREQ: + ospf6_lsreq_print (oh); + break; + case OSPF6_MESSAGE_TYPE_LSUPDATE: + ospf6_lsupdate_print (oh); + break; + case OSPF6_MESSAGE_TYPE_LSACK: + ospf6_lsack_print (oh); + break; + default: + zlog_debug ("Unknown message"); + assert (0); + break; + } + } + + /* send message */ + len = ospf6_sendmsg (src, dst, &oi->interface->ifindex, iovector); + if (len != ntohs (oh->length)) + zlog_err ("Could not send entire message"); +} + +static uint32_t +ospf6_packet_max(struct ospf6_interface *oi) +{ + assert (oi->ifmtu > sizeof (struct ip6_hdr)); + return oi->ifmtu - (sizeof (struct ip6_hdr)); +} + +int +ospf6_hello_send (struct thread *thread) +{ + struct ospf6_interface *oi; + struct ospf6_header *oh; + struct ospf6_hello *hello; + u_char *p; + struct listnode *node, *nnode; + struct ospf6_neighbor *on; + + oi = (struct ospf6_interface *) THREAD_ARG (thread); + oi->thread_send_hello = (struct thread *) NULL; + + if (oi->state <= OSPF6_INTERFACE_DOWN) + { + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_HELLO, SEND)) + zlog_debug ("Unable to send Hello on down interface %s", + oi->interface->name); + 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); + + memset (sendbuf, 0, iobuflen); + oh = (struct ospf6_header *) sendbuf; + hello = (struct ospf6_hello *)((caddr_t) oh + sizeof (struct ospf6_header)); + + hello->interface_id = htonl (oi->interface->ifindex); + hello->priority = oi->priority; + hello->options[0] = oi->area->options[0]; + hello->options[1] = oi->area->options[1]; + hello->options[2] = oi->area->options[2]; + hello->hello_interval = htons (oi->hello_interval); + hello->dead_interval = htons (oi->dead_interval); + hello->drouter = oi->drouter; + hello->bdrouter = oi->bdrouter; + + p = (u_char *)((caddr_t) hello + sizeof (struct ospf6_hello)); + + for (ALL_LIST_ELEMENTS (oi->neighbor_list, node, nnode, on)) + { + if (on->state < OSPF6_NEIGHBOR_INIT) + continue; + + if (p - sendbuf + sizeof (u_int32_t) > ospf6_packet_max(oi)) + { + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_HELLO, SEND)) + zlog_debug ("sending Hello message: exceeds I/F MTU"); + break; + } + + memcpy (p, &on->router_id, sizeof (u_int32_t)); + p += sizeof (u_int32_t); + } + + oh->type = OSPF6_MESSAGE_TYPE_HELLO; + oh->length = htons (p - sendbuf); + + ospf6_send (oi->linklocal_addr, &allspfrouters6, oi, oh); + return 0; +} + +int +ospf6_dbdesc_send (struct thread *thread) +{ + struct ospf6_neighbor *on; + struct ospf6_header *oh; + 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; + + if (on->state < OSPF6_NEIGHBOR_EXSTART) + { + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_DBDESC, SEND)) + zlog_debug ("Quit to send DbDesc to neighbor %s state %s", + on->name, ospf6_neighbor_state_str[on->state]); + return 0; + } + + /* set next thread if master */ + if (CHECK_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MSBIT)) + on->thread_send_dbdesc = + thread_add_timer (master, ospf6_dbdesc_send, on, + on->ospf6_if->rxmt_interval); + + memset (sendbuf, 0, iobuflen); + oh = (struct ospf6_header *) sendbuf; + dbdesc = (struct ospf6_dbdesc *)((caddr_t) oh + + sizeof (struct ospf6_header)); + + /* if this is initial one, initialize sequence number for DbDesc */ + if (CHECK_FLAG (on->dbdesc_bits, OSPF6_DBDESC_IBIT) && + (on->dbdesc_seqnum == 0)) + { + struct timeval tv; + if (quagga_gettime (QUAGGA_CLK_MONOTONIC, &tv) < 0) + tv.tv_sec = 1; + on->dbdesc_seqnum = tv.tv_sec; + } + + dbdesc->options[0] = on->ospf6_if->area->options[0]; + dbdesc->options[1] = on->ospf6_if->area->options[1]; + dbdesc->options[2] = on->ospf6_if->area->options[2]; + dbdesc->ifmtu = htons (on->ospf6_if->ifmtu); + dbdesc->bits = on->dbdesc_bits; + dbdesc->seqnum = htonl (on->dbdesc_seqnum); + + /* if this is not initial one, set LSA headers in dbdesc */ + p = (u_char *)((caddr_t) dbdesc + sizeof (struct ospf6_dbdesc)); + if (! CHECK_FLAG (on->dbdesc_bits, OSPF6_DBDESC_IBIT)) + { + for (lsa = ospf6_lsdb_head (on->dbdesc_list); lsa; + lsa = ospf6_lsdb_next (lsa)) + { + ospf6_lsa_age_update_to_send (lsa, on->ospf6_if->transdelay); + + /* MTU check */ + if (p - sendbuf + sizeof (struct ospf6_lsa_header) > + ospf6_packet_max(on->ospf6_if)) + { + ospf6_lsdb_lsa_unlock (lsa); + break; + } + memcpy (p, lsa->header, sizeof (struct ospf6_lsa_header)); + p += sizeof (struct ospf6_lsa_header); + } + } + + oh->type = OSPF6_MESSAGE_TYPE_DBDESC; + oh->length = htons (p - sendbuf); + + + 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; +} + +int +ospf6_dbdesc_send_newone (struct thread *thread) +{ + struct ospf6_neighbor *on; + struct ospf6_lsa *lsa; + unsigned int size = 0; + + on = (struct ospf6_neighbor *) THREAD_ARG (thread); + ospf6_lsdb_remove_all (on->dbdesc_list); + + /* move LSAs from summary_list to dbdesc_list (within neighbor structure) + so that ospf6_send_dbdesc () can send those LSAs */ + size = sizeof (struct ospf6_lsa_header) + sizeof (struct ospf6_dbdesc); + for (lsa = ospf6_lsdb_head (on->summary_list); lsa; + lsa = ospf6_lsdb_next (lsa)) + { + if (size + sizeof (struct ospf6_lsa_header) > ospf6_packet_max(on->ospf6_if)) + { + ospf6_lsdb_lsa_unlock (lsa); + break; + } + + ospf6_lsdb_add (ospf6_lsa_copy (lsa), on->dbdesc_list); + ospf6_lsdb_remove (lsa, on->summary_list); + size += sizeof (struct ospf6_lsa_header); + } + + if (on->summary_list->count == 0) + UNSET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MBIT); + + /* If slave, More bit check must be done here */ + if (! CHECK_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MSBIT) && /* Slave */ + ! CHECK_FLAG (on->dbdesc_last.bits, OSPF6_DBDESC_MBIT) && + ! CHECK_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MBIT)) + thread_add_event (master, exchange_done, on, 0); + + thread_execute (master, ospf6_dbdesc_send, on, 0); + return 0; +} + +int +ospf6_lsreq_send (struct thread *thread) +{ + struct ospf6_neighbor *on; + struct ospf6_header *oh; + struct ospf6_lsreq_entry *e; + u_char *p; + struct ospf6_lsa *lsa, *last_req; + + on = (struct ospf6_neighbor *) THREAD_ARG (thread); + on->thread_send_lsreq = (struct thread *) NULL; + + /* LSReq will be sent only in ExStart or Loading */ + if (on->state != OSPF6_NEIGHBOR_EXCHANGE && + on->state != OSPF6_NEIGHBOR_LOADING) + { + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_LSREQ, SEND)) + zlog_debug ("Quit to send LSReq to neighbor %s state %s", + on->name, ospf6_neighbor_state_str[on->state]); + return 0; + } + + /* schedule loading_done if request list is empty */ + if (on->request_list->count == 0) + { + thread_add_event (master, loading_done, on, 0); + return 0; + } + + 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)); + for (lsa = ospf6_lsdb_head (on->request_list); lsa; + lsa = ospf6_lsdb_next (lsa)) + { + /* MTU check */ + if (p - sendbuf + sizeof (struct ospf6_lsreq_entry) > ospf6_packet_max(on->ospf6_if)) + { + ospf6_lsdb_lsa_unlock (lsa); + break; + } + + e = (struct ospf6_lsreq_entry *) p; + e->type = lsa->header->type; + 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); + + 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; +} + +int +ospf6_lsupdate_send_neighbor (struct thread *thread) +{ + struct ospf6_neighbor *on; + struct ospf6_header *oh; + struct ospf6_lsupdate *lsupdate; + u_char *p; + int lsa_cnt; + struct ospf6_lsa *lsa; + + on = (struct ospf6_neighbor *) THREAD_ARG (thread); + on->thread_send_lsupdate = (struct thread *) NULL; + + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_LSUPDATE, SEND)) + zlog_debug ("LSUpdate to neighbor %s", on->name); + + if (on->state < OSPF6_NEIGHBOR_EXCHANGE) + { + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_LSUPDATE, SEND)) + zlog_debug ("Quit to send (neighbor state %s)", + ospf6_neighbor_state_str[on->state]); + 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)); + lsa_cnt = 0; + + /* lsupdate_list lists those LSA which doesn't need to be + retransmitted. remove those from the list */ + for (lsa = ospf6_lsdb_head (on->lsupdate_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_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); + 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_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); + lsa_cnt++; + } + + 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) + 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); + } + + 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; +} + +int +ospf6_lsupdate_send_interface (struct thread *thread) +{ + struct ospf6_interface *oi; + struct ospf6_header *oh; + struct ospf6_lsupdate *lsupdate; + u_char *p; + int lsa_cnt; + struct ospf6_lsa *lsa; + + oi = (struct ospf6_interface *) THREAD_ARG (thread); + oi->thread_send_lsupdate = (struct thread *) NULL; + + if (oi->state <= OSPF6_INTERFACE_WAITING) + { + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_LSUPDATE, SEND)) + zlog_debug ("Quit to send LSUpdate to interface %s state %s", + oi->interface->name, ospf6_interface_state_str[oi->state]); + return 0; + } + + /* if we have nothing to send, return */ + if (oi->lsupdate_list->count == 0) + 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)); + 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_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); + lsa_cnt++; + + assert (lsa->lock == 2); + ospf6_lsdb_remove (lsa, oi->lsupdate_list); + } + + if (lsa_cnt) + { + lsupdate->lsa_number = htonl (lsa_cnt); + + oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE; + oh->length = htons (p - sendbuf); + + 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) + { + oi->thread_send_lsupdate = + thread_add_event (master, ospf6_lsupdate_send_interface, oi, 0); + } + + return 0; +} + +int +ospf6_lsack_send_neighbor (struct thread *thread) +{ + struct ospf6_neighbor *on; + 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; + + if (on->state < OSPF6_NEIGHBOR_EXCHANGE) + { + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_LSACK, SEND)) + zlog_debug ("Quit to send LSAck to neighbor %s state %s", + on->name, ospf6_neighbor_state_str[on->state]); + return 0; + } + + /* if we have nothing to send, return */ + if (on->lsack_list->count == 0) + return 0; + + memset (sendbuf, 0, iobuflen); + oh = (struct ospf6_header *) sendbuf; + + p = (u_char *)((caddr_t) oh + sizeof (struct ospf6_header)); + + for (lsa = ospf6_lsdb_head (on->lsack_list); lsa; + lsa = ospf6_lsdb_next (lsa)) + { + /* 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); + + 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)); + p += sizeof (struct ospf6_lsa_header); + + assert (lsa->lock == 2); + ospf6_lsdb_remove (lsa, on->lsack_list); + lsa_cnt++; + } + + 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); + } + + return 0; +} + +int +ospf6_lsack_send_interface (struct thread *thread) +{ + struct ospf6_interface *oi; + 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; + + if (oi->state <= OSPF6_INTERFACE_WAITING) + { + if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_LSACK, SEND)) + zlog_debug ("Quit to send LSAck to interface %s state %s", + oi->interface->name, ospf6_interface_state_str[oi->state]); + return 0; + } + + /* if we have nothing to send, return */ + if (oi->lsack_list->count == 0) + return 0; + + memset (sendbuf, 0, iobuflen); + oh = (struct ospf6_header *) sendbuf; + + p = (u_char *)((caddr_t) oh + sizeof (struct ospf6_header)); + + for (lsa = ospf6_lsdb_head (oi->lsack_list); lsa; + lsa = ospf6_lsdb_next (lsa)) + { + /* 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); + + ospf6_lsdb_lsa_unlock (lsa); + break; + } + + ospf6_lsa_age_update_to_send (lsa, oi->transdelay); + memcpy (p, lsa->header, sizeof (struct ospf6_lsa_header)); + p += sizeof (struct ospf6_lsa_header); + + assert (lsa->lock == 2); + ospf6_lsdb_remove (lsa, oi->lsack_list); + lsa_cnt++; + } + + if (lsa_cnt) + { + oh->type = OSPF6_MESSAGE_TYPE_LSACK; + oh->length = htons (p - sendbuf); + + 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) + { + oi->thread_send_lsack = + thread_add_event (master, ospf6_lsack_send_interface, oi, 0); + } + + return 0; +} + + +/* Commands */ +DEFUN (debug_ospf6_message, + debug_ospf6_message_cmd, + "debug ospf6 message (unknown|hello|dbdesc|lsreq|lsupdate|lsack|all)", + DEBUG_STR + OSPF6_STR + "Debug OSPFv3 message\n" + "Debug Unknown message\n" + "Debug Hello message\n" + "Debug Database Description message\n" + "Debug Link State Request message\n" + "Debug Link State Update message\n" + "Debug Link State Acknowledgement message\n" + "Debug All message\n" + ) +{ + unsigned char level = 0; + int type = 0; + int i; + + assert (argc > 0); + + /* check type */ + if (! strncmp (argv[0], "u", 1)) + type = OSPF6_MESSAGE_TYPE_UNKNOWN; + else if (! strncmp (argv[0], "h", 1)) + type = OSPF6_MESSAGE_TYPE_HELLO; + else if (! strncmp (argv[0], "d", 1)) + type = OSPF6_MESSAGE_TYPE_DBDESC; + else if (! strncmp (argv[0], "lsr", 3)) + type = OSPF6_MESSAGE_TYPE_LSREQ; + else if (! strncmp (argv[0], "lsu", 3)) + type = OSPF6_MESSAGE_TYPE_LSUPDATE; + else if (! strncmp (argv[0], "lsa", 3)) + type = OSPF6_MESSAGE_TYPE_LSACK; + else if (! strncmp (argv[0], "a", 1)) + type = OSPF6_MESSAGE_TYPE_ALL; + + if (argc == 1) + level = OSPF6_DEBUG_MESSAGE_SEND | OSPF6_DEBUG_MESSAGE_RECV; + else if (! strncmp (argv[1], "s", 1)) + level = OSPF6_DEBUG_MESSAGE_SEND; + else if (! strncmp (argv[1], "r", 1)) + level = OSPF6_DEBUG_MESSAGE_RECV; + + if (type == OSPF6_MESSAGE_TYPE_ALL) + { + for (i = 0; i < 6; i++) + OSPF6_DEBUG_MESSAGE_ON (i, level); + } + else + OSPF6_DEBUG_MESSAGE_ON (type, level); + + return CMD_SUCCESS; +} + +ALIAS (debug_ospf6_message, + debug_ospf6_message_sendrecv_cmd, + "debug ospf6 message (unknown|hello|dbdesc|lsreq|lsupdate|lsack|all) (send|recv)", + DEBUG_STR + OSPF6_STR + "Debug OSPFv3 message\n" + "Debug Unknown message\n" + "Debug Hello message\n" + "Debug Database Description message\n" + "Debug Link State Request message\n" + "Debug Link State Update message\n" + "Debug Link State Acknowledgement message\n" + "Debug All message\n" + "Debug only sending message\n" + "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)", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug OSPFv3 message\n" + "Debug Unknown message\n" + "Debug Hello message\n" + "Debug Database Description message\n" + "Debug Link State Request message\n" + "Debug Link State Update message\n" + "Debug Link State Acknowledgement message\n" + "Debug All message\n" + ) +{ + unsigned char level = 0; + int type = 0; + int i; + + assert (argc > 0); + + /* check type */ + if (! strncmp (argv[0], "u", 1)) + type = OSPF6_MESSAGE_TYPE_UNKNOWN; + else if (! strncmp (argv[0], "h", 1)) + type = OSPF6_MESSAGE_TYPE_HELLO; + else if (! strncmp (argv[0], "d", 1)) + type = OSPF6_MESSAGE_TYPE_DBDESC; + else if (! strncmp (argv[0], "lsr", 3)) + type = OSPF6_MESSAGE_TYPE_LSREQ; + else if (! strncmp (argv[0], "lsu", 3)) + type = OSPF6_MESSAGE_TYPE_LSUPDATE; + else if (! strncmp (argv[0], "lsa", 3)) + type = OSPF6_MESSAGE_TYPE_LSACK; + else if (! strncmp (argv[0], "a", 1)) + type = OSPF6_MESSAGE_TYPE_ALL; + + if (argc == 1) + level = OSPF6_DEBUG_MESSAGE_SEND | OSPF6_DEBUG_MESSAGE_RECV; + else if (! strncmp (argv[1], "s", 1)) + level = OSPF6_DEBUG_MESSAGE_SEND; + else if (! strncmp (argv[1], "r", 1)) + level = OSPF6_DEBUG_MESSAGE_RECV; + + if (type == OSPF6_MESSAGE_TYPE_ALL) + { + for (i = 0; i < 6; i++) + OSPF6_DEBUG_MESSAGE_OFF (i, level); + } + else + OSPF6_DEBUG_MESSAGE_OFF (type, level); + + return CMD_SUCCESS; +} + +ALIAS (no_debug_ospf6_message, + no_debug_ospf6_message_sendrecv_cmd, + "no debug ospf6 message " + "(unknown|hello|dbdesc|lsreq|lsupdate|lsack|all) (send|recv)", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug OSPFv3 message\n" + "Debug Unknown message\n" + "Debug Hello message\n" + "Debug Database Description message\n" + "Debug Link State Request message\n" + "Debug Link State Update message\n" + "Debug Link State Acknowledgement message\n" + "Debug All message\n" + "Debug only sending message\n" + "Debug only receiving message\n" + ) + +int +config_write_ospf6_debug_message (struct vty *vty) +{ + const char *type_str[] = {"unknown", "hello", "dbdesc", + "lsreq", "lsupdate", "lsack"}; + unsigned char s = 0, r = 0; + int i; + + for (i = 0; i < 6; i++) + { + if (IS_OSPF6_DEBUG_MESSAGE (i, SEND)) + s |= 1 << i; + if (IS_OSPF6_DEBUG_MESSAGE (i, RECV)) + r |= 1 << i; + } + + if (s == 0x3f && r == 0x3f) + { + vty_out (vty, "debug ospf6 message all%s", VNL); + return 0; + } + + if (s == 0x3f && r == 0) + { + vty_out (vty, "debug ospf6 message all send%s", VNL); + return 0; + } + else if (s == 0 && r == 0x3f) + { + vty_out (vty, "debug ospf6 message all recv%s", VNL); + return 0; + } + + /* Unknown message is logged by default */ + if (! IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, SEND) && + ! IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) + vty_out (vty, "no debug ospf6 message unknown%s", VNL); + else if (! IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, SEND)) + vty_out (vty, "no debug ospf6 message unknown send%s", VNL); + else if (! IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) + vty_out (vty, "no debug ospf6 message unknown recv%s", VNL); + + for (i = 1; i < 6; i++) + { + if (IS_OSPF6_DEBUG_MESSAGE (i, SEND) && + IS_OSPF6_DEBUG_MESSAGE (i, RECV)) + vty_out (vty, "debug ospf6 message %s%s", type_str[i], VNL); + else if (IS_OSPF6_DEBUG_MESSAGE (i, SEND)) + vty_out (vty, "debug ospf6 message %s send%s", type_str[i], + VNL); + else if (IS_OSPF6_DEBUG_MESSAGE (i, RECV)) + vty_out (vty, "debug ospf6 message %s recv%s", type_str[i], + VNL); + } + + return 0; +} + +void +install_element_ospf6_debug_message (void) +{ + install_element (ENABLE_NODE, &debug_ospf6_message_cmd); + install_element (ENABLE_NODE, &no_debug_ospf6_message_cmd); + install_element (ENABLE_NODE, &debug_ospf6_message_sendrecv_cmd); + install_element (ENABLE_NODE, &no_debug_ospf6_message_sendrecv_cmd); + install_element (CONFIG_NODE, &debug_ospf6_message_cmd); + install_element (CONFIG_NODE, &no_debug_ospf6_message_cmd); + install_element (CONFIG_NODE, &debug_ospf6_message_sendrecv_cmd); + install_element (CONFIG_NODE, &no_debug_ospf6_message_sendrecv_cmd); +} + + diff --git a/ospf6d/ospf6_message.h b/ospf6d/ospf6_message.h new file mode 100644 index 0000000..b085a96 --- /dev/null +++ b/ospf6d/ospf6_message.h @@ -0,0 +1,142 @@ +/* + * Copyright (C) 1999-2003 Yasuhiro Ohara + * + * 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 OSPF6_MESSAGE_H +#define OSPF6_MESSAGE_H + +#define OSPF6_MESSAGE_BUFSIZ 4096 + +/* Debug option */ +extern unsigned char conf_debug_ospf6_message[]; +#define OSPF6_DEBUG_MESSAGE_SEND 0x01 +#define OSPF6_DEBUG_MESSAGE_RECV 0x02 +#define OSPF6_DEBUG_MESSAGE_ON(type, level) \ + (conf_debug_ospf6_message[type] |= (level)) +#define OSPF6_DEBUG_MESSAGE_OFF(type, level) \ + (conf_debug_ospf6_message[type] &= ~(level)) +#define IS_OSPF6_DEBUG_MESSAGE(t, e) \ + (conf_debug_ospf6_message[t] & OSPF6_DEBUG_MESSAGE_ ## e) + +/* Type */ +#define OSPF6_MESSAGE_TYPE_UNKNOWN 0x0 +#define OSPF6_MESSAGE_TYPE_HELLO 0x1 /* Discover/maintain neighbors */ +#define OSPF6_MESSAGE_TYPE_DBDESC 0x2 /* Summarize database contents */ +#define OSPF6_MESSAGE_TYPE_LSREQ 0x3 /* Database download request */ +#define OSPF6_MESSAGE_TYPE_LSUPDATE 0x4 /* Database update */ +#define OSPF6_MESSAGE_TYPE_LSACK 0x5 /* Flooding acknowledgment */ +#define OSPF6_MESSAGE_TYPE_ALL 0x6 /* For debug option */ + +/* OSPFv3 packet header */ +#define OSPF6_HEADER_SIZE 16U +struct ospf6_header +{ + u_char version; + u_char type; + u_int16_t length; + u_int32_t router_id; + u_int32_t area_id; + u_int16_t checksum; + u_char instance_id; + u_char reserved; +}; + +#define OSPF6_MESSAGE_END(H) ((caddr_t) (H) + ntohs ((H)->length)) + +/* Hello */ +#define OSPF6_HELLO_MIN_SIZE 20U +struct ospf6_hello +{ + u_int32_t interface_id; + u_char priority; + u_char options[3]; + u_int16_t hello_interval; + u_int16_t dead_interval; + u_int32_t drouter; + u_int32_t bdrouter; + /* Followed by Router-IDs */ +}; + +/* Database Description */ +#define OSPF6_DB_DESC_MIN_SIZE 12U +struct ospf6_dbdesc +{ + u_char reserved1; + u_char options[3]; + u_int16_t ifmtu; + u_char reserved2; + u_char bits; + u_int32_t seqnum; + /* Followed by LSA Headers */ +}; + +#define OSPF6_DBDESC_MSBIT (0x01) /* master/slave bit */ +#define OSPF6_DBDESC_MBIT (0x02) /* more bit */ +#define OSPF6_DBDESC_IBIT (0x04) /* initial bit */ + +/* Link State Request */ +#define OSPF6_LS_REQ_MIN_SIZE 0U +/* It is just a sequence of entries below */ +#define OSPF6_LSREQ_LSDESC_FIX_SIZE 12U +struct ospf6_lsreq_entry +{ + u_int16_t reserved; /* Must Be Zero */ + u_int16_t type; /* LS type */ + u_int32_t id; /* Link State ID */ + u_int32_t adv_router; /* Advertising Router */ +}; + +/* Link State Update */ +#define OSPF6_LS_UPD_MIN_SIZE 4U +struct ospf6_lsupdate +{ + u_int32_t lsa_number; + /* Followed by LSAs */ +}; + +/* Link State Acknowledgement */ +#define OSPF6_LS_ACK_MIN_SIZE 0U +/* It is just a sequence of LSA Headers */ + +/* Function definition */ +extern void ospf6_hello_print (struct ospf6_header *); +extern void ospf6_dbdesc_print (struct ospf6_header *); +extern void ospf6_lsreq_print (struct ospf6_header *); +extern void ospf6_lsupdate_print (struct ospf6_header *); +extern void ospf6_lsack_print (struct ospf6_header *); + +extern int ospf6_iobuf_size (unsigned int size); +extern void ospf6_message_terminate (void); +extern int ospf6_receive (struct thread *thread); + +extern int ospf6_hello_send (struct thread *thread); +extern int ospf6_dbdesc_send (struct thread *thread); +extern int ospf6_dbdesc_send_newone (struct thread *thread); +extern int ospf6_lsreq_send (struct thread *thread); +extern int ospf6_lsupdate_send_interface (struct thread *thread); +extern int ospf6_lsupdate_send_neighbor (struct thread *thread); +extern int ospf6_lsack_send_interface (struct thread *thread); +extern int ospf6_lsack_send_neighbor (struct thread *thread); + +extern int config_write_ospf6_debug_message (struct vty *); +extern void install_element_ospf6_debug_message (void); + +#endif /* OSPF6_MESSAGE_H */ + diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c new file mode 100644 index 0000000..fbad62a --- /dev/null +++ b/ospf6d/ospf6_neighbor.c @@ -0,0 +1,1036 @@ +/* + * Copyright (C) 2003 Yasuhiro Ohara + * + * 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 "memory.h" +#include "thread.h" +#include "linklist.h" +#include "vty.h" +#include "command.h" + +#include "ospf6_proto.h" +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" +#include "ospf6_message.h" +#include "ospf6_top.h" +#include "ospf6_area.h" +#include "ospf6_interface.h" +#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; + +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) +{ + struct ospf6_neighbor *ona = (struct ospf6_neighbor *) va; + struct ospf6_neighbor *onb = (struct ospf6_neighbor *) vb; + return (ntohl (ona->router_id) < ntohl (onb->router_id) ? -1 : 1); +} + +struct ospf6_neighbor * +ospf6_neighbor_lookup (u_int32_t router_id, + struct ospf6_interface *oi) +{ + struct listnode *n; + struct ospf6_neighbor *on; + + for (ALL_LIST_ELEMENTS_RO (oi->neighbor_list, n, on)) + if (on->router_id == router_id) + return on; + + return (struct ospf6_neighbor *) NULL; +} + +/* create ospf6_neighbor */ +struct ospf6_neighbor * +ospf6_neighbor_create (u_int32_t router_id, struct ospf6_interface *oi) +{ + struct ospf6_neighbor *on; + char buf[16]; + + on = (struct ospf6_neighbor *) + XMALLOC (MTYPE_OSPF6_NEIGHBOR, sizeof (struct ospf6_neighbor)); + if (on == NULL) + { + zlog_warn ("neighbor: malloc failed"); + return NULL; + } + + memset (on, 0, sizeof (struct ospf6_neighbor)); + inet_ntop (AF_INET, &router_id, buf, sizeof (buf)); + snprintf (on->name, sizeof (on->name), "%s%%%s", + 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; + + on->summary_list = ospf6_lsdb_create (on); + on->request_list = ospf6_lsdb_create (on); + on->retrans_list = ospf6_lsdb_create (on); + + on->dbdesc_list = ospf6_lsdb_create (on); + on->lsupdate_list = ospf6_lsdb_create (on); + on->lsack_list = ospf6_lsdb_create (on); + + listnode_add_sort (oi->neighbor_list, on); + return on; +} + +void +ospf6_neighbor_delete (struct ospf6_neighbor *on) +{ + struct ospf6_lsa *lsa; + + ospf6_lsdb_remove_all (on->summary_list); + ospf6_lsdb_remove_all (on->request_list); + for (lsa = ospf6_lsdb_head (on->retrans_list); lsa; + lsa = ospf6_lsdb_next (lsa)) + { + ospf6_decrement_retrans_count (lsa); + ospf6_lsdb_remove (lsa, on->retrans_list); + } + + ospf6_lsdb_remove_all (on->dbdesc_list); + ospf6_lsdb_remove_all (on->lsupdate_list); + ospf6_lsdb_remove_all (on->lsack_list); + + ospf6_lsdb_delete (on->summary_list); + ospf6_lsdb_delete (on->request_list); + ospf6_lsdb_delete (on->retrans_list); + + ospf6_lsdb_delete (on->dbdesc_list); + ospf6_lsdb_delete (on->lsupdate_list); + ospf6_lsdb_delete (on->lsack_list); + + THREAD_OFF (on->inactivity_timer); + + THREAD_OFF (on->thread_send_dbdesc); + THREAD_OFF (on->thread_send_lsreq); + THREAD_OFF (on->thread_send_lsupdate); + THREAD_OFF (on->thread_send_lsack); + + XFREE (MTYPE_OSPF6_NEIGHBOR, on); +} + +static void +ospf6_neighbor_state_change (u_char next_state, struct ospf6_neighbor *on, int event) +{ + u_char prev_state; + + prev_state = on->state; + on->state = next_state; + + 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] (%s)", on->name, + ospf6_neighbor_state_str[prev_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); + if (on->ospf6_if->state == OSPF6_INTERFACE_DR) + { + OSPF6_NETWORK_LSA_SCHEDULE (on->ospf6_if); + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT (on->ospf6_if); + } + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB (on->ospf6_if->area); + } + + if ((prev_state == OSPF6_NEIGHBOR_EXCHANGE || + prev_state == OSPF6_NEIGHBOR_LOADING) && + (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 */ +static int +need_adjacency (struct ospf6_neighbor *on) +{ + if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT || + on->ospf6_if->state == OSPF6_INTERFACE_DR || + on->ospf6_if->state == OSPF6_INTERFACE_BDR) + return 1; + + if (on->ospf6_if->drouter == on->router_id || + on->ospf6_if->bdrouter == on->router_id) + return 1; + + return 0; +} + +int +hello_received (struct thread *thread) +{ + struct ospf6_neighbor *on; + + on = (struct ospf6_neighbor *) THREAD_ARG (thread); + assert (on); + + if (IS_OSPF6_DEBUG_NEIGHBOR (EVENT)) + zlog_debug ("Neighbor Event %s: *HelloReceived*", on->name); + + /* reset Inactivity Timer */ + THREAD_OFF (on->inactivity_timer); + on->inactivity_timer = thread_add_timer (master, inactivity_timer, on, + on->ospf6_if->dead_interval); + + if (on->state <= OSPF6_NEIGHBOR_DOWN) + ospf6_neighbor_state_change (OSPF6_NEIGHBOR_INIT, on, + OSPF6_NEIGHBOR_EVENT_HELLO_RCVD); + + return 0; +} + +int +twoway_received (struct thread *thread) +{ + struct ospf6_neighbor *on; + + on = (struct ospf6_neighbor *) THREAD_ARG (thread); + assert (on); + + if (on->state > OSPF6_NEIGHBOR_INIT) + return 0; + + if (IS_OSPF6_DEBUG_NEIGHBOR (EVENT)) + zlog_debug ("Neighbor Event %s: *2Way-Received*", on->name); + + thread_add_event (master, neighbor_change, on->ospf6_if, 0); + + if (! need_adjacency (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_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); + + THREAD_OFF (on->thread_send_dbdesc); + on->thread_send_dbdesc = + thread_add_event (master, ospf6_dbdesc_send, on, 0); + + return 0; +} + +int +negotiation_done (struct thread *thread) +{ + struct ospf6_neighbor *on; + struct ospf6_lsa *lsa; + + on = (struct ospf6_neighbor *) THREAD_ARG (thread); + assert (on); + + if (on->state != OSPF6_NEIGHBOR_EXSTART) + return 0; + + if (IS_OSPF6_DEBUG_NEIGHBOR (EVENT)) + zlog_debug ("Neighbor Event %s: *NegotiationDone*", on->name); + + /* clear ls-list */ + ospf6_lsdb_remove_all (on->summary_list); + ospf6_lsdb_remove_all (on->request_list); + for (lsa = ospf6_lsdb_head (on->retrans_list); lsa; + lsa = ospf6_lsdb_next (lsa)) + { + ospf6_decrement_retrans_count (lsa); + ospf6_lsdb_remove (lsa, on->retrans_list); + } + + /* Interface scoped LSAs */ + for (lsa = ospf6_lsdb_head (on->ospf6_if->lsdb); lsa; + lsa = ospf6_lsdb_next (lsa)) + { + if (OSPF6_LSA_IS_MAXAGE (lsa)) + { + ospf6_increment_retrans_count (lsa); + ospf6_lsdb_add (ospf6_lsa_copy (lsa), on->retrans_list); + } + else + ospf6_lsdb_add (ospf6_lsa_copy (lsa), on->summary_list); + } + + /* Area scoped LSAs */ + for (lsa = ospf6_lsdb_head (on->ospf6_if->area->lsdb); lsa; + lsa = ospf6_lsdb_next (lsa)) + { + if (OSPF6_LSA_IS_MAXAGE (lsa)) + { + ospf6_increment_retrans_count (lsa); + ospf6_lsdb_add (ospf6_lsa_copy (lsa), on->retrans_list); + } + else + ospf6_lsdb_add (ospf6_lsa_copy (lsa), on->summary_list); + } + + /* AS scoped LSAs */ + for (lsa = ospf6_lsdb_head (on->ospf6_if->area->ospf6->lsdb); lsa; + lsa = ospf6_lsdb_next (lsa)) + { + if (OSPF6_LSA_IS_MAXAGE (lsa)) + { + ospf6_increment_retrans_count (lsa); + ospf6_lsdb_add (ospf6_lsa_copy (lsa), on->retrans_list); + } + else + ospf6_lsdb_add (ospf6_lsa_copy (lsa), on->summary_list); + } + + UNSET_FLAG (on->dbdesc_bits, OSPF6_DBDESC_IBIT); + ospf6_neighbor_state_change (OSPF6_NEIGHBOR_EXCHANGE, on, + OSPF6_NEIGHBOR_EVENT_NEGOTIATION_DONE); + + return 0; +} + +int +exchange_done (struct thread *thread) +{ + struct ospf6_neighbor *on; + + on = (struct ospf6_neighbor *) THREAD_ARG (thread); + assert (on); + + if (on->state != OSPF6_NEIGHBOR_EXCHANGE) + return 0; + + if (IS_OSPF6_DEBUG_NEIGHBOR (EVENT)) + zlog_debug ("Neighbor Event %s: *ExchangeDone*", on->name); + + THREAD_OFF (on->thread_send_dbdesc); + ospf6_lsdb_remove_all (on->dbdesc_list); + +/* XXX + thread_add_timer (master, ospf6_neighbor_last_dbdesc_release, on, + on->ospf6_if->dead_interval); +*/ + + if (on->request_list->count == 0) + ospf6_neighbor_state_change (OSPF6_NEIGHBOR_FULL, on, + OSPF6_NEIGHBOR_EVENT_EXCHANGE_DONE); + else + { + 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) +{ + struct ospf6_neighbor *on; + + on = (struct ospf6_neighbor *) THREAD_ARG (thread); + assert (on); + + if (on->state != OSPF6_NEIGHBOR_LOADING) + return 0; + + if (IS_OSPF6_DEBUG_NEIGHBOR (EVENT)) + zlog_debug ("Neighbor Event %s: *LoadingDone*", on->name); + + assert (on->request_list->count == 0); + + ospf6_neighbor_state_change (OSPF6_NEIGHBOR_FULL, on, + OSPF6_NEIGHBOR_EVENT_LOADING_DONE); + + return 0; +} + +int +adj_ok (struct thread *thread) +{ + struct ospf6_neighbor *on; + struct ospf6_lsa *lsa; + + on = (struct ospf6_neighbor *) THREAD_ARG (thread); + assert (on); + + if (IS_OSPF6_DEBUG_NEIGHBOR (EVENT)) + zlog_debug ("Neighbor Event %s: *AdjOK?*", on->name); + + if (on->state == OSPF6_NEIGHBOR_TWOWAY && need_adjacency (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); + + THREAD_OFF (on->thread_send_dbdesc); + on->thread_send_dbdesc = + thread_add_event (master, ospf6_dbdesc_send, on, 0); + + } + else if (on->state >= OSPF6_NEIGHBOR_EXSTART && + ! need_adjacency (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; + lsa = ospf6_lsdb_next (lsa)) + { + ospf6_decrement_retrans_count (lsa); + ospf6_lsdb_remove (lsa, on->retrans_list); + } + } + + return 0; +} + +int +seqnumber_mismatch (struct thread *thread) +{ + struct ospf6_neighbor *on; + struct ospf6_lsa *lsa; + + on = (struct ospf6_neighbor *) THREAD_ARG (thread); + assert (on); + + if (on->state < OSPF6_NEIGHBOR_EXCHANGE) + return 0; + + if (IS_OSPF6_DEBUG_NEIGHBOR (EVENT)) + zlog_debug ("Neighbor Event %s: *SeqNumberMismatch*", on->name); + + 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); + + ospf6_lsdb_remove_all (on->summary_list); + ospf6_lsdb_remove_all (on->request_list); + for (lsa = ospf6_lsdb_head (on->retrans_list); lsa; + lsa = ospf6_lsdb_next (lsa)) + { + ospf6_decrement_retrans_count (lsa); + ospf6_lsdb_remove (lsa, on->retrans_list); + } + + 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); + + return 0; +} + +int +bad_lsreq (struct thread *thread) +{ + struct ospf6_neighbor *on; + struct ospf6_lsa *lsa; + + on = (struct ospf6_neighbor *) THREAD_ARG (thread); + assert (on); + + if (on->state < OSPF6_NEIGHBOR_EXCHANGE) + return 0; + + if (IS_OSPF6_DEBUG_NEIGHBOR (EVENT)) + zlog_debug ("Neighbor Event %s: *BadLSReq*", on->name); + + 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); + + ospf6_lsdb_remove_all (on->summary_list); + ospf6_lsdb_remove_all (on->request_list); + for (lsa = ospf6_lsdb_head (on->retrans_list); lsa; + lsa = ospf6_lsdb_next (lsa)) + { + ospf6_decrement_retrans_count (lsa); + ospf6_lsdb_remove (lsa, on->retrans_list); + } + + 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); + + return 0; +} + +int +oneway_received (struct thread *thread) +{ + struct ospf6_neighbor *on; + struct ospf6_lsa *lsa; + + on = (struct ospf6_neighbor *) THREAD_ARG (thread); + assert (on); + + if (on->state < OSPF6_NEIGHBOR_TWOWAY) + return 0; + + 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_EVENT_ONEWAY_RCVD); + thread_add_event (master, neighbor_change, on->ospf6_if, 0); + + ospf6_lsdb_remove_all (on->summary_list); + ospf6_lsdb_remove_all (on->request_list); + for (lsa = ospf6_lsdb_head (on->retrans_list); lsa; + lsa = ospf6_lsdb_next (lsa)) + { + ospf6_decrement_retrans_count (lsa); + ospf6_lsdb_remove (lsa, on->retrans_list); + } + + THREAD_OFF (on->thread_send_dbdesc); + THREAD_OFF (on->thread_send_lsreq); + THREAD_OFF (on->thread_send_lsupdate); + THREAD_OFF (on->thread_send_lsack); + + return 0; +} + +int +inactivity_timer (struct thread *thread) +{ + struct ospf6_neighbor *on; + + on = (struct ospf6_neighbor *) THREAD_ARG (thread); + assert (on); + + if (IS_OSPF6_DEBUG_NEIGHBOR (EVENT)) + zlog_debug ("Neighbor Event %s: *InactivityTimer*", on->name); + + on->inactivity_timer = NULL; + on->drouter = on->prev_drouter = 0; + on->bdrouter = on->prev_bdrouter = 0; + + 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); + ospf6_neighbor_delete (on); + + return 0; +} + + + +/* vty functions */ +/* show neighbor structure */ +static void +ospf6_neighbor_show (struct vty *vty, struct ospf6_neighbor *on) +{ + char router_id[16]; + char duration[16]; + struct timeval now, res; + char nstate[16]; + char deadtime[16]; + long h, m, s; + + /* Router-ID (Name) */ + inet_ntop (AF_INET, &on->router_id, router_id, sizeof (router_id)); +#ifdef HAVE_GETNAMEINFO + { + } +#endif /*HAVE_GETNAMEINFO*/ + + quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); + + /* Dead time */ + h = m = s = 0; + if (on->inactivity_timer) + { + s = on->inactivity_timer->u.sands.tv_sec - recent_relative_time().tv_sec; + h = s / 3600; + s -= h * 3600; + m = s / 60; + s -= m * 60; + } + snprintf (deadtime, sizeof (deadtime), "%02ld:%02ld:%02ld", h, m, s); + + /* Neighbor State */ + if (if_is_pointopoint (on->ospf6_if->interface)) + snprintf (nstate, sizeof (nstate), "PointToPoint"); + else + { + if (on->router_id == on->drouter) + snprintf (nstate, sizeof (nstate), "DR"); + else if (on->router_id == on->bdrouter) + snprintf (nstate, sizeof (nstate), "BDR"); + else + snprintf (nstate, sizeof (nstate), "DROther"); + } + + /* Duration */ + timersub (&now, &on->last_changed, &res); + timerstring (&res, duration, sizeof (duration)); + + /* + vty_out (vty, "%-15s %3d %11s %6s/%-12s %11s %s[%s]%s", + "Neighbor ID", "Pri", "DeadTime", "State", "", "Duration", + "I/F", "State", VNL); + */ + + vty_out (vty, "%-15s %3d %11s %6s/%-12s %11s %s[%s]%s", + router_id, on->priority, deadtime, + ospf6_neighbor_state_str[on->state], nstate, duration, + on->ospf6_if->interface->name, + ospf6_interface_state_str[on->ospf6_if->state], VNL); +} + +static void +ospf6_neighbor_show_drchoice (struct vty *vty, struct ospf6_neighbor *on) +{ + char router_id[16]; + char drouter[16], bdrouter[16]; + char duration[16]; + struct timeval now, res; + +/* + vty_out (vty, "%-15s %6s/%-11s %-15s %-15s %s[%s]%s", + "RouterID", "State", "Duration", "DR", "BDR", "I/F", + "State", VNL); +*/ + + inet_ntop (AF_INET, &on->router_id, router_id, sizeof (router_id)); + inet_ntop (AF_INET, &on->drouter, drouter, sizeof (drouter)); + inet_ntop (AF_INET, &on->bdrouter, bdrouter, sizeof (bdrouter)); + + quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); + timersub (&now, &on->last_changed, &res); + timerstring (&res, duration, sizeof (duration)); + + vty_out (vty, "%-15s %6s/%-11s %-15s %-15s %s[%s]%s", + router_id, ospf6_neighbor_state_str[on->state], + duration, drouter, bdrouter, on->ospf6_if->interface->name, + ospf6_interface_state_str[on->ospf6_if->state], + VNL); +} + +static void +ospf6_neighbor_show_detail (struct vty *vty, struct ospf6_neighbor *on) +{ + char drouter[16], bdrouter[16]; + char linklocal_addr[64], duration[32]; + struct timeval now, res; + struct ospf6_lsa *lsa; + + inet_ntop (AF_INET6, &on->linklocal_addr, linklocal_addr, + sizeof (linklocal_addr)); + inet_ntop (AF_INET, &on->drouter, drouter, sizeof (drouter)); + inet_ntop (AF_INET, &on->bdrouter, bdrouter, sizeof (bdrouter)); + + quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); + timersub (&now, &on->last_changed, &res); + timerstring (&res, duration, sizeof (duration)); + + vty_out (vty, " Neighbor %s%s", on->name, + VNL); + vty_out (vty, " Area %s via interface %s (ifindex %d)%s", + on->ospf6_if->area->name, + on->ospf6_if->interface->name, + on->ospf6_if->interface->ifindex, + VNL); + vty_out (vty, " His IfIndex: %d Link-local address: %s%s", + on->ifindex, linklocal_addr, + VNL); + vty_out (vty, " State %s for a duration of %s%s", + ospf6_neighbor_state_str[on->state], duration, + VNL); + vty_out (vty, " His choice of DR/BDR %s/%s, Priority %d%s", + drouter, bdrouter, on->priority, + VNL); + vty_out (vty, " DbDesc status: %s%s%s SeqNum: %#lx%s", + (CHECK_FLAG (on->dbdesc_bits, OSPF6_DBDESC_IBIT) ? "Initial " : ""), + (CHECK_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MBIT) ? "More " : ""), + (CHECK_FLAG (on->dbdesc_bits, OSPF6_DBDESC_MSBIT) ? + "Master" : "Slave"), (u_long) ntohl (on->dbdesc_seqnum), + VNL); + + vty_out (vty, " Summary-List: %d LSAs%s", on->summary_list->count, + VNL); + for (lsa = ospf6_lsdb_head (on->summary_list); lsa; + lsa = ospf6_lsdb_next (lsa)) + vty_out (vty, " %s%s", lsa->name, VNL); + + vty_out (vty, " Request-List: %d LSAs%s", on->request_list->count, + VNL); + for (lsa = ospf6_lsdb_head (on->request_list); lsa; + lsa = ospf6_lsdb_next (lsa)) + vty_out (vty, " %s%s", lsa->name, VNL); + + vty_out (vty, " Retrans-List: %d LSAs%s", on->retrans_list->count, + VNL); + for (lsa = ospf6_lsdb_head (on->retrans_list); lsa; + lsa = ospf6_lsdb_next (lsa)) + vty_out (vty, " %s%s", lsa->name, VNL); + + timerclear (&res); + if (on->thread_send_dbdesc) + timersub (&on->thread_send_dbdesc->u.sands, &now, &res); + timerstring (&res, duration, sizeof (duration)); + vty_out (vty, " %d Pending LSAs for DbDesc in Time %s [thread %s]%s", + on->dbdesc_list->count, duration, + (on->thread_send_dbdesc ? "on" : "off"), + VNL); + for (lsa = ospf6_lsdb_head (on->dbdesc_list); lsa; + lsa = ospf6_lsdb_next (lsa)) + vty_out (vty, " %s%s", lsa->name, VNL); + + timerclear (&res); + if (on->thread_send_lsreq) + 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->request_list->count, duration, + (on->thread_send_lsreq ? "on" : "off"), + VNL); + for (lsa = ospf6_lsdb_head (on->request_list); lsa; + lsa = ospf6_lsdb_next (lsa)) + vty_out (vty, " %s%s", lsa->name, VNL); + + timerclear (&res); + if (on->thread_send_lsupdate) + timersub (&on->thread_send_lsupdate->u.sands, &now, &res); + timerstring (&res, duration, sizeof (duration)); + vty_out (vty, " %d Pending LSAs for LSUpdate in Time %s [thread %s]%s", + on->lsupdate_list->count, duration, + (on->thread_send_lsupdate ? "on" : "off"), + VNL); + for (lsa = ospf6_lsdb_head (on->lsupdate_list); lsa; + lsa = ospf6_lsdb_next (lsa)) + vty_out (vty, " %s%s", lsa->name, VNL); + + timerclear (&res); + if (on->thread_send_lsack) + timersub (&on->thread_send_lsack->u.sands, &now, &res); + timerstring (&res, duration, sizeof (duration)); + vty_out (vty, " %d Pending LSAs for LSAck in Time %s [thread %s]%s", + on->lsack_list->count, duration, + (on->thread_send_lsack ? "on" : "off"), + VNL); + for (lsa = ospf6_lsdb_head (on->lsack_list); lsa; + lsa = ospf6_lsdb_next (lsa)) + vty_out (vty, " %s%s", lsa->name, VNL); + +} + +DEFUN (show_ipv6_ospf6_neighbor, + show_ipv6_ospf6_neighbor_cmd, + "show ipv6 ospf6 neighbor", + SHOW_STR + IP6_STR + OSPF6_STR + "Neighbor list\n" + ) +{ + struct ospf6_neighbor *on; + struct ospf6_interface *oi; + struct ospf6_area *oa; + struct listnode *i, *j, *k; + void (*showfunc) (struct vty *, struct ospf6_neighbor *); + + OSPF6_CMD_CHECK_RUNNING (); + showfunc = ospf6_neighbor_show; + + if (argc) + { + if (! strncmp (argv[0], "de", 2)) + showfunc = ospf6_neighbor_show_detail; + else if (! strncmp (argv[0], "dr", 2)) + showfunc = ospf6_neighbor_show_drchoice; + } + + if (showfunc == ospf6_neighbor_show) + vty_out (vty, "%-15s %3s %11s %6s/%-12s %11s %s[%s]%s", + "Neighbor ID", "Pri", "DeadTime", "State", "IfState", "Duration", + "I/F", "State", VNL); + else if (showfunc == ospf6_neighbor_show_drchoice) + vty_out (vty, "%-15s %6s/%-11s %-15s %-15s %s[%s]%s", + "RouterID", "State", "Duration", "DR", "BDR", "I/F", + "State", VNL); + + for (ALL_LIST_ELEMENTS_RO (ospf6->area_list, i, oa)) + for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi)) + for (ALL_LIST_ELEMENTS_RO (oi->neighbor_list, k, on)) + (*showfunc) (vty, on); + + return CMD_SUCCESS; +} + +ALIAS (show_ipv6_ospf6_neighbor, + show_ipv6_ospf6_neighbor_detail_cmd, + "show ipv6 ospf6 neighbor (detail|drchoice)", + SHOW_STR + IP6_STR + OSPF6_STR + "Neighbor list\n" + "Display details\n" + "Display DR choices\n" + ) + +DEFUN (show_ipv6_ospf6_neighbor_one, + show_ipv6_ospf6_neighbor_one_cmd, + "show ipv6 ospf6 neighbor A.B.C.D", + SHOW_STR + IP6_STR + OSPF6_STR + "Neighbor list\n" + "Specify Router-ID as IPv4 address notation\n" + ) +{ + struct ospf6_neighbor *on; + struct ospf6_interface *oi; + struct ospf6_area *oa; + struct listnode *i, *j, *k; + void (*showfunc) (struct vty *, struct ospf6_neighbor *); + u_int32_t router_id; + + OSPF6_CMD_CHECK_RUNNING (); + showfunc = ospf6_neighbor_show_detail; + + if ((inet_pton (AF_INET, argv[0], &router_id)) != 1) + { + vty_out (vty, "Router-ID is not parsable: %s%s", argv[0], + VNL); + return CMD_SUCCESS; + } + + for (ALL_LIST_ELEMENTS_RO (ospf6->area_list, i, oa)) + for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi)) + for (ALL_LIST_ELEMENTS_RO (oi->neighbor_list, k, on)) + (*showfunc) (vty, on); + + return CMD_SUCCESS; +} + +void +ospf6_neighbor_init (void) +{ + install_element (VIEW_NODE, &show_ipv6_ospf6_neighbor_cmd); + install_element (VIEW_NODE, &show_ipv6_ospf6_neighbor_detail_cmd); +} + +DEFUN (debug_ospf6_neighbor, + debug_ospf6_neighbor_cmd, + "debug ospf6 neighbor", + DEBUG_STR + OSPF6_STR + "Debug OSPFv3 Neighbor\n" + ) +{ + unsigned char level = 0; + if (argc) + { + if (! strncmp (argv[0], "s", 1)) + level = OSPF6_DEBUG_NEIGHBOR_STATE; + if (! strncmp (argv[0], "e", 1)) + level = OSPF6_DEBUG_NEIGHBOR_EVENT; + } + else + level = OSPF6_DEBUG_NEIGHBOR_STATE | OSPF6_DEBUG_NEIGHBOR_EVENT; + + OSPF6_DEBUG_NEIGHBOR_ON (level); + return CMD_SUCCESS; +} + +ALIAS (debug_ospf6_neighbor, + debug_ospf6_neighbor_detail_cmd, + "debug ospf6 neighbor (state|event)", + DEBUG_STR + OSPF6_STR + "Debug OSPFv3 Neighbor\n" + "Debug OSPFv3 Neighbor State Change\n" + "Debug OSPFv3 Neighbor Event\n" + ) + +DEFUN (no_debug_ospf6_neighbor, + no_debug_ospf6_neighbor_cmd, + "no debug ospf6 neighbor", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug OSPFv3 Neighbor\n" + ) +{ + unsigned char level = 0; + if (argc) + { + if (! strncmp (argv[0], "s", 1)) + level = OSPF6_DEBUG_NEIGHBOR_STATE; + if (! strncmp (argv[0], "e", 1)) + level = OSPF6_DEBUG_NEIGHBOR_EVENT; + } + else + level = OSPF6_DEBUG_NEIGHBOR_STATE | OSPF6_DEBUG_NEIGHBOR_EVENT; + + OSPF6_DEBUG_NEIGHBOR_OFF (level); + return CMD_SUCCESS; +} + +ALIAS (no_debug_ospf6_neighbor, + no_debug_ospf6_neighbor_detail_cmd, + "no debug ospf6 neighbor (state|event)", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug OSPFv3 Neighbor\n" + "Debug OSPFv3 Neighbor State Change\n" + "Debug OSPFv3 Neighbor Event\n" + ) + +int +config_write_ospf6_debug_neighbor (struct vty *vty) +{ + if (IS_OSPF6_DEBUG_NEIGHBOR (STATE) && + IS_OSPF6_DEBUG_NEIGHBOR (EVENT)) + vty_out (vty, "debug ospf6 neighbor%s", VNL); + else if (IS_OSPF6_DEBUG_NEIGHBOR (STATE)) + vty_out (vty, "debug ospf6 neighbor state%s", VNL); + else if (IS_OSPF6_DEBUG_NEIGHBOR (EVENT)) + vty_out (vty, "debug ospf6 neighbor event%s", VNL); + return 0; +} + +void +install_element_ospf6_debug_neighbor (void) +{ + install_element (ENABLE_NODE, &debug_ospf6_neighbor_cmd); + install_element (ENABLE_NODE, &debug_ospf6_neighbor_detail_cmd); + install_element (ENABLE_NODE, &no_debug_ospf6_neighbor_cmd); + install_element (ENABLE_NODE, &no_debug_ospf6_neighbor_detail_cmd); + install_element (CONFIG_NODE, &debug_ospf6_neighbor_cmd); + install_element (CONFIG_NODE, &debug_ospf6_neighbor_detail_cmd); + install_element (CONFIG_NODE, &no_debug_ospf6_neighbor_cmd); + install_element (CONFIG_NODE, &no_debug_ospf6_neighbor_detail_cmd); +} + + + diff --git a/ospf6d/ospf6_neighbor.h b/ospf6d/ospf6_neighbor.h new file mode 100644 index 0000000..54b7ffe --- /dev/null +++ b/ospf6d/ospf6_neighbor.h @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2003 Yasuhiro Ohara + * + * 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 OSPF6_NEIGHBOR_H +#define OSPF6_NEIGHBOR_H + +/* Debug option */ +extern unsigned char conf_debug_ospf6_neighbor; +#define OSPF6_DEBUG_NEIGHBOR_STATE 0x01 +#define OSPF6_DEBUG_NEIGHBOR_EVENT 0x02 +#define OSPF6_DEBUG_NEIGHBOR_ON(level) \ + (conf_debug_ospf6_neighbor |= (level)) +#define OSPF6_DEBUG_NEIGHBOR_OFF(level) \ + (conf_debug_ospf6_neighbor &= ~(level)) +#define IS_OSPF6_DEBUG_NEIGHBOR(level) \ + (conf_debug_ospf6_neighbor & OSPF6_DEBUG_NEIGHBOR_ ## level) + +/* Neighbor structure */ +struct ospf6_neighbor +{ + /* Neighbor Router ID String */ + char name[32]; + + /* OSPFv3 Interface this neighbor belongs to */ + struct ospf6_interface *ospf6_if; + + /* Neighbor state */ + 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 */ + ifindex_t ifindex; + + /* Router Priority of this neighbor */ + u_char priority; + + u_int32_t drouter; + u_int32_t bdrouter; + u_int32_t prev_drouter; + u_int32_t prev_bdrouter; + + /* Options field (Capability) */ + char options[3]; + + /* IPaddr of I/F on our side link */ + struct in6_addr linklocal_addr; + + /* For Database Exchange */ + u_char dbdesc_bits; + u_int32_t dbdesc_seqnum; + /* Last received Database Description packet */ + struct ospf6_dbdesc dbdesc_last; + + /* LS-list */ + struct ospf6_lsdb *summary_list; + struct ospf6_lsdb *request_list; + struct ospf6_lsdb *retrans_list; + + /* LSA list for message transmission */ + struct ospf6_lsdb *dbdesc_list; + struct ospf6_lsdb *lsreq_list; + struct ospf6_lsdb *lsupdate_list; + struct ospf6_lsdb *lsack_list; + + struct ospf6_lsa *last_ls_req; + + /* Inactivity timer */ + struct thread *inactivity_timer; + + /* Thread for sending message */ + struct thread *thread_send_dbdesc; + struct thread *thread_send_lsreq; + struct thread *thread_send_lsupdate; + struct thread *thread_send_lsack; +}; + +/* Neighbor state */ +#define OSPF6_NEIGHBOR_DOWN 1 +#define OSPF6_NEIGHBOR_ATTEMPT 2 +#define OSPF6_NEIGHBOR_INIT 3 +#define OSPF6_NEIGHBOR_TWOWAY 4 +#define OSPF6_NEIGHBOR_EXSTART 5 +#define OSPF6_NEIGHBOR_EXCHANGE 6 +#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); + +struct ospf6_neighbor *ospf6_neighbor_lookup (u_int32_t, + struct ospf6_interface *); +struct ospf6_neighbor *ospf6_neighbor_create (u_int32_t, + struct ospf6_interface *); +void ospf6_neighbor_delete (struct ospf6_neighbor *); + +/* Neighbor event */ +extern int hello_received (struct thread *); +extern int twoway_received (struct thread *); +extern int negotiation_done (struct thread *); +extern int exchange_done (struct thread *); +extern int loading_done (struct thread *); +extern int adj_ok (struct thread *); +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); +extern void install_element_ospf6_debug_neighbor (void); + +#endif /* OSPF6_NEIGHBOR_H */ diff --git a/ospf6d/ospf6_network.c b/ospf6d/ospf6_network.c new file mode 100644 index 0000000..53d6c35 --- /dev/null +++ b/ospf6d/ospf6_network.c @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2003 Yasuhiro Ohara + * + * 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 "memory.h" +#include "sockunion.h" +#include "sockopt.h" +#include "privs.h" + +#include "libospf.h" +#include "ospf6_proto.h" +#include "ospf6_network.h" + +extern struct zebra_privs_t ospf6d_privs; + +int ospf6_sock; +struct in6_addr allspfrouters6; +struct in6_addr alldrouters6; + +/* setsockopt MulticastLoop to off */ +static void +ospf6_reset_mcastloop (void) +{ + u_int off = 0; + if (setsockopt (ospf6_sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, + &off, sizeof (u_int)) < 0) + zlog_warn ("Network: reset IPV6_MULTICAST_LOOP failed: %s", + safe_strerror (errno)); +} + +static void +ospf6_set_pktinfo (void) +{ + setsockopt_ipv6_pktinfo (ospf6_sock, 1); +} + +static void +ospf6_set_transport_class (void) +{ +#ifdef IPTOS_PREC_INTERNETCONTROL + setsockopt_ipv6_tclass (ospf6_sock, IPTOS_PREC_INTERNETCONTROL); +#endif +} + +static void +ospf6_set_checksum (void) +{ + int offset = 12; +#ifndef DISABLE_IPV6_CHECKSUM + if (setsockopt (ospf6_sock, IPPROTO_IPV6, IPV6_CHECKSUM, + &offset, sizeof (offset)) < 0) + zlog_warn ("Network: set IPV6_CHECKSUM failed: %s", safe_strerror (errno)); +#else + zlog_warn ("Network: Don't set IPV6_CHECKSUM"); +#endif /* DISABLE_IPV6_CHECKSUM */ +} + +/* Make ospf6d's server socket. */ +int +ospf6_serv_sock (void) +{ + if (ospf6d_privs.change (ZPRIVS_RAISE)) + zlog_err ("ospf6_serv_sock: could not raise privs"); + + ospf6_sock = socket (AF_INET6, SOCK_RAW, IPPROTO_OSPFIGP); + if (ospf6_sock < 0) + { + zlog_warn ("Network: can't create OSPF6 socket."); + if (ospf6d_privs.change (ZPRIVS_LOWER)) + zlog_err ("ospf_sock_init: could not lower privs"); + return -1; + } + if (ospf6d_privs.change (ZPRIVS_LOWER)) + zlog_err ("ospf_sock_init: could not lower privs"); + + /* set socket options */ +#if 1 + sockopt_reuseaddr (ospf6_sock); +#else + ospf6_set_reuseaddr (); +#endif /*1*/ + ospf6_reset_mcastloop (); + ospf6_set_pktinfo (); + ospf6_set_transport_class (); + ospf6_set_checksum (); + + /* setup global in6_addr, allspf6 and alldr6 for later use */ + inet_pton (AF_INET6, ALLSPFROUTERS6, &allspfrouters6); + inet_pton (AF_INET6, ALLDROUTERS6, &alldrouters6); + + return 0; +} + +/* ospf6 set socket option */ +int +ospf6_sso (ifindex_t ifindex, struct in6_addr *group, int option) +{ + struct ipv6_mreq mreq6; + int ret; + + assert (ifindex); + mreq6.ipv6mr_interface = ifindex; + memcpy (&mreq6.ipv6mr_multiaddr, group, sizeof (struct in6_addr)); + + 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)); + } + + return ret; +} + +static int +iov_count (struct iovec *iov) +{ + int i; + for (i = 0; iov[i].iov_base; i++) + ; + return i; +} + +static int +iov_totallen (struct iovec *iov) +{ + int i; + int totallen = 0; + for (i = 0; iov[i].iov_base; i++) + totallen += iov[i].iov_len; + return totallen; +} + +int +ospf6_sendmsg (struct in6_addr *src, struct in6_addr *dst, + ifindex_t *ifindex, struct iovec *message) +{ + int retval; + struct msghdr smsghdr; + struct cmsghdr *scmsgp; + u_char cmsgbuf[CMSG_SPACE(sizeof (struct in6_pktinfo))]; + struct in6_pktinfo *pktinfo; + struct sockaddr_in6 dst_sin6; + + assert (dst); + assert (*ifindex); + + scmsgp = (struct cmsghdr *)cmsgbuf; + pktinfo = (struct in6_pktinfo *)(CMSG_DATA(scmsgp)); + memset (&dst_sin6, 0, sizeof (struct sockaddr_in6)); + + /* source address */ + pktinfo->ipi6_ifindex = *ifindex; + if (src) + memcpy (&pktinfo->ipi6_addr, src, sizeof (struct in6_addr)); + else + memset (&pktinfo->ipi6_addr, 0, sizeof (struct in6_addr)); + + /* destination address */ + dst_sin6.sin6_family = AF_INET6; +#ifdef SIN6_LEN + dst_sin6.sin6_len = sizeof (struct sockaddr_in6); +#endif /*SIN6_LEN*/ + memcpy (&dst_sin6.sin6_addr, dst, sizeof (struct in6_addr)); +#ifdef HAVE_SIN6_SCOPE_ID + dst_sin6.sin6_scope_id = *ifindex; +#endif + + /* send control msg */ + scmsgp->cmsg_level = IPPROTO_IPV6; + scmsgp->cmsg_type = IPV6_PKTINFO; + scmsgp->cmsg_len = CMSG_LEN (sizeof (struct in6_pktinfo)); + /* scmsgp = CMSG_NXTHDR (&smsghdr, scmsgp); */ + + /* send msg hdr */ + memset (&smsghdr, 0, sizeof (smsghdr)); + smsghdr.msg_iov = message; + smsghdr.msg_iovlen = iov_count (message); + smsghdr.msg_name = (caddr_t) &dst_sin6; + smsghdr.msg_namelen = sizeof (struct sockaddr_in6); + smsghdr.msg_control = (caddr_t) cmsgbuf; + smsghdr.msg_controllen = scmsgp->cmsg_len; + + retval = sendmsg (ospf6_sock, &smsghdr, 0); + if (retval != iov_totallen (message)) + zlog_warn ("sendmsg failed: ifindex: %d: %s (%d)", + *ifindex, safe_strerror (errno), errno); + + return retval; +} + +int +ospf6_recvmsg (struct in6_addr *src, struct in6_addr *dst, + ifindex_t *ifindex, struct iovec *message) +{ + int retval; + struct msghdr rmsghdr; + struct cmsghdr *rcmsgp; + u_char cmsgbuf[CMSG_SPACE(sizeof (struct in6_pktinfo))]; + struct in6_pktinfo *pktinfo; + struct sockaddr_in6 src_sin6; + + rcmsgp = (struct cmsghdr *)cmsgbuf; + pktinfo = (struct in6_pktinfo *)(CMSG_DATA(rcmsgp)); + memset (&src_sin6, 0, sizeof (struct sockaddr_in6)); + + /* receive control msg */ + rcmsgp->cmsg_level = IPPROTO_IPV6; + rcmsgp->cmsg_type = IPV6_PKTINFO; + rcmsgp->cmsg_len = CMSG_LEN (sizeof (struct in6_pktinfo)); + /* rcmsgp = CMSG_NXTHDR (&rmsghdr, rcmsgp); */ + + /* receive msg hdr */ + memset (&rmsghdr, 0, sizeof (rmsghdr)); + rmsghdr.msg_iov = message; + rmsghdr.msg_iovlen = iov_count (message); + rmsghdr.msg_name = (caddr_t) &src_sin6; + rmsghdr.msg_namelen = sizeof (struct sockaddr_in6); + rmsghdr.msg_control = (caddr_t) cmsgbuf; + rmsghdr.msg_controllen = sizeof (cmsgbuf); + + retval = recvmsg (ospf6_sock, &rmsghdr, 0); + if (retval < 0) + zlog_warn ("recvmsg failed: %s", safe_strerror (errno)); + else if (retval == iov_totallen (message)) + zlog_warn ("recvmsg read full buffer size: %d", retval); + + /* source address */ + assert (src); + memcpy (src, &src_sin6.sin6_addr, sizeof (struct in6_addr)); + + /* destination address */ + if (ifindex) + *ifindex = pktinfo->ipi6_ifindex; + if (dst) + memcpy (dst, &pktinfo->ipi6_addr, sizeof (struct in6_addr)); + + return retval; +} + + diff --git a/ospf6d/ospf6_network.h b/ospf6d/ospf6_network.h new file mode 100644 index 0000000..4fa2839 --- /dev/null +++ b/ospf6d/ospf6_network.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2003 Yasuhiro Ohara + * + * 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 OSPF6_NETWORK_H +#define OSPF6_NETWORK_H + + + +extern int ospf6_sock; +extern struct in6_addr allspfrouters6; +extern struct in6_addr alldrouters6; + +extern int ospf6_serv_sock (void); +extern int ospf6_sso (ifindex_t ifindex, struct in6_addr *group, int option); + +extern int ospf6_sendmsg (struct in6_addr *, struct in6_addr *, + ifindex_t *, struct iovec *); +extern int ospf6_recvmsg (struct in6_addr *, struct in6_addr *, + ifindex_t *, struct iovec *); + +#endif /* OSPF6_NETWORK_H */ + diff --git a/ospf6d/ospf6_proto.c b/ospf6d/ospf6_proto.c new file mode 100644 index 0000000..d011601 --- /dev/null +++ b/ospf6d/ospf6_proto.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2003 Yasuhiro Ohara + * + * 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 "ospf6_proto.h" + +void +ospf6_prefix_apply_mask (struct ospf6_prefix *op) +{ + u_char *pnt, mask; + int index, offset; + + pnt = (u_char *)((caddr_t) op + sizeof (struct ospf6_prefix)); + index = op->prefix_length / 8; + offset = op->prefix_length % 8; + mask = 0xff << (8 - offset); + + if (index > 16) + { + zlog_warn ("Prefix length too long: %d", op->prefix_length); + return; + } + + /* nonzero mask means no check for this byte because if it contains + * prefix bits it must be there for us to write */ + if (mask) + pnt[index++] &= mask; + + while (index < OSPF6_PREFIX_SPACE (op->prefix_length)) + pnt[index++] = 0; +} + +void +ospf6_prefix_options_printbuf (u_int8_t prefix_options, char *buf, int size) +{ + snprintf (buf, size, "xxx"); +} + +void +ospf6_capability_printbuf (char capability, char *buf, int size) +{ + char w, v, e, b; + w = (capability & OSPF6_ROUTER_BIT_W ? 'W' : '-'); + v = (capability & OSPF6_ROUTER_BIT_V ? 'V' : '-'); + e = (capability & OSPF6_ROUTER_BIT_E ? 'E' : '-'); + b = (capability & OSPF6_ROUTER_BIT_B ? 'B' : '-'); + snprintf (buf, size, "----%c%c%c%c", w, v, e, b); +} + +void +ospf6_options_printbuf (u_char *options, char *buf, int size) +{ + const char *dc, *r, *n, *mc, *e, *v6; + dc = (OSPF6_OPT_ISSET (options, OSPF6_OPT_DC) ? "DC" : "--"); + r = (OSPF6_OPT_ISSET (options, OSPF6_OPT_R) ? "R" : "-" ); + n = (OSPF6_OPT_ISSET (options, OSPF6_OPT_N) ? "N" : "-" ); + mc = (OSPF6_OPT_ISSET (options, OSPF6_OPT_MC) ? "MC" : "--"); + e = (OSPF6_OPT_ISSET (options, OSPF6_OPT_E) ? "E" : "-" ); + v6 = (OSPF6_OPT_ISSET (options, OSPF6_OPT_V6) ? "V6" : "--"); + snprintf (buf, size, "%s|%s|%s|%s|%s|%s", dc, r, n, mc, e, v6); +} + + diff --git a/ospf6d/ospf6_proto.h b/ospf6d/ospf6_proto.h new file mode 100644 index 0000000..af60eb9 --- /dev/null +++ b/ospf6d/ospf6_proto.h @@ -0,0 +1,101 @@ +/* + * Common protocol data and data structures. + * Copyright (C) 2003 Yasuhiro Ohara + * + * 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 OSPF6_PROTO_H +#define OSPF6_PROTO_H + +/* OSPF protocol version */ +#define OSPFV3_VERSION 3 + +/* TOS field normaly null */ +#define DEFAULT_TOS_VALUE 0x0 + +#define ALLSPFROUTERS6 "ff02::5" +#define ALLDROUTERS6 "ff02::6" + +#define OSPF6_ROUTER_BIT_W (1 << 3) +#define OSPF6_ROUTER_BIT_V (1 << 2) +#define OSPF6_ROUTER_BIT_E (1 << 1) +#define OSPF6_ROUTER_BIT_B (1 << 0) + +/* OSPF options */ +/* present in HELLO, DD, LSA */ +#define OSPF6_OPT_SET(x,opt) ((x)[2] |= (opt)) +#define OSPF6_OPT_ISSET(x,opt) ((x)[2] & (opt)) +#define OSPF6_OPT_CLEAR(x,opt) ((x)[2] &= ~(opt)) +#define OSPF6_OPT_CLEAR_ALL(x) ((x)[0] = (x)[1] = (x)[2] = 0) + +#define OSPF6_OPT_DC (1 << 5) /* Demand Circuit handling Capability */ +#define OSPF6_OPT_R (1 << 4) /* Forwarding Capability (Any Protocol) */ +#define OSPF6_OPT_N (1 << 3) /* Handling Type-7 LSA Capability */ +#define OSPF6_OPT_MC (1 << 2) /* Multicasting Capability */ +#define OSPF6_OPT_E (1 << 1) /* AS External Capability */ +#define OSPF6_OPT_V6 (1 << 0) /* IPv6 forwarding Capability */ + +/* OSPF6 Prefix */ +#define OSPF6_PREFIX_MIN_SIZE 4U /* .length == 0 */ +struct ospf6_prefix +{ + u_int8_t prefix_length; + u_int8_t prefix_options; + union { + u_int16_t _prefix_metric; + u_int16_t _prefix_referenced_lstype; + } u; +#define prefix_metric u._prefix_metric +#define prefix_refer_lstype u._prefix_referenced_lstype + /* followed by one address_prefix */ +}; + +#define OSPF6_PREFIX_OPTION_NU (1 << 0) /* No Unicast */ +#define OSPF6_PREFIX_OPTION_LA (1 << 1) /* Local Address */ +#define OSPF6_PREFIX_OPTION_MC (1 << 2) /* MultiCast */ +#define OSPF6_PREFIX_OPTION_P (1 << 3) /* Propagate (NSSA) */ + +/* caddr_t OSPF6_PREFIX_BODY (struct ospf6_prefix *); */ +#define OSPF6_PREFIX_BODY(x) ((caddr_t)(x) + sizeof (struct ospf6_prefix)) + +/* size_t OSPF6_PREFIX_SPACE (int prefixlength); */ +#define OSPF6_PREFIX_SPACE(x) ((((x) + 31) / 32) * 4) + +/* size_t OSPF6_PREFIX_SIZE (struct ospf6_prefix *); */ +#define OSPF6_PREFIX_SIZE(x) \ + (OSPF6_PREFIX_SPACE ((x)->prefix_length) + sizeof (struct ospf6_prefix)) + +/* struct ospf6_prefix *OSPF6_PREFIX_NEXT (struct ospf6_prefix *); */ +#define OSPF6_PREFIX_NEXT(x) \ + ((struct ospf6_prefix *)((caddr_t)(x) + OSPF6_PREFIX_SIZE (x))) + +#define ospf6_prefix_in6_addr(in6, op) \ +do { \ + memset (in6, 0, sizeof (struct in6_addr)); \ + memcpy (in6, (caddr_t) (op) + sizeof (struct ospf6_prefix), \ + OSPF6_PREFIX_SPACE ((op)->prefix_length)); \ +} while (0) + +extern void ospf6_prefix_apply_mask (struct ospf6_prefix *op); +extern void ospf6_prefix_options_printbuf (u_int8_t prefix_options, + char *buf, int size); +extern void ospf6_capability_printbuf (char capability, char *buf, int size); +extern void ospf6_options_printbuf (u_char *options, char *buf, int size); + +#endif /* OSPF6_PROTO_H */ diff --git a/ospf6d/ospf6_route.c b/ospf6d/ospf6_route.c new file mode 100644 index 0000000..3cfbab5 --- /dev/null +++ b/ospf6d/ospf6_route.c @@ -0,0 +1,1413 @@ +/* + * Copyright (C) 2003 Yasuhiro Ohara + * + * 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 "memory.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" +#include "command.h" +#include "linklist.h" + +#include "ospf6_proto.h" +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" +#include "ospf6_route.h" +#include "ospf6_top.h" +#include "ospf6_area.h" +#include "ospf6_interface.h" +#include "ospf6d.h" + +unsigned char conf_debug_ospf6_route = 0; + +static char * +ospf6_route_table_name (struct ospf6_route_table *table) +{ + static char name[32]; + switch (table->scope_type) + { + case OSPF6_SCOPE_TYPE_GLOBAL: + { + switch (table->table_type) + { + case OSPF6_TABLE_TYPE_ROUTES: + snprintf (name, sizeof (name), "global route table"); + break; + case OSPF6_TABLE_TYPE_BORDER_ROUTERS: + snprintf (name, sizeof (name), "global brouter table"); + break; + case OSPF6_TABLE_TYPE_EXTERNAL_ROUTES: + snprintf (name, sizeof (name), "global external table"); + break; + default: + snprintf (name, sizeof (name), "global unknown table"); + break; + } + } + break; + + case OSPF6_SCOPE_TYPE_AREA: + { + struct ospf6_area *oa = (struct ospf6_area *) table->scope; + switch (table->table_type) + { + case OSPF6_TABLE_TYPE_SPF_RESULTS: + snprintf (name, sizeof (name), + "area %s spf table", oa->name); + break; + case OSPF6_TABLE_TYPE_ROUTES: + snprintf (name, sizeof (name), + "area %s route table", oa->name); + break; + case OSPF6_TABLE_TYPE_PREFIX_RANGES: + snprintf (name, sizeof (name), + "area %s range table", oa->name); + break; + case OSPF6_TABLE_TYPE_SUMMARY_PREFIXES: + snprintf (name, sizeof (name), + "area %s summary prefix table", oa->name); + break; + case OSPF6_TABLE_TYPE_SUMMARY_ROUTERS: + snprintf (name, sizeof (name), + "area %s summary router table", oa->name); + break; + default: + snprintf (name, sizeof (name), + "area %s unknown table", oa->name); + break; + } + } + break; + + case OSPF6_SCOPE_TYPE_INTERFACE: + { + struct ospf6_interface *oi = (struct ospf6_interface *) table->scope; + switch (table->table_type) + { + case OSPF6_TABLE_TYPE_CONNECTED_ROUTES: + snprintf (name, sizeof (name), "interface %s connected table", + oi->interface->name); + break; + default: + snprintf (name, sizeof (name), "interface %s unknown table", + oi->interface->name); + break; + } + } + break; + + default: + { + switch (table->table_type) + { + case OSPF6_TABLE_TYPE_SPF_RESULTS: + snprintf (name, sizeof (name), "temporary spf table"); + break; + default: + snprintf (name, sizeof (name), "temporary unknown table"); + break; + } + } + break; + } + return name; +} + +void +ospf6_linkstate_prefix (u_int32_t adv_router, u_int32_t id, + struct prefix *prefix) +{ + memset (prefix, 0, sizeof (struct prefix)); + prefix->family = AF_INET6; + prefix->prefixlen = 64; + memcpy (&prefix->u.prefix6.s6_addr[0], &adv_router, 4); + memcpy (&prefix->u.prefix6.s6_addr[4], &id, 4); +} + +void +ospf6_linkstate_prefix2str (struct prefix *prefix, char *buf, int size) +{ + u_int32_t adv_router, id; + char adv_router_str[16], id_str[16]; + memcpy (&adv_router, &prefix->u.prefix6.s6_addr[0], 4); + memcpy (&id, &prefix->u.prefix6.s6_addr[4], 4); + inet_ntop (AF_INET, &adv_router, adv_router_str, sizeof (adv_router_str)); + inet_ntop (AF_INET, &id, id_str, sizeof (id_str)); + if (ntohl (id)) + snprintf (buf, size, "%s Net-ID: %s", adv_router_str, id_str); + else + snprintf (buf, size, "%s", adv_router_str); +} + +/* Global strings for logging */ +const char *ospf6_dest_type_str[OSPF6_DEST_TYPE_MAX] = +{ "Unknown", "Router", "Network", "Discard", "Linkstate", "AddressRange", }; + +const char *ospf6_dest_type_substr[OSPF6_DEST_TYPE_MAX] = +{ "?", "R", "N", "D", "L", "A", }; + +const char *ospf6_path_type_str[OSPF6_PATH_TYPE_MAX] = +{ "Unknown", "Intra-Area", "Inter-Area", "External-1", "External-2", }; + +const char *ospf6_path_type_substr[OSPF6_PATH_TYPE_MAX] = +{ "??", "IA", "IE", "E1", "E2", }; + + +struct ospf6_route * +ospf6_route_create (void) +{ + struct ospf6_route *route; + route = XCALLOC (MTYPE_OSPF6_ROUTE, sizeof (struct ospf6_route)); + return route; +} + +void +ospf6_route_delete (struct ospf6_route *route) +{ + XFREE (MTYPE_OSPF6_ROUTE, route); +} + +struct ospf6_route * +ospf6_route_copy (struct ospf6_route *route) +{ + struct ospf6_route *new; + + new = ospf6_route_create (); + memcpy (new, route, sizeof (struct ospf6_route)); + new->rnode = NULL; + new->prev = NULL; + new->next = NULL; + new->table = NULL; + new->lock = 0; + return new; +} + +void +ospf6_route_lock (struct ospf6_route *route) +{ + route->lock++; +} + +void +ospf6_route_unlock (struct ospf6_route *route) +{ + assert (route->lock > 0); + route->lock--; + if (route->lock == 0) + { + /* Can't detach from the table until here + because ospf6_route_next () will use + the 'route->table' pointer for logging */ + route->table = NULL; + ospf6_route_delete (route); + } +} + +/* Route compare function. If ra is more preferred, it returns + less than 0. If rb is more preferred returns greater than 0. + Otherwise (neither one is preferred), returns 0 */ +static int +ospf6_route_cmp (struct ospf6_route *ra, struct ospf6_route *rb) +{ + assert (ospf6_route_is_same (ra, rb)); + assert (OSPF6_PATH_TYPE_NONE < ra->path.type && + ra->path.type < OSPF6_PATH_TYPE_MAX); + assert (OSPF6_PATH_TYPE_NONE < rb->path.type && + rb->path.type < OSPF6_PATH_TYPE_MAX); + + if (ra->type != rb->type) + return (ra->type - rb->type); + + if (ra->path.area_id != rb->path.area_id) + return (ntohl (ra->path.area_id) - ntohl (rb->path.area_id)); + + if (ra->path.type != rb->path.type) + return (ra->path.type - rb->path.type); + + if (ra->path.type == OSPF6_PATH_TYPE_EXTERNAL2) + { + if (ra->path.cost_e2 != rb->path.cost_e2) + return (ra->path.cost_e2 - rb->path.cost_e2); + } + else + { + if (ra->path.cost != rb->path.cost) + return (ra->path.cost - rb->path.cost); + } + + return 0; +} + +struct ospf6_route * +ospf6_route_lookup (struct prefix *prefix, + struct ospf6_route_table *table) +{ + struct route_node *node; + struct ospf6_route *route; + + node = route_node_lookup (table->table, prefix); + if (node == NULL) + return NULL; + + route = (struct ospf6_route *) node->info; + return route; +} + +struct ospf6_route * +ospf6_route_lookup_identical (struct ospf6_route *route, + struct ospf6_route_table *table) +{ + struct ospf6_route *target; + + for (target = ospf6_route_lookup (&route->prefix, table); + target; target = target->next) + { + if (ospf6_route_is_identical (target, route)) + return target; + } + return NULL; +} + +struct ospf6_route * +ospf6_route_lookup_bestmatch (struct prefix *prefix, + struct ospf6_route_table *table) +{ + struct route_node *node; + struct ospf6_route *route; + + node = route_node_match (table->table, prefix); + if (node == NULL) + return NULL; + route_unlock_node (node); + + route = (struct ospf6_route *) node->info; + return route; +} + +#ifdef DEBUG +static void +route_table_assert (struct ospf6_route_table *table) +{ + struct ospf6_route *prev, *r, *next; + char buf[64]; + unsigned int link_error = 0, num = 0; + + r = ospf6_route_head (table); + prev = NULL; + while (r) + { + if (r->prev != prev) + link_error++; + + next = ospf6_route_next (r); + + if (r->next != next) + link_error++; + + prev = r; + r = next; + } + + for (r = ospf6_route_head (table); r; r = ospf6_route_next (r)) + num++; + + if (link_error == 0 && num == table->count) + return; + + zlog_err ("PANIC !!"); + zlog_err ("Something has gone wrong with ospf6_route_table[%p]", table); + zlog_debug ("table count = %d, real number = %d", table->count, num); + zlog_debug ("DUMP START"); + for (r = ospf6_route_head (table); r; r = ospf6_route_next (r)) + { + prefix2str (&r->prefix, buf, sizeof (buf)); + zlog_info ("%p<-[%p]->%p : %s", r->prev, r, r->next, buf); + } + zlog_debug ("DUMP END"); + + assert (link_error == 0 && num == table->count); +} +#define ospf6_route_table_assert(t) (route_table_assert (t)) +#else +#define ospf6_route_table_assert(t) ((void) 0) +#endif /*DEBUG*/ + +struct ospf6_route * +ospf6_route_add (struct ospf6_route *route, + struct ospf6_route_table *table) +{ + struct route_node *node, *nextnode, *prevnode; + struct ospf6_route *current = NULL; + struct ospf6_route *prev = NULL, *old = NULL, *next = NULL; + char buf[64]; + struct timeval now; + + assert (route->rnode == NULL); + assert (route->lock == 0); + assert (route->next == NULL); + assert (route->prev == NULL); + + if (route->type == OSPF6_DEST_TYPE_LINKSTATE) + ospf6_linkstate_prefix2str (&route->prefix, buf, sizeof (buf)); + else + prefix2str (&route->prefix, buf, sizeof (buf)); + + if (IS_OSPF6_DEBUG_ROUTE (MEMORY)) + zlog_debug ("%s %p: route add %p: %s", ospf6_route_table_name (table), + (void *)table, (void *)route, buf); + else if (IS_OSPF6_DEBUG_ROUTE (TABLE)) + zlog_debug ("%s: route add: %s", ospf6_route_table_name (table), buf); + + quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); + + node = route_node_get (table->table, &route->prefix); + route->rnode = node; + + /* find place to insert */ + for (current = node->info; current; current = current->next) + { + if (! ospf6_route_is_same (current, route)) + next = current; + else if (current->type != route->type) + prev = current; + else if (ospf6_route_is_same_origin (current, route)) + old = current; + else if (ospf6_route_cmp (current, route) > 0) + next = current; + else + prev = current; + + if (old || next) + break; + } + + if (old) + { + /* if route does not actually change, return unchanged */ + if (ospf6_route_is_identical (old, route)) + { + if (IS_OSPF6_DEBUG_ROUTE (MEMORY)) + zlog_debug ("%s %p: route add %p: needless update of %p", + 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)); + + ospf6_route_delete (route); + SET_FLAG (old->flag, OSPF6_ROUTE_ADD); + ospf6_route_table_assert (table); + + return old; + } + + if (IS_OSPF6_DEBUG_ROUTE (MEMORY)) + zlog_debug ("%s %p: route add %p: update of %p", + 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)); + + /* replace old one if exists */ + if (node->info == old) + { + node->info = route; + SET_FLAG (route->flag, OSPF6_ROUTE_BEST); + } + + if (old->prev) + old->prev->next = route; + route->prev = old->prev; + if (old->next) + old->next->prev = route; + route->next = old->next; + + route->installed = old->installed; + route->changed = now; + assert (route->table == NULL); + route->table = table; + + ospf6_route_unlock (old); /* will be deleted later */ + ospf6_route_lock (route); + + SET_FLAG (route->flag, OSPF6_ROUTE_CHANGE); + ospf6_route_table_assert (table); + + if (table->hook_add) + (*table->hook_add) (route); + + return route; + } + + /* insert if previous or next node found */ + if (prev || next) + { + if (IS_OSPF6_DEBUG_ROUTE (MEMORY)) + zlog_debug ("%s %p: route add %p: another path: prev %p, next %p", + 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)); + + if (prev == NULL) + prev = next->prev; + if (next == NULL) + next = prev->next; + + if (prev) + prev->next = route; + route->prev = prev; + if (next) + next->prev = route; + route->next = next; + + if (node->info == next) + { + assert (next->rnode == node); + node->info = route; + UNSET_FLAG (next->flag, OSPF6_ROUTE_BEST); + 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), + (void *)table, (void *)route, (void *)next); + } + + route->installed = now; + route->changed = now; + assert (route->table == NULL); + route->table = table; + + ospf6_route_lock (route); + table->count++; + ospf6_route_table_assert (table); + + SET_FLAG (route->flag, OSPF6_ROUTE_ADD); + if (table->hook_add) + (*table->hook_add) (route); + + return 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), (void *)table, (void *)route); + else if (IS_OSPF6_DEBUG_ROUTE (TABLE)) + zlog_debug ("%s: route add: brand new route", + ospf6_route_table_name (table)); + + assert (node->info == NULL); + node->info = route; + SET_FLAG (route->flag, OSPF6_ROUTE_BEST); + ospf6_route_lock (route); + route->installed = now; + route->changed = now; + assert (route->table == NULL); + route->table = table; + + /* lookup real existing next route */ + nextnode = node; + route_lock_node (nextnode); + do { + nextnode = route_next (nextnode); + } while (nextnode && nextnode->info == NULL); + + /* set next link */ + if (nextnode == NULL) + route->next = NULL; + else + { + route_unlock_node (nextnode); + + next = nextnode->info; + route->next = next; + next->prev = route; + } + + /* lookup real existing prev route */ + prevnode = node; + route_lock_node (prevnode); + do { + prevnode = route_prev (prevnode); + } while (prevnode && prevnode->info == NULL); + + /* set prev link */ + if (prevnode == NULL) + route->prev = NULL; + else + { + route_unlock_node (prevnode); + + prev = prevnode->info; + while (prev->next && ospf6_route_is_same (prev, prev->next)) + prev = prev->next; + route->prev = prev; + prev->next = route; + } + + table->count++; + ospf6_route_table_assert (table); + + SET_FLAG (route->flag, OSPF6_ROUTE_ADD); + if (table->hook_add) + (*table->hook_add) (route); + + return route; +} + +void +ospf6_route_remove (struct ospf6_route *route, + struct ospf6_route_table *table) +{ + struct route_node *node; + struct ospf6_route *current; + char buf[64]; + + if (route->type == OSPF6_DEST_TYPE_LINKSTATE) + ospf6_linkstate_prefix2str (&route->prefix, buf, sizeof (buf)); + else + prefix2str (&route->prefix, buf, sizeof (buf)); + + if (IS_OSPF6_DEBUG_ROUTE (MEMORY)) + zlog_debug ("%s %p: route remove %p: %s", + 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); + + node = route_node_lookup (table->table, &route->prefix); + assert (node); + + /* find the route to remove, making sure that the route pointer + is from the route table. */ + current = node->info; + while (current && ospf6_route_is_same (current, route)) + { + if (current == route) + break; + current = current->next; + } + assert (current == route); + + /* adjust doubly linked list */ + if (route->prev) + route->prev->next = route->next; + if (route->next) + route->next->prev = route->prev; + + if (node->info == route) + { + if (route->next && route->next->rnode == node) + { + node->info = route->next; + SET_FLAG (route->next->flag, OSPF6_ROUTE_BEST); + } + else + node->info = NULL; /* should unlock route_node here ? */ + } + + table->count--; + ospf6_route_table_assert (table); + + SET_FLAG (route->flag, OSPF6_ROUTE_WAS_REMOVED); + + if (table->hook_remove) + (*table->hook_remove) (route); + + ospf6_route_unlock (route); +} + +struct ospf6_route * +ospf6_route_head (struct ospf6_route_table *table) +{ + struct route_node *node; + struct ospf6_route *route; + + node = route_top (table->table); + if (node == NULL) + return NULL; + + /* skip to the real existing entry */ + while (node && node->info == NULL) + node = route_next (node); + if (node == NULL) + return NULL; + + route_unlock_node (node); + assert (node->info); + + route = (struct ospf6_route *) node->info; + assert (route->prev == NULL); + assert (route->table == table); + ospf6_route_lock (route); + + if (IS_OSPF6_DEBUG_ROUTE (MEMORY)) + zlog_info ("%s %p: route head: %p<-[%p]->%p", + ospf6_route_table_name (table), (void *)table, + (void *)route->prev, (void *)route, (void *)route->next); + + return route; +} + +struct ospf6_route * +ospf6_route_next (struct ospf6_route *route) +{ + struct ospf6_route *next = route->next; + + if (IS_OSPF6_DEBUG_ROUTE (MEMORY)) + zlog_info ("%s %p: route next: %p<-[%p]->%p", + ospf6_route_table_name (route->table), (void *)route->table, + (void *)route->prev, (void *)route, (void *)route->next); + + ospf6_route_unlock (route); + if (next) + ospf6_route_lock (next); + + return next; +} + +struct ospf6_route * +ospf6_route_best_next (struct ospf6_route *route) +{ + struct route_node *rnode; + struct ospf6_route *next; + + ospf6_route_unlock (route); + + rnode = route->rnode; + route_lock_node (rnode); + rnode = route_next (rnode); + while (rnode && rnode->info == NULL) + rnode = route_next (rnode); + if (rnode == NULL) + return NULL; + route_unlock_node (rnode); + + assert (rnode->info); + next = (struct ospf6_route *) rnode->info; + ospf6_route_lock (next); + return next; +} + +struct ospf6_route * +ospf6_route_match_head (struct prefix *prefix, + struct ospf6_route_table *table) +{ + struct route_node *node; + struct ospf6_route *route; + + /* Walk down tree. */ + node = table->table->top; + while (node && node->p.prefixlen < prefix->prefixlen && + prefix_match (&node->p, prefix)) + node = node->link[prefix_bit(&prefix->u.prefix, node->p.prefixlen)]; + + if (node) + route_lock_node (node); + while (node && node->info == NULL) + node = route_next (node); + if (node == NULL) + return NULL; + route_unlock_node (node); + + if (! prefix_match (prefix, &node->p)) + return NULL; + + route = node->info; + ospf6_route_lock (route); + return route; +} + +struct ospf6_route * +ospf6_route_match_next (struct prefix *prefix, + struct ospf6_route *route) +{ + struct ospf6_route *next; + + next = ospf6_route_next (route); + if (next && ! prefix_match (prefix, &next->prefix)) + { + ospf6_route_unlock (next); + next = NULL; + } + + return next; +} + +void +ospf6_route_remove_all (struct ospf6_route_table *table) +{ + struct ospf6_route *route; + for (route = ospf6_route_head (table); route; + route = ospf6_route_next (route)) + ospf6_route_remove (route, table); +} + +struct ospf6_route_table * +ospf6_route_table_create (int s, int t) +{ + struct ospf6_route_table *new; + new = XCALLOC (MTYPE_OSPF6_ROUTE, sizeof (struct ospf6_route_table)); + new->table = route_table_init (); + new->scope_type = s; + new->table_type = t; + return new; +} + +void +ospf6_route_table_delete (struct ospf6_route_table *table) +{ + ospf6_route_remove_all (table); + route_table_finish (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]; + const char *ifname; + struct timeval now, res; + + quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); + timersub (&now, &route->changed, &res); + timerstring (&res, duration, sizeof (duration)); + + /* destination */ + if (route->type == OSPF6_DEST_TYPE_LINKSTATE) + ospf6_linkstate_prefix2str (&route->prefix, destination, + sizeof (destination)); + else if (route->type == OSPF6_DEST_TYPE_ROUTER) + inet_ntop (route->prefix.family, &route->prefix.u.prefix, + destination, sizeof (destination)); + else + prefix2str (&route->prefix, destination, sizeof (destination)); + + /* nexthop */ + inet_ntop (AF_INET6, &route->nexthop[0].address, nexthop, + sizeof (nexthop)); + ifname = ifindex2ifname (route->nexthop[0].ifindex); + + vty_out (vty, "%c%1s %2s %-30s %-25s %6.*s %s%s", + (ospf6_route_is_best (route) ? '*' : ' '), + OSPF6_DEST_TYPE_SUBSTR (route->type), + OSPF6_PATH_TYPE_SUBSTR (route->path.type), + destination, nexthop, IFNAMSIZ, ifname, duration, VNL); + + 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)); + ifname = ifindex2ifname (route->nexthop[i].ifindex); + + vty_out (vty, "%c%1s %2s %-30s %-25s %6.*s %s%s", + ' ', "", "", "", nexthop, IFNAMSIZ, ifname, "", VNL); + } +} + +void +ospf6_route_show_detail (struct vty *vty, struct ospf6_route *route) +{ + 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]; + int i; + + quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); + + /* destination */ + if (route->type == OSPF6_DEST_TYPE_LINKSTATE) + ospf6_linkstate_prefix2str (&route->prefix, destination, + sizeof (destination)); + else if (route->type == OSPF6_DEST_TYPE_ROUTER) + inet_ntop (route->prefix.family, &route->prefix.u.prefix, + destination, sizeof (destination)); + else + prefix2str (&route->prefix, destination, sizeof (destination)); + vty_out (vty, "Destination: %s%s", destination, VNL); + + /* destination type */ + vty_out (vty, "Destination type: %s%s", + OSPF6_DEST_TYPE_NAME (route->type), + VNL); + + /* Time */ + timersub (&now, &route->installed, &res); + timerstring (&res, duration, sizeof (duration)); + vty_out (vty, "Installed Time: %s ago%s", duration, VNL); + + timersub (&now, &route->changed, &res); + timerstring (&res, duration, sizeof (duration)); + vty_out (vty, " Changed Time: %s ago%s", duration, VNL); + + /* Debugging info */ + vty_out (vty, "Lock: %d Flags: %s%s%s%s%s", route->lock, + (CHECK_FLAG (route->flag, OSPF6_ROUTE_BEST) ? "B" : "-"), + (CHECK_FLAG (route->flag, OSPF6_ROUTE_ADD) ? "A" : "-"), + (CHECK_FLAG (route->flag, OSPF6_ROUTE_REMOVE) ? "R" : "-"), + (CHECK_FLAG (route->flag, OSPF6_ROUTE_CHANGE) ? "C" : "-"), + VNL); + vty_out (vty, "Memory: prev: %p this: %p next: %p%s", + (void *)route->prev, (void *)route, (void *)route->next, VNL); + + /* Path section */ + + /* Area-ID */ + inet_ntop (AF_INET, &route->path.area_id, area_id, sizeof (area_id)); + vty_out (vty, "Associated Area: %s%s", area_id, VNL); + + /* Path type */ + vty_out (vty, "Path Type: %s%s", + OSPF6_PATH_TYPE_NAME (route->path.type), VNL); + + /* LS Origin */ + inet_ntop (AF_INET, &route->path.origin.id, id, sizeof (id)); + inet_ntop (AF_INET, &route->path.origin.adv_router, adv_router, + sizeof (adv_router)); + vty_out (vty, "LS Origin: %s Id: %s Adv: %s%s", + ospf6_lstype_name (route->path.origin.type), + id, adv_router, VNL); + + /* Options */ + ospf6_options_printbuf (route->path.options, options, sizeof (options)); + vty_out (vty, "Options: %s%s", options, VNL); + + /* Router Bits */ + ospf6_capability_printbuf (route->path.router_bits, capa, sizeof (capa)); + vty_out (vty, "Router Bits: %s%s", capa, VNL); + + /* Prefix Options */ + vty_out (vty, "Prefix Options: xxx%s", VNL); + + /* Metrics */ + vty_out (vty, "Metric Type: %d%s", route->path.metric_type, + VNL); + vty_out (vty, "Metric: %d (%d)%s", + route->path.cost, route->path.cost_e2, VNL); + + /* Nexthops */ + vty_out (vty, "Nexthop:%s", VNL); + 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)); + ifname = ifindex2ifname (route->nexthop[i].ifindex); + vty_out (vty, " %s %.*s%s", nexthop, IFNAMSIZ, ifname, VNL); + } + vty_out (vty, "%s", VNL); +} + +static void +ospf6_route_show_table_summary (struct vty *vty, + struct ospf6_route_table *table) +{ + struct ospf6_route *route, *prev = NULL; + int i, pathtype[OSPF6_PATH_TYPE_MAX]; + unsigned int number = 0; + int nhinval = 0, ecmp = 0; + int alternative = 0, destination = 0; + + for (i = 0; i < OSPF6_PATH_TYPE_MAX; i++) + pathtype[i] = 0; + + for (route = ospf6_route_head (table); route; + route = ospf6_route_next (route)) + { + if (prev == NULL || ! ospf6_route_is_same (prev, route)) + destination++; + else + alternative++; + if (! ospf6_nexthop_is_set (&route->nexthop[0])) + nhinval++; + else if (ospf6_nexthop_is_set (&route->nexthop[1])) + ecmp++; + pathtype[route->path.type]++; + number++; + + prev = route; + } + + assert (number == table->count); + + vty_out (vty, "Number of OSPFv3 routes: %d%s", number, VNL); + vty_out (vty, "Number of Destination: %d%s", destination, VNL); + vty_out (vty, "Number of Alternative routes: %d%s", alternative, VNL); + vty_out (vty, "Number of Equal Cost Multi Path: %d%s", ecmp, VNL); + for (i = OSPF6_PATH_TYPE_INTRA; i <= OSPF6_PATH_TYPE_EXTERNAL2; i++) + { + vty_out (vty, "Number of %s routes: %d%s", + OSPF6_PATH_TYPE_NAME (i), pathtype[i], VNL); + } +} + +static void +ospf6_route_show_table_prefix (struct vty *vty, + struct prefix *prefix, + struct ospf6_route_table *table) +{ + struct ospf6_route *route; + + route = ospf6_route_lookup (prefix, table); + if (route == NULL) + return; + + ospf6_route_lock (route); + while (route && ospf6_route_is_prefix (prefix, route)) + { + /* Specifying a prefix will always display details */ + ospf6_route_show_detail (vty, route); + route = ospf6_route_next (route); + } + if (route) + ospf6_route_unlock (route); +} + +static void +ospf6_route_show_table_address (struct vty *vty, + struct prefix *prefix, + struct ospf6_route_table *table) +{ + struct ospf6_route *route; + + route = ospf6_route_lookup_bestmatch (prefix, table); + if (route == NULL) + return; + + prefix = &route->prefix; + ospf6_route_lock (route); + while (route && ospf6_route_is_prefix (prefix, route)) + { + /* Specifying a prefix will always display details */ + ospf6_route_show_detail (vty, route); + route = ospf6_route_next (route); + } + if (route) + ospf6_route_unlock (route); +} + +static void +ospf6_route_show_table_match (struct vty *vty, int detail, + struct prefix *prefix, + struct ospf6_route_table *table) +{ + struct ospf6_route *route; + assert (prefix->family); + + route = ospf6_route_match_head (prefix, table); + while (route) + { + if (detail) + ospf6_route_show_detail (vty, route); + else + ospf6_route_show (vty, route); + route = ospf6_route_match_next (prefix, route); + } +} + +static void +ospf6_route_show_table_type (struct vty *vty, int detail, u_char type, + struct ospf6_route_table *table) +{ + struct ospf6_route *route; + + route = ospf6_route_head (table); + while (route) + { + if (route->path.type == type) + { + if (detail) + ospf6_route_show_detail (vty, route); + else + ospf6_route_show (vty, route); + } + route = ospf6_route_next (route); + } +} + +static void +ospf6_route_show_table (struct vty *vty, int detail, + struct ospf6_route_table *table) +{ + struct ospf6_route *route; + + route = ospf6_route_head (table); + while (route) + { + if (detail) + ospf6_route_show_detail (vty, route); + else + ospf6_route_show (vty, route); + route = ospf6_route_next (route); + } +} + +int +ospf6_route_table_show (struct vty *vty, int argc, const char *argv[], + struct ospf6_route_table *table) +{ + int summary = 0; + int match = 0; + int detail = 0; + int slash = 0; + int isprefix = 0; + int i, ret; + struct prefix prefix; + u_char type = 0; + + memset (&prefix, 0, sizeof (struct prefix)); + + for (i = 0; i < argc; i++) + { + if (! strcmp (argv[i], "summary")) + { + summary++; + continue; + } + + if (! strcmp (argv[i], "intra-area")) + { + type = OSPF6_PATH_TYPE_INTRA; + continue; + } + + if (! strcmp (argv[i], "inter-area")) + { + type = OSPF6_PATH_TYPE_INTER; + continue; + } + + if (! strcmp (argv[i], "external-1")) + { + type = OSPF6_PATH_TYPE_EXTERNAL1; + continue; + } + + if (! strcmp (argv[i], "external-2")) + { + type = OSPF6_PATH_TYPE_EXTERNAL2; + continue; + } + + if (! strcmp (argv[i], "detail")) + { + detail++; + continue; + } + + if (! strcmp (argv[i], "match")) + { + match++; + continue; + } + + ret = str2prefix (argv[i], &prefix); + if (ret == 1 && prefix.family == AF_INET6) + { + isprefix++; + if (strchr (argv[i], '/')) + slash++; + continue; + } + + vty_out (vty, "Malformed argument: %s%s", argv[i], VNL); + return CMD_SUCCESS; + } + + /* Give summary of this route table */ + if (summary) + { + ospf6_route_show_table_summary (vty, table); + return CMD_SUCCESS; + } + + /* Give exact prefix-match route */ + if (isprefix && ! match) + { + /* If exact address, give best matching route */ + if (! slash) + ospf6_route_show_table_address (vty, &prefix, table); + else + ospf6_route_show_table_prefix (vty, &prefix, table); + + return CMD_SUCCESS; + } + + if (match) + ospf6_route_show_table_match (vty, detail, &prefix, table); + else if (type) + ospf6_route_show_table_type (vty, detail, type, table); + else + ospf6_route_show_table (vty, detail, table); + + return CMD_SUCCESS; +} + +static void +ospf6_linkstate_show_header (struct vty *vty) +{ + vty_out (vty, "%-7s %-15s %-15s %-8s %-14s %s%s", + "Type", "Router-ID", "Net-ID", "Rtr-Bits", "Options", "Cost", VNL); +} + +static void +ospf6_linkstate_show (struct vty *vty, struct ospf6_route *route) +{ + u_int32_t router, id; + char routername[16], idname[16], rbits[16], options[16]; + + router = ospf6_linkstate_prefix_adv_router (&route->prefix); + inet_ntop (AF_INET, &router, routername, sizeof (routername)); + id = ospf6_linkstate_prefix_id (&route->prefix); + inet_ntop (AF_INET, &id, idname, sizeof (idname)); + + ospf6_capability_printbuf (route->path.router_bits, rbits, sizeof (rbits)); + ospf6_options_printbuf (route->path.options, options, sizeof (options)); + + if (ntohl (id)) + vty_out (vty, "%-7s %-15s %-15s %-8s %-14s %lu%s", + "Network", routername, idname, rbits, options, + (unsigned long) route->path.cost, VNL); + else + vty_out (vty, "%-7s %-15s %-15s %-8s %-14s %lu%s", + "Router", routername, idname, rbits, options, + (unsigned long) route->path.cost, VNL); +} + + +static void +ospf6_linkstate_show_table_exact (struct vty *vty, + struct prefix *prefix, + struct ospf6_route_table *table) +{ + struct ospf6_route *route; + + route = ospf6_route_lookup (prefix, table); + if (route == NULL) + return; + + ospf6_route_lock (route); + while (route && ospf6_route_is_prefix (prefix, route)) + { + /* Specifying a prefix will always display details */ + ospf6_route_show_detail (vty, route); + route = ospf6_route_next (route); + } + if (route) + ospf6_route_unlock (route); +} + +static void +ospf6_linkstate_show_table (struct vty *vty, int detail, + struct ospf6_route_table *table) +{ + struct ospf6_route *route; + + if (! detail) + ospf6_linkstate_show_header (vty); + + route = ospf6_route_head (table); + while (route) + { + if (detail) + ospf6_route_show_detail (vty, route); + else + ospf6_linkstate_show (vty, route); + route = ospf6_route_next (route); + } +} + +int +ospf6_linkstate_table_show (struct vty *vty, int argc, const char *argv[], + struct ospf6_route_table *table) +{ + int detail = 0; + int is_id = 0; + int is_router = 0; + int i, ret; + struct prefix router, id, prefix; + + memset (&router, 0, sizeof (struct prefix)); + memset (&id, 0, sizeof (struct prefix)); + memset (&prefix, 0, sizeof (struct prefix)); + + for (i = 0; i < argc; i++) + { + if (! strcmp (argv[i], "detail")) + { + detail++; + continue; + } + + if (! is_router) + { + ret = str2prefix (argv[i], &router); + if (ret == 1 && router.family == AF_INET) + { + is_router++; + continue; + } + vty_out (vty, "Malformed argument: %s%s", argv[i], VNL); + return CMD_SUCCESS; + } + + if (! is_id) + { + ret = str2prefix (argv[i], &id); + if (ret == 1 && id.family == AF_INET) + { + is_id++; + continue; + } + vty_out (vty, "Malformed argument: %s%s", argv[i], VNL); + return CMD_SUCCESS; + } + + vty_out (vty, "Malformed argument: %s%s", argv[i], VNL); + return CMD_SUCCESS; + } + + if (is_router) + ospf6_linkstate_prefix (router.u.prefix4.s_addr, + id.u.prefix4.s_addr, &prefix); + + if (prefix.family) + ospf6_linkstate_show_table_exact (vty, &prefix, table); + else + ospf6_linkstate_show_table (vty, detail, table); + + return CMD_SUCCESS; +} + + +void +ospf6_brouter_show_header (struct vty *vty) +{ + vty_out (vty, "%-15s %-8s %-14s %-10s %-15s%s", + "Router-ID", "Rtr-Bits", "Options", "Path-Type", "Area", VNL); +} + +void +ospf6_brouter_show (struct vty *vty, struct ospf6_route *route) +{ + u_int32_t adv_router; + char adv[16], rbits[16], options[16], area[16]; + + adv_router = ospf6_linkstate_prefix_adv_router (&route->prefix); + inet_ntop (AF_INET, &adv_router, adv, sizeof (adv)); + ospf6_capability_printbuf (route->path.router_bits, rbits, sizeof (rbits)); + ospf6_options_printbuf (route->path.options, options, sizeof (options)); + inet_ntop (AF_INET, &route->path.area_id, area, sizeof (area)); + + /* vty_out (vty, "%-15s %-8s %-14s %-10s %-15s%s", + "Router-ID", "Rtr-Bits", "Options", "Path-Type", "Area", VNL); */ + vty_out (vty, "%-15s %-8s %-14s %-10s %-15s%s", + adv, rbits, options, OSPF6_PATH_TYPE_NAME (route->path.type), + area, VNL); +} + +DEFUN (debug_ospf6_route, + debug_ospf6_route_cmd, + "debug ospf6 route (table|intra-area|inter-area|memory)", + DEBUG_STR + OSPF6_STR + "Debug route table calculation\n" + "Debug detail\n" + "Debug intra-area route calculation\n" + "Debug inter-area route calculation\n" + "Debug route memory use\n" + ) +{ + unsigned char level = 0; + + if (! strncmp (argv[0], "table", 5)) + level = OSPF6_DEBUG_ROUTE_TABLE; + else if (! strncmp (argv[0], "intra", 5)) + level = OSPF6_DEBUG_ROUTE_INTRA; + else if (! strncmp (argv[0], "inter", 5)) + level = OSPF6_DEBUG_ROUTE_INTER; + else if (! strncmp (argv[0], "memor", 5)) + level = OSPF6_DEBUG_ROUTE_MEMORY; + OSPF6_DEBUG_ROUTE_ON (level); + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf6_route, + no_debug_ospf6_route_cmd, + "no debug ospf6 route (table|intra-area|inter-area|memory)", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug route table calculation\n" + "Debug intra-area route calculation\n" + "Debug route memory use\n") +{ + unsigned char level = 0; + + if (! strncmp (argv[0], "table", 5)) + level = OSPF6_DEBUG_ROUTE_TABLE; + else if (! strncmp (argv[0], "intra", 5)) + level = OSPF6_DEBUG_ROUTE_INTRA; + else if (! strncmp (argv[0], "inter", 5)) + level = OSPF6_DEBUG_ROUTE_INTER; + else if (! strncmp (argv[0], "memor", 5)) + level = OSPF6_DEBUG_ROUTE_MEMORY; + OSPF6_DEBUG_ROUTE_OFF (level); + return CMD_SUCCESS; +} + +int +config_write_ospf6_debug_route (struct vty *vty) +{ + if (IS_OSPF6_DEBUG_ROUTE (TABLE)) + vty_out (vty, "debug ospf6 route table%s", VNL); + if (IS_OSPF6_DEBUG_ROUTE (INTRA)) + vty_out (vty, "debug ospf6 route intra-area%s", VNL); + if (IS_OSPF6_DEBUG_ROUTE (INTER)) + vty_out (vty, "debug ospf6 route inter-area%s", VNL); + return 0; +} + +void +install_element_ospf6_debug_route (void) +{ + install_element (ENABLE_NODE, &debug_ospf6_route_cmd); + install_element (ENABLE_NODE, &no_debug_ospf6_route_cmd); + install_element (CONFIG_NODE, &debug_ospf6_route_cmd); + install_element (CONFIG_NODE, &no_debug_ospf6_route_cmd); +} + + + diff --git a/ospf6d/ospf6_route.h b/ospf6d/ospf6_route.h new file mode 100644 index 0000000..027a648 --- /dev/null +++ b/ospf6d/ospf6_route.h @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2003 Yasuhiro Ohara + * + * 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 OSPF6_ROUTE_H +#define OSPF6_ROUTE_H + +#define OSPF6_MULTI_PATH_LIMIT 4 + +/* Debug option */ +extern unsigned char conf_debug_ospf6_route; +#define OSPF6_DEBUG_ROUTE_TABLE 0x01 +#define OSPF6_DEBUG_ROUTE_INTRA 0x02 +#define OSPF6_DEBUG_ROUTE_INTER 0x04 +#define OSPF6_DEBUG_ROUTE_MEMORY 0x80 +#define OSPF6_DEBUG_ROUTE_ON(level) \ + (conf_debug_ospf6_route |= (level)) +#define OSPF6_DEBUG_ROUTE_OFF(level) \ + (conf_debug_ospf6_route &= ~(level)) +#define IS_OSPF6_DEBUG_ROUTE(e) \ + (conf_debug_ospf6_route & OSPF6_DEBUG_ROUTE_ ## e) + +/* Nexthop */ +struct ospf6_nexthop +{ + /* Interface index */ + ifindex_t ifindex; + + /* IP address, if any */ + struct in6_addr address; +}; + +#define ospf6_nexthop_is_set(x) \ + ((x)->ifindex || ! IN6_IS_ADDR_UNSPECIFIED (&(x)->address)) +#define ospf6_nexthop_is_same(a,b) \ + ((a)->ifindex == (b)->ifindex && \ + IN6_ARE_ADDR_EQUAL (&(a)->address, &(b)->address)) +#define ospf6_nexthop_clear(x) \ + do { \ + (x)->ifindex = 0; \ + memset (&(x)->address, 0, sizeof (struct in6_addr)); \ + } while (0) +#define ospf6_nexthop_copy(a, b) \ + do { \ + (a)->ifindex = (b)->ifindex; \ + memcpy (&(a)->address, &(b)->address, \ + sizeof (struct in6_addr)); \ + } while (0) + +/* Path */ +struct ospf6_ls_origin +{ + u_int16_t type; + u_int32_t id; + u_int32_t adv_router; +}; + +struct ospf6_path +{ + /* Link State Origin */ + struct ospf6_ls_origin origin; + + /* Router bits */ + u_char router_bits; + + /* Optional Capabilities */ + u_char options[3]; + + /* Prefix Options */ + u_char prefix_options; + + /* Associated Area */ + u_int32_t area_id; + + /* Path-type */ + u_char type; + u_char subtype; /* only used for redistribute i.e ZEBRA_ROUTE_XXX */ + + /* Cost */ + u_int8_t metric_type; + u_int32_t cost; + u_int32_t cost_e2; + u_int32_t tag; +}; + +#define OSPF6_PATH_TYPE_NONE 0 +#define OSPF6_PATH_TYPE_INTRA 1 +#define OSPF6_PATH_TYPE_INTER 2 +#define OSPF6_PATH_TYPE_EXTERNAL1 3 +#define OSPF6_PATH_TYPE_EXTERNAL2 4 +#define OSPF6_PATH_TYPE_REDISTRIBUTE 5 +#define OSPF6_PATH_TYPE_MAX 6 + +#include "prefix.h" +#include "table.h" + +struct ospf6_route +{ + struct route_node *rnode; + struct ospf6_route_table *table; + struct ospf6_route *prev; + struct ospf6_route *next; + + unsigned int lock; + + /* 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; + + /* Time */ + struct timeval installed; + struct timeval changed; + + /* flag */ + u_char flag; + + /* path */ + struct ospf6_path path; + + /* nexthop */ + struct ospf6_nexthop nexthop[OSPF6_MULTI_PATH_LIMIT]; + + /* route option */ + void *route_option; + + /* link state id for advertising */ + u_int32_t linkstate_id; +}; + +#define OSPF6_DEST_TYPE_NONE 0 +#define OSPF6_DEST_TYPE_ROUTER 1 +#define OSPF6_DEST_TYPE_NETWORK 2 +#define OSPF6_DEST_TYPE_DISCARD 3 +#define OSPF6_DEST_TYPE_LINKSTATE 4 +#define OSPF6_DEST_TYPE_RANGE 5 +#define OSPF6_DEST_TYPE_MAX 6 + +#define OSPF6_ROUTE_CHANGE 0x01 +#define OSPF6_ROUTE_ADD 0x02 +#define OSPF6_ROUTE_REMOVE 0x04 +#define OSPF6_ROUTE_BEST 0x08 +#define OSPF6_ROUTE_ACTIVE_SUMMARY 0x10 +#define OSPF6_ROUTE_DO_NOT_ADVERTISE 0x20 +#define OSPF6_ROUTE_WAS_REMOVED 0x40 + +struct ospf6_route_table +{ + int scope_type; + int table_type; + void *scope; + + /* patricia tree */ + struct route_table *table; + + u_int32_t count; + + /* hooks */ + void (*hook_add) (struct ospf6_route *); + void (*hook_change) (struct ospf6_route *); + void (*hook_remove) (struct ospf6_route *); +}; + +#define OSPF6_SCOPE_TYPE_NONE 0 +#define OSPF6_SCOPE_TYPE_GLOBAL 1 +#define OSPF6_SCOPE_TYPE_AREA 2 +#define OSPF6_SCOPE_TYPE_INTERFACE 3 + +#define OSPF6_TABLE_TYPE_NONE 0 +#define OSPF6_TABLE_TYPE_ROUTES 1 +#define OSPF6_TABLE_TYPE_BORDER_ROUTERS 2 +#define OSPF6_TABLE_TYPE_CONNECTED_ROUTES 3 +#define OSPF6_TABLE_TYPE_EXTERNAL_ROUTES 4 +#define OSPF6_TABLE_TYPE_SPF_RESULTS 5 +#define OSPF6_TABLE_TYPE_PREFIX_RANGES 6 +#define OSPF6_TABLE_TYPE_SUMMARY_PREFIXES 7 +#define OSPF6_TABLE_TYPE_SUMMARY_ROUTERS 8 + +#define OSPF6_ROUTE_TABLE_CREATE(s, t) \ + ospf6_route_table_create (OSPF6_SCOPE_TYPE_ ## s, \ + OSPF6_TABLE_TYPE_ ## t) + +extern const char *ospf6_dest_type_str[OSPF6_DEST_TYPE_MAX]; +extern const char *ospf6_dest_type_substr[OSPF6_DEST_TYPE_MAX]; +#define OSPF6_DEST_TYPE_NAME(x) \ + (0 < (x) && (x) < OSPF6_DEST_TYPE_MAX ? \ + ospf6_dest_type_str[(x)] : ospf6_dest_type_str[0]) +#define OSPF6_DEST_TYPE_SUBSTR(x) \ + (0 < (x) && (x) < OSPF6_DEST_TYPE_MAX ? \ + ospf6_dest_type_substr[(x)] : ospf6_dest_type_substr[0]) + +extern const char *ospf6_path_type_str[OSPF6_PATH_TYPE_MAX]; +extern const char *ospf6_path_type_substr[OSPF6_PATH_TYPE_MAX]; +#define OSPF6_PATH_TYPE_NAME(x) \ + (0 < (x) && (x) < OSPF6_PATH_TYPE_MAX ? \ + ospf6_path_type_str[(x)] : ospf6_path_type_str[0]) +#define OSPF6_PATH_TYPE_SUBSTR(x) \ + (0 < (x) && (x) < OSPF6_PATH_TYPE_MAX ? \ + ospf6_path_type_substr[(x)] : ospf6_path_type_substr[0]) + +#define OSPF6_ROUTE_ADDRESS_STR "Display the route bestmatches the address\n" +#define OSPF6_ROUTE_PREFIX_STR "Display the route\n" +#define OSPF6_ROUTE_MATCH_STR "Display the route matches the prefix\n" + +#define ospf6_route_is_prefix(p, r) \ + (memcmp (p, &(r)->prefix, sizeof (struct prefix)) == 0) +#define ospf6_route_is_same(ra, rb) \ + (prefix_same (&(ra)->prefix, &(rb)->prefix)) +#define ospf6_route_is_same_origin(ra, rb) \ + ((ra)->path.area_id == (rb)->path.area_id && \ + memcmp (&(ra)->path.origin, &(rb)->path.origin, \ + sizeof (struct ospf6_ls_origin)) == 0) +#define ospf6_route_is_identical(ra, rb) \ + ((ra)->type == (rb)->type && \ + memcmp (&(ra)->prefix, &(rb)->prefix, sizeof (struct prefix)) == 0 && \ + memcmp (&(ra)->path, &(rb)->path, sizeof (struct ospf6_path)) == 0 && \ + memcmp (&(ra)->nexthop, &(rb)->nexthop, \ + sizeof (struct ospf6_nexthop) * OSPF6_MULTI_PATH_LIMIT) == 0) +#define ospf6_route_is_best(r) (CHECK_FLAG ((r)->flag, OSPF6_ROUTE_BEST)) + +#define ospf6_linkstate_prefix_adv_router(x) \ + ((x)->u.lp.id.s_addr) +#define ospf6_linkstate_prefix_id(x) \ + ((x)->u.lp.adv_router.s_addr) + +#define ADV_ROUTER_IN_PREFIX(x) \ + ((x)->u.lp.id.s_addr) +#define ID_IN_PREFIX(x) \ + ((x)->u.lp.adv_router.s_addr) + +/* Function prototype */ +extern void ospf6_linkstate_prefix (u_int32_t adv_router, u_int32_t id, + struct prefix *prefix); +extern void ospf6_linkstate_prefix2str (struct prefix *prefix, char *buf, + int size); + +extern struct ospf6_route *ospf6_route_create (void); +extern void ospf6_route_delete (struct ospf6_route *); +extern struct ospf6_route *ospf6_route_copy (struct ospf6_route *route); + +extern void ospf6_route_lock (struct ospf6_route *route); +extern void ospf6_route_unlock (struct ospf6_route *route); + +extern struct ospf6_route *ospf6_route_lookup (struct prefix *prefix, + struct ospf6_route_table *table); +extern struct ospf6_route *ospf6_route_lookup_identical (struct ospf6_route *route, + struct ospf6_route_table *table); +extern struct ospf6_route *ospf6_route_lookup_bestmatch (struct prefix *prefix, + struct ospf6_route_table *table); + +extern struct ospf6_route *ospf6_route_add (struct ospf6_route *route, + struct ospf6_route_table *table); +extern void ospf6_route_remove (struct ospf6_route *route, + struct ospf6_route_table *table); + +extern struct ospf6_route *ospf6_route_head (struct ospf6_route_table *table); +extern struct ospf6_route *ospf6_route_next (struct ospf6_route *route); +extern struct ospf6_route *ospf6_route_best_next (struct ospf6_route *route); + +extern struct ospf6_route *ospf6_route_match_head (struct prefix *prefix, + struct ospf6_route_table *table); +extern struct ospf6_route *ospf6_route_match_next (struct prefix *prefix, + struct ospf6_route *route); + +extern void ospf6_route_remove_all (struct ospf6_route_table *); +extern struct ospf6_route_table *ospf6_route_table_create (int s, int t); +extern void ospf6_route_table_delete (struct ospf6_route_table *); +extern void ospf6_route_dump (struct ospf6_route_table *table); + + +extern void ospf6_route_show (struct vty *vty, struct ospf6_route *route); +extern void ospf6_route_show_detail (struct vty *vty, struct ospf6_route *route); + +extern int ospf6_route_table_show (struct vty *, int, const char *[], + struct ospf6_route_table *); +extern int ospf6_linkstate_table_show (struct vty *vty, int argc, + const char *argv[], + struct ospf6_route_table *table); + +extern void ospf6_brouter_show_header (struct vty *vty); +extern void ospf6_brouter_show (struct vty *vty, struct ospf6_route *route); + +extern int config_write_ospf6_debug_route (struct vty *vty); +extern void install_element_ospf6_debug_route (void); +extern void ospf6_route_init (void); +extern void ospf6_clean (void); + +#endif /* OSPF6_ROUTE_H */ + diff --git a/ospf6d/ospf6_snmp.c b/ospf6d/ospf6_snmp.c new file mode 100644 index 0000000..266031e --- /dev/null +++ b/ospf6d/ospf6_snmp.c @@ -0,0 +1,1183 @@ +/* OSPFv3 SNMP support + * Copyright (C) 2004 Yasuhiro Ohara + * + * 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 "log.h" +#include "vty.h" +#include "linklist.h" +#include "smux.h" + +#include "ospf6_proto.h" +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" +#include "ospf6_route.h" +#include "ospf6_top.h" +#include "ospf6_area.h" +#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,2,1,191 + +/* OSPFv3 MIB General Group values. */ +#define OSPFv3ROUTERID 1 +#define OSPFv3ADMINSTAT 2 +#define OSPFv3VERSIONNUMBER 3 +#define OSPFv3AREABDRRTRSTATUS 4 +#define OSPFv3ASBDRRTRSTATUS 5 +#define OSPFv3ASSCOPELSACOUNT 6 +#define OSPFv3ASSCOPELSACHECKSUMSUM 7 +#define OSPFv3ORIGINATENEWLSAS 8 +#define OSPFv3RXNEWLSAS 9 +#define OSPFv3EXTLSACOUNT 10 +#define OSPFv3EXTAREALSDBLIMIT 11 +#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 +#define OSPFv3AREAASBDRRTRCOUNT 5 +#define OSPFv3AREASCOPELSACOUNT 6 +#define OSPFv3AREASCOPELSACKSUMSUM 7 +#define OSPFv3AREASUMMARY 8 +#define OSPFv3AREAROWSTATUS 9 +#define OSPFv3AREASTUBMETRIC 10 +#define OSPFv3AREANSSATRANSLATORROLE 11 +#define OSPFv3AREANSSATRANSLATORSTATE 12 +#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 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 +#define OSPF_STATUS_DISABLED 2 + +/* SNMP value hack. */ +#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 + +/* For return values e.g. SNMP_INTEGER macro */ +SNMP_LOCAL_VARIABLES + +/* OSPFv3-MIB instances. */ +oid ospfv3_oid [] = { OSPFv3MIB }; +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 *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, UNSIGNED, RWRITE, ospfv3GeneralGroup, + 3, {1, 1, 1}}, + {OSPFv3ADMINSTAT, INTEGER, RWRITE, ospfv3GeneralGroup, + 3, {1, 1, 2}}, + {OSPFv3VERSIONNUMBER, INTEGER, RONLY, ospfv3GeneralGroup, + 3, {1, 1, 3}}, + {OSPFv3AREABDRRTRSTATUS, INTEGER, RONLY, ospfv3GeneralGroup, + 3, {1, 1, 4}}, + {OSPFv3ASBDRRTRSTATUS, INTEGER, RWRITE, ospfv3GeneralGroup, + 3, {1, 1, 5}}, + {OSPFv3ASSCOPELSACOUNT, GAUGE, RONLY, ospfv3GeneralGroup, + 3, {1, 1, 6}}, + {OSPFv3ASSCOPELSACHECKSUMSUM,UNSIGNED, RONLY, ospfv3GeneralGroup, + 3, {1, 1, 7}}, + {OSPFv3ORIGINATENEWLSAS, COUNTER, RONLY, ospfv3GeneralGroup, + 3, {1, 1, 8}}, + {OSPFv3RXNEWLSAS, COUNTER, RONLY, ospfv3GeneralGroup, + 3, {1, 1, 9}}, + {OSPFv3EXTLSACOUNT, GAUGE, RONLY, ospfv3GeneralGroup, + 3, {1, 1, 10}}, + {OSPFv3EXTAREALSDBLIMIT, INTEGER, RWRITE, ospfv3GeneralGroup, + 3, {1, 1, 11}}, + {OSPFv3EXITOVERFLOWINTERVAL, UNSIGNED, RWRITE, ospfv3GeneralGroup, + 3, {1, 1, 12}}, + {OSPFv3DEMANDEXTENSIONS, INTEGER, RWRITE, ospfv3GeneralGroup, + 3, {1, 1, 13}}, + {OSPFv3REFERENCEBANDWIDTH, UNSIGNED, RWRITE, ospfv3GeneralGroup, + 3, {1, 1, 14}}, + {OSPFv3RESTARTSUPPORT, INTEGER, RWRITE, ospfv3GeneralGroup, + 3, {1, 1, 15}}, + {OSPFv3RESTARTINTERVAL, UNSIGNED, RWRITE, ospfv3GeneralGroup, + 3, {1, 1, 16}}, + {OSPFv3RESTARTSTRICTLSACHECKING, INTEGER, RWRITE, ospfv3GeneralGroup, + 3, {1, 1, 17}}, + {OSPFv3RESTARTSTATUS, INTEGER, RONLY, ospfv3GeneralGroup, + 3, {1, 1, 18}}, + {OSPFv3RESTARTAGE, UNSIGNED, RONLY, ospfv3GeneralGroup, + 3, {1, 1, 19}}, + {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 */ + {OSPFv3IMPORTASEXTERN, INTEGER, RWRITE, ospfv3AreaEntry, + 4, {1, 2, 1, 2}}, + {OSPFv3AREASPFRUNS, COUNTER, RONLY, ospfv3AreaEntry, + 4, {1, 2, 1, 3}}, + {OSPFv3AREABDRRTRCOUNT, GAUGE, RONLY, ospfv3AreaEntry, + 4, {1, 2, 1, 4}}, + {OSPFv3AREAASBDRRTRCOUNT, GAUGE, RONLY, ospfv3AreaEntry, + 4, {1, 2, 1, 5}}, + {OSPFv3AREASCOPELSACOUNT, GAUGE, RONLY, ospfv3AreaEntry, + 4, {1, 2, 1, 6}}, + {OSPFv3AREASCOPELSACKSUMSUM, UNSIGNED, RONLY, ospfv3AreaEntry, + 4, {1, 2, 1, 7}}, + {OSPFv3AREASUMMARY, INTEGER, RWRITE, ospfv3AreaEntry, + 4, {1, 2, 1, 8}}, + {OSPFv3AREAROWSTATUS, INTEGER, RWRITE, ospfv3AreaEntry, + 4, {1, 2, 1, 9}}, + {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}}, + {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}}, + + /* 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}}, + {OSPFv3WWLSDBAGE | OSPFv3WWAREATABLE, UNSIGNED, RONLY, ospfv3WwLsdbEntry, + 4, {1, 4, 1, 6}}, + {OSPFv3WWLSDBCHECKSUM | OSPFv3WWAREATABLE, INTEGER, RONLY, ospfv3WwLsdbEntry, + 4, {1, 4, 1, 7}}, + {OSPFv3WWLSDBADVERTISEMENT | OSPFv3WWAREATABLE, STRING, RONLY, ospfv3WwLsdbEntry, + 4, {1, 4, 1, 8}}, + {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) + return NULL; + + /* Return the current value of the variable */ + switch (v->magic) + { + case OSPFv3ROUTERID: + /* Router-ID of this OSPF instance. */ + if (ospf6) + 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; +} + +static u_char * +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 >= 1 ? 1 : 0); + if (exact && len != 1) + return NULL; + if (len) + 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", + a, exact, len, (u_long)*length); + + for (ALL_LIST_ELEMENTS_RO (ospf6->area_list, node, oa)) + { + if (area == NULL) + { + if (len == 0) /* return first area entry */ + area = oa; + else if (exact && ntohl (oa->area_id) == ntohl (area_id)) + area = oa; + else if (ntohl (oa->area_id) > ntohl (area_id)) + area = oa; + } + } + + if (area == NULL) + return NULL; + + *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", + a, exact, len, (u_long)*length); + + switch (v->magic) + { + 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; + } + return NULL; +} + +static int +if_icmp_func (struct interface *ifp1, struct interface *ifp2) +{ + return (ifp1->ifindex - ifp2->ifindex); +} + +static u_char * +ospfv3WwLsdbEntry (struct variable *v, oid *name, size_t *length, + int exact, size_t *var_len, WriteMethod **write_method) +{ + struct ospf6_lsa *lsa = NULL; + ifindex_t ifindex, area_id, id, instid, adv_router; + u_int16_t type; + int len; + oid *offset; + int offsetlen; + 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; + + instid = ifindex = area_id = type = id = adv_router = 0; + + /* Check OSPFv3 instance. */ + if (ospf6 == NULL) + return NULL; + + /* Get variable length. */ + offset = name + v->namelen; + offsetlen = *length - v->namelen; + + 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; + + 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 ? 0 : 1); + if (len) + type = htons (*offset); + offset += len; + offsetlen -= len; + + /* Parse Router-ID */ + len = (offsetlen < 1 ? 0 : 1); + if (len) + adv_router = htonl (*offset); + offset += len; + offsetlen -= len; + + /* Parse LS-ID */ + len = (offsetlen < 1 ? 0 : 1); + if (len) + id = htonl (*offset); + offset += len; + offsetlen -= len; + + if (exact) + { + 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 + { + 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) + { + /* 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; + id = 0; + adv_router = 0; + oi = NULL; + } + + list_delete_all_node (ifslist); + } + } + + if (! lsa) + return NULL; + + /* 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++; + *offset = ntohl (lsa->header->adv_router); + offset++; + *offset = ntohl (lsa->header->id); + offset++; + + /* Return the current value of the variable */ + switch (v->magic & OSPFv3WWCOLUMN) + { + case OSPFv3WWLSDBSEQUENCE: + return SNMP_INTEGER (ntohl (lsa->header->seqnum)); + break; + case OSPFv3WWLSDBAGE: + ospf6_lsa_age_current (lsa); + return SNMP_INTEGER (ntohs (lsa->header->age)); + break; + case OSPFv3WWLSDBCHECKSUM: + return SNMP_INTEGER (ntohs (lsa->header->checksum)); + break; + case OSPFv3WWLSDBADVERTISEMENT: + *var_len = ntohs (lsa->header->length); + return (u_char *) lsa->header; + break; + case OSPFv3WWLSDBTYPEKNOWN: + return SNMP_INTEGER (OSPF6_LSA_IS_KNOWN (lsa->header->type) ? + SNMP_TRUE : SNMP_FALSE); + break; + } + 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 +ospf6_snmp_init (struct thread_master *master) +{ + smux_init (master); + REGISTER_MIB ("OSPFv3MIB", ospfv3_variables, variable, ospfv3_oid); +} + +#endif /* HAVE_SNMP */ + diff --git a/ospf6d/ospf6_snmp.h b/ospf6d/ospf6_snmp.h new file mode 100644 index 0000000..fa1b0c3 --- /dev/null +++ b/ospf6d/ospf6_snmp.h @@ -0,0 +1,31 @@ +/* OSPFv3 SNMP support + * Copyright (C) 2004 Yasuhiro Ohara + * + * 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 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 new file mode 100644 index 0000000..b6dbd0d --- /dev/null +++ b/ospf6d/ospf6_spf.c @@ -0,0 +1,918 @@ +/* + * Copyright (C) 2003 Yasuhiro Ohara + * + * 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. + */ + +/* Shortest Path First calculation for OSPFv3 */ + +#include + +#include "log.h" +#include "memory.h" +#include "command.h" +#include "vty.h" +#include "prefix.h" +#include "pqueue.h" +#include "linklist.h" +#include "thread.h" + +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" +#include "ospf6_route.h" +#include "ospf6_area.h" +#include "ospf6_spf.h" +#include "ospf6_intra.h" +#include "ospf6_interface.h" +#include "ospf6d.h" +#include "ospf6_abr.h" + +unsigned char conf_debug_ospf6_spf = 0; + +static int +ospf6_vertex_cmp (void *a, void *b) +{ + struct ospf6_vertex *va = (struct ospf6_vertex *) a; + struct ospf6_vertex *vb = (struct ospf6_vertex *) b; + + /* ascending order */ + if (va->cost != vb->cost) + return (va->cost - vb->cost); + return (va->hops - vb->hops); +} + +static int +ospf6_vertex_id_cmp (void *a, void *b) +{ + struct ospf6_vertex *va = (struct ospf6_vertex *) a; + struct ospf6_vertex *vb = (struct ospf6_vertex *) b; + int ret = 0; + + ret = ntohl (ospf6_linkstate_prefix_adv_router (&va->vertex_id)) - + ntohl (ospf6_linkstate_prefix_adv_router (&vb->vertex_id)); + if (ret) + return ret; + + ret = ntohl (ospf6_linkstate_prefix_id (&va->vertex_id)) - + ntohl (ospf6_linkstate_prefix_id (&vb->vertex_id)); + return ret; +} + +static struct ospf6_vertex * +ospf6_vertex_create (struct ospf6_lsa *lsa) +{ + struct ospf6_vertex *v; + int i; + + v = (struct ospf6_vertex *) + XMALLOC (MTYPE_OSPF6_VERTEX, sizeof (struct ospf6_vertex)); + + /* type */ + if (ntohs (lsa->header->type) == OSPF6_LSTYPE_ROUTER) + v->type = OSPF6_VERTEX_TYPE_ROUTER; + else if (ntohs (lsa->header->type) == OSPF6_LSTYPE_NETWORK) + v->type = OSPF6_VERTEX_TYPE_NETWORK; + else + assert (0); + + /* vertex_id */ + ospf6_linkstate_prefix (lsa->header->adv_router, lsa->header->id, + &v->vertex_id); + + /* name */ + ospf6_linkstate_prefix2str (&v->vertex_id, v->name, sizeof (v->name)); + + /* Associated LSA */ + v->lsa = lsa; + + /* capability bits + options */ + v->capability = *(u_char *)(OSPF6_LSA_HEADER_END (lsa->header)); + v->options[0] = *(u_char *)(OSPF6_LSA_HEADER_END (lsa->header) + 1); + v->options[1] = *(u_char *)(OSPF6_LSA_HEADER_END (lsa->header) + 2); + v->options[2] = *(u_char *)(OSPF6_LSA_HEADER_END (lsa->header) + 3); + + for (i = 0; i < OSPF6_MULTI_PATH_LIMIT; i++) + ospf6_nexthop_clear (&v->nexthop[i]); + + v->parent = NULL; + v->child_list = list_new (); + v->child_list->cmp = ospf6_vertex_id_cmp; + + return v; +} + +static void +ospf6_vertex_delete (struct ospf6_vertex *v) +{ + list_delete (v->child_list); + XFREE (MTYPE_OSPF6_VERTEX, v); +} + +static struct ospf6_lsa * +ospf6_lsdesc_lsa (caddr_t lsdesc, struct ospf6_vertex *v) +{ + struct ospf6_lsa *lsa; + u_int16_t type = 0; + u_int32_t id = 0, adv_router = 0; + + if (VERTEX_IS_TYPE (NETWORK, v)) + { + type = htons (OSPF6_LSTYPE_ROUTER); + id = htonl (0); + adv_router = NETWORK_LSDESC_GET_NBR_ROUTERID (lsdesc); + } + else + { + if (ROUTER_LSDESC_IS_TYPE (POINTTOPOINT, lsdesc)) + { + type = htons (OSPF6_LSTYPE_ROUTER); + id = htonl (0); + adv_router = ROUTER_LSDESC_GET_NBR_ROUTERID (lsdesc); + } + else if (ROUTER_LSDESC_IS_TYPE (TRANSIT_NETWORK, lsdesc)) + { + type = htons (OSPF6_LSTYPE_NETWORK); + id = htonl (ROUTER_LSDESC_GET_NBR_IFID (lsdesc)); + adv_router = ROUTER_LSDESC_GET_NBR_ROUTERID (lsdesc); + } + } + + lsa = ospf6_lsdb_lookup (type, id, adv_router, v->area->lsdb); + + if (IS_OSPF6_DEBUG_SPF (PROCESS)) + { + char ibuf[16], abuf[16]; + inet_ntop (AF_INET, &id, ibuf, sizeof (ibuf)); + inet_ntop (AF_INET, &adv_router, abuf, sizeof (abuf)); + if (lsa) + zlog_debug (" Link to: %s", lsa->name); + else + zlog_debug (" Link to: [%s Id:%s Adv:%s] No LSA", + ospf6_lstype_name (type), ibuf, abuf); + } + + return lsa; +} + +static char * +ospf6_lsdesc_backlink (struct ospf6_lsa *lsa, + caddr_t lsdesc, struct ospf6_vertex *v) +{ + caddr_t backlink, found = NULL; + int size; + + size = (OSPF6_LSA_IS_TYPE (ROUTER, lsa) ? + sizeof (struct ospf6_router_lsdesc) : + sizeof (struct ospf6_network_lsdesc)); + for (backlink = OSPF6_LSA_HEADER_END (lsa->header) + 4; + backlink + size <= OSPF6_LSA_END (lsa->header); backlink += size) + { + assert (! (OSPF6_LSA_IS_TYPE (NETWORK, lsa) && + VERTEX_IS_TYPE (NETWORK, v))); + + if (OSPF6_LSA_IS_TYPE (NETWORK, lsa) && + NETWORK_LSDESC_GET_NBR_ROUTERID (backlink) + == v->lsa->header->adv_router) + found = backlink; + else if (VERTEX_IS_TYPE (NETWORK, v) && + ROUTER_LSDESC_IS_TYPE (TRANSIT_NETWORK, backlink) && + ROUTER_LSDESC_GET_NBR_ROUTERID (backlink) + == v->lsa->header->adv_router && + ROUTER_LSDESC_GET_NBR_IFID (backlink) + == ntohl (v->lsa->header->id)) + found = backlink; + else + { + if (! ROUTER_LSDESC_IS_TYPE (POINTTOPOINT, backlink) || + ! ROUTER_LSDESC_IS_TYPE (POINTTOPOINT, lsdesc)) + continue; + if (ROUTER_LSDESC_GET_NBR_IFID (backlink) != + ROUTER_LSDESC_GET_IFID (lsdesc) || + ROUTER_LSDESC_GET_NBR_IFID (lsdesc) != + ROUTER_LSDESC_GET_IFID (backlink)) + continue; + if (ROUTER_LSDESC_GET_NBR_ROUTERID (backlink) != + v->lsa->header->adv_router || + ROUTER_LSDESC_GET_NBR_ROUTERID (lsdesc) != + lsa->header->adv_router) + continue; + found = backlink; + } + } + + if (IS_OSPF6_DEBUG_SPF (PROCESS)) + zlog_debug (" Backlink %s", (found ? "OK" : "FAIL")); + + return found; +} + +static void +ospf6_nexthop_calc (struct ospf6_vertex *w, struct ospf6_vertex *v, + caddr_t lsdesc) +{ + int i; + ifindex_t ifindex; + struct ospf6_interface *oi; + u_int16_t type; + u_int32_t adv_router; + struct ospf6_lsa *lsa; + struct ospf6_link_lsa *link_lsa; + char buf[64]; + + assert (VERTEX_IS_TYPE (ROUTER, w)); + ifindex = (VERTEX_IS_TYPE (NETWORK, v) ? v->nexthop[0].ifindex : + /* 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) + { + if (IS_OSPF6_DEBUG_SPF (PROCESS)) + zlog_debug ("Can't find interface in SPF: ifindex %d", ifindex); + return; + } + + type = htons (OSPF6_LSTYPE_LINK); + adv_router = (VERTEX_IS_TYPE (NETWORK, v) ? + NETWORK_LSDESC_GET_NBR_ROUTERID (lsdesc) : + ROUTER_LSDESC_GET_NBR_ROUTERID (lsdesc)); + + i = 0; + for (lsa = ospf6_lsdb_type_router_head (type, adv_router, oi->lsdb); lsa; + lsa = ospf6_lsdb_type_router_next (type, adv_router, lsa)) + { + if (VERTEX_IS_TYPE (ROUTER, v) && + htonl (ROUTER_LSDESC_GET_NBR_IFID (lsdesc)) != lsa->header->id) + continue; + + link_lsa = (struct ospf6_link_lsa *) OSPF6_LSA_HEADER_END (lsa->header); + if (IS_OSPF6_DEBUG_SPF (PROCESS)) + { + inet_ntop (AF_INET6, &link_lsa->linklocal_addr, buf, sizeof (buf)); + zlog_debug (" nexthop %s from %s", buf, lsa->name); + } + + if (i < OSPF6_MULTI_PATH_LIMIT) + { + memcpy (&w->nexthop[i].address, &link_lsa->linklocal_addr, + sizeof (struct in6_addr)); + w->nexthop[i].ifindex = ifindex; + i++; + } + } + + if (i == 0 && IS_OSPF6_DEBUG_SPF (PROCESS)) + zlog_debug ("No nexthop for %s found", w->name); +} + +static int +ospf6_spf_install (struct ospf6_vertex *v, + struct ospf6_route_table *result_table) +{ + struct ospf6_route *route; + int i, j; + struct ospf6_vertex *prev; + + if (IS_OSPF6_DEBUG_SPF (PROCESS)) + zlog_debug ("SPF install %s hops %d cost %d", + v->name, v->hops, v->cost); + + route = ospf6_route_lookup (&v->vertex_id, result_table); + if (route && route->path.cost < v->cost) + { + if (IS_OSPF6_DEBUG_SPF (PROCESS)) + zlog_debug (" already installed with lower cost (%d), ignore", + route->path.cost); + ospf6_vertex_delete (v); + return -1; + } + else if (route && route->path.cost == v->cost) + { + if (IS_OSPF6_DEBUG_SPF (PROCESS)) + zlog_debug (" another path found, merge"); + + 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++) + { + if (ospf6_nexthop_is_set (&route->nexthop[j])) + { + if (ospf6_nexthop_is_same (&route->nexthop[j], + &v->nexthop[i])) + break; + else + continue; + } + ospf6_nexthop_copy (&route->nexthop[j], &v->nexthop[i]); + break; + } + } + + prev = (struct ospf6_vertex *) route->route_option; + assert (prev->hops <= v->hops); + ospf6_vertex_delete (v); + + return -1; + } + + /* There should be no case where candidate being installed (variable + "v") is closer than the one in the SPF tree (variable "route"). + In the case something has gone wrong with the behavior of + Priority-Queue. */ + + /* the case where the route exists already is handled and returned + up to here. */ + assert (route == NULL); + + route = ospf6_route_create (); + memcpy (&route->prefix, &v->vertex_id, sizeof (struct prefix)); + route->type = OSPF6_DEST_TYPE_LINKSTATE; + route->path.type = OSPF6_PATH_TYPE_INTRA; + route->path.origin.type = v->lsa->header->type; + route->path.origin.id = v->lsa->header->id; + route->path.origin.adv_router = v->lsa->header->adv_router; + route->path.metric_type = 1; + route->path.cost = v->cost; + route->path.cost_e2 = v->hops; + route->path.router_bits = v->capability; + route->path.options[0] = v->options[0]; + route->path.options[1] = v->options[1]; + route->path.options[2] = v->options[2]; + + 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) + listnode_add_sort (v->parent->child_list, v); + route->route_option = v; + + ospf6_route_add (route, result_table); + return 0; +} + +void +ospf6_spf_table_finish (struct ospf6_route_table *result_table) +{ + struct ospf6_route *route, *nroute; + struct ospf6_vertex *v; + for (route = ospf6_route_head (result_table); 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 +ospf6_spf_calculation (u_int32_t router_id, + struct ospf6_route_table *result_table, + struct ospf6_area *oa) +{ + struct pqueue *candidate_list; + struct ospf6_vertex *root, *v, *w; + int i; + int size; + caddr_t lsdesc; + struct ospf6_lsa *lsa; + + ospf6_spf_table_finish (result_table); + + /* Install the calculating router itself as the root of the SPF tree */ + /* construct root vertex */ + lsa = ospf6_lsdb_lookup (htons (OSPF6_LSTYPE_ROUTER), htonl (0), + router_id, oa->lsdb); + if (lsa == NULL) + return; + + /* initialize */ + candidate_list = pqueue_create (); + candidate_list->cmp = ospf6_vertex_cmp; + + root = ospf6_vertex_create (lsa); + root->area = oa; + root->cost = 0; + root->hops = 0; + root->nexthop[0].ifindex = 0; /* loopbak I/F is better ... */ + inet_pton (AF_INET6, "::1", &root->nexthop[0].address); + + /* Actually insert root to the candidate-list as the only candidate */ + pqueue_enqueue (root, candidate_list); + + /* Iterate until candidate-list becomes empty */ + while (candidate_list->size) + { + /* get closest candidate from priority queue */ + v = pqueue_dequeue (candidate_list); + + /* installing may result in merging or rejecting of the vertex */ + 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) : + sizeof (struct ospf6_network_lsdesc)); + for (lsdesc = OSPF6_LSA_HEADER_END (v->lsa->header) + 4; + lsdesc + size <= OSPF6_LSA_END (v->lsa->header); lsdesc += size) + { + lsa = ospf6_lsdesc_lsa (lsdesc, v); + if (lsa == NULL) + continue; + + if (! ospf6_lsdesc_backlink (lsa, lsdesc, v)) + continue; + + w = ospf6_vertex_create (lsa); + w->area = oa; + w->parent = v; + if (VERTEX_IS_TYPE (ROUTER, v)) + { + w->cost = v->cost + ROUTER_LSDESC_GET_METRIC (lsdesc); + w->hops = v->hops + (VERTEX_IS_TYPE (NETWORK, w) ? 0 : 1); + } + else /* NETWORK */ + { + w->cost = v->cost; + w->hops = v->hops + 1; + } + + /* nexthop calculation */ + if (w->hops == 0) + w->nexthop[0].ifindex = ROUTER_LSDESC_GET_IFID (lsdesc); + else if (w->hops == 1 && v->hops == 0) + ospf6_nexthop_calc (w, v, lsdesc); + else + { + 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]); + } + + /* add new candidate to the candidate_list */ + if (IS_OSPF6_DEBUG_SPF (PROCESS)) + zlog_debug (" New candidate: %s hops %d cost %d", + w->name, w->hops, w->cost); + pqueue_enqueue (w, candidate_list); + } + } + + pqueue_delete (candidate_list); + + oa->spf_calculation++; +} + +static void +ospf6_spf_log_database (struct ospf6_area *oa) +{ + char *p, *end, buffer[256]; + struct listnode *node; + struct ospf6_interface *oi; + + p = buffer; + end = buffer + sizeof (buffer); + + snprintf (p, end - p, "SPF on DB (#LSAs):"); + p = (buffer + strlen (buffer) < end ? buffer + strlen (buffer) : end); + snprintf (p, end - p, " Area %s: %d", oa->name, oa->lsdb->count); + p = (buffer + strlen (buffer) < end ? buffer + strlen (buffer) : end); + + for (ALL_LIST_ELEMENTS_RO (oa->if_list, node, oi)) + { + snprintf (p, end - p, " I/F %s: %d", + oi->interface->name, oi->lsdb->count); + p = (buffer + strlen (buffer) < end ? buffer + strlen (buffer) : end); + } + + zlog_debug ("%s", buffer); +} + +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]; + + ospf6 = (struct ospf6 *)THREAD_ARG (t); + ospf6->t_spf_calc = NULL; + + /* execute SPF calculation */ + quagga_gettime (QUAGGA_CLK_MONOTONIC, &start); + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) + { + + if (oa == ospf6->backbone) + continue; + + if (IS_OSPF6_DEBUG_SPF (PROCESS)) + zlog_debug ("SPF calculation for Area %s", oa->name); + if (IS_OSPF6_DEBUG_SPF (DATABASE)) + ospf6_spf_log_database (oa); + + ospf6_spf_calculation (ospf6->router_id, oa->spf_table, oa); + ospf6_intra_route_calculation (oa); + ospf6_intra_brouter_calculation (oa); + + areas_processed++; + } + + if (ospf6->backbone) + { + if (IS_OSPF6_DEBUG_SPF (PROCESS)) + zlog_debug ("SPF calculation for Backbone area %s", + ospf6->backbone->name); + if (IS_OSPF6_DEBUG_SPF (DATABASE)) + ospf6_spf_log_database(ospf6->backbone); + + ospf6_spf_calculation(ospf6->router_id, ospf6->backbone->spf_table, + ospf6->backbone); + ospf6_intra_route_calculation(ospf6->backbone); + ospf6_intra_brouter_calculation(ospf6->backbone); + areas_processed++; + } + + /* Redo summaries if required */ + for (route = ospf6_route_head (ospf6->route_table); route; + route = ospf6_route_next (route)) + ospf6_abr_originate_summary(route); + + quagga_gettime (QUAGGA_CLK_MONOTONIC, &end); + timersub (&end, &start, &runtime); + + ospf6->ts_spf_duration = runtime; + + ospf6_spf_reason_string(ospf6->spf_reason, rbuf, sizeof(rbuf)); + + if (IS_OSPF6_DEBUG_SPF (PROCESS) || IS_OSPF6_DEBUG_SPF (TIME)) + zlog_debug ("SPF runtime: %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 *ospf6, unsigned int reason) +{ + 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; + + /* 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 +ospf6_spf_display_subtree (struct vty *vty, const char *prefix, int rest, + struct ospf6_vertex *v) +{ + struct listnode *node, *nnode; + struct ospf6_vertex *c; + char *next_prefix; + int len; + int restnum; + + /* "prefix" is the space prefix of the display line */ + vty_out (vty, "%s+-%s [%d]%s", prefix, v->name, v->cost, VNL); + + len = strlen (prefix) + 4; + next_prefix = (char *) malloc (len); + if (next_prefix == NULL) + { + vty_out (vty, "malloc failed%s", VNL); + return; + } + snprintf (next_prefix, len, "%s%s", prefix, (rest ? "| " : " ")); + + restnum = listcount (v->child_list); + for (ALL_LIST_ELEMENTS (v->child_list, node, nnode, c)) + { + restnum--; + ospf6_spf_display_subtree (vty, next_prefix, restnum, c); + } + + free (next_prefix); +} + +DEFUN (debug_ospf6_spf_process, + debug_ospf6_spf_process_cmd, + "debug ospf6 spf process", + DEBUG_STR + OSPF6_STR + "Debug SPF Calculation\n" + "Debug Detailed SPF Process\n" + ) +{ + unsigned char level = 0; + level = OSPF6_DEBUG_SPF_PROCESS; + OSPF6_DEBUG_SPF_ON (level); + return CMD_SUCCESS; +} + +DEFUN (debug_ospf6_spf_time, + debug_ospf6_spf_time_cmd, + "debug ospf6 spf time", + DEBUG_STR + OSPF6_STR + "Debug SPF Calculation\n" + "Measure time taken by SPF Calculation\n" + ) +{ + unsigned char level = 0; + level = OSPF6_DEBUG_SPF_TIME; + OSPF6_DEBUG_SPF_ON (level); + return CMD_SUCCESS; +} + +DEFUN (debug_ospf6_spf_database, + debug_ospf6_spf_database_cmd, + "debug ospf6 spf database", + DEBUG_STR + OSPF6_STR + "Debug SPF Calculation\n" + "Log number of LSAs at SPF Calculation time\n" + ) +{ + unsigned char level = 0; + level = OSPF6_DEBUG_SPF_DATABASE; + OSPF6_DEBUG_SPF_ON (level); + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf6_spf_process, + no_debug_ospf6_spf_process_cmd, + "no debug ospf6 spf process", + NO_STR + DEBUG_STR + OSPF6_STR + "Quit Debugging SPF Calculation\n" + "Quit Debugging Detailed SPF Process\n" + ) +{ + unsigned char level = 0; + level = OSPF6_DEBUG_SPF_PROCESS; + OSPF6_DEBUG_SPF_OFF (level); + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf6_spf_time, + no_debug_ospf6_spf_time_cmd, + "no debug ospf6 spf time", + NO_STR + DEBUG_STR + OSPF6_STR + "Quit Debugging SPF Calculation\n" + "Quit Measuring time taken by SPF Calculation\n" + ) +{ + unsigned char level = 0; + level = OSPF6_DEBUG_SPF_TIME; + OSPF6_DEBUG_SPF_OFF (level); + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf6_spf_database, + no_debug_ospf6_spf_database_cmd, + "no debug ospf6 spf database", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug SPF Calculation\n" + "Quit Logging number of LSAs at SPF Calculation time\n" + ) +{ + unsigned char level = 0; + level = OSPF6_DEBUG_SPF_DATABASE; + OSPF6_DEBUG_SPF_OFF (level); + 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) +{ + if (IS_OSPF6_DEBUG_SPF (PROCESS)) + vty_out (vty, "debug ospf6 spf process%s", VNL); + if (IS_OSPF6_DEBUG_SPF (TIME)) + vty_out (vty, "debug ospf6 spf time%s", VNL); + if (IS_OSPF6_DEBUG_SPF (DATABASE)) + vty_out (vty, "debug ospf6 spf database%s", VNL); + 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) +{ + install_element (ENABLE_NODE, &debug_ospf6_spf_process_cmd); + install_element (ENABLE_NODE, &debug_ospf6_spf_time_cmd); + install_element (ENABLE_NODE, &debug_ospf6_spf_database_cmd); + install_element (ENABLE_NODE, &no_debug_ospf6_spf_process_cmd); + install_element (ENABLE_NODE, &no_debug_ospf6_spf_time_cmd); + install_element (ENABLE_NODE, &no_debug_ospf6_spf_database_cmd); + install_element (CONFIG_NODE, &debug_ospf6_spf_process_cmd); + install_element (CONFIG_NODE, &debug_ospf6_spf_time_cmd); + install_element (CONFIG_NODE, &debug_ospf6_spf_database_cmd); + install_element (CONFIG_NODE, &no_debug_ospf6_spf_process_cmd); + install_element (CONFIG_NODE, &no_debug_ospf6_spf_time_cmd); + install_element (CONFIG_NODE, &no_debug_ospf6_spf_database_cmd); +} + +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 new file mode 100644 index 0000000..b3481dc --- /dev/null +++ b/ospf6d/ospf6_spf.h @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2003 Yasuhiro Ohara + * + * 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 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 +#define OSPF6_DEBUG_SPF_TIME 0x02 +#define OSPF6_DEBUG_SPF_DATABASE 0x04 +#define OSPF6_DEBUG_SPF_ON(level) \ + (conf_debug_ospf6_spf |= (level)) +#define OSPF6_DEBUG_SPF_OFF(level) \ + (conf_debug_ospf6_spf &= ~(level)) +#define IS_OSPF6_DEBUG_SPF(level) \ + (conf_debug_ospf6_spf & OSPF6_DEBUG_SPF_ ## level) + +/* Transit Vertex */ +struct ospf6_vertex +{ + /* type of this vertex */ + u_int8_t type; + + /* Vertex Identifier */ + struct prefix vertex_id; + + /* Identifier String */ + char name[128]; + + /* Associated Area */ + struct ospf6_area *area; + + /* Associated LSA */ + struct ospf6_lsa *lsa; + + /* Distance from Root (i.e. Cost) */ + u_int32_t cost; + + /* Router hops to this node */ + u_char hops; + + /* nexthops to this node */ + struct ospf6_nexthop nexthop[OSPF6_MULTI_PATH_LIMIT]; + + /* capability bits */ + u_char capability; + + /* Optional capabilities */ + u_char options[3]; + + /* For tree display */ + struct ospf6_vertex *parent; + struct list *child_list; +}; + +#define OSPF6_VERTEX_TYPE_ROUTER 0x01 +#define OSPF6_VERTEX_TYPE_NETWORK 0x02 +#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 *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 new file mode 100644 index 0000000..7ec4309 --- /dev/null +++ b/ospf6d/ospf6_top.c @@ -0,0 +1,1463 @@ +/* + * Copyright (C) 2003 Yasuhiro Ohara + * + * 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 "memory.h" +#include "vty.h" +#include "linklist.h" +#include "prefix.h" +#include "table.h" +#include "thread.h" +#include "command.h" + +#include "ospf6_proto.h" +#include "ospf6_message.h" +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" +#include "ospf6_route.h" +#include "ospf6_zebra.h" + +#include "ospf6_top.h" +#include "ospf6_area.h" +#include "ospf6_interface.h" +#include "ospf6_neighbor.h" + +#include "ospf6_flood.h" +#include "ospf6_asbr.h" +#include "ospf6_abr.h" +#include "ospf6_intra.h" +#include "ospf6_spf.h" +#include "ospf6d.h" + +/* global ospf6d variable */ +struct ospf6 *ospf6; + +static void ospf6_disable (struct ospf6 *o); + +static void +ospf6_top_lsdb_hook_add (struct ospf6_lsa *lsa) +{ + switch (ntohs (lsa->header->type)) + { + case OSPF6_LSTYPE_AS_EXTERNAL: + ospf6_asbr_lsa_add (lsa); + break; + + default: + break; + } +} + +static void +ospf6_top_lsdb_hook_remove (struct ospf6_lsa *lsa) +{ + switch (ntohs (lsa->header->type)) + { + case OSPF6_LSTYPE_AS_EXTERNAL: + ospf6_asbr_lsa_remove (lsa); + break; + + default: + break; + } +} + +static void +ospf6_top_route_hook_add (struct ospf6_route *route) +{ + ospf6_abr_originate_summary (route); + ospf6_zebra_route_update_add (route); +} + +static void +ospf6_top_route_hook_remove (struct ospf6_route *route) +{ + ospf6_abr_originate_summary (route); + ospf6_zebra_route_update_remove (route); +} + +static void +ospf6_top_brouter_hook_add (struct ospf6_route *route) +{ + ospf6_abr_examin_brouter (ADV_ROUTER_IN_PREFIX (&route->prefix)); + ospf6_asbr_lsentry_add (route); + ospf6_abr_originate_summary (route); +} + +static void +ospf6_top_brouter_hook_remove (struct ospf6_route *route) +{ + ospf6_abr_examin_brouter (ADV_ROUTER_IN_PREFIX (&route->prefix)); + ospf6_asbr_lsentry_remove (route); + ospf6_abr_originate_summary (route); +} + +static struct ospf6 * +ospf6_create (void) +{ + struct ospf6 *o; + + o = XCALLOC (MTYPE_OSPF6_TOP, sizeof (struct ospf6)); + + /* initialize */ + quagga_gettime (QUAGGA_CLK_MONOTONIC, &o->starttime); + o->area_list = list_new (); + o->area_list->cmp = ospf6_area_cmp; + o->lsdb = ospf6_lsdb_create (o); + o->lsdb_self = ospf6_lsdb_create (o); + 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; + o->route_table->hook_remove = ospf6_top_route_hook_remove; + + o->brouter_table = OSPF6_ROUTE_TABLE_CREATE (GLOBAL, BORDER_ROUTERS); + o->brouter_table->scope = o; + o->brouter_table->hook_add = ospf6_top_brouter_hook_add; + o->brouter_table->hook_remove = ospf6_top_brouter_hook_remove; + + o->external_table = OSPF6_ROUTE_TABLE_CREATE (GLOBAL, EXTERNAL_ROUTES); + o->external_table->scope = o; + + o->external_id_table = route_table_init (); + + o->ref_bandwidth = OSPF6_REFERENCE_BANDWIDTH; + + o->distance_table = route_table_init (); + + return o; +} + +void +ospf6_delete (struct ospf6 *o) +{ + struct listnode *node, *nnode; + struct ospf6_area *oa; + + ospf6_disable (ospf6); + + for (ALL_LIST_ELEMENTS (o->area_list, node, nnode, oa)) + ospf6_area_delete (oa); + + + list_delete (o->area_list); + + ospf6_lsdb_delete (o->lsdb); + ospf6_lsdb_delete (o->lsdb_self); + + ospf6_route_table_delete (o->route_table); + ospf6_route_table_delete (o->brouter_table); + + 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; + struct ospf6_area *oa; + + if (CHECK_FLAG (o->flag, OSPF6_DISABLED)) + { + UNSET_FLAG (o->flag, OSPF6_DISABLED); + for (ALL_LIST_ELEMENTS (o->area_list, node, nnode, oa)) + ospf6_area_enable (oa); + } +} + +static void +ospf6_disable (struct ospf6 *o) +{ + struct listnode *node, *nnode; + struct ospf6_area *oa; + + if (! CHECK_FLAG (o->flag, OSPF6_DISABLED)) + { + SET_FLAG (o->flag, OSPF6_DISABLED); + + 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); + } +} + +static int +ospf6_maxage_remover (struct thread *thread) +{ + struct ospf6 *o = (struct ospf6 *) THREAD_ARG (thread); + struct ospf6_area *oa; + struct ospf6_interface *oi; + struct ospf6_neighbor *on; + struct listnode *i, *j, *k; + int reschedule = 0; + + o->maxage_remover = (struct thread *) NULL; + + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + { + for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi)) + { + for (ALL_LIST_ELEMENTS_RO (oi->neighbor_list, k, on)) + { + if (on->state != OSPF6_NEIGHBOR_EXCHANGE && + on->state != OSPF6_NEIGHBOR_LOADING) + continue; + + ospf6_maxage_remove (o); + return 0; + } + } + } + + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + { + for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi)) + { + if (ospf6_lsdb_maxage_remover (oi->lsdb)) + { + reschedule = 1; + } + } + + if (ospf6_lsdb_maxage_remover (oa->lsdb)) + { + reschedule = 1; + } + } + + if (ospf6_lsdb_maxage_remover (o->lsdb)) + { + reschedule = 1; + } + + if (reschedule) + { + ospf6_maxage_remove (o); + } + + return 0; +} + +void +ospf6_maxage_remove (struct ospf6 *o) +{ + if (o && ! o->maxage_remover) + o->maxage_remover = thread_add_timer (master, ospf6_maxage_remover, o, + OSPF_LSA_MAXAGE_REMOVE_DELAY_DEFAULT); +} + +/* start ospf6 */ +DEFUN (router_ospf6, + router_ospf6_cmd, + "router ospf6", + ROUTER_STR + OSPF6_STR) +{ + if (ospf6 == NULL) + ospf6 = ospf6_create (); + + /* set current ospf point. */ + vty->node = OSPF6_NODE; + vty->index = ospf6; + + return CMD_SUCCESS; +} + +/* stop ospf6 */ +DEFUN (no_router_ospf6, + no_router_ospf6_cmd, + "no router ospf6", + NO_STR + OSPF6_ROUTER_STR) +{ + if (ospf6 == NULL) + vty_out (vty, "OSPFv3 is not configured%s", VNL); + else + { + ospf6_delete (ospf6); + ospf6 = NULL; + } + + /* return to config node . */ + vty->node = CONFIG_NODE; + vty->index = NULL; + + return CMD_SUCCESS; +} + +/* change Router_ID commands. */ +DEFUN (ospf6_router_id, + ospf6_router_id_cmd, + "router-id A.B.C.D", + "Configure OSPF Router-ID\n" + V4NOTATION_STR) +{ + int ret; + u_int32_t router_id; + struct ospf6 *o; + + o = (struct ospf6 *) vty->index; + + ret = inet_pton (AF_INET, argv[0], &router_id); + if (ret == 0) + { + vty_out (vty, "malformed OSPF Router-ID: %s%s", argv[0], VNL); + return CMD_SUCCESS; + } + + o->router_id_static = router_id; + if (o->router_id == 0) + o->router_id = 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", + "Enable routing on an IPv6 interface\n" + IFNAME_STR + "Specify the OSPF6 area ID\n" + "OSPF6 area ID in IPv4 address notation\n" + ) +{ + struct ospf6 *o; + struct ospf6_area *oa; + struct ospf6_interface *oi; + struct interface *ifp; + u_int32_t area_id; + + o = (struct ospf6 *) vty->index; + + /* find/create ospf6 interface */ + ifp = if_get_by_name (argv[0]); + oi = (struct ospf6_interface *) ifp->info; + if (oi == NULL) + oi = ospf6_interface_create (ifp); + if (oi->area) + { + vty_out (vty, "%s already attached to Area %s%s", + oi->interface->name, oi->area->name, VNL); + return CMD_SUCCESS; + } + + /* parse Area-ID */ + if (inet_pton (AF_INET, argv[1], &area_id) != 1) + { + vty_out (vty, "Invalid Area-ID: %s%s", argv[1], VNL); + return CMD_SUCCESS; + } + + /* find/create ospf6 area */ + oa = ospf6_area_lookup (area_id, o); + if (oa == NULL) + oa = ospf6_area_create (area_id, o); + + /* attach interface to area */ + listnode_add (oa->if_list, oi); /* sort ?? */ + oi->area = oa; + + 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 */ + ospf6_interface_enable (oi); + + /* If the router is ABR, originate summary routes */ + if (ospf6_is_router_abr (o)) + ospf6_abr_enable_area (oa); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf6_interface_area, + no_ospf6_interface_area_cmd, + "no interface IFNAME area A.B.C.D", + NO_STR + "Disable routing on an IPv6 interface\n" + IFNAME_STR + "Specify the OSPF6 area ID\n" + "OSPF6 area ID in IPv4 address notation\n" + ) +{ + struct ospf6_interface *oi; + struct ospf6_area *oa; + struct interface *ifp; + u_int32_t area_id; + + ifp = if_lookup_by_name (argv[0]); + if (ifp == NULL) + { + vty_out (vty, "No such interface %s%s", argv[0], VNL); + return CMD_SUCCESS; + } + + oi = (struct ospf6_interface *) ifp->info; + if (oi == NULL) + { + vty_out (vty, "Interface %s not enabled%s", ifp->name, VNL); + return CMD_SUCCESS; + } + + /* parse Area-ID */ + if (inet_pton (AF_INET, argv[1], &area_id) != 1) + { + vty_out (vty, "Invalid Area-ID: %s%s", argv[1], VNL); + return CMD_SUCCESS; + } + + /* Verify Area */ + if (oi->area == NULL) + { + vty_out (vty, "No such Area-ID: %s%s", argv[1], VNL); + return CMD_SUCCESS; + } + + if (oi->area->area_id != area_id) + { + vty_out (vty, "Wrong Area-ID: %s is attached to area %s%s", + oi->interface->name, oi->area->name, VNL); + return CMD_SUCCESS; + } + + thread_execute (master, interface_down, oi, 0); + + oa = oi->area; + listnode_delete (oi->area->if_list, oi); + oi->area = (struct ospf6_area *) NULL; + + /* Withdraw inter-area routes from this area, if necessary */ + if (oa->if_list->count == 0) + { + UNSET_FLAG (oa->flag, OSPF6_AREA_ENABLE); + ospf6_abr_disable_area (oa); + } + + 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, result; + char buf[32], rbuf[32]; + + /* process id, router id */ + inet_ntop (AF_INET, &o->router_id, router_id, sizeof (router_id)); + vty_out (vty, " OSPFv3 Routing Process (0) with Router-ID %s%s", + router_id, VNL); + + /* running time */ + quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); + timersub (&now, &o->starttime, &running); + timerstring (&running, duration, sizeof (duration)); + vty_out (vty, " Running %s%s", duration, VNL); + + /* 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); + + /* Areas */ + 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); +} + +/* show top level structures */ +DEFUN (show_ipv6_ospf6, + show_ipv6_ospf6_cmd, + "show ipv6 ospf6", + SHOW_STR + IP6_STR + OSPF6_STR) +{ + OSPF6_CMD_CHECK_RUNNING (); + + ospf6_show (vty, ospf6); + return CMD_SUCCESS; +} + +DEFUN (show_ipv6_ospf6_route, + show_ipv6_ospf6_route_cmd, + "show ipv6 ospf6 route", + SHOW_STR + IP6_STR + OSPF6_STR + ROUTE_STR + ) +{ + OSPF6_CMD_CHECK_RUNNING (); + + ospf6_route_table_show (vty, argc, argv, ospf6->route_table); + return CMD_SUCCESS; +} + +ALIAS (show_ipv6_ospf6_route, + show_ipv6_ospf6_route_detail_cmd, + "show ipv6 ospf6 route (X:X::X:X|X:X::X:X/M|detail|summary)", + SHOW_STR + IP6_STR + OSPF6_STR + ROUTE_STR + "Specify IPv6 address\n" + "Specify IPv6 prefix\n" + "Detailed information\n" + "Summary of route table\n" + ) + +DEFUN (show_ipv6_ospf6_route_match, + show_ipv6_ospf6_route_match_cmd, + "show ipv6 ospf6 route X:X::X:X/M match", + SHOW_STR + IP6_STR + OSPF6_STR + ROUTE_STR + "Specify IPv6 prefix\n" + "Display routes which match the specified route\n" + ) +{ + 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]; + sargc = argc; + sargv[sargc++] = "match"; + sargv[sargc] = NULL; + + ospf6_route_table_show (vty, sargc, sargv, ospf6->route_table); + return CMD_SUCCESS; +} + +DEFUN (show_ipv6_ospf6_route_match_detail, + show_ipv6_ospf6_route_match_detail_cmd, + "show ipv6 ospf6 route X:X::X:X/M match detail", + SHOW_STR + IP6_STR + OSPF6_STR + ROUTE_STR + "Specify IPv6 prefix\n" + "Display routes which match the specified route\n" + "Detailed information\n" + ) +{ + const char *sargv[CMD_ARGC_MAX]; + int i, sargc; + + /* copy argv to sargv and then append "match" and "detail" */ + for (i = 0; i < argc; i++) + sargv[i] = argv[i]; + sargc = argc; + sargv[sargc++] = "match"; + sargv[sargc++] = "detail"; + sargv[sargc] = NULL; + + OSPF6_CMD_CHECK_RUNNING (); + + ospf6_route_table_show (vty, sargc, sargv, ospf6->route_table); + return CMD_SUCCESS; +} + +ALIAS (show_ipv6_ospf6_route_match, + show_ipv6_ospf6_route_longer_cmd, + "show ipv6 ospf6 route X:X::X:X/M longer", + SHOW_STR + IP6_STR + OSPF6_STR + ROUTE_STR + "Specify IPv6 prefix\n" + "Display routes longer than the specified route\n" + ) + +DEFUN (show_ipv6_ospf6_route_match_detail, + show_ipv6_ospf6_route_longer_detail_cmd, + "show ipv6 ospf6 route X:X::X:X/M longer detail", + SHOW_STR + IP6_STR + OSPF6_STR + ROUTE_STR + "Specify IPv6 prefix\n" + "Display routes longer than the specified route\n" + "Detailed information\n" + ); + +ALIAS (show_ipv6_ospf6_route, + show_ipv6_ospf6_route_type_cmd, + "show ipv6 ospf6 route (intra-area|inter-area|external-1|external-2)", + SHOW_STR + IP6_STR + OSPF6_STR + ROUTE_STR + "Display Intra-Area routes\n" + "Display Inter-Area routes\n" + "Display Type-1 External routes\n" + "Display Type-2 External routes\n" + ) + +DEFUN (show_ipv6_ospf6_route_type_detail, + show_ipv6_ospf6_route_type_detail_cmd, + "show ipv6 ospf6 route (intra-area|inter-area|external-1|external-2) detail", + SHOW_STR + IP6_STR + OSPF6_STR + ROUTE_STR + "Display Intra-Area routes\n" + "Display Inter-Area routes\n" + "Display Type-1 External routes\n" + "Display Type-2 External routes\n" + "Detailed information\n" + ) +{ + const char *sargv[CMD_ARGC_MAX]; + int i, sargc; + + /* copy argv to sargv and then append "detail" */ + for (i = 0; i < argc; i++) + sargv[i] = argv[i]; + sargc = argc; + 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) +{ + char router_id[16]; + struct listnode *j, *k; + struct ospf6_area *oa; + struct ospf6_interface *oi; + + /* OSPFv6 configuration. */ + if (ospf6 == NULL) + 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)) + { + for (ALL_LIST_ELEMENTS_RO (oa->if_list, k, oi)) + vty_out (vty, " interface %s area %s%s", + oi->interface->name, oa->name, VNL); + } + vty_out (vty, "!%s", VNL); + return 0; +} + +/* OSPF6 node structure. */ +static struct cmd_node ospf6_node = +{ + OSPF6_NODE, + "%s(config-ospf6)# ", + 1 /* VTYSH */ +}; + +/* Install ospf related commands. */ +void +ospf6_top_init (void) +{ + /* Install ospf6 top node. */ + install_node (&ospf6_node, config_write_ospf6); + + install_element (VIEW_NODE, &show_ipv6_ospf6_cmd); + install_element (CONFIG_NODE, &router_ospf6_cmd); + install_element (CONFIG_NODE, &no_router_ospf6_cmd); + + install_element (VIEW_NODE, &show_ipv6_ospf6_route_cmd); + install_element (VIEW_NODE, &show_ipv6_ospf6_route_detail_cmd); + install_element (VIEW_NODE, &show_ipv6_ospf6_route_match_cmd); + install_element (VIEW_NODE, &show_ipv6_ospf6_route_match_detail_cmd); + install_element (VIEW_NODE, &show_ipv6_ospf6_route_longer_cmd); + 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_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 new file mode 100644 index 0000000..97fac0d --- /dev/null +++ b/ospf6d/ospf6_top.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2003 Yasuhiro Ohara + * + * 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 OSPF6_TOP_H +#define OSPF6_TOP_H + +#include "routemap.h" + +/* OSPFv3 top level data structure */ +struct ospf6 +{ + /* my router id */ + u_int32_t router_id; + + /* static router id */ + u_int32_t router_id_static; + + /* start time */ + struct timeval starttime; + + /* list of areas */ + struct list *area_list; + struct ospf6_area *backbone; + + /* AS scope link state database */ + struct ospf6_lsdb *lsdb; + struct ospf6_lsdb *lsdb_self; + + struct ospf6_route_table *route_table; + struct ospf6_route_table *brouter_table; + + struct ospf6_route_table *external_table; + struct route_table *external_id_table; + u_int32_t external_id; + + /* redistribute route-map */ + struct + { + char *name; + struct route_map *map; + } rmap[ZEBRA_ROUTE_MAX]; + + 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; + +/* prototypes */ +extern void ospf6_top_init (void); +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 new file mode 100644 index 0000000..2976214 --- /dev/null +++ b/ospf6d/ospf6_zebra.c @@ -0,0 +1,864 @@ +/* + * Copyright (C) 2003 Yasuhiro Ohara + * + * 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 "vty.h" +#include "command.h" +#include "prefix.h" +#include "stream.h" +#include "zclient.h" +#include "memory.h" + +#include "ospf6_proto.h" +#include "ospf6_top.h" +#include "ospf6_interface.h" +#include "ospf6_route.h" +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" +#include "ospf6_asbr.h" +#include "ospf6_zebra.h" +#include "ospf6d.h" + +unsigned char conf_debug_ospf6_zebra = 0; + +/* information about zebra. */ +struct zclient *zclient = NULL; + +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, vrf_id_t vrf_id) +{ + struct prefix router_id; + struct ospf6 *o = ospf6; + + zebra_router_id_update_read(zclient->ibuf,&router_id); + router_id_zebra = router_id.u.prefix4; + + if (o == NULL) + return 0; + + if (o->router_id == 0) + o->router_id = (u_int32_t) router_id_zebra.s_addr; + + return 0; +} + +/* redistribute function */ +void +ospf6_zebra_redistribute (int type) +{ + if (vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT)) + return; + vrf_bitmap_set (zclient->redist[type], VRF_DEFAULT); + if (zclient->sock > 0) + zebra_redistribute_send (ZEBRA_REDISTRIBUTE_ADD, zclient, type, + VRF_DEFAULT); +} + +void +ospf6_zebra_no_redistribute (int type) +{ + if (! vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT)) + return; + vrf_bitmap_unset (zclient->redist[type], VRF_DEFAULT); + if (zclient->sock > 0) + 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, + vrf_id_t vrf_id) +{ + struct interface *ifp; + + 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); + ospf6_interface_if_add (ifp); + return 0; +} + +static int +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, vrf_id))) + return 0; + + if (if_is_up (ifp)) + zlog_warn ("Zebra: got delete of %s, but interface is still up", ifp->name); + + if (IS_OSPF6_DEBUG_ZEBRA (RECV)) + zlog_debug ("Zebra Interface delete: %s index %d mtu %d", + ifp->name, ifp->ifindex, ifp->mtu6); + + ifp->ifindex = IFINDEX_INTERNAL; + return 0; +} + +static int +ospf6_zebra_if_state_update (int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct interface *ifp; + + 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 bandwidth %d", + ifp->name, ifp->ifindex, (unsigned long long)ifp->flags, + ifp->metric, ifp->mtu6, ifp->bandwidth); + + ospf6_interface_state_update (ifp); + return 0; +} + +static int +ospf6_zebra_if_address_update_add (int command, struct zclient *zclient, + 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, + vrf_id); + if (c == NULL) + return 0; + + if (IS_OSPF6_DEBUG_ZEBRA (RECV)) + zlog_debug ("Zebra Interface address add: %s %5s %s/%d", + c->ifp->name, prefix_family_str (c->address), + inet_ntop (c->address->family, &c->address->u.prefix, + buf, sizeof (buf)), c->address->prefixlen); + + if (c->address->family == AF_INET6) + { + 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, vrf_id_t vrf_id) +{ + struct connected *c; + char buf[128]; + + c = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_DELETE, zclient->ibuf, + vrf_id); + if (c == NULL) + return 0; + + if (IS_OSPF6_DEBUG_ZEBRA (RECV)) + zlog_debug ("Zebra Interface address delete: %s %5s %s/%d", + c->ifp->name, prefix_family_str (c->address), + inet_ntop (c->address->family, &c->address->u.prefix, + 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); + } + + return 0; +} + +static int +ospf6_zebra_read_ipv6 (int command, struct zclient *zclient, + 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; + nexthop = NULL; + memset (&api, 0, sizeof (api)); + + /* Type, flags, message. */ + api.type = stream_getc (s); + api.flags = stream_getc (s); + api.message = stream_getc (s); + + /* IPv6 prefix. */ + memset (&p, 0, sizeof (struct prefix_ipv6)); + p.family = AF_INET6; + plength = stream_getc (s); + p.prefixlen = MIN(IPV6_MAX_PREFIXLEN, plength); + 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 = (struct in6_addr *) + malloc (api.nexthop_num * sizeof (struct in6_addr)); + stream_get (nexthop, s, api.nexthop_num * sizeof (struct in6_addr)); + } + 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 (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]; + prefix2str ((struct prefix *)&p, prefixstr, sizeof (prefixstr)); + if (nexthop) + inet_ntop (AF_INET6, nexthop, nexthopstr, sizeof (nexthopstr)); + else + snprintf (nexthopstr, sizeof (nexthopstr), "::"); + + 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, api.tag); + } + + if (command == ZEBRA_IPV6_ROUTE_ADD) + ospf6_asbr_redistribute_add (api.type, ifindex, (struct prefix *) &p, + api.nexthop_num, nexthop, api.tag); + else + ospf6_asbr_redistribute_remove (api.type, ifindex, (struct prefix *) &p); + + if (CHECK_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP)) + free (nexthop); + + return 0; +} + + + + +DEFUN (show_zebra, + show_zebra_cmd, + "show zebra", + SHOW_STR + "Zebra information\n") +{ + int i; + if (zclient == NULL) + { + vty_out (vty, "Not connected to zebra%s", VNL); + return CMD_SUCCESS; + } + + 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", + vrf_bitmap_check (zclient->default_information, VRF_DEFAULT), + VNL); + vty_out (vty, " redistribute:"); + for (i = 0; i < ZEBRA_ROUTE_MAX; i++) + { + if (vrf_bitmap_check (zclient->redist[i], VRF_DEFAULT)) + vty_out (vty, " %s", zebra_route_string(i)); + } + vty_out (vty, "%s", VNL); + return CMD_SUCCESS; +} + +DEFUN (router_zebra, + router_zebra_cmd, + "router zebra", + "Enable a routing process\n" + "Make connection to zebra daemon\n") +{ + vty->node = ZEBRA_NODE; + zclient->enable = 1; + zclient_start (zclient); + return CMD_SUCCESS; +} + +DEFUN (no_router_zebra, + no_router_zebra_cmd, + "no router zebra", + NO_STR + "Configure routing process\n" + "Disable connection to zebra daemon\n") +{ + zclient->enable = 0; + zclient_stop (zclient); + return CMD_SUCCESS; +} + +/* Zebra configuration write function. */ +static int +config_write_ospf6_zebra (struct vty *vty) +{ + if (! zclient->enable) + { + vty_out (vty, "no router zebra%s", VNL); + vty_out (vty, "!%s", VNL); + } + 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); + vty_out (vty, "!%s", VNL); + } + return 0; +} + +/* Zebra node structure. */ +static struct cmd_node zebra_node = +{ + ZEBRA_NODE, + "%s(config-zebra)# ", +}; + +#define ADD 0 +#define REM 1 +static void +ospf6_zebra_route_update (int type, struct ospf6_route *request) +{ + struct zapi_ipv6 api; + char buf[64]; + int nhcount; + struct in6_addr **nexthops; + ifindex_t *ifindexes; + int i, ret = 0; + struct prefix_ipv6 *dest; + + if (IS_OSPF6_DEBUG_ZEBRA (SEND)) + { + prefix2str (&request->prefix, buf, sizeof (buf)); + zlog_debug ("Send %s route: %s", + (type == REM ? "remove" : "add"), buf); + } + + if (zclient->sock < 0) + { + if (IS_OSPF6_DEBUG_ZEBRA (SEND)) + zlog_debug (" Not connected to Zebra"); + return; + } + + if (request->path.origin.adv_router == ospf6->router_id && + (request->path.type == OSPF6_PATH_TYPE_EXTERNAL1 || + request->path.type == OSPF6_PATH_TYPE_EXTERNAL2)) + { + if (IS_OSPF6_DEBUG_ZEBRA (SEND)) + zlog_debug (" Ignore self-originated external route"); + return; + } + + /* If removing is the best path and if there's another path, + treat this request as add the secondary path */ + if (type == REM && ospf6_route_is_best (request) && + request->next && ospf6_route_is_same (request, request->next)) + { + if (IS_OSPF6_DEBUG_ZEBRA (SEND)) + zlog_debug (" Best-path removal resulted Sencondary addition"); + type = ADD; + request = request->next; + } + + /* Only the best path will be sent to zebra. */ + if (! ospf6_route_is_best (request)) + { + /* this is not preferred best route, ignore */ + if (IS_OSPF6_DEBUG_ZEBRA (SEND)) + zlog_debug (" Ignore non-best route"); + return; + } + + nhcount = 0; + for (i = 0; i < OSPF6_MULTI_PATH_LIMIT; i++) + if (ospf6_nexthop_is_set (&request->nexthop[i])) + nhcount++; + + if (nhcount == 0) + { + if (IS_OSPF6_DEBUG_ZEBRA (SEND)) + zlog_debug (" No nexthop, ignore"); + return; + } + + /* allocate memory for nexthop_list */ + nexthops = XCALLOC (MTYPE_OSPF6_OTHER, + nhcount * sizeof (struct in6_addr *)); + if (nexthops == NULL) + { + zlog_warn ("Can't send route to zebra: malloc failed"); + return; + } + + /* allocate memory for ifindex_list */ + ifindexes = XCALLOC (MTYPE_OSPF6_OTHER, + nhcount * sizeof (unsigned int)); + if (ifindexes == NULL) + { + zlog_warn ("Can't send route to zebra: malloc failed"); + XFREE (MTYPE_OSPF6_OTHER, nexthops); + return; + } + + for (i = 0; i < nhcount; i++) + { + if (IS_OSPF6_DEBUG_ZEBRA (SEND)) + { + const char *ifname; + inet_ntop (AF_INET6, &request->nexthop[i].address, + buf, sizeof (buf)); + ifname = ifindex2ifname (request->nexthop[i].ifindex); + zlog_debug (" nexthop: %s%%%.*s(%d)", buf, IFNAMSIZ, ifname, + request->nexthop[i].ifindex); + } + nexthops[i] = &request->nexthop[i].address; + ifindexes[i] = request->nexthop[i].ifindex; + } + + api.vrf_id = VRF_DEFAULT; + api.type = ZEBRA_ROUTE_OSPF6; + api.flags = 0; + api.message = 0; + api.safi = SAFI_UNICAST; + SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP); + api.nexthop_num = nhcount; + api.nexthop = nexthops; + SET_FLAG (api.message, ZAPI_MESSAGE_IFINDEX); + api.ifindex_num = nhcount; + api.ifindex = ifindexes; + 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); + else + ret = zapi_ipv6_route (ZEBRA_IPV6_ROUTE_ADD, zclient, dest, &api); + + if (ret < 0) + zlog_err ("zapi_ipv6_route() %s failed: %s", + (type == REM ? "delete" : "add"), safe_strerror (errno)); + + XFREE (MTYPE_OSPF6_OTHER, nexthops); + XFREE (MTYPE_OSPF6_OTHER, ifindexes); + + return; +} + +void +ospf6_zebra_route_update_add (struct ospf6_route *request) +{ + if (! vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_OSPF6], VRF_DEFAULT)) + { + ospf6->route_table->hook_add = NULL; + ospf6->route_table->hook_remove = NULL; + return; + } + ospf6_zebra_route_update (ADD, request); +} + +void +ospf6_zebra_route_update_remove (struct ospf6_route *request) +{ + if (! vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_OSPF6], VRF_DEFAULT)) + { + ospf6->route_table->hook_add = NULL; + ospf6->route_table->hook_remove = NULL; + return; + } + ospf6_zebra_route_update (REM, request); +} + +DEFUN (redistribute_ospf6, + redistribute_ospf6_cmd, + "redistribute ospf6", + "Redistribute control\n" + "OSPF6 route\n") +{ + struct ospf6_route *route; + + if (vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_OSPF6], VRF_DEFAULT)) + return CMD_SUCCESS; + + vrf_bitmap_set (zclient->redist[ZEBRA_ROUTE_OSPF6], VRF_DEFAULT); + + if (ospf6 == NULL) + return CMD_SUCCESS; + + /* send ospf6 route to zebra route table */ + for (route = ospf6_route_head (ospf6->route_table); route; + route = ospf6_route_next (route)) + ospf6_zebra_route_update_add (route); + + ospf6->route_table->hook_add = ospf6_zebra_route_update_add; + ospf6->route_table->hook_remove = ospf6_zebra_route_update_remove; + + return CMD_SUCCESS; +} + +DEFUN (no_redistribute_ospf6, + no_redistribute_ospf6_cmd, + "no redistribute ospf6", + NO_STR + "Redistribute control\n" + "OSPF6 route\n") +{ + struct ospf6_route *route; + + if (! vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_OSPF6], VRF_DEFAULT)) + return CMD_SUCCESS; + + vrf_bitmap_unset (zclient->redist[ZEBRA_ROUTE_OSPF6], VRF_DEFAULT); + + if (ospf6 == NULL) + return CMD_SUCCESS; + + ospf6->route_table->hook_add = NULL; + ospf6->route_table->hook_remove = NULL; + + /* withdraw ospf6 route from zebra route table */ + for (route = ospf6_route_head (ospf6->route_table); route; + route = ospf6_route_next (route)) + ospf6_zebra_route_update_remove (route); + + 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_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 (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; + zclient->interface_up = ospf6_zebra_if_state_update; + zclient->interface_down = ospf6_zebra_if_state_update; + zclient->interface_address_add = ospf6_zebra_if_address_update_add; + zclient->interface_address_delete = ospf6_zebra_if_address_update_delete; + zclient->ipv4_route_add = NULL; + zclient->ipv4_route_delete = NULL; + zclient->ipv6_route_add = ospf6_zebra_read_ipv6; + zclient->ipv6_route_delete = ospf6_zebra_read_ipv6; + + /* redistribute connected route by default */ + /* ospf6_zebra_redistribute (ZEBRA_ROUTE_CONNECT); */ + + /* Install zebra node. */ + install_node (&zebra_node, config_write_ospf6_zebra); + + /* Install command element for zebra node. */ + install_element (VIEW_NODE, &show_zebra_cmd); + install_element (CONFIG_NODE, &router_zebra_cmd); + install_element (CONFIG_NODE, &no_router_zebra_cmd); + + install_default (ZEBRA_NODE); + install_element (ZEBRA_NODE, &redistribute_ospf6_cmd); + install_element (ZEBRA_NODE, &no_redistribute_ospf6_cmd); + + return; +} + +/* Debug */ + +DEFUN (debug_ospf6_zebra_sendrecv, + debug_ospf6_zebra_sendrecv_cmd, + "debug ospf6 zebra (send|recv)", + DEBUG_STR + OSPF6_STR + "Debug connection between zebra\n" + "Debug Sending zebra\n" + "Debug Receiving zebra\n" + ) +{ + unsigned char level = 0; + + if (argc) + { + if (! strncmp (argv[0], "s", 1)) + level = OSPF6_DEBUG_ZEBRA_SEND; + else if (! strncmp (argv[0], "r", 1)) + level = OSPF6_DEBUG_ZEBRA_RECV; + } + else + level = OSPF6_DEBUG_ZEBRA_SEND | OSPF6_DEBUG_ZEBRA_RECV; + + OSPF6_DEBUG_ZEBRA_ON (level); + return CMD_SUCCESS; +} + +ALIAS (debug_ospf6_zebra_sendrecv, + debug_ospf6_zebra_cmd, + "debug ospf6 zebra", + DEBUG_STR + OSPF6_STR + "Debug connection between zebra\n" + ) + + +DEFUN (no_debug_ospf6_zebra_sendrecv, + no_debug_ospf6_zebra_sendrecv_cmd, + "no debug ospf6 zebra (send|recv)", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug connection between zebra\n" + "Debug Sending zebra\n" + "Debug Receiving zebra\n" + ) +{ + unsigned char level = 0; + + if (argc) + { + if (! strncmp (argv[0], "s", 1)) + level = OSPF6_DEBUG_ZEBRA_SEND; + else if (! strncmp (argv[0], "r", 1)) + level = OSPF6_DEBUG_ZEBRA_RECV; + } + else + level = OSPF6_DEBUG_ZEBRA_SEND | OSPF6_DEBUG_ZEBRA_RECV; + + OSPF6_DEBUG_ZEBRA_OFF (level); + return CMD_SUCCESS; +} + +ALIAS (no_debug_ospf6_zebra_sendrecv, + no_debug_ospf6_zebra_cmd, + "no debug ospf6 zebra", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug connection between zebra\n" + ) + +int +config_write_ospf6_debug_zebra (struct vty *vty) +{ + if (IS_OSPF6_DEBUG_ZEBRA (SEND) && IS_OSPF6_DEBUG_ZEBRA (RECV)) + vty_out (vty, "debug ospf6 zebra%s", VNL); + else + { + if (IS_OSPF6_DEBUG_ZEBRA (SEND)) + vty_out (vty, "debug ospf6 zebra send%s", VNL); + if (IS_OSPF6_DEBUG_ZEBRA (RECV)) + vty_out (vty, "debug ospf6 zebra recv%s", VNL); + } + return 0; +} + +void +install_element_ospf6_debug_zebra (void) +{ + install_element (ENABLE_NODE, &debug_ospf6_zebra_cmd); + install_element (ENABLE_NODE, &no_debug_ospf6_zebra_cmd); + install_element (ENABLE_NODE, &debug_ospf6_zebra_sendrecv_cmd); + install_element (ENABLE_NODE, &no_debug_ospf6_zebra_sendrecv_cmd); + install_element (CONFIG_NODE, &debug_ospf6_zebra_cmd); + install_element (CONFIG_NODE, &no_debug_ospf6_zebra_cmd); + install_element (CONFIG_NODE, &debug_ospf6_zebra_sendrecv_cmd); + install_element (CONFIG_NODE, &no_debug_ospf6_zebra_sendrecv_cmd); +} + + diff --git a/ospf6d/ospf6_zebra.h b/ospf6d/ospf6_zebra.h new file mode 100644 index 0000000..51eb9d7 --- /dev/null +++ b/ospf6d/ospf6_zebra.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2003 Yasuhiro Ohara + * + * 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 OSPF6_ZEBRA_H +#define OSPF6_ZEBRA_H + +#include "zclient.h" +#include "ospf6_top.h" + +/* Debug option */ +extern unsigned char conf_debug_ospf6_zebra; +#define OSPF6_DEBUG_ZEBRA_SEND 0x01 +#define OSPF6_DEBUG_ZEBRA_RECV 0x02 +#define OSPF6_DEBUG_ZEBRA_ON(level) \ + (conf_debug_ospf6_zebra |= level) +#define OSPF6_DEBUG_ZEBRA_OFF(level) \ + (conf_debug_ospf6_zebra &= ~(level)) +#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); +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) \ + 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); + +#endif /*OSPF6_ZEBRA_H*/ diff --git a/ospf6d/ospf6d.c b/ospf6d/ospf6d.c new file mode 100644 index 0000000..387f692 --- /dev/null +++ b/ospf6d/ospf6d.c @@ -0,0 +1,1863 @@ +/* + * Copyright (C) 2003 Yasuhiro Ohara + * + * 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 "thread.h" +#include "linklist.h" +#include "vty.h" +#include "command.h" + +#include "ospf6_proto.h" +#include "ospf6_network.h" +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" +#include "ospf6_message.h" +#include "ospf6_route.h" +#include "ospf6_zebra.h" +#include "ospf6_spf.h" +#include "ospf6_top.h" +#include "ospf6_area.h" +#include "ospf6_interface.h" +#include "ospf6_neighbor.h" +#include "ospf6_intra.h" +#include "ospf6_asbr.h" +#include "ospf6_abr.h" +#include "ospf6_flood.h" +#include "ospf6d.h" + +#ifdef HAVE_SNMP +#include "ospf6_snmp.h" +#endif /*HAVE_SNMP*/ + +char ospf6_daemon_version[] = OSPF6_DAEMON_VERSION; + +struct route_node * +route_prev (struct route_node *node) +{ + struct route_node *end; + struct route_node *prev = NULL; + + end = node; + node = node->parent; + if (node) + route_lock_node (node); + while (node) + { + prev = node; + node = route_next (node); + if (node == end) + { + route_unlock_node (node); + node = NULL; + } + } + route_unlock_node (end); + if (prev) + route_lock_node (prev); + + return prev; +} + + +/* show database functions */ +DEFUN (show_version_ospf6, + show_version_ospf6_cmd, + "show version ospf6", + SHOW_STR + "Displays ospf6d version\n" + ) +{ + vty_out (vty, "Zebra OSPF6d Version: %s%s", + ospf6_daemon_version, VNL); + + return CMD_SUCCESS; +} + +static struct cmd_node debug_node = +{ + DEBUG_NODE, + "", + 1 /* VTYSH */ +}; + +static int +config_write_ospf6_debug (struct vty *vty) +{ + config_write_ospf6_debug_message (vty); + config_write_ospf6_debug_lsa (vty); + config_write_ospf6_debug_zebra (vty); + config_write_ospf6_debug_interface (vty); + config_write_ospf6_debug_neighbor (vty); + config_write_ospf6_debug_spf (vty); + config_write_ospf6_debug_route (vty); + config_write_ospf6_debug_brouter (vty); + config_write_ospf6_debug_asbr (vty); + config_write_ospf6_debug_abr (vty); + config_write_ospf6_debug_flood (vty); + vty_out (vty, "!%s", VNL); + return 0; +} + +#define AREA_LSDB_TITLE_FORMAT \ + "%s Area Scoped Link State Database (Area %s)%s%s" +#define IF_LSDB_TITLE_FORMAT \ + "%s I/F Scoped Link State Database (I/F %s in Area %s)%s%s" +#define AS_LSDB_TITLE_FORMAT \ + "%s AS Scoped Link State Database%s%s" + +static int +parse_show_level (int argc, const char *argv[]) +{ + int level = 0; + if (argc) + { + if (! strncmp (argv[0], "de", 2)) + level = OSPF6_LSDB_SHOW_LEVEL_DETAIL; + else if (! strncmp (argv[0], "du", 2)) + level = OSPF6_LSDB_SHOW_LEVEL_DUMP; + else if (! strncmp (argv[0], "in", 2)) + level = OSPF6_LSDB_SHOW_LEVEL_INTERNAL; + } + else + level = OSPF6_LSDB_SHOW_LEVEL_NORMAL; + return level; +} + +static u_int16_t +parse_type_spec (int argc, const char *argv[]) +{ + u_int16_t type = 0; + assert (argc); + if (! strcmp (argv[0], "router")) + type = htons (OSPF6_LSTYPE_ROUTER); + else if (! strcmp (argv[0], "network")) + type = htons (OSPF6_LSTYPE_NETWORK); + else if (! strcmp (argv[0], "as-external")) + type = htons (OSPF6_LSTYPE_AS_EXTERNAL); + else if (! strcmp (argv[0], "intra-prefix")) + type = htons (OSPF6_LSTYPE_INTRA_PREFIX); + else if (! strcmp (argv[0], "inter-router")) + type = htons (OSPF6_LSTYPE_INTER_ROUTER); + else if (! strcmp (argv[0], "inter-prefix")) + type = htons (OSPF6_LSTYPE_INTER_PREFIX); + else if (! strcmp (argv[0], "link")) + type = htons (OSPF6_LSTYPE_LINK); + return type; +} + +DEFUN (show_ipv6_ospf6_database, + show_ipv6_ospf6_database_cmd, + "show ipv6 ospf6 database", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + ) +{ + int level; + struct listnode *i, *j; + struct ospf6 *o = ospf6; + struct ospf6_area *oa; + struct ospf6_interface *oi; + + OSPF6_CMD_CHECK_RUNNING (); + + level = parse_show_level (argc, argv); + + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + { + vty_out (vty, AREA_LSDB_TITLE_FORMAT, VNL, oa->name, VNL, VNL); + ospf6_lsdb_show (vty, level, NULL, NULL, NULL, oa->lsdb); + } + + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + { + for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi)) + { + vty_out (vty, IF_LSDB_TITLE_FORMAT, VNL, + oi->interface->name, oa->name, VNL, VNL); + ospf6_lsdb_show (vty, level, NULL, NULL, NULL, oi->lsdb); + } + } + + vty_out (vty, AS_LSDB_TITLE_FORMAT, VNL, VNL, VNL); + ospf6_lsdb_show (vty, level, NULL, NULL, NULL, o->lsdb); + + vty_out (vty, "%s", VNL); + return CMD_SUCCESS; +} + +ALIAS (show_ipv6_ospf6_database, + show_ipv6_ospf6_database_detail_cmd, + "show ipv6 ospf6 database (detail|dump|internal)", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Display details of LSAs\n" + "Dump LSAs\n" + "Display LSA's internal information\n" + ) + +DEFUN (show_ipv6_ospf6_database_type, + show_ipv6_ospf6_database_type_cmd, + "show ipv6 ospf6 database " + "(router|network|inter-prefix|inter-router|as-external|" + "group-membership|type-7|link|intra-prefix)", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Display Router LSAs\n" + "Display Network LSAs\n" + "Display Inter-Area-Prefix LSAs\n" + "Display Inter-Area-Router LSAs\n" + "Display As-External LSAs\n" + "Display Group-Membership LSAs\n" + "Display Type-7 LSAs\n" + "Display Link LSAs\n" + "Display Intra-Area-Prefix LSAs\n" + ) +{ + int level; + struct listnode *i, *j; + struct ospf6 *o = ospf6; + struct ospf6_area *oa; + struct ospf6_interface *oi; + u_int16_t type = 0; + + OSPF6_CMD_CHECK_RUNNING (); + + type = parse_type_spec (argc, argv); + argc--; + argv++; + level = parse_show_level (argc, argv); + + switch (OSPF6_LSA_SCOPE (type)) + { + case OSPF6_SCOPE_AREA: + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + { + vty_out (vty, AREA_LSDB_TITLE_FORMAT, VNL, oa->name, VNL, VNL); + ospf6_lsdb_show (vty, level, &type, NULL, NULL, oa->lsdb); + } + break; + + case OSPF6_SCOPE_LINKLOCAL: + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + { + for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi)) + { + vty_out (vty, IF_LSDB_TITLE_FORMAT, VNL, + oi->interface->name, oa->name, VNL, VNL); + ospf6_lsdb_show (vty, level, &type, NULL, NULL, oi->lsdb); + } + } + break; + + case OSPF6_SCOPE_AS: + vty_out (vty, AS_LSDB_TITLE_FORMAT, VNL, VNL, VNL); + ospf6_lsdb_show (vty, level, &type, NULL, NULL, o->lsdb); + break; + + default: + assert (0); + break; + } + + vty_out (vty, "%s", VNL); + return CMD_SUCCESS; +} + +ALIAS (show_ipv6_ospf6_database_type, + show_ipv6_ospf6_database_type_detail_cmd, + "show ipv6 ospf6 database " + "(router|network|inter-prefix|inter-router|as-external|" + "group-membership|type-7|link|intra-prefix) " + "(detail|dump|internal)", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Display Router LSAs\n" + "Display Network LSAs\n" + "Display Inter-Area-Prefix LSAs\n" + "Display Inter-Area-Router LSAs\n" + "Display As-External LSAs\n" + "Display Group-Membership LSAs\n" + "Display Type-7 LSAs\n" + "Display Link LSAs\n" + "Display Intra-Area-Prefix LSAs\n" + "Display details of LSAs\n" + "Dump LSAs\n" + "Display LSA's internal information\n" + ) + +DEFUN (show_ipv6_ospf6_database_id, + show_ipv6_ospf6_database_id_cmd, + "show ipv6 ospf6 database * A.B.C.D", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Any Link state Type\n" + "Specify Link state ID as IPv4 address notation\n" + ) +{ + int level; + struct listnode *i, *j; + struct ospf6 *o = ospf6; + struct ospf6_area *oa; + struct ospf6_interface *oi; + u_int32_t id = 0; + + OSPF6_CMD_CHECK_RUNNING (); + + if ((inet_pton (AF_INET, argv[0], &id)) != 1) + { + vty_out (vty, "Link State ID is not parsable: %s%s", + argv[0], VNL); + return CMD_SUCCESS; + } + + argc--; + argv++; + level = parse_show_level (argc, argv); + + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + { + vty_out (vty, AREA_LSDB_TITLE_FORMAT, VNL, oa->name, VNL, VNL); + ospf6_lsdb_show (vty, level, NULL, &id, NULL, oa->lsdb); + } + + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + { + for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi)) + { + vty_out (vty, IF_LSDB_TITLE_FORMAT, VNL, + oi->interface->name, oa->name, VNL, VNL); + ospf6_lsdb_show (vty, level, NULL, &id, NULL, oi->lsdb); + } + } + + vty_out (vty, AS_LSDB_TITLE_FORMAT, VNL, VNL, VNL); + ospf6_lsdb_show (vty, level, NULL, &id, NULL, o->lsdb); + + vty_out (vty, "%s", VNL); + return CMD_SUCCESS; +} + +ALIAS (show_ipv6_ospf6_database_id, + show_ipv6_ospf6_database_id_detail_cmd, + "show ipv6 ospf6 database * A.B.C.D " + "(detail|dump|internal)", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Any Link state Type\n" + "Specify Link state ID as IPv4 address notation\n" + "Display details of LSAs\n" + "Dump LSAs\n" + "Display LSA's internal information\n" + ) + +ALIAS (show_ipv6_ospf6_database_id, + show_ipv6_ospf6_database_linkstate_id_cmd, + "show ipv6 ospf6 database linkstate-id A.B.C.D", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Search by Link state ID\n" + "Specify Link state ID as IPv4 address notation\n" + ) + +ALIAS (show_ipv6_ospf6_database_id, + show_ipv6_ospf6_database_linkstate_id_detail_cmd, + "show ipv6 ospf6 database linkstate-id A.B.C.D " + "(detail|dump|internal)", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Search by Link state ID\n" + "Specify Link state ID as IPv4 address notation\n" + "Display details of LSAs\n" + "Dump LSAs\n" + "Display LSA's internal information\n" + ) + +DEFUN (show_ipv6_ospf6_database_router, + show_ipv6_ospf6_database_router_cmd, + "show ipv6 ospf6 database * * A.B.C.D", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Any Link state Type\n" + "Any Link state ID\n" + "Specify Advertising Router as IPv4 address notation\n" + ) +{ + int level; + struct listnode *i, *j; + struct ospf6 *o = ospf6; + struct ospf6_area *oa; + struct ospf6_interface *oi; + u_int32_t adv_router = 0; + + OSPF6_CMD_CHECK_RUNNING (); + + if ((inet_pton (AF_INET, argv[0], &adv_router)) != 1) + { + vty_out (vty, "Advertising Router is not parsable: %s%s", + argv[0], VNL); + return CMD_SUCCESS; + } + + argc--; + argv++; + level = parse_show_level (argc, argv); + + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + { + vty_out (vty, AREA_LSDB_TITLE_FORMAT, VNL, oa->name, VNL, VNL); + ospf6_lsdb_show (vty, level, NULL, NULL, &adv_router, oa->lsdb); + } + + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + { + for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi)) + { + vty_out (vty, IF_LSDB_TITLE_FORMAT, VNL, + oi->interface->name, oa->name, VNL, VNL); + ospf6_lsdb_show (vty, level, NULL, NULL, &adv_router, oi->lsdb); + } + } + + vty_out (vty, AS_LSDB_TITLE_FORMAT, VNL, VNL, VNL); + ospf6_lsdb_show (vty, level, NULL, NULL, &adv_router, o->lsdb); + + vty_out (vty, "%s", VNL); + return CMD_SUCCESS; +} + +ALIAS (show_ipv6_ospf6_database_router, + show_ipv6_ospf6_database_router_detail_cmd, + "show ipv6 ospf6 database * * A.B.C.D " + "(detail|dump|internal)", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Any Link state Type\n" + "Any Link state ID\n" + "Specify Advertising Router as IPv4 address notation\n" + "Display details of LSAs\n" + "Dump LSAs\n" + "Display LSA's internal information\n" + ) + +ALIAS (show_ipv6_ospf6_database_router, + show_ipv6_ospf6_database_adv_router_cmd, + "show ipv6 ospf6 database adv-router A.B.C.D", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Search by Advertising Router\n" + "Specify Advertising Router as IPv4 address notation\n" + ) + +ALIAS (show_ipv6_ospf6_database_router, + show_ipv6_ospf6_database_adv_router_detail_cmd, + "show ipv6 ospf6 database adv-router A.B.C.D " + "(detail|dump|internal)", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Search by Advertising Router\n" + "Specify Advertising Router as IPv4 address notation\n" + "Display details of LSAs\n" + "Dump LSAs\n" + "Display LSA's internal information\n" + ) + +DEFUN (show_ipv6_ospf6_database_type_id, + show_ipv6_ospf6_database_type_id_cmd, + "show ipv6 ospf6 database " + "(router|network|inter-prefix|inter-router|as-external|" + "group-membership|type-7|link|intra-prefix) A.B.C.D", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Display Router LSAs\n" + "Display Network LSAs\n" + "Display Inter-Area-Prefix LSAs\n" + "Display Inter-Area-Router LSAs\n" + "Display As-External LSAs\n" + "Display Group-Membership LSAs\n" + "Display Type-7 LSAs\n" + "Display Link LSAs\n" + "Display Intra-Area-Prefix LSAs\n" + "Specify Link state ID as IPv4 address notation\n" + ) +{ + int level; + struct listnode *i, *j; + struct ospf6 *o = ospf6; + struct ospf6_area *oa; + struct ospf6_interface *oi; + u_int16_t type = 0; + u_int32_t id = 0; + + OSPF6_CMD_CHECK_RUNNING (); + + type = parse_type_spec (argc, argv); + argc--; + argv++; + + if ((inet_pton (AF_INET, argv[0], &id)) != 1) + { + vty_out (vty, "Link state ID is not parsable: %s%s", + argv[0], VNL); + return CMD_SUCCESS; + } + + argc--; + argv++; + level = parse_show_level (argc, argv); + + switch (OSPF6_LSA_SCOPE (type)) + { + case OSPF6_SCOPE_AREA: + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + { + vty_out (vty, AREA_LSDB_TITLE_FORMAT, VNL, oa->name, VNL, VNL); + ospf6_lsdb_show (vty, level, &type, &id, NULL, oa->lsdb); + } + break; + + case OSPF6_SCOPE_LINKLOCAL: + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + { + for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi)) + { + vty_out (vty, IF_LSDB_TITLE_FORMAT, VNL, + oi->interface->name, oa->name, VNL, VNL); + ospf6_lsdb_show (vty, level, &type, &id, NULL, oi->lsdb); + } + } + break; + + case OSPF6_SCOPE_AS: + vty_out (vty, AS_LSDB_TITLE_FORMAT, VNL, VNL, VNL); + ospf6_lsdb_show (vty, level, &type, &id, NULL, o->lsdb); + break; + + default: + assert (0); + break; + } + + vty_out (vty, "%s", VNL); + return CMD_SUCCESS; +} + +ALIAS (show_ipv6_ospf6_database_type_id, + show_ipv6_ospf6_database_type_id_detail_cmd, + "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)", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Display Router LSAs\n" + "Display Network LSAs\n" + "Display Inter-Area-Prefix LSAs\n" + "Display Inter-Area-Router LSAs\n" + "Display As-External LSAs\n" + "Display Group-Membership LSAs\n" + "Display Type-7 LSAs\n" + "Display Link LSAs\n" + "Display Intra-Area-Prefix LSAs\n" + "Specify Link state ID as IPv4 address notation\n" + "Display details of LSAs\n" + "Dump LSAs\n" + "Display LSA's internal information\n" + ) + +ALIAS (show_ipv6_ospf6_database_type_id, + show_ipv6_ospf6_database_type_linkstate_id_cmd, + "show ipv6 ospf6 database " + "(router|network|inter-prefix|inter-router|as-external|" + "group-membership|type-7|link|intra-prefix) linkstate-id A.B.C.D", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Display Router LSAs\n" + "Display Network LSAs\n" + "Display Inter-Area-Prefix LSAs\n" + "Display Inter-Area-Router LSAs\n" + "Display As-External LSAs\n" + "Display Group-Membership LSAs\n" + "Display Type-7 LSAs\n" + "Display Link LSAs\n" + "Display Intra-Area-Prefix LSAs\n" + "Search by Link state ID\n" + "Specify Link state ID as IPv4 address notation\n" + ) + +ALIAS (show_ipv6_ospf6_database_type_id, + show_ipv6_ospf6_database_type_linkstate_id_detail_cmd, + "show ipv6 ospf6 database " + "(router|network|inter-prefix|inter-router|as-external|" + "group-membership|type-7|link|intra-prefix) linkstate-id A.B.C.D " + "(detail|dump|internal)", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Display Router LSAs\n" + "Display Network LSAs\n" + "Display Inter-Area-Prefix LSAs\n" + "Display Inter-Area-Router LSAs\n" + "Display As-External LSAs\n" + "Display Group-Membership LSAs\n" + "Display Type-7 LSAs\n" + "Display Link LSAs\n" + "Display Intra-Area-Prefix LSAs\n" + "Search by Link state ID\n" + "Specify Link state ID as IPv4 address notation\n" + "Display details of LSAs\n" + "Dump LSAs\n" + "Display LSA's internal information\n" + ) + +DEFUN (show_ipv6_ospf6_database_type_router, + show_ipv6_ospf6_database_type_router_cmd, + "show ipv6 ospf6 database " + "(router|network|inter-prefix|inter-router|as-external|" + "group-membership|type-7|link|intra-prefix) * A.B.C.D", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Display Router LSAs\n" + "Display Network LSAs\n" + "Display Inter-Area-Prefix LSAs\n" + "Display Inter-Area-Router LSAs\n" + "Display As-External LSAs\n" + "Display Group-Membership LSAs\n" + "Display Type-7 LSAs\n" + "Display Link LSAs\n" + "Display Intra-Area-Prefix LSAs\n" + "Any Link state ID\n" + "Specify Advertising Router as IPv4 address notation\n" + ) +{ + int level; + struct listnode *i, *j; + struct ospf6 *o = ospf6; + struct ospf6_area *oa; + struct ospf6_interface *oi; + u_int16_t type = 0; + u_int32_t adv_router = 0; + + OSPF6_CMD_CHECK_RUNNING (); + + type = parse_type_spec (argc, argv); + argc--; + argv++; + + if ((inet_pton (AF_INET, argv[0], &adv_router)) != 1) + { + vty_out (vty, "Advertising Router is not parsable: %s%s", + argv[0], VNL); + return CMD_SUCCESS; + } + + argc--; + argv++; + level = parse_show_level (argc, argv); + + switch (OSPF6_LSA_SCOPE (type)) + { + case OSPF6_SCOPE_AREA: + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + { + vty_out (vty, AREA_LSDB_TITLE_FORMAT, VNL, oa->name, VNL, VNL); + ospf6_lsdb_show (vty, level, &type, NULL, &adv_router, oa->lsdb); + } + break; + + case OSPF6_SCOPE_LINKLOCAL: + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + { + for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi)) + { + vty_out (vty, IF_LSDB_TITLE_FORMAT, VNL, + oi->interface->name, oa->name, VNL, VNL); + ospf6_lsdb_show (vty, level, &type, NULL, &adv_router, oi->lsdb); + } + } + break; + + case OSPF6_SCOPE_AS: + vty_out (vty, AS_LSDB_TITLE_FORMAT, VNL, VNL, VNL); + ospf6_lsdb_show (vty, level, &type, NULL, &adv_router, o->lsdb); + break; + + default: + assert (0); + break; + } + + vty_out (vty, "%s", VNL); + return CMD_SUCCESS; +} + +ALIAS (show_ipv6_ospf6_database_type_router, + show_ipv6_ospf6_database_type_router_detail_cmd, + "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)", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Display Router LSAs\n" + "Display Network LSAs\n" + "Display Inter-Area-Prefix LSAs\n" + "Display Inter-Area-Router LSAs\n" + "Display As-External LSAs\n" + "Display Group-Membership LSAs\n" + "Display Type-7 LSAs\n" + "Display Link LSAs\n" + "Display Intra-Area-Prefix LSAs\n" + "Any Link state ID\n" + "Specify Advertising Router as IPv4 address notation\n" + "Display details of LSAs\n" + "Dump LSAs\n" + "Display LSA's internal information\n" + ) + +ALIAS (show_ipv6_ospf6_database_type_router, + show_ipv6_ospf6_database_type_adv_router_cmd, + "show ipv6 ospf6 database " + "(router|network|inter-prefix|inter-router|as-external|" + "group-membership|type-7|link|intra-prefix) adv-router A.B.C.D", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Display Router LSAs\n" + "Display Network LSAs\n" + "Display Inter-Area-Prefix LSAs\n" + "Display Inter-Area-Router LSAs\n" + "Display As-External LSAs\n" + "Display Group-Membership LSAs\n" + "Display Type-7 LSAs\n" + "Display Link LSAs\n" + "Display Intra-Area-Prefix LSAs\n" + "Search by Advertising Router\n" + "Specify Advertising Router as IPv4 address notation\n" + ) + +ALIAS (show_ipv6_ospf6_database_type_router, + show_ipv6_ospf6_database_type_adv_router_detail_cmd, + "show ipv6 ospf6 database " + "(router|network|inter-prefix|inter-router|as-external|" + "group-membership|type-7|link|intra-prefix) adv-router A.B.C.D " + "(detail|dump|internal)", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Display Router LSAs\n" + "Display Network LSAs\n" + "Display Inter-Area-Prefix LSAs\n" + "Display Inter-Area-Router LSAs\n" + "Display As-External LSAs\n" + "Display Group-Membership LSAs\n" + "Display Type-7 LSAs\n" + "Display Link LSAs\n" + "Display Intra-Area-Prefix LSAs\n" + "Search by Advertising Router\n" + "Specify Advertising Router as IPv4 address notation\n" + "Display details of LSAs\n" + "Dump LSAs\n" + "Display LSA's internal information\n" + ) + +DEFUN (show_ipv6_ospf6_database_id_router, + show_ipv6_ospf6_database_id_router_cmd, + "show ipv6 ospf6 database * A.B.C.D A.B.C.D", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Any Link state Type\n" + "Specify Link state ID as IPv4 address notation\n" + "Specify Advertising Router as IPv4 address notation\n" + ) +{ + int level; + struct listnode *i, *j; + struct ospf6 *o = ospf6; + struct ospf6_area *oa; + struct ospf6_interface *oi; + u_int32_t id = 0; + u_int32_t adv_router = 0; + + OSPF6_CMD_CHECK_RUNNING (); + + if ((inet_pton (AF_INET, argv[0], &id)) != 1) + { + vty_out (vty, "Link state ID is not parsable: %s%s", + argv[0], VNL); + return CMD_SUCCESS; + } + + argc--; + argv++; + + if ((inet_pton (AF_INET, argv[0], &adv_router)) != 1) + { + vty_out (vty, "Advertising Router is not parsable: %s%s", + argv[0], VNL); + return CMD_SUCCESS; + } + + argc--; + argv++; + level = parse_show_level (argc, argv); + + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + { + vty_out (vty, AREA_LSDB_TITLE_FORMAT, VNL, oa->name, VNL, VNL); + ospf6_lsdb_show (vty, level, NULL, &id, &adv_router, oa->lsdb); + } + + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + { + for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi)) + { + vty_out (vty, IF_LSDB_TITLE_FORMAT, VNL, + oi->interface->name, oa->name, VNL, VNL); + ospf6_lsdb_show (vty, level, NULL, &id, &adv_router, oi->lsdb); + } + } + + vty_out (vty, AS_LSDB_TITLE_FORMAT, VNL, VNL, VNL); + ospf6_lsdb_show (vty, level, NULL, &id, &adv_router, o->lsdb); + + vty_out (vty, "%s", VNL); + return CMD_SUCCESS; +} + +ALIAS (show_ipv6_ospf6_database_id_router, + show_ipv6_ospf6_database_id_router_detail_cmd, + "show ipv6 ospf6 database * A.B.C.D A.B.C.D " + "(detail|dump|internal)", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Any Link state Type\n" + "Specify Link state ID as IPv4 address notation\n" + "Specify Advertising Router as IPv4 address notation\n" + "Display details of LSAs\n" + "Dump LSAs\n" + "Display LSA's internal information\n" + ) + +DEFUN (show_ipv6_ospf6_database_adv_router_linkstate_id, + show_ipv6_ospf6_database_adv_router_linkstate_id_cmd, + "show ipv6 ospf6 database adv-router A.B.C.D linkstate-id A.B.C.D", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Search by Advertising Router\n" + "Specify Advertising Router as IPv4 address notation\n" + "Search by Link state ID\n" + "Specify Link state ID as IPv4 address notation\n" + ) +{ + int level; + struct listnode *i, *j; + struct ospf6 *o = ospf6; + struct ospf6_area *oa; + struct ospf6_interface *oi; + u_int32_t id = 0; + u_int32_t adv_router = 0; + + OSPF6_CMD_CHECK_RUNNING (); + + if ((inet_pton (AF_INET, argv[0], &adv_router)) != 1) + { + vty_out (vty, "Advertising Router is not parsable: %s%s", + argv[0], VNL); + return CMD_SUCCESS; + } + + argc--; + argv++; + + if ((inet_pton (AF_INET, argv[0], &id)) != 1) + { + vty_out (vty, "Link state ID is not parsable: %s%s", + argv[0], VNL); + return CMD_SUCCESS; + } + + argc--; + argv++; + level = parse_show_level (argc, argv); + + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + { + vty_out (vty, AREA_LSDB_TITLE_FORMAT, VNL, oa->name, VNL, VNL); + ospf6_lsdb_show (vty, level, NULL, &id, &adv_router, oa->lsdb); + } + + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + { + for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi)) + { + vty_out (vty, IF_LSDB_TITLE_FORMAT, VNL, + oi->interface->name, oa->name, VNL, VNL); + ospf6_lsdb_show (vty, level, NULL, &id, &adv_router, oi->lsdb); + } + } + + vty_out (vty, AS_LSDB_TITLE_FORMAT, VNL, VNL, VNL); + ospf6_lsdb_show (vty, level, NULL, &id, &adv_router, o->lsdb); + + vty_out (vty, "%s", VNL); + return CMD_SUCCESS; +} + +ALIAS (show_ipv6_ospf6_database_adv_router_linkstate_id, + show_ipv6_ospf6_database_adv_router_linkstate_id_detail_cmd, + "show ipv6 ospf6 database adv-router A.B.C.D linkstate-id A.B.C.D " + "(detail|dump|internal)", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Search by Advertising Router\n" + "Specify Advertising Router as IPv4 address notation\n" + "Search by Link state ID\n" + "Specify Link state ID as IPv4 address notation\n" + "Display details of LSAs\n" + "Dump LSAs\n" + "Display LSA's internal information\n" + ) + +DEFUN (show_ipv6_ospf6_database_type_id_router, + show_ipv6_ospf6_database_type_id_router_cmd, + "show ipv6 ospf6 database " + "(router|network|inter-prefix|inter-router|as-external|" + "group-membership|type-7|link|intra-prefix) A.B.C.D A.B.C.D", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Display Router LSAs\n" + "Display Network LSAs\n" + "Display Inter-Area-Prefix LSAs\n" + "Display Inter-Area-Router LSAs\n" + "Display As-External LSAs\n" + "Display Group-Membership LSAs\n" + "Display Type-7 LSAs\n" + "Display Link LSAs\n" + "Display Intra-Area-Prefix LSAs\n" + "Specify Link state ID as IPv4 address notation\n" + "Specify Advertising Router as IPv4 address notation\n" + ) +{ + int level; + struct listnode *i, *j; + struct ospf6 *o = ospf6; + struct ospf6_area *oa; + struct ospf6_interface *oi; + u_int16_t type = 0; + u_int32_t id = 0; + u_int32_t adv_router = 0; + + OSPF6_CMD_CHECK_RUNNING (); + + type = parse_type_spec (argc, argv); + argc--; + argv++; + + if ((inet_pton (AF_INET, argv[0], &id)) != 1) + { + vty_out (vty, "Link state ID is not parsable: %s%s", + argv[0], VNL); + return CMD_SUCCESS; + } + + argc--; + argv++; + + if ((inet_pton (AF_INET, argv[0], &adv_router)) != 1) + { + vty_out (vty, "Advertising Router is not parsable: %s%s", + argv[0], VNL); + return CMD_SUCCESS; + } + + argc--; + argv++; + level = parse_show_level (argc, argv); + + switch (OSPF6_LSA_SCOPE (type)) + { + case OSPF6_SCOPE_AREA: + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + { + vty_out (vty, AREA_LSDB_TITLE_FORMAT, VNL, oa->name, VNL, VNL); + ospf6_lsdb_show (vty, level, &type, &id, &adv_router, oa->lsdb); + } + break; + + case OSPF6_SCOPE_LINKLOCAL: + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + { + for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi)) + { + vty_out (vty, IF_LSDB_TITLE_FORMAT, VNL, + oi->interface->name, oa->name, VNL, VNL); + ospf6_lsdb_show (vty, level, &type, &id, &adv_router, oi->lsdb); + } + } + break; + + case OSPF6_SCOPE_AS: + vty_out (vty, AS_LSDB_TITLE_FORMAT, VNL, VNL, VNL); + ospf6_lsdb_show (vty, level, &type, &id, &adv_router, o->lsdb); + break; + + default: + assert (0); + break; + } + + vty_out (vty, "%s", VNL); + return CMD_SUCCESS; +} + +ALIAS (show_ipv6_ospf6_database_type_id_router, + show_ipv6_ospf6_database_type_id_router_detail_cmd, + "show ipv6 ospf6 database " + "(router|network|inter-prefix|inter-router|as-external|" + "group-membership|type-7|link|intra-prefix) A.B.C.D A.B.C.D " + "(dump|internal)", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Display Router LSAs\n" + "Display Network LSAs\n" + "Display Inter-Area-Prefix LSAs\n" + "Display Inter-Area-Router LSAs\n" + "Display As-External LSAs\n" + "Display Group-Membership LSAs\n" + "Display Type-7 LSAs\n" + "Display Link LSAs\n" + "Display Intra-Area-Prefix LSAs\n" + "Specify Link state ID as IPv4 address notation\n" + "Specify Advertising Router as IPv4 address notation\n" + "Dump LSAs\n" + "Display LSA's internal information\n" + ) + +DEFUN (show_ipv6_ospf6_database_type_adv_router_linkstate_id, + show_ipv6_ospf6_database_type_adv_router_linkstate_id_cmd, + "show ipv6 ospf6 database " + "(router|network|inter-prefix|inter-router|as-external|" + "group-membership|type-7|link|intra-prefix) " + "adv-router A.B.C.D linkstate-id A.B.C.D", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Display Router LSAs\n" + "Display Network LSAs\n" + "Display Inter-Area-Prefix LSAs\n" + "Display Inter-Area-Router LSAs\n" + "Display As-External LSAs\n" + "Display Group-Membership LSAs\n" + "Display Type-7 LSAs\n" + "Display Link LSAs\n" + "Display Intra-Area-Prefix LSAs\n" + "Search by Advertising Router\n" + "Specify Advertising Router as IPv4 address notation\n" + "Search by Link state ID\n" + "Specify Link state ID as IPv4 address notation\n" + ) +{ + int level; + struct listnode *i, *j; + struct ospf6 *o = ospf6; + struct ospf6_area *oa; + struct ospf6_interface *oi; + u_int16_t type = 0; + u_int32_t id = 0; + u_int32_t adv_router = 0; + + OSPF6_CMD_CHECK_RUNNING (); + + type = parse_type_spec (argc, argv); + argc--; + argv++; + + if ((inet_pton (AF_INET, argv[0], &adv_router)) != 1) + { + vty_out (vty, "Advertising Router is not parsable: %s%s", + argv[0], VNL); + return CMD_SUCCESS; + } + + argc--; + argv++; + + if ((inet_pton (AF_INET, argv[0], &id)) != 1) + { + vty_out (vty, "Link state ID is not parsable: %s%s", + argv[0], VNL); + return CMD_SUCCESS; + } + + argc--; + argv++; + level = parse_show_level (argc, argv); + + switch (OSPF6_LSA_SCOPE (type)) + { + case OSPF6_SCOPE_AREA: + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + { + vty_out (vty, AREA_LSDB_TITLE_FORMAT, VNL, oa->name, VNL, VNL); + ospf6_lsdb_show (vty, level, &type, &id, &adv_router, oa->lsdb); + } + break; + + case OSPF6_SCOPE_LINKLOCAL: + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + { + for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi)) + { + vty_out (vty, IF_LSDB_TITLE_FORMAT, VNL, + oi->interface->name, oa->name, VNL, VNL); + ospf6_lsdb_show (vty, level, &type, &id, &adv_router, oi->lsdb); + } + } + break; + + case OSPF6_SCOPE_AS: + vty_out (vty, AS_LSDB_TITLE_FORMAT, VNL, VNL, VNL); + ospf6_lsdb_show (vty, level, &type, &id, &adv_router, o->lsdb); + break; + + default: + assert (0); + break; + } + + vty_out (vty, "%s", VNL); + return CMD_SUCCESS; +} + +ALIAS (show_ipv6_ospf6_database_type_adv_router_linkstate_id, + show_ipv6_ospf6_database_type_adv_router_linkstate_id_detail_cmd, + "show ipv6 ospf6 database " + "(router|network|inter-prefix|inter-router|as-external|" + "group-membership|type-7|link|intra-prefix) " + "adv-router A.B.C.D linkstate-id A.B.C.D " + "(dump|internal)", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Display Router LSAs\n" + "Display Network LSAs\n" + "Display Inter-Area-Prefix LSAs\n" + "Display Inter-Area-Router LSAs\n" + "Display As-External LSAs\n" + "Display Group-Membership LSAs\n" + "Display Type-7 LSAs\n" + "Display Link LSAs\n" + "Display Intra-Area-Prefix LSAs\n" + "Search by Advertising Router\n" + "Specify Advertising Router as IPv4 address notation\n" + "Search by Link state ID\n" + "Specify Link state ID as IPv4 address notation\n" + "Dump LSAs\n" + "Display LSA's internal information\n" + ) + +DEFUN (show_ipv6_ospf6_database_self_originated, + show_ipv6_ospf6_database_self_originated_cmd, + "show ipv6 ospf6 database self-originated", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Self-originated LSAs\n" + ) +{ + int level; + struct listnode *i, *j; + struct ospf6 *o = ospf6; + struct ospf6_area *oa; + struct ospf6_interface *oi; + u_int32_t adv_router = 0; + + OSPF6_CMD_CHECK_RUNNING (); + + level = parse_show_level (argc, argv); + + adv_router = o->router_id; + + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + { + vty_out (vty, AREA_LSDB_TITLE_FORMAT, VNL, oa->name, VNL, VNL); + ospf6_lsdb_show (vty, level, NULL, NULL, &adv_router, oa->lsdb); + } + + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + { + for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi)) + { + vty_out (vty, IF_LSDB_TITLE_FORMAT, VNL, + oi->interface->name, oa->name, VNL, VNL); + ospf6_lsdb_show (vty, level, NULL, NULL, &adv_router, oi->lsdb); + } + } + + vty_out (vty, AS_LSDB_TITLE_FORMAT, VNL, VNL, VNL); + ospf6_lsdb_show (vty, level, NULL, NULL, &adv_router, o->lsdb); + + vty_out (vty, "%s", VNL); + return CMD_SUCCESS; +} + +ALIAS (show_ipv6_ospf6_database_self_originated, + show_ipv6_ospf6_database_self_originated_detail_cmd, + "show ipv6 ospf6 database self-originated " + "(detail|dump|internal)", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Self-originated LSAs\n" + "Display details of LSAs\n" + "Dump LSAs\n" + "Display LSA's internal information\n" + ) + +DEFUN (show_ipv6_ospf6_database_type_self_originated, + show_ipv6_ospf6_database_type_self_originated_cmd, + "show ipv6 ospf6 database " + "(router|network|inter-prefix|inter-router|as-external|" + "group-membership|type-7|link|intra-prefix) self-originated", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Display Router LSAs\n" + "Display Network LSAs\n" + "Display Inter-Area-Prefix LSAs\n" + "Display Inter-Area-Router LSAs\n" + "Display As-External LSAs\n" + "Display Group-Membership LSAs\n" + "Display Type-7 LSAs\n" + "Display Link LSAs\n" + "Display Intra-Area-Prefix LSAs\n" + "Display Self-originated LSAs\n" + ) +{ + int level; + struct listnode *i, *j; + struct ospf6 *o = ospf6; + struct ospf6_area *oa; + struct ospf6_interface *oi; + u_int16_t type = 0; + u_int32_t adv_router = 0; + + OSPF6_CMD_CHECK_RUNNING (); + + type = parse_type_spec (argc, argv); + argc--; + argv++; + level = parse_show_level (argc, argv); + + adv_router = o->router_id; + + switch (OSPF6_LSA_SCOPE (type)) + { + case OSPF6_SCOPE_AREA: + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + { + vty_out (vty, AREA_LSDB_TITLE_FORMAT, VNL, oa->name, VNL, VNL); + ospf6_lsdb_show (vty, level, &type, NULL, &adv_router, oa->lsdb); + } + break; + + case OSPF6_SCOPE_LINKLOCAL: + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + { + for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi)) + { + vty_out (vty, IF_LSDB_TITLE_FORMAT, VNL, + oi->interface->name, oa->name, VNL, VNL); + ospf6_lsdb_show (vty, level, &type, NULL, &adv_router, oi->lsdb); + } + } + break; + + case OSPF6_SCOPE_AS: + vty_out (vty, AS_LSDB_TITLE_FORMAT, VNL, VNL, VNL); + ospf6_lsdb_show (vty, level, &type, NULL, &adv_router, o->lsdb); + break; + + default: + assert (0); + break; + } + + vty_out (vty, "%s", VNL); + return CMD_SUCCESS; +} + +ALIAS (show_ipv6_ospf6_database_type_self_originated, + show_ipv6_ospf6_database_type_self_originated_detail_cmd, + "show ipv6 ospf6 database " + "(router|network|inter-prefix|inter-router|as-external|" + "group-membership|type-7|link|intra-prefix) self-originated " + "(detail|dump|internal)", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Display Router LSAs\n" + "Display Network LSAs\n" + "Display Inter-Area-Prefix LSAs\n" + "Display Inter-Area-Router LSAs\n" + "Display As-External LSAs\n" + "Display Group-Membership LSAs\n" + "Display Type-7 LSAs\n" + "Display Link LSAs\n" + "Display Intra-Area-Prefix LSAs\n" + "Display Self-originated LSAs\n" + "Display details of LSAs\n" + "Dump LSAs\n" + "Display LSA's internal information\n" + ) + +DEFUN (show_ipv6_ospf6_database_type_self_originated_linkstate_id, + show_ipv6_ospf6_database_type_self_originated_linkstate_id_cmd, + "show ipv6 ospf6 database " + "(router|network|inter-prefix|inter-router|as-external|" + "group-membership|type-7|link|intra-prefix) self-originated " + "linkstate-id A.B.C.D", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Display Router LSAs\n" + "Display Network LSAs\n" + "Display Inter-Area-Prefix LSAs\n" + "Display Inter-Area-Router LSAs\n" + "Display As-External LSAs\n" + "Display Group-Membership LSAs\n" + "Display Type-7 LSAs\n" + "Display Link LSAs\n" + "Display Intra-Area-Prefix LSAs\n" + "Display Self-originated LSAs\n" + "Search by Link state ID\n" + "Specify Link state ID as IPv4 address notation\n" + ) +{ + int level; + struct listnode *i, *j; + struct ospf6 *o = ospf6; + struct ospf6_area *oa; + struct ospf6_interface *oi; + u_int16_t type = 0; + u_int32_t adv_router = 0; + u_int32_t id = 0; + + OSPF6_CMD_CHECK_RUNNING (); + + type = parse_type_spec (argc, argv); + argc--; + argv++; + + if ((inet_pton (AF_INET, argv[0], &id)) != 1) + { + vty_out (vty, "Link State ID is not parsable: %s%s", + argv[0], VNL); + return CMD_SUCCESS; + } + + argc--; + argv++; + level = parse_show_level (argc, argv); + + adv_router = o->router_id; + + switch (OSPF6_LSA_SCOPE (type)) + { + case OSPF6_SCOPE_AREA: + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + { + vty_out (vty, AREA_LSDB_TITLE_FORMAT, VNL, oa->name, VNL, VNL); + ospf6_lsdb_show (vty, level, &type, &id, &adv_router, oa->lsdb); + } + break; + + case OSPF6_SCOPE_LINKLOCAL: + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + { + for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi)) + { + vty_out (vty, IF_LSDB_TITLE_FORMAT, VNL, + oi->interface->name, oa->name, VNL, VNL); + ospf6_lsdb_show (vty, level, &type, &id, &adv_router, oi->lsdb); + } + } + break; + + case OSPF6_SCOPE_AS: + vty_out (vty, AS_LSDB_TITLE_FORMAT, VNL, VNL, VNL); + ospf6_lsdb_show (vty, level, &type, &id, &adv_router, o->lsdb); + break; + + default: + assert (0); + break; + } + + vty_out (vty, "%s", VNL); + return CMD_SUCCESS; +} + +ALIAS (show_ipv6_ospf6_database_type_self_originated_linkstate_id, + show_ipv6_ospf6_database_type_self_originated_linkstate_id_detail_cmd, + "show ipv6 ospf6 database " + "(router|network|inter-prefix|inter-router|as-external|" + "group-membership|type-7|link|intra-prefix) self-originated " + "linkstate-id A.B.C.D (detail|dump|internal)", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Display Router LSAs\n" + "Display Network LSAs\n" + "Display Inter-Area-Prefix LSAs\n" + "Display Inter-Area-Router LSAs\n" + "Display As-External LSAs\n" + "Display Group-Membership LSAs\n" + "Display Type-7 LSAs\n" + "Display Link LSAs\n" + "Display Intra-Area-Prefix LSAs\n" + "Display Self-originated LSAs\n" + "Search by Link state ID\n" + "Specify Link state ID as IPv4 address notation\n" + "Display details of LSAs\n" + "Dump LSAs\n" + "Display LSA's internal information\n" + ) + +DEFUN (show_ipv6_ospf6_database_type_id_self_originated, + show_ipv6_ospf6_database_type_id_self_originated_cmd, + "show ipv6 ospf6 database " + "(router|network|inter-prefix|inter-router|as-external|" + "group-membership|type-7|link|intra-prefix) A.B.C.D self-originated", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Display Router LSAs\n" + "Display Network LSAs\n" + "Display Inter-Area-Prefix LSAs\n" + "Display Inter-Area-Router LSAs\n" + "Display As-External LSAs\n" + "Display Group-Membership LSAs\n" + "Display Type-7 LSAs\n" + "Display Link LSAs\n" + "Display Intra-Area-Prefix LSAs\n" + "Specify Link state ID as IPv4 address notation\n" + "Display Self-originated LSAs\n" + ) +{ + int level; + struct listnode *i, *j; + struct ospf6 *o = ospf6; + struct ospf6_area *oa; + struct ospf6_interface *oi; + u_int16_t type = 0; + u_int32_t adv_router = 0; + u_int32_t id = 0; + + OSPF6_CMD_CHECK_RUNNING (); + + type = parse_type_spec (argc, argv); + argc--; + argv++; + + if ((inet_pton (AF_INET, argv[0], &id)) != 1) + { + vty_out (vty, "Link State ID is not parsable: %s%s", + argv[0], VNL); + return CMD_SUCCESS; + } + + argc--; + argv++; + level = parse_show_level (argc, argv); + + adv_router = o->router_id; + + switch (OSPF6_LSA_SCOPE (type)) + { + case OSPF6_SCOPE_AREA: + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + { + vty_out (vty, AREA_LSDB_TITLE_FORMAT, VNL, oa->name, VNL, VNL); + ospf6_lsdb_show (vty, level, &type, &id, &adv_router, oa->lsdb); + } + break; + + case OSPF6_SCOPE_LINKLOCAL: + for (ALL_LIST_ELEMENTS_RO (o->area_list, i, oa)) + { + for (ALL_LIST_ELEMENTS_RO (oa->if_list, j, oi)) + { + vty_out (vty, IF_LSDB_TITLE_FORMAT, VNL, + oi->interface->name, oa->name, VNL, VNL); + ospf6_lsdb_show (vty, level, &type, &id, &adv_router, oi->lsdb); + } + } + break; + + case OSPF6_SCOPE_AS: + vty_out (vty, AS_LSDB_TITLE_FORMAT, VNL, VNL, VNL); + ospf6_lsdb_show (vty, level, &type, &id, &adv_router, o->lsdb); + break; + + default: + assert (0); + break; + } + + vty_out (vty, "%s", VNL); + return CMD_SUCCESS; +} + +ALIAS (show_ipv6_ospf6_database_type_id_self_originated, + show_ipv6_ospf6_database_type_id_self_originated_detail_cmd, + "show ipv6 ospf6 database " + "(router|network|inter-prefix|inter-router|as-external|" + "group-membership|type-7|link|intra-prefix) A.B.C.D self-originated " + "(detail|dump|internal)", + SHOW_STR + IPV6_STR + OSPF6_STR + "Display Link state database\n" + "Display Router LSAs\n" + "Display Network LSAs\n" + "Display Inter-Area-Prefix LSAs\n" + "Display Inter-Area-Router LSAs\n" + "Display As-External LSAs\n" + "Display Group-Membership LSAs\n" + "Display Type-7 LSAs\n" + "Display Link LSAs\n" + "Display Intra-Area-Prefix LSAs\n" + "Display Self-originated LSAs\n" + "Search by Link state ID\n" + "Specify Link state ID as IPv4 address notation\n" + "Display details of LSAs\n" + "Dump LSAs\n" + "Display LSA's internal information\n" + ) + + +DEFUN (show_ipv6_ospf6_border_routers, + show_ipv6_ospf6_border_routers_cmd, + "show ipv6 ospf6 border-routers", + SHOW_STR + IP6_STR + OSPF6_STR + "Display routing table for ABR and ASBR\n" + ) +{ + u_int32_t adv_router; + void (*showfunc) (struct vty *, struct ospf6_route *); + struct ospf6_route *ro; + struct prefix prefix; + + OSPF6_CMD_CHECK_RUNNING (); + + if (argc && ! strcmp ("detail", argv[0])) + { + showfunc = ospf6_route_show_detail; + argc--; + argv++; + } + else + showfunc = ospf6_brouter_show; + + if (argc) + { + if ((inet_pton (AF_INET, argv[0], &adv_router)) != 1) + { + vty_out (vty, "Router ID is not parsable: %s%s", argv[0], VNL); + return CMD_SUCCESS; + } + + ospf6_linkstate_prefix (adv_router, 0, &prefix); + ro = ospf6_route_lookup (&prefix, ospf6->brouter_table); + if (!ro) + { + vty_out (vty, "No Route found for Router ID: %s%s", argv[0], VNL); + return CMD_SUCCESS; + } + + ospf6_route_show_detail (vty, ro); + return CMD_SUCCESS; + } + + if (showfunc == ospf6_brouter_show) + ospf6_brouter_show_header (vty); + + for (ro = ospf6_route_head (ospf6->brouter_table); ro; + ro = ospf6_route_next (ro)) + (*showfunc) (vty, ro); + + return CMD_SUCCESS; +} + +ALIAS (show_ipv6_ospf6_border_routers, + show_ipv6_ospf6_border_routers_detail_cmd, + "show ipv6 ospf6 border-routers (A.B.C.D|detail)", + SHOW_STR + IP6_STR + OSPF6_STR + "Display routing table for ABR and ASBR\n" + "Specify Router-ID\n" + "Display Detail\n" + ) + +DEFUN (show_ipv6_ospf6_linkstate, + show_ipv6_ospf6_linkstate_cmd, + "show ipv6 ospf6 linkstate", + SHOW_STR + IP6_STR + OSPF6_STR + "Display linkstate routing table\n" + ) +{ + 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", + VNL, oa->name, VNL, VNL); + ospf6_linkstate_table_show (vty, argc, argv, oa->spf_table); + } + + vty_out (vty, "%s", VNL); + return CMD_SUCCESS; +} + +ALIAS (show_ipv6_ospf6_linkstate, + show_ipv6_ospf6_linkstate_router_cmd, + "show ipv6 ospf6 linkstate router A.B.C.D", + SHOW_STR + IP6_STR + OSPF6_STR + "Display linkstate routing table\n" + "Display Router Entry\n" + "Specify Router ID as IPv4 address notation\n" + ) + +ALIAS (show_ipv6_ospf6_linkstate, + show_ipv6_ospf6_linkstate_network_cmd, + "show ipv6 ospf6 linkstate network A.B.C.D A.B.C.D", + SHOW_STR + IP6_STR + OSPF6_STR + "Display linkstate routing table\n" + "Display Network Entry\n" + "Specify Router ID as IPv4 address notation\n" + "Specify Link state ID as IPv4 address notation\n" + ) + +DEFUN (show_ipv6_ospf6_linkstate_detail, + show_ipv6_ospf6_linkstate_detail_cmd, + "show ipv6 ospf6 linkstate detail", + SHOW_STR + IP6_STR + OSPF6_STR + "Display linkstate routing table\n" + ) +{ + const char *sargv[CMD_ARGC_MAX]; + int i, sargc; + 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]; + sargc = argc; + sargv[sargc++] = "detail"; + sargv[sargc] = NULL; + + for (ALL_LIST_ELEMENTS_RO (ospf6->area_list, node, oa)) + { + vty_out (vty, "%s SPF Result in Area %s%s%s", + VNL, oa->name, VNL, VNL); + ospf6_linkstate_table_show (vty, sargc, sargv, oa->spf_table); + } + + vty_out (vty, "%s", VNL); + return CMD_SUCCESS; +} + +/* Install ospf related commands. */ +void +ospf6_init (void) +{ + ospf6_top_init (); + ospf6_area_init (); + ospf6_interface_init (); + ospf6_neighbor_init (); + ospf6_zebra_init (master); + + ospf6_lsa_init (); + ospf6_spf_init (); + ospf6_intra_init (); + ospf6_asbr_init (); + ospf6_abr_init (); + +#ifdef HAVE_SNMP + ospf6_snmp_init (master); +#endif /*HAVE_SNMP*/ + + install_node (&debug_node, config_write_ospf6_debug); + + install_element_ospf6_debug_message (); + install_element_ospf6_debug_lsa (); + install_element_ospf6_debug_interface (); + install_element_ospf6_debug_neighbor (); + install_element_ospf6_debug_zebra (); + install_element_ospf6_debug_spf (); + install_element_ospf6_debug_route (); + install_element_ospf6_debug_brouter (); + install_element_ospf6_debug_asbr (); + 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 (VIEW_NODE, &show_ipv6_ospf6_border_routers_cmd); + install_element (VIEW_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); + +#define INSTALL(n,c) \ + install_element (n ## _NODE, &show_ipv6_ospf6_ ## c) + + INSTALL (VIEW, database_cmd); + INSTALL (VIEW, database_detail_cmd); + INSTALL (VIEW, database_type_cmd); + INSTALL (VIEW, database_type_detail_cmd); + INSTALL (VIEW, database_id_cmd); + INSTALL (VIEW, database_id_detail_cmd); + INSTALL (VIEW, database_linkstate_id_cmd); + INSTALL (VIEW, database_linkstate_id_detail_cmd); + INSTALL (VIEW, database_router_cmd); + INSTALL (VIEW, database_router_detail_cmd); + INSTALL (VIEW, database_adv_router_cmd); + INSTALL (VIEW, database_adv_router_detail_cmd); + INSTALL (VIEW, database_type_id_cmd); + INSTALL (VIEW, database_type_id_detail_cmd); + INSTALL (VIEW, database_type_linkstate_id_cmd); + INSTALL (VIEW, database_type_linkstate_id_detail_cmd); + INSTALL (VIEW, database_type_router_cmd); + INSTALL (VIEW, database_type_router_detail_cmd); + INSTALL (VIEW, database_type_adv_router_cmd); + INSTALL (VIEW, database_type_adv_router_detail_cmd); + INSTALL (VIEW, database_adv_router_linkstate_id_cmd); + INSTALL (VIEW, database_adv_router_linkstate_id_detail_cmd); + INSTALL (VIEW, database_id_router_cmd); + INSTALL (VIEW, database_id_router_detail_cmd); + INSTALL (VIEW, database_type_id_router_cmd); + INSTALL (VIEW, database_type_id_router_detail_cmd); + INSTALL (VIEW, database_type_adv_router_linkstate_id_cmd); + INSTALL (VIEW, database_type_adv_router_linkstate_id_detail_cmd); + INSTALL (VIEW, database_self_originated_cmd); + INSTALL (VIEW, database_self_originated_detail_cmd); + INSTALL (VIEW, database_type_self_originated_cmd); + INSTALL (VIEW, database_type_self_originated_detail_cmd); + INSTALL (VIEW, database_type_id_self_originated_cmd); + INSTALL (VIEW, database_type_id_self_originated_detail_cmd); + INSTALL (VIEW, database_type_self_originated_linkstate_id_cmd); + INSTALL (VIEW, database_type_self_originated_linkstate_id_detail_cmd); + + /* Make ospf protocol socket. */ + ospf6_serv_sock (); + thread_add_read (master, ospf6_receive, NULL, ospf6_sock); +} + +void +ospf6_clean (void) +{ + if (!ospf6) + return; + if (ospf6->route_table) + ospf6_route_remove_all (ospf6->route_table); + if (ospf6->brouter_table) + ospf6_route_remove_all (ospf6->brouter_table); +} diff --git a/ospf6d/ospf6d.conf.sample b/ospf6d/ospf6d.conf.sample new file mode 100644 index 0000000..0a6ddb7 --- /dev/null +++ b/ospf6d/ospf6d.conf.sample @@ -0,0 +1,52 @@ +! +! Zebra configuration saved from vty +! 2003/11/28 00:49:49 +! +hostname ospf6d@plant +password zebra +log stdout +service advanced-vty +! +debug ospf6 neighbor state +! +interface fxp0 + ipv6 ospf6 cost 1 + ipv6 ospf6 hello-interval 10 + ipv6 ospf6 dead-interval 40 + ipv6 ospf6 retransmit-interval 5 + ipv6 ospf6 priority 0 + ipv6 ospf6 transmit-delay 1 + ipv6 ospf6 instance-id 0 +! +interface lo0 + ipv6 ospf6 cost 1 + ipv6 ospf6 hello-interval 10 + ipv6 ospf6 dead-interval 40 + ipv6 ospf6 retransmit-interval 5 + ipv6 ospf6 priority 1 + ipv6 ospf6 transmit-delay 1 + ipv6 ospf6 instance-id 0 +! +router ospf6 + router-id 255.1.1.1 + redistribute static route-map static-ospf6 + interface fxp0 area 0.0.0.0 +! +access-list access4 permit 127.0.0.1/32 +! +ipv6 access-list access6 permit 3ffe:501::/32 +ipv6 access-list access6 permit 2001:200::/48 +ipv6 access-list access6 permit ::1/128 +! +ipv6 prefix-list test-prefix seq 1000 deny any +! +route-map static-ospf6 permit 10 + match ipv6 address prefix-list test-prefix + set metric-type type-2 + set metric 2000 +! +line vty + access-class access4 + ipv6 access-class access6 + exec-timeout 0 0 +! diff --git a/ospf6d/ospf6d.h b/ospf6d/ospf6d.h new file mode 100644 index 0000000..9e2efb4 --- /dev/null +++ b/ospf6d/ospf6d.h @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2003 Yasuhiro Ohara + * + * 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 OSPF6D_H +#define OSPF6D_H + +#define OSPF6_DAEMON_VERSION "0.9.7r" + +#include "libospf.h" +#include "thread.h" + +/* global variables */ +extern struct thread_master *master; + +/* Historical for KAME. */ +#ifndef IPV6_JOIN_GROUP +#ifdef IPV6_ADD_MEMBERSHIP +#define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP +#endif /* IPV6_ADD_MEMBERSHIP. */ +#ifdef IPV6_JOIN_MEMBERSHIP +#define IPV6_JOIN_GROUP IPV6_JOIN_MEMBERSHIP +#endif /* IPV6_JOIN_MEMBERSHIP. */ +#endif /* ! IPV6_JOIN_GROUP*/ + +#ifndef IPV6_LEAVE_GROUP +#ifdef IPV6_DROP_MEMBERSHIP +#define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP +#endif /* IPV6_DROP_MEMBERSHIP */ +#endif /* ! IPV6_LEAVE_GROUP */ + +#define MSG_OK 0 +#define MSG_NG 1 + +/* cast macro: XXX - these *must* die, ick ick. */ +#define OSPF6_PROCESS(x) ((struct ospf6 *) (x)) +#define OSPF6_AREA(x) ((struct ospf6_area *) (x)) +#define OSPF6_INTERFACE(x) ((struct ospf6_interface *) (x)) +#define OSPF6_NEIGHBOR(x) ((struct ospf6_neighbor *) (x)) + +/* operation on timeval structure */ +#ifndef timerclear +#define timerclear(a) (a)->tv_sec = (tvp)->tv_usec = 0 +#endif /*timerclear*/ +#ifndef timersub +#define timersub(a, b, res) \ + do { \ + (res)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ + (res)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ + if ((res)->tv_usec < 0) \ + { \ + (res)->tv_sec--; \ + (res)->tv_usec += 1000000; \ + } \ + } while (0) +#endif /*timersub*/ +#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 { \ + int ret; \ + struct tm *tm; \ + tm = localtime (&(tv)->tv_sec); \ + ret = strftime (buf, size, "%Y/%m/%d %H:%M:%S", tm); \ + if (ret == 0) \ + 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" +#define OSPF6_SPF_STR "Shortest Path First tree information\n" +#define OSPF6_ROUTER_ID_STR "Specify Router-ID\n" +#define OSPF6_LS_ID_STR "Specify Link State ID\n" + +#define VNL VTY_NEWLINE +#define OSPF6_CMD_CHECK_RUNNING() \ + if (ospf6 == NULL) \ + { \ + vty_out (vty, "OSPFv3 is not running%s", VTY_NEWLINE); \ + return CMD_SUCCESS; \ + } + + +/* Function Prototypes */ +extern struct route_node *route_prev (struct route_node *node); + +extern void ospf6_debug (void); +extern void ospf6_init (void); + +#endif /* OSPF6D_H */ + + diff --git a/ospfclient/.gitignore b/ospfclient/.gitignore new file mode 100644 index 0000000..191322b --- /dev/null +++ b/ospfclient/.gitignore @@ -0,0 +1,16 @@ +Makefile +Makefile.in +*.o +ospfclient +tags +TAGS +.deps +.nfs* +*.lo +*.la +*.libs +.arch-inventory +.arch-ids +*~ +*.loT + diff --git a/ospfclient/AUTHORS b/ospfclient/AUTHORS new file mode 100644 index 0000000..b865c55 --- /dev/null +++ b/ospfclient/AUTHORS @@ -0,0 +1 @@ +Ralph Keller diff --git a/ospfclient/COPYING b/ospfclient/COPYING new file mode 100644 index 0000000..b8cf3a1 --- /dev/null +++ b/ospfclient/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, 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 + + 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 +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) 19yy + + 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., 675 Mass Ave, Cambridge, MA 02139, 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) 19yy 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/ospfclient/INSTALL b/ospfclient/INSTALL new file mode 100644 index 0000000..b42a17a --- /dev/null +++ b/ospfclient/INSTALL @@ -0,0 +1,182 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. diff --git a/ospfclient/Makefile.am b/ospfclient/Makefile.am new file mode 100644 index 0000000..1fca431 --- /dev/null +++ b/ospfclient/Makefile.am @@ -0,0 +1,27 @@ +## Automake.am for OSPF API client + +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-info 0:0:0 +libospfapiclient_la_LIBADD = ../ospfd/libospf.la ../lib/libzebra.la + +sbin_PROGRAMS = ospfclient + +libospfapiclient_la_SOURCES = \ + ospf_apiclient.c + +ospfapiheaderdir = $(pkgincludedir)/ospfapi + +ospfapiheader_HEADERS = \ + ospf_apiclient.h + +ospfclient_SOURCES = \ + ospfclient.c + +ospfclient_LDADD = libospfapiclient.la \ + ../ospfd/libospf.la ../lib/libzebra.la @LIBCAP@ + +ospfclient_CFLAGS = $(AM_CFLAGS) +ospfclient_LDFLAGS = $(AM_LDFLAGS) diff --git a/ospfclient/NEWS b/ospfclient/NEWS new file mode 100644 index 0000000..5b1ec4f --- /dev/null +++ b/ospfclient/NEWS @@ -0,0 +1 @@ +This file contains news. diff --git a/ospfclient/README b/ospfclient/README new file mode 100644 index 0000000..894cd78 --- /dev/null +++ b/ospfclient/README @@ -0,0 +1,4 @@ +For more information about this software check out: + +http://www.tik.ee.ethz.ch/~keller/ospfapi/ + diff --git a/ospfclient/ospf_apiclient.c b/ospfclient/ospf_apiclient.c new file mode 100644 index 0000000..ed7ca94 --- /dev/null +++ b/ospfclient/ospf_apiclient.c @@ -0,0 +1,746 @@ +/* + * Client side of OSPF API. + * Copyright (C) 2001, 2002, 2003 Ralph Keller + * + * 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 "getopt.h" +#include "thread.h" +#include "prefix.h" +#include "linklist.h" +#include "if.h" +#include "vector.h" +#include "vty.h" +#include "command.h" +#include "filter.h" +#include "stream.h" +#include "log.h" +#include "memory.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_opaque.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_zebra.h" +#include "ospfd/ospf_api.h" + +#include "ospf_apiclient.h" + +/* Backlog for listen */ +#define BACKLOG 5 + +/* ----------------------------------------------------------- + * Forward declarations + * ----------------------------------------------------------- + */ + +void ospf_apiclient_handle_reply (struct ospf_apiclient *oclient, + struct msg *msg); +void ospf_apiclient_handle_update_notify (struct ospf_apiclient *oclient, + struct msg *msg); +void ospf_apiclient_handle_delete_notify (struct ospf_apiclient *oclient, + struct msg *msg); + +/* ----------------------------------------------------------- + * Initialization + * ----------------------------------------------------------- + */ + +static unsigned short +ospf_apiclient_getport (void) +{ + struct servent *sp = getservbyname ("ospfapi", "tcp"); + + return sp ? ntohs (sp->s_port) : OSPF_API_SYNC_PORT; +} + +/* ----------------------------------------------------------- + * Followings are functions for connection management + * ----------------------------------------------------------- + */ + +struct ospf_apiclient * +ospf_apiclient_connect (char *host, int syncport) +{ + struct sockaddr_in myaddr_sync; + struct sockaddr_in myaddr_async; + struct sockaddr_in peeraddr; + struct hostent *hp; + struct ospf_apiclient *new; + int size = 0; + unsigned int peeraddrlen; + int async_server_sock; + int fd1, fd2; + int ret; + int on = 1; + + /* There are two connections between the client and the server. + First the client opens a connection for synchronous requests/replies + to the server. The server will accept this connection and + as a reaction open a reverse connection channel for + asynchronous messages. */ + + async_server_sock = socket (AF_INET, SOCK_STREAM, 0); + if (async_server_sock < 0) + { + fprintf (stderr, + "ospf_apiclient_connect: creating async socket failed\n"); + return NULL; + } + + /* Prepare socket for asynchronous messages */ + /* Initialize async address structure */ + memset (&myaddr_async, 0, sizeof (struct sockaddr_in)); + myaddr_async.sin_family = AF_INET; + myaddr_async.sin_addr.s_addr = htonl (INADDR_ANY); + myaddr_async.sin_port = htons (syncport+1); + size = sizeof (struct sockaddr_in); +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + myaddr_async.sin_len = size; +#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ + + /* This is a server socket, reuse addr and port */ + ret = setsockopt (async_server_sock, SOL_SOCKET, + SO_REUSEADDR, (void *) &on, sizeof (on)); + if (ret < 0) + { + fprintf (stderr, "ospf_apiclient_connect: SO_REUSEADDR failed\n"); + close (async_server_sock); + return NULL; + } + +#ifdef SO_REUSEPORT + ret = setsockopt (async_server_sock, SOL_SOCKET, SO_REUSEPORT, + (void *) &on, sizeof (on)); + if (ret < 0) + { + fprintf (stderr, "ospf_apiclient_connect: SO_REUSEPORT failed\n"); + close (async_server_sock); + return NULL; + } +#endif /* SO_REUSEPORT */ + + /* Bind socket to address structure */ + ret = bind (async_server_sock, (struct sockaddr *) &myaddr_async, size); + if (ret < 0) + { + fprintf (stderr, "ospf_apiclient_connect: bind async socket failed\n"); + close (async_server_sock); + return NULL; + } + + /* Wait for reverse channel connection establishment from server */ + ret = listen (async_server_sock, BACKLOG); + if (ret < 0) + { + fprintf (stderr, "ospf_apiclient_connect: listen: %s\n", safe_strerror (errno)); + close (async_server_sock); + return NULL; + } + + /* Make connection for synchronous requests and connect to server */ + /* Resolve address of server */ + hp = gethostbyname (host); + if (!hp) + { + fprintf (stderr, "ospf_apiclient_connect: no such host %s\n", host); + close (async_server_sock); + return NULL; + } + + fd1 = socket (AF_INET, SOCK_STREAM, 0); + if (fd1 < 0) + { + fprintf (stderr, + "ospf_apiclient_connect: creating sync socket failed\n"); + return NULL; + } + + + /* Reuse addr and port */ + ret = setsockopt (fd1, SOL_SOCKET, + SO_REUSEADDR, (void *) &on, sizeof (on)); + if (ret < 0) + { + fprintf (stderr, "ospf_apiclient_connect: SO_REUSEADDR failed\n"); + close (fd1); + return NULL; + } + +#ifdef SO_REUSEPORT + ret = setsockopt (fd1, SOL_SOCKET, SO_REUSEPORT, + (void *) &on, sizeof (on)); + if (ret < 0) + { + fprintf (stderr, "ospf_apiclient_connect: SO_REUSEPORT failed\n"); + close (fd1); + return NULL; + } +#endif /* SO_REUSEPORT */ + + + /* Bind sync socket to address structure. This is needed since we + want the sync port number on a fixed port number. The reverse + async channel will be at this port+1 */ + + memset (&myaddr_sync, 0, sizeof (struct sockaddr_in)); + myaddr_sync.sin_family = AF_INET; + myaddr_sync.sin_port = htons (syncport); +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + myaddr_sync.sin_len = sizeof (struct sockaddr_in); +#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ + + ret = bind (fd1, (struct sockaddr *) &myaddr_sync, size); + if (ret < 0) + { + fprintf (stderr, "ospf_apiclient_connect: bind sync socket failed\n"); + close (fd1); + return NULL; + } + + /* Prepare address structure for connect */ + memcpy (&myaddr_sync.sin_addr, hp->h_addr, hp->h_length); + myaddr_sync.sin_family = AF_INET; + myaddr_sync.sin_port = htons(ospf_apiclient_getport ()); +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + myaddr_sync.sin_len = sizeof (struct sockaddr_in); +#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ + + /* Now establish synchronous channel with OSPF daemon */ + ret = connect (fd1, (struct sockaddr *) &myaddr_sync, + sizeof (struct sockaddr_in)); + if (ret < 0) + { + fprintf (stderr, "ospf_apiclient_connect: sync connect failed\n"); + close (async_server_sock); + close (fd1); + return NULL; + } + + /* Accept reverse connection */ + peeraddrlen = sizeof (struct sockaddr_in); + memset (&peeraddr, 0, peeraddrlen); + + fd2 = + accept (async_server_sock, (struct sockaddr *) &peeraddr, &peeraddrlen); + if (fd2 < 0) + { + fprintf (stderr, "ospf_apiclient_connect: accept async failed\n"); + close (async_server_sock); + close (fd1); + return NULL; + } + + /* Server socket is not needed anymore since we are not accepting more + connections */ + close (async_server_sock); + + /* Create new client-side instance */ + new = XCALLOC (MTYPE_OSPF_APICLIENT, sizeof (struct ospf_apiclient)); + + /* Initialize socket descriptors for sync and async channels */ + new->fd_sync = fd1; + new->fd_async = fd2; + + return new; +} + +int +ospf_apiclient_close (struct ospf_apiclient *oclient) +{ + + if (oclient->fd_sync >= 0) + { + close (oclient->fd_sync); + } + + if (oclient->fd_async >= 0) + { + close (oclient->fd_async); + } + + /* Free client structure */ + XFREE (MTYPE_OSPF_APICLIENT, oclient); + return 0; +} + +/* ----------------------------------------------------------- + * Followings are functions to send a request to OSPFd + * ----------------------------------------------------------- + */ + +/* Send synchronous request, wait for reply */ +static int +ospf_apiclient_send_request (struct ospf_apiclient *oclient, struct msg *msg) +{ + u_int32_t reqseq; + struct msg_reply *msgreply; + int rc; + + /* NB: Given "msg" is freed inside this function. */ + + /* Remember the sequence number of the request */ + reqseq = ntohl (msg->hdr.msgseq); + + /* Write message to OSPFd */ + rc = msg_write (oclient->fd_sync, msg); + msg_free (msg); + + if (rc < 0) + { + return -1; + } + + /* Wait for reply *//* NB: New "msg" is allocated by "msg_read()". */ + msg = msg_read (oclient->fd_sync); + if (!msg) + return -1; + + assert (msg->hdr.msgtype == MSG_REPLY); + assert (ntohl (msg->hdr.msgseq) == reqseq); + + msgreply = (struct msg_reply *) STREAM_DATA (msg->s); + rc = msgreply->errcode; + msg_free (msg); + + return rc; +} + + +/* ----------------------------------------------------------- + * Helper functions + * ----------------------------------------------------------- + */ + +static u_int32_t +ospf_apiclient_get_seqnr (void) +{ + static u_int32_t seqnr = MIN_SEQ; + u_int32_t tmp; + + tmp = seqnr; + /* Increment sequence number */ + if (seqnr < MAX_SEQ) + { + seqnr++; + } + else + { + seqnr = MIN_SEQ; + } + return tmp; +} + +/* ----------------------------------------------------------- + * API to access OSPF daemon by client applications. + * ----------------------------------------------------------- + */ + +/* + * Synchronous request to register opaque type. + */ +int +ospf_apiclient_register_opaque_type (struct ospf_apiclient *cl, + u_char ltype, u_char otype) +{ + struct msg *msg; + int rc; + + /* just put 1 as a sequence number. */ + msg = new_msg_register_opaque_type (ospf_apiclient_get_seqnr (), + ltype, otype); + if (!msg) + { + fprintf (stderr, "new_msg_register_opaque_type failed\n"); + return -1; + } + + rc = ospf_apiclient_send_request (cl, msg); + return rc; +} + +/* + * Synchronous request to synchronize with OSPF's LSDB. + * Two steps required: register_event in order to get + * dynamic updates and LSDB_Sync. + */ +int +ospf_apiclient_sync_lsdb (struct ospf_apiclient *oclient) +{ + struct msg *msg; + int rc; + struct lsa_filter_type filter; + + filter.typemask = 0xFFFF; /* all LSAs */ + filter.origin = ANY_ORIGIN; + filter.num_areas = 0; /* all Areas. */ + + msg = new_msg_register_event (ospf_apiclient_get_seqnr (), &filter); + if (!msg) + { + fprintf (stderr, "new_msg_register_event failed\n"); + return -1; + } + rc = ospf_apiclient_send_request (oclient, msg); + + if (rc != 0) + goto out; + + msg = new_msg_sync_lsdb (ospf_apiclient_get_seqnr (), &filter); + if (!msg) + { + fprintf (stderr, "new_msg_sync_lsdb failed\n"); + return -1; + } + rc = ospf_apiclient_send_request (oclient, msg); + +out: + return rc; +} + +/* + * Synchronous request to originate or update an LSA. + */ + +int +ospf_apiclient_lsa_originate (struct ospf_apiclient *oclient, + struct in_addr ifaddr, + struct in_addr area_id, + u_char lsa_type, + u_char opaque_type, u_int32_t opaque_id, + void *opaquedata, int opaquelen) +{ + struct msg *msg; + int rc; + u_char buf[OSPF_MAX_LSA_SIZE]; + struct lsa_header *lsah; + u_int32_t tmp; + + + /* We can only originate opaque LSAs */ + if (!IS_OPAQUE_LSA (lsa_type)) + { + fprintf (stderr, "Cannot originate non-opaque LSA type %d\n", lsa_type); + return OSPF_API_ILLEGALLSATYPE; + } + + /* Make a new LSA from parameters */ + lsah = (struct lsa_header *) buf; + lsah->ls_age = 0; + lsah->options = 0; + lsah->type = lsa_type; + + tmp = SET_OPAQUE_LSID (opaque_type, opaque_id); + lsah->id.s_addr = htonl (tmp); + lsah->adv_router.s_addr = 0; + lsah->ls_seqnum = 0; + lsah->checksum = 0; + lsah->length = htons (sizeof (struct lsa_header) + opaquelen); + + memcpy (((u_char *) lsah) + sizeof (struct lsa_header), opaquedata, + opaquelen); + + msg = new_msg_originate_request (ospf_apiclient_get_seqnr (), + ifaddr, area_id, lsah); + if (!msg) + { + fprintf (stderr, "new_msg_originate_request failed\n"); + return OSPF_API_NOMEMORY; + } + + rc = ospf_apiclient_send_request (oclient, msg); + return rc; +} + +int +ospf_apiclient_lsa_delete (struct ospf_apiclient *oclient, + struct in_addr area_id, u_char lsa_type, + u_char opaque_type, u_int32_t opaque_id) +{ + struct msg *msg; + int rc; + + /* Only opaque LSA can be deleted */ + if (!IS_OPAQUE_LSA (lsa_type)) + { + fprintf (stderr, "Cannot delete non-opaque LSA type %d\n", lsa_type); + return OSPF_API_ILLEGALLSATYPE; + } + + /* opaque_id is in host byte order and will be converted + * to network byte order by new_msg_delete_request */ + msg = new_msg_delete_request (ospf_apiclient_get_seqnr (), + area_id, lsa_type, opaque_type, opaque_id); + + rc = ospf_apiclient_send_request (oclient, msg); + return rc; +} + +/* ----------------------------------------------------------- + * Followings are handlers for messages from OSPF daemon + * ----------------------------------------------------------- + */ + +static void +ospf_apiclient_handle_ready (struct ospf_apiclient *oclient, struct msg *msg) +{ + struct msg_ready_notify *r; + r = (struct msg_ready_notify *) STREAM_DATA (msg->s); + + /* Invoke registered callback function. */ + if (oclient->ready_notify) + { + (oclient->ready_notify) (r->lsa_type, r->opaque_type, r->addr); + } +} + +static void +ospf_apiclient_handle_new_if (struct ospf_apiclient *oclient, struct msg *msg) +{ + struct msg_new_if *n; + n = (struct msg_new_if *) STREAM_DATA (msg->s); + + /* Invoke registered callback function. */ + if (oclient->new_if) + { + (oclient->new_if) (n->ifaddr, n->area_id); + } +} + +static void +ospf_apiclient_handle_del_if (struct ospf_apiclient *oclient, struct msg *msg) +{ + struct msg_del_if *d; + d = (struct msg_del_if *) STREAM_DATA (msg->s); + + /* Invoke registered callback function. */ + if (oclient->del_if) + { + (oclient->del_if) (d->ifaddr); + } +} + +static void +ospf_apiclient_handle_ism_change (struct ospf_apiclient *oclient, + struct msg *msg) +{ + struct msg_ism_change *m; + m = (struct msg_ism_change *) STREAM_DATA (msg->s); + + /* Invoke registered callback function. */ + if (oclient->ism_change) + { + (oclient->ism_change) (m->ifaddr, m->area_id, m->status); + } + +} + +static void +ospf_apiclient_handle_nsm_change (struct ospf_apiclient *oclient, + struct msg *msg) +{ + struct msg_nsm_change *m; + m = (struct msg_nsm_change *) STREAM_DATA (msg->s); + + /* Invoke registered callback function. */ + if (oclient->nsm_change) + { + (oclient->nsm_change) (m->ifaddr, m->nbraddr, m->router_id, m->status); + } +} + +static void +ospf_apiclient_handle_lsa_update (struct ospf_apiclient *oclient, + struct msg *msg) +{ + struct msg_lsa_change_notify *cn; + struct lsa_header *lsa; + int lsalen; + + cn = (struct msg_lsa_change_notify *) STREAM_DATA (msg->s); + + /* Extract LSA from message */ + lsalen = ntohs (cn->data.length); + lsa = XMALLOC (MTYPE_OSPF_APICLIENT, lsalen); + if (!lsa) + { + fprintf (stderr, "LSA update: Cannot allocate memory for LSA\n"); + return; + } + memcpy (lsa, &(cn->data), lsalen); + + /* Invoke registered update callback function */ + if (oclient->update_notify) + { + (oclient->update_notify) (cn->ifaddr, cn->area_id, + cn->is_self_originated, lsa); + } + + /* free memory allocated by ospf apiclient library */ + XFREE (MTYPE_OSPF_APICLIENT, lsa); +} + +static void +ospf_apiclient_handle_lsa_delete (struct ospf_apiclient *oclient, + struct msg *msg) +{ + struct msg_lsa_change_notify *cn; + struct lsa_header *lsa; + int lsalen; + + cn = (struct msg_lsa_change_notify *) STREAM_DATA (msg->s); + + /* Extract LSA from message */ + lsalen = ntohs (cn->data.length); + lsa = XMALLOC (MTYPE_OSPF_APICLIENT, lsalen); + if (!lsa) + { + fprintf (stderr, "LSA delete: Cannot allocate memory for LSA\n"); + return; + } + memcpy (lsa, &(cn->data), lsalen); + + /* Invoke registered update callback function */ + if (oclient->delete_notify) + { + (oclient->delete_notify) (cn->ifaddr, cn->area_id, + cn->is_self_originated, lsa); + } + + /* free memory allocated by ospf apiclient library */ + XFREE (MTYPE_OSPF_APICLIENT, lsa); +} + +static void +ospf_apiclient_msghandle (struct ospf_apiclient *oclient, struct msg *msg) +{ + /* Call message handler function. */ + switch (msg->hdr.msgtype) + { + case MSG_READY_NOTIFY: + ospf_apiclient_handle_ready (oclient, msg); + break; + case MSG_NEW_IF: + ospf_apiclient_handle_new_if (oclient, msg); + break; + case MSG_DEL_IF: + ospf_apiclient_handle_del_if (oclient, msg); + break; + case MSG_ISM_CHANGE: + ospf_apiclient_handle_ism_change (oclient, msg); + break; + case MSG_NSM_CHANGE: + ospf_apiclient_handle_nsm_change (oclient, msg); + break; + case MSG_LSA_UPDATE_NOTIFY: + ospf_apiclient_handle_lsa_update (oclient, msg); + break; + case MSG_LSA_DELETE_NOTIFY: + ospf_apiclient_handle_lsa_delete (oclient, msg); + break; + default: + fprintf (stderr, "ospf_apiclient_read: Unknown message type: %d\n", + msg->hdr.msgtype); + break; + } +} + +/* ----------------------------------------------------------- + * Callback handler registration + * ----------------------------------------------------------- + */ + +void +ospf_apiclient_register_callback (struct ospf_apiclient *oclient, + void (*ready_notify) (u_char lsa_type, + u_char opaque_type, + struct in_addr addr), + void (*new_if) (struct in_addr ifaddr, + struct in_addr area_id), + void (*del_if) (struct in_addr ifaddr), + void (*ism_change) (struct in_addr ifaddr, + struct in_addr area_id, + u_char status), + void (*nsm_change) (struct in_addr ifaddr, + struct in_addr nbraddr, + struct in_addr + router_id, + u_char status), + void (*update_notify) (struct in_addr + ifaddr, + struct in_addr + area_id, + u_char self_origin, + struct lsa_header * + lsa), + void (*delete_notify) (struct in_addr + ifaddr, + struct in_addr + area_id, + u_char self_origin, + struct lsa_header * + lsa)) +{ + assert (oclient); + assert (update_notify); + + /* Register callback function */ + oclient->ready_notify = ready_notify; + oclient->new_if = new_if; + oclient->del_if = del_if; + oclient->ism_change = ism_change; + oclient->nsm_change = nsm_change; + oclient->update_notify = update_notify; + oclient->delete_notify = delete_notify; +} + +/* ----------------------------------------------------------- + * Asynchronous message handling + * ----------------------------------------------------------- + */ + +int +ospf_apiclient_handle_async (struct ospf_apiclient *oclient) +{ + struct msg *msg; + + /* Get a message */ + msg = msg_read (oclient->fd_async); + + if (!msg) + { + /* Connection broke down */ + return -1; + } + + /* Handle message */ + ospf_apiclient_msghandle (oclient, msg); + + /* Don't forget to free this message */ + msg_free (msg); + + return 0; +} diff --git a/ospfclient/ospf_apiclient.h b/ospfclient/ospf_apiclient.h new file mode 100644 index 0000000..8098619 --- /dev/null +++ b/ospfclient/ospf_apiclient.h @@ -0,0 +1,135 @@ +/* + * Client side of OSPF API. + * Copyright (C) 2001, 2002, 2003 Ralph Keller + * + * 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 _OSPF_APICLIENT_H +#define _OSPF_APICLIENT_H + +#define MTYPE_OSPF_APICLIENT MTYPE_TMP + +/* Structure for the OSPF API client */ +struct ospf_apiclient +{ + + /* Sockets for sync requests and async notifications */ + int fd_sync; + int fd_async; + + /* Pointer to callback functions */ + void (*ready_notify) (u_char lsa_type, u_char opaque_type, + struct in_addr addr); + void (*new_if) (struct in_addr ifaddr, struct in_addr area_id); + void (*del_if) (struct in_addr ifaddr); + void (*ism_change) (struct in_addr ifaddr, struct in_addr area_id, + u_char status); + void (*nsm_change) (struct in_addr ifaddr, struct in_addr nbraddr, + struct in_addr router_id, u_char status); + void (*update_notify) (struct in_addr ifaddr, struct in_addr area_id, + u_char self_origin, + struct lsa_header * lsa); + void (*delete_notify) (struct in_addr ifaddr, struct in_addr area_id, + u_char self_origin, + struct lsa_header * lsa); +}; + + +/* --------------------------------------------------------- + * API function prototypes. + * --------------------------------------------------------- */ + +/* Open connection to OSPF daemon. Two ports will be allocated on + client, sync channel at syncport and reverse channel at syncport+1 */ +struct ospf_apiclient *ospf_apiclient_connect (char *host, int syncport); + +/* Shutdown connection to OSPF daemon. */ +int ospf_apiclient_close (struct ospf_apiclient *oclient); + +/* Synchronous request to register opaque type. */ +int ospf_apiclient_register_opaque_type (struct ospf_apiclient *oclient, + u_char ltype, u_char otype); + +/* Synchronous request to register event mask. */ +int ospf_apiclient_register_events (struct ospf_apiclient *oclient, + u_int32_t mask); + +/* Register callback functions.*/ +void ospf_apiclient_register_callback (struct ospf_apiclient *oclient, + void (*ready_notify) (u_char lsa_type, + u_char + opaque_type, + struct in_addr + addr), + void (*new_if) (struct in_addr ifaddr, + struct in_addr + area_id), + void (*del_if) (struct in_addr ifaddr), + void (*ism_change) (struct in_addr + ifaddr, + struct in_addr + area_id, + u_char status), + void (*nsm_change) (struct in_addr + ifaddr, + struct in_addr + nbraddr, + struct in_addr + router_id, + u_char status), + void (*update_notify) (struct in_addr + ifaddr, + struct in_addr + area_id, + u_char selforig, + struct + lsa_header * + lsa), + void (*delete_notify) (struct in_addr + ifaddr, + struct in_addr + area_id, + u_char selforig, + struct + lsa_header * + lsa)); + +/* Synchronous request to synchronize LSDB. */ +int ospf_apiclient_sync_lsdb (struct ospf_apiclient *oclient); + +/* Synchronous request to originate or update opaque LSA. */ +int +ospf_apiclient_lsa_originate(struct ospf_apiclient *oclient, + struct in_addr ifaddr, + struct in_addr area_id, + u_char lsa_type, + u_char opaque_type, u_int32_t opaque_id, + void *opaquedata, int opaquelen); + + +/* Synchronous request to delete opaque LSA. Parameter opaque_id is in + host byte order */ +int ospf_apiclient_lsa_delete (struct ospf_apiclient *oclient, + struct in_addr area_id, u_char lsa_type, + u_char opaque_type, u_int32_t opaque_id); + +/* Fetch async message and handle it */ +int ospf_apiclient_handle_async (struct ospf_apiclient *oclient); + +#endif /* _OSPF_APICLIENT_H */ diff --git a/ospfclient/ospfclient.c b/ospfclient/ospfclient.c new file mode 100644 index 0000000..1de7644 --- /dev/null +++ b/ospfclient/ospfclient.c @@ -0,0 +1,352 @@ +/* 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 + * originates, updates and finally deletes an application-specific + * opaque LSA. You can use this application as a template when writing + * your own application. + */ + +/* The following includes are needed in all OSPF API client + applications. */ + +#include +#include "prefix.h" /* needed by ospf_asbr.h */ +#include "privs.h" +#include "log.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_opaque.h" +#include "ospfd/ospf_api.h" +#include "ospf_apiclient.h" + +/* privileges struct. + * set cap_num_* and uid/gid to nothing to use NULL privs + * as ospfapiclient links in libospf.a which uses privs. + */ +struct zebra_privs_t ospfd_privs = +{ + .user = NULL, + .group = NULL, + .cap_num_p = 0, + .cap_num_i = 0 +}; + +/* The following includes are specific to this application. For + example it uses threads from libzebra, however your application is + free to use any thread library (like pthreads). */ + +#include "ospfd/ospf_dump.h" /* for ospf_lsa_header_dump */ +#include "thread.h" +#include "log.h" + +/* Local portnumber for async channel. Note that OSPF API library will also + allocate a sync channel at ASYNCPORT+1. */ +#define ASYNCPORT 4000 + +/* Master thread */ +struct thread_master *master; + +/* Global variables */ +struct ospf_apiclient *oclient; +char **args; + +/* Our opaque LSAs have the following format. */ +struct my_opaque_lsa +{ + struct lsa_header hdr; /* include common LSA header */ + u_char data[4]; /* our own data format then follows here */ +}; + + +/* --------------------------------------------------------- + * Threads for asynchronous messages and LSA update/delete + * --------------------------------------------------------- + */ + +static int +lsa_delete (struct thread *t) +{ + struct ospf_apiclient *oclient; + struct in_addr area_id; + int rc; + + oclient = THREAD_ARG (t); + + inet_aton (args[6], &area_id); + + printf ("Deleting LSA... "); + rc = ospf_apiclient_lsa_delete (oclient, + area_id, + atoi (args[2]), /* lsa type */ + atoi (args[3]), /* opaque type */ + atoi (args[4])); /* opaque ID */ + printf ("done, return code is = %d\n", rc); + return rc; +} + +static int +lsa_inject (struct thread *t) +{ + struct ospf_apiclient *cl; + struct in_addr ifaddr; + struct in_addr area_id; + u_char lsa_type; + u_char opaque_type; + u_int32_t opaque_id; + void *opaquedata; + int opaquelen; + + static u_int32_t counter = 1; /* Incremented each time invoked */ + int rc; + + cl = THREAD_ARG (t); + + inet_aton (args[5], &ifaddr); + inet_aton (args[6], &area_id); + lsa_type = atoi (args[2]); + opaque_type = atoi (args[3]); + opaque_id = atoi (args[4]); + opaquedata = &counter; + opaquelen = sizeof (u_int32_t); + + printf ("Originating/updating LSA with counter=%d... ", counter); + rc = ospf_apiclient_lsa_originate(cl, ifaddr, area_id, + lsa_type, + opaque_type, opaque_id, + opaquedata, opaquelen); + + printf ("done, return code is %d\n", rc); + + counter++; + + return 0; +} + + +/* This thread handles asynchronous messages coming in from the OSPF + API server */ +static int +lsa_read (struct thread *thread) +{ + struct ospf_apiclient *oclient; + int fd; + int ret; + + printf ("lsa_read called\n"); + + oclient = THREAD_ARG (thread); + fd = THREAD_FD (thread); + + /* Handle asynchronous message */ + ret = ospf_apiclient_handle_async (oclient); + if (ret < 0) { + printf ("Connection closed, exiting..."); + exit(0); + } + + /* Reschedule read thread */ + thread_add_read (master, lsa_read, oclient, fd); + + return 0; +} + +/* --------------------------------------------------------- + * Callback functions for asynchronous events + * --------------------------------------------------------- + */ + +static void +lsa_update_callback (struct in_addr ifaddr, struct in_addr area_id, + u_char is_self_originated, + struct lsa_header *lsa) +{ + printf ("lsa_update_callback: "); + printf ("ifaddr: %s ", inet_ntoa (ifaddr)); + printf ("area: %s\n", inet_ntoa (area_id)); + printf ("is_self_origin: %u\n", is_self_originated); + + /* It is important to note that lsa_header does indeed include the + header and the LSA payload. To access the payload, first check + the LSA type and then typecast lsa into the corresponding type, + e.g.: + + if (lsa->type == OSPF_ROUTER_LSA) { + struct router_lsa *rl = (struct router_lsa) lsa; + ... + u_int16_t links = rl->links; + ... + } + */ + + ospf_lsa_header_dump (lsa); +} + +static void +lsa_delete_callback (struct in_addr ifaddr, struct in_addr area_id, + u_char is_self_originated, + struct lsa_header *lsa) +{ + printf ("lsa_delete_callback: "); + printf ("ifaddr: %s ", inet_ntoa (ifaddr)); + printf ("area: %s\n", inet_ntoa (area_id)); + printf ("is_self_origin: %u\n", is_self_originated); + + ospf_lsa_header_dump (lsa); +} + +static void +ready_callback (u_char lsa_type, u_char opaque_type, struct in_addr addr) +{ + printf ("ready_callback: lsa_type: %d opaque_type: %d addr=%s\n", + lsa_type, opaque_type, inet_ntoa (addr)); + + /* Schedule opaque LSA originate in 5 secs */ + thread_add_timer (master, lsa_inject, oclient, 5); + + /* Schedule opaque LSA update with new value */ + thread_add_timer (master, lsa_inject, oclient, 10); + + /* Schedule delete */ + thread_add_timer (master, lsa_delete, oclient, 30); +} + +static void +new_if_callback (struct in_addr ifaddr, struct in_addr area_id) +{ + printf ("new_if_callback: ifaddr: %s ", inet_ntoa (ifaddr)); + printf ("area_id: %s\n", inet_ntoa (area_id)); +} + +static void +del_if_callback (struct in_addr ifaddr) +{ + printf ("new_if_callback: ifaddr: %s\n ", inet_ntoa (ifaddr)); +} + +static void +ism_change_callback (struct in_addr ifaddr, struct in_addr area_id, + u_char state) +{ + printf ("ism_change: ifaddr: %s ", inet_ntoa (ifaddr)); + printf ("area_id: %s\n", inet_ntoa (area_id)); + printf ("state: %d [%s]\n", state, LOOKUP (ospf_ism_state_msg, state)); +} + +static void +nsm_change_callback (struct in_addr ifaddr, struct in_addr nbraddr, + struct in_addr router_id, u_char state) +{ + printf ("nsm_change: ifaddr: %s ", inet_ntoa (ifaddr)); + printf ("nbraddr: %s\n", inet_ntoa (nbraddr)); + printf ("router_id: %s\n", inet_ntoa (router_id)); + printf ("state: %d [%s]\n", state, LOOKUP (ospf_nsm_state_msg, state)); +} + + +/* --------------------------------------------------------- + * Main program + * --------------------------------------------------------- + */ + +static int usage() +{ + printf("Usage: ospfclient \n"); + printf("where ospfd : router where API-enabled OSPF daemon is running\n"); + printf(" lsatype : either 9, 10, or 11 depending on flooding scope\n"); + printf(" opaquetype: 0-255 (e.g., experimental applications use > 128)\n"); + printf(" opaqueid : arbitrary application instance (24 bits)\n"); + printf(" ifaddr : interface IP address (for type 9) otherwise ignored\n"); + printf(" areaid : area in IP address format (for type 10) otherwise ignored\n"); + + exit(1); +} + +int +main (int argc, char *argv[]) +{ + struct thread thread; + + args = argv; + + /* ospfclient should be started with the following arguments: + * + * (1) host (2) lsa_type (3) opaque_type (4) opaque_id (5) if_addr + * (6) area_id + * + * host: name or IP of host where ospfd is running + * lsa_type: 9, 10, or 11 + * opaque_type: 0-255 (e.g., experimental applications use > 128) + * opaque_id: arbitrary application instance (24 bits) + * if_addr: interface IP address (for type 9) otherwise ignored + * area_id: area in IP address format (for type 10) otherwise ignored + */ + + if (argc != 7) + { + usage(); + } + + /* Initialization */ + zprivs_init (&ospfd_privs); + master = thread_master_create (); + + /* Open connection to OSPF daemon */ + oclient = ospf_apiclient_connect (args[1], ASYNCPORT); + if (!oclient) + { + printf ("Connecting to OSPF daemon on %s failed!\n", + args[1]); + exit (1); + } + + /* Register callback functions. */ + ospf_apiclient_register_callback (oclient, + ready_callback, + new_if_callback, + del_if_callback, + ism_change_callback, + nsm_change_callback, + lsa_update_callback, + lsa_delete_callback); + + /* Register LSA type and opaque type. */ + ospf_apiclient_register_opaque_type (oclient, atoi (args[2]), + atoi (args[3])); + + /* Synchronize database with OSPF daemon. */ + ospf_apiclient_sync_lsdb (oclient); + + /* Schedule thread that handles asynchronous messages */ + thread_add_read (master, lsa_read, oclient, oclient->fd_async); + + /* Now connection is established, run loop */ + while (1) + { + thread_fetch (master, &thread); + thread_call (&thread); + } + + /* Never reached */ + return 0; +} + diff --git a/ospfd/.gitignore b/ospfd/.gitignore new file mode 100644 index 0000000..f7d6f09 --- /dev/null +++ b/ospfd/.gitignore @@ -0,0 +1,17 @@ +Makefile +Makefile.in +*.o +ospfd +ospfd.conf +tags +TAGS +.deps +.nfs* +*.lo +*.la +*.libs +.arch-inventory +.arch-ids +*~ +*.loT + diff --git a/ospfd/ChangeLog.opaque.txt b/ospfd/ChangeLog.opaque.txt new file mode 100644 index 0000000..782e332 --- /dev/null +++ b/ospfd/ChangeLog.opaque.txt @@ -0,0 +1,221 @@ +----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- +Changes 2002.12.20 + +1. Bug fixes + + 1.1 When an opaque LSA is being removed from (or added to) the LSDB, + it does not mean a change in network topology. Therefore, SPF + recalculation should not be triggered in that case. + There was an assertion failure problem "assert (rn && rn->info)" + inside the function "ospf_ase_incremental_update()", because + the upper function "ospf_lsa_maxage_walker_remover()" called it + when a type-11 opaque LSA is removed due to MaxAge. + + 1.2 Type-9 LSA is defined to have "link-local" flooding scope. + In the Database exchange procedure with a new neighbor, a type-9 + LSA was added in the database summary of a DD message, even if + the link is different from the one that have bound to. + +2. Feature enhancements + + 2.1 Though a "wildcard" concept to handle type-9/10/11 LSAs altogether + has introduced about a year ago, it was only a symbol definition + and actual handling mechanism was not implemented. Now it works. + +----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- +Changes 2002.7.8 + +1. Bug fixes + + 1.1 When "ospf_delete_opaque_functab()" is called, internal structure + "oipt" remain unfreed. If register/delete functab is repeated, + illegal memory access happens due to this "oipt". + + 1.2 In "free_opaque_info_per_id()", there was a crucial typo which + ignores a condition test. + + "if (oipi->lsa != NULL);" <-- semicolon! + +2. Feature enhancements + + None. + +----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- +Changes 2001.12.03 + +1. Bug fixes + + 1.1 Though a new member "oi" has added to "struct ospf_lsa" to control + flooding scope of type-9 Opaque-LSAs, the value was always NULL + because no one set it. + + 1.2 In the function "show_ip_ospf_database_summary()" and "show_lsa_ + detail_adv_router()", VTY output for type-11 Opaque-LSAs did not + work properly. + + 1.3 URL for the opaque-type assignment reference has changed. + + 1.4 In the file "ospf_mpls_te.c", printf formats have changed to + avoid compiler warning messages; "%lu" -> "%u", "%lx" -> "%x". + Note that this hack depends on OS, compiler and their versions. + + 1.5 One of attached documentation "opaque_lsa.txt" has changed to + reflect the latest coding. + +2. Feature enhancements + + 2.1 Knowing that it is an ugly hack, an "officially unallocated" + opaque-type value 0 has newly introduced as a "wildcard", + which matches to all opaque-type. + This value must not be flooded to the network, of course. + + 2.2 The Opaque-core module makes use of newly introduced hooks to + dispatch every LSDB change (LSA installation and deletion) to + preregistered opaque users. + Therefore, by providing appropriate callback functions as new + parameters of "ospf_register_opaque_functab()", an opaque user + can refer to every LSA instance to be installed into, or to be + deleted from, the LSDB. + +----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- +Changes 2001.10.31 + +1. Bug fixes + + 1.1 Since each LSA has their own lifetime, they will remain in a + routing domain (being stored in LSDB of each router), until their + age naturally reach to MaxAge or explicitly being flushed by the + originated router. Therefore, if a router restarted with a short + downtime, it is possible that previously flooded self-originated + LSAs might received if the NSM status is not less than Exchange. + + There were some problems in the way of handling self-originated + Opaque-LSAs if they are contained in a received LSUpd message, + but not installed to the local LSDB yet. + Regardless of some conditions to start originating Opaque-LSAs + (there should be at least one opaque-capable full-state neighbor), + the function "ospf_flood()" will be called to flood and install + this brand-new looking LSA. + As the result, when the NSM of an opaque-capable neighbor gets + full, internal state inconsistency happens; a user of Opaque-LSA + such as MPLS-TE can refer to self-originated LSAs in the local + LSDB, but cannot modify their contents... + + Above problems have fixed with a policy "flush it from the whole + routing domain and keep silent until the flushing completed". + By using this sweeping technique, we can be free from confusion + caused by self-originated LSAs received via network. + + 1.2 The function "ospf_opaque_type_name()" contained massive ifdefs + corresponding to each "opaque-type". + These unnecessary ifdefs are removed completely. + + 1.3 In the function "ospf_delete_opaque_functab()", there was an + improper loop control that causes illegal memory access. + Original coding was "next = nextnode (node)". + + 1.4 The function "ospf_mpls_te_ism_change()" could not handle the + case when the ISM changes from Waiting to DR/BDR/Other. + So, there was a case that even if one of an ISM become + operational and MPLS-TE module has started, the corresponding + Opaque-LSA cannot be originated. + + 1.5 The function "ospf_opaque_lsa_reoriginate_schedule()" did not + allow to be called multiple times, simply because handling + module for the given "lsa-type & opaque-type" already exists. + But this assumption seems to be wrong. + Change the policy to allow this function to be called multiple + times and let the caller to decide what should do when the + corresponding callback function "(* functab->lsa_originator)()" + is called. + +2. Feature enhancements + + 2.1 The global bitmap "opaque" has introduced instead of former flag + "OpaqueCapable", to store complex conditions to handle Opaque-LSAs. + + 2.2 The MPLS-TE module now referes to "draft-katz-yeung-ospf-traffic + -06.txt", no significant changes with 05 version, though. + +----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- +Changes 2001.08.03 + +1. Bug fixes + + 1.1 Even if the ospfd started with opaque capability enabled, when + the ospfd receives an unknown opaque-type (unregistered by the + function "ospf_register_opaque_functab()" beforehand), the LSA + was discarded. As the result, only the opaque-LSAs that have + commonly registered by opaque-capable ospf routers can be + flooded in a routing domain. + + This behavior has fixed so that arbitrary opaque-type LSAs can + be flooded among opaque-capable ospf routers. + If the ospfd has opaque-LSA capability but disabled at runtime, + received opaque-LSAs can be accepted and registered to LSDB as + is, but not be flooded to the network; those opaque LSAs will + remain in LSDB until explicitly flushed by incoming LSUpd + messages with MaxAge, or their age naturally reaches to MaxAge. + + 1.2 The function "ospf_register_opaque_functab()" did not check + if the entry corresponding to the given "lsa-type, opaque-type" + combination already exists or not. + This problem has fixed not to allow multiple registration. + + 1.3 Since type-11 (AS external) LSAs will be flooded beyond areas, + there is little relationship between "struct lsa" and "struct + area". More specifically, the pointer address "lsa->area" can + be NULL if the lsa-type is 11, thus an illegal memory access + will happen. This problem has fixed. + + 1.4 When self-originated opaque-LSAs are received via network and + if the corresponding opaque-type functions are not available + (they have already deleted) at that time, those LSAs were + dropped due to "unknown opaque-type" error. + After the problem 1.1 has fixed, those "self-originated" LSAs + were registered to LSDB and then flooded to the network, even + if the processing functions did not exist... + + After all, this problem has fixed so that those LSAs should + explicitly be flushed from the routing domain immediately, if + the processing functions cannot find at that time. + + 1.5 Some typo have fixed. + + --- EXAMPLE --- + static int + opaque_lsa_originate_callback (list funclist, void *lsa_type_dependent) + ^^^^^ + --- EXAMPLE --- + +2. Feature enhancements + + 2.1 According to the description of rfc2328 in section 10.8, any + change in the router's optional capabilities should trigger + the option re-negotiation procedures with neighbors. + + --- EXCERPT --- + If for some reason the router's optional + capabilities change, the Database Exchange procedure should be + restarted by reverting to neighbor state ExStart. + --- EXCERPT --- + + For the opaque-capability changes, this feature has implemented. + More specifically, if "ospf opaque-lsa" or "no ospf opaque-lsa" + VTY command is given at runtime, all self-originated LSAs will + be flushed immediately and then all neighbor status will be + forced to ExStart by generating SeqNumberMismatch events. + + 2.1 When we change opaque-capability dynamically (ON -> OFF -> ON), + there was no trigger at "OFF->ON" timing to reactivate opaque + LSA handling modules (such as MPLS-TE) that have once forcibly + stopped at "ON->OFF" timing. + Now this dynamic reactivation feature has added. + + 2.2 The MPLS-TE module now referes to "draft-katz-yeung-ospf-traffic + -05.txt", no significant changes with 04 version, though. + +----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- +Changes 2001.03.28 + + Initial release of Opaque-LSA/MPLS-TE extensions for the zebra/ospfd. diff --git a/ospfd/Makefile.am b/ospfd/Makefile.am new file mode 100644 index 0000000..f586d73 --- /dev/null +++ b/ospfd/Makefile.am @@ -0,0 +1,40 @@ +## Process this file with automake to produce Makefile.in. + +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-info 0:0:0 +libospf_la_LIBADD = ../lib/libzebra.la + +sbin_PROGRAMS = ospfd + +libospf_la_SOURCES = \ + ospfd.c ospf_zebra.c ospf_interface.c ospf_ism.c ospf_neighbor.c \ + 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_ri.c ospf_vty.c ospf_api.c ospf_apiserver.c + +ospfdheaderdir = $(pkgincludedir)/ospfd + +ospfdheader_HEADERS = \ + ospf_api.h ospf_asbr.h ospf_dump.h ospf_lsa.h ospf_lsdb.h \ + ospf_nsm.h ospf_ism.h ospf_opaque.h ospfd.h + +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_ri.h ospf_vty.h ospf_apiserver.h + +ospfd_SOURCES = ospf_main.c + +ospfd_LDADD = libospf.la ../lib/libzebra.la @LIBCAP@ @LIBM@ + +EXTRA_DIST = OSPF-MIB.txt OSPF-TRAP-MIB.txt ChangeLog.opaque.txt + +examplesdir = $(exampledir) +dist_examples_DATA = ospfd.conf.sample + diff --git a/ospfd/OSPF-ALIGNMENT.txt b/ospfd/OSPF-ALIGNMENT.txt new file mode 100644 index 0000000..dac6182 --- /dev/null +++ b/ospfd/OSPF-ALIGNMENT.txt @@ -0,0 +1,119 @@ +$Id: OSPF-ALIGNMENT.txt,v 1.1 2004/11/17 17:59:52 gdt Exp $ + +Greg Troxel +2004-11-17 + +The OSPF specification (RFC2328) and the OSPF Opaque LSA specification +(RFC2370) are ambiguous about LSAs whose data section is not an +integral multiple of 4 octets. This note examines the issue and +proposes clarifications to ensure interoperability. + +RFC2328 does not specify that LSA lengths be a multiple of 4. +It does not require that LSAs in update packets be aligned. +However, all structures defined by RFC2328 are multiples of 4, and +thus update packets with those structures must be aligned. +LSA length is defined in Appendix A.4 as + + length + The length in bytes of the LSA. This includes the 20 byte LSA + header. + +RFC2370 defines Opaque LSAs, which are intended to contain arbitrary +data: + + This memo defines enhancements to the OSPF protocol to support a new + class of link-state advertisements (LSA) called Opaque LSAs. Opaque + LSAs provide a generalized mechanism to allow for the future + extensibility of OSPF. Opaque LSAs consist of a standard LSA header + followed by application-specific information. The information field + may be used directly by OSPF or by other applications. Standard OSPF + link-state database flooding mechanisms are used to distribute Opaque + LSAs to all or some limited portion of the OSPF topology. + + +Later, 2370 says: + + Opaque LSAs contain some number of octets (of application-specific + data) padded to 32-bit alignment. + +This can be interpreted in several ways: + +A) The payload may be any number of octets, and the length field +reflects the payload length (e.g. length 23 for 3 octets of payload), +but there are padding octets following the LSA in packets, so that the +next LSA starts on a 4-octet boundary. (This approach is common in +the BSD user/kernel interface.) + +B) The payload must be a multiple of 4 octets, so that the length is a +multiple of 4 octets. This corresponds to an implementation that +treats an Opaque LSA publish request that is not a multiple of 4 +octets as an error. + +C) The payload can be any number of octets, but padding is added and +included in the length field. This interpretation corresponds to an +OSPF implementation that accepts a publish request for an Opaque LSA +that is not a multiple of 4 octets. This interpretation is +nonsensical, because it claims to represent arbitrary lengths, but +does not actually do so --- the receiver cannot distinguish padding +from supplied data. + +D) Accept according to A, and transmit according to B. + +Option A arguably violates RFC 2328, which doesn't say anything about +adding padding (A.3.5 shows a diagram of adjacent LSAs which are shown +as all multiples of 4). This option is thus likely to lead to a lack +of interoperability. + +Option B restricts what data can be represented as an Opaque LSA, but +probably not in a serious way. It is likely to lead to +interoperability in that the complex case of non-multiple-of-4 lengths +will not arise. + +However, an implementation that follows A and emits an LSA with +payload length not a multiple of 4 will not interoperate with an +Option B implementation. + +Given that all known and documented uses of Opaque LSAs seem to be +multiples of 4 octets, we choose Option B as the clarification. + +CLARIFYING TEXT + +In RFC2328: + +In section A.4, add a second sentence about length: + + length + The length in bytes of the LSA. This includes the 20 byte LSA + header. The length must be an integral multiple of 4 bytes. + +Add to the list in Section 13: + + Verify that the length of the LSA is a multiple of 4 bytes. If + not, discard the entire Link State Update Packet. + +In RFC2380: + +Change text: + + Opaque LSAs contain some number of octets (of application-specific + data) padded to 32-bit alignment. + +to: + + Opaque LSAs contain some a number of octets (of + application-specific data). The number of octets must be a + multiple of four. + + +HOW THIS ISSUE AROSE + +At BBN, we use Opaque LSAs to exchange data among routers; the format +of the data is naturally aligned to 4 bytes, and thus does not raise +this issue. We created a test program to publish Opaque data via IPC +to the OSPF daemon (quagga), and this program accepts strings on the +command line to publish. We then used this test program to publish +software version strings. Quagga's ospfd then crashed on a +NetBSD/sparc64 machine with an alignment fault, because the odd-length +LSAs were marshalled into a link-state update packet with no padding. +While this behavior was a clear violation of RFC2380, it was not clear +how to remedy the problem. diff --git a/ospfd/OSPF-MIB.txt b/ospfd/OSPF-MIB.txt new file mode 100644 index 0000000..217c1e5 --- /dev/null +++ b/ospfd/OSPF-MIB.txt @@ -0,0 +1,2723 @@ +OSPF-MIB DEFINITIONS ::= BEGIN + + IMPORTS + MODULE-IDENTITY, OBJECT-TYPE, Counter32, Gauge32, + Integer32, IpAddress + FROM SNMPv2-SMI + TEXTUAL-CONVENTION, TruthValue, RowStatus + FROM SNMPv2-TC + MODULE-COMPLIANCE, OBJECT-GROUP FROM SNMPv2-CONF + mib-2 FROM RFC1213-MIB; + +-- This MIB module uses the extended OBJECT-TYPE macro as +-- defined in [9]. + +ospf MODULE-IDENTITY + LAST-UPDATED "9501201225Z" -- Fri Jan 20 12:25:50 PST 1995 + ORGANIZATION "IETF OSPF Working Group" + CONTACT-INFO + " Fred Baker + Postal: Cisco Systems + 519 Lado Drive + Santa Barbara, California 93111 + Tel: +1 805 681 0115 + E-Mail: fred@cisco.com + + Rob Coltun + Postal: RainbowBridge Communications + Tel: (301) 340-9416 + E-Mail: rcoltun@rainbow-bridge.com" + DESCRIPTION + "The MIB module to describe the OSPF Version 2 + Protocol" + ::= { mib-2 14 } + +-- The Area ID, in OSPF, has the same format as an IP Address, +-- but has the function of defining a summarization point for +-- Link State Advertisements + +AreaID ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "An OSPF Area Identifier." + SYNTAX IpAddress + + +-- The Router ID, in OSPF, has the same format as an IP Address, +-- but identifies the router independent of its IP Address. + +RouterID ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "A OSPF Router Identifier." + SYNTAX IpAddress + + +-- The OSPF Metric is defined as an unsigned value in the range + +Metric ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "The OSPF Internal Metric." + SYNTAX Integer32 (0..'FFFF'h) + +BigMetric ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "The OSPF External Metric." + SYNTAX Integer32 (0..'FFFFFF'h) + +-- Status Values + +Status ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "The status of an interface: 'enabled' indicates that + it is willing to communicate with other OSPF Routers, + while 'disabled' indicates that it is not." + SYNTAX INTEGER { enabled (1), disabled (2) } + +-- Time Durations measured in seconds + +PositiveInteger ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "A positive integer. Values in excess are precluded as + unnecessary and prone to interoperability issues." + SYNTAX Integer32 (0..'7FFFFFFF'h) + +HelloRange ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "The range of intervals on which hello messages are + exchanged." + SYNTAX Integer32 (1..'FFFF'h) + +UpToMaxAge ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "The values that one might find or configure for + variables bounded by the maximum age of an LSA." + SYNTAX Integer32 (0..3600) + + +-- The range of ifIndex + +InterfaceIndex ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "The range of ifIndex." + SYNTAX Integer32 + + +-- Potential Priorities for the Designated Router Election + +DesignatedRouterPriority ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "The values defined for the priority of a system for + becoming the designated router." + SYNTAX Integer32 (0..'FF'h) + +TOSType ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "Type of Service is defined as a mapping to the IP Type of + Service Flags as defined in the IP Forwarding Table MIB + + +-----+-----+-----+-----+-----+-----+-----+-----+ + | | | | + | PRECEDENCE | TYPE OF SERVICE | 0 | + | | | | + +-----+-----+-----+-----+-----+-----+-----+-----+ + + IP TOS IP TOS + Field Policy Field Policy + + Contents Code Contents Code + 0 0 0 0 ==> 0 0 0 0 1 ==> 2 + 0 0 1 0 ==> 4 0 0 1 1 ==> 6 + 0 1 0 0 ==> 8 0 1 0 1 ==> 10 + 0 1 1 0 ==> 12 0 1 1 1 ==> 14 + 1 0 0 0 ==> 16 1 0 0 1 ==> 18 + 1 0 1 0 ==> 20 1 0 1 1 ==> 22 + 1 1 0 0 ==> 24 1 1 0 1 ==> 26 + 1 1 1 0 ==> 28 1 1 1 1 ==> 30 + + The remaining values are left for future definition." + SYNTAX Integer32 (0..30) + + +-- OSPF General Variables + +-- These parameters apply globally to the Router's +-- OSPF Process. + +ospfGeneralGroup OBJECT IDENTIFIER ::= { ospf 1 } + + + ospfRouterId OBJECT-TYPE + SYNTAX RouterID + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "A 32-bit integer uniquely identifying the + router in the Autonomous System. + + By convention, to ensure uniqueness, this + should default to the value of one of the + router's IP interface addresses." + REFERENCE + "OSPF Version 2, C.1 Global parameters" + ::= { ospfGeneralGroup 1 } + + + ospfAdminStat OBJECT-TYPE + SYNTAX Status + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The administrative status of OSPF in the + router. The value 'enabled' denotes that the + OSPF Process is active on at least one inter- + face; 'disabled' disables it on all inter- + faces." + ::= { ospfGeneralGroup 2 } + + ospfVersionNumber OBJECT-TYPE + SYNTAX INTEGER { version2 (2) } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The current version number of the OSPF proto- + col is 2." + REFERENCE + "OSPF Version 2, Title" + ::= { ospfGeneralGroup 3 } + + + ospfAreaBdrRtrStatus 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" + ::= { ospfGeneralGroup 4 } + + + ospfASBdrRtrStatus 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" + ::= { ospfGeneralGroup 5 } + + ospfExternLsaCount OBJECT-TYPE + SYNTAX Gauge32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of external (LS type 5) link-state + advertisements in the link-state database." + REFERENCE + "OSPF Version 2, Appendix A.4.5 AS external link + advertisements" + ::= { ospfGeneralGroup 6 } + + + ospfExternLsaCksumSum OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The 32-bit unsigned sum of the LS checksums of + the external 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." + ::= { ospfGeneralGroup 7 } + + + ospfTOSSupport OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The router's support for type-of-service rout- + ing." + REFERENCE + "OSPF Version 2, Appendix F.1.2 Optional TOS + support" + ::= { ospfGeneralGroup 8 } + + ospfOriginateNewLsas 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." + ::= { ospfGeneralGroup 9 } + + + ospfRxNewLsas 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." + ::= { ospfGeneralGroup 10 } + + ospfExtLsdbLimit 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 + ospfExtLsdbLimit, the router enters Overflow- + State. The router never holds more than + ospfExtLsdbLimit non-default AS-external-LSAs + in its database. OspfExtLsdbLimit MUST be set + identically in all routers attached to the OSPF + backbone and/or any regular OSPF area. (i.e., + OSPF stub areas and NSSAs are excluded)." + DEFVAL { -1 } + ::= { ospfGeneralGroup 11 } + + ospfMulticastExtensions OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "A Bit Mask indicating whether the router is + forwarding IP multicast (Class D) datagrams + based on the algorithms defined in the Multi- + cast Extensions to OSPF. + + Bit 0, if set, indicates that the router can + forward IP multicast datagrams in the router's + directly attached areas (called intra-area mul- + ticast routing). + + Bit 1, if set, indicates that the router can + forward IP multicast datagrams between OSPF + areas (called inter-area multicast routing). + + Bit 2, if set, indicates that the router can + forward IP multicast datagrams between Auto- + nomous Systems (called inter-AS multicast rout- + ing). + + Only certain combinations of bit settings are + allowed, namely: 0 (no multicast forwarding is + enabled), 1 (intra-area multicasting only), 3 + (intra-area and inter-area multicasting), 5 + (intra-area and inter-AS multicasting) and 7 + (multicasting everywhere). By default, no mul- + ticast forwarding is enabled." + DEFVAL { 0 } + ::= { ospfGeneralGroup 12 } + + ospfExitOverflowInterval OBJECT-TYPE + SYNTAX PositiveInteger + 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." + DEFVAL { 0 } + ::= { ospfGeneralGroup 13 } + + + ospfDemandExtensions 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" + ::= { ospfGeneralGroup 14 } + + +-- The OSPF 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 + + + ospfAreaTable OBJECT-TYPE + SYNTAX SEQUENCE OF OspfAreaEntry + 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" + ::= { ospf 2 } + + + ospfAreaEntry OBJECT-TYPE + SYNTAX OspfAreaEntry + 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 { ospfAreaId } + ::= { ospfAreaTable 1 } + +OspfAreaEntry ::= + SEQUENCE { + ospfAreaId + AreaID, + ospfAuthType + Integer32, + ospfImportAsExtern + INTEGER, + ospfSpfRuns + Counter32, + ospfAreaBdrRtrCount + Gauge32, + ospfAsBdrRtrCount + Gauge32, + ospfAreaLsaCount + Gauge32, + ospfAreaLsaCksumSum + Integer32, + ospfAreaSummary + INTEGER, + ospfAreaStatus + RowStatus + } + + ospfAreaId 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 OSPF backbone." + REFERENCE + "OSPF Version 2, Appendix C.2 Area parameters" + ::= { ospfAreaEntry 1 } + + + ospfAuthType OBJECT-TYPE + SYNTAX Integer32 + -- none (0), + -- simplePassword (1) + -- md5 (2) + -- reserved for specification by IANA (> 2) + MAX-ACCESS read-create + STATUS obsolete + DESCRIPTION + "The authentication type specified for an area. + Additional authentication types may be assigned + locally on a per Area basis." + REFERENCE + "OSPF Version 2, Appendix E Authentication" + DEFVAL { 0 } -- no authentication, by default + ::= { ospfAreaEntry 2 } + + ospfImportAsExtern OBJECT-TYPE + SYNTAX INTEGER { + importExternal (1), + importNoExternal (2), + importNssa (3) + } + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The area's support for importing AS external + link- state advertisements." + REFERENCE + "OSPF Version 2, Appendix C.2 Area parameters" + DEFVAL { importExternal } + ::= { ospfAreaEntry 3 } + + + ospfSpfRuns 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." + ::= { ospfAreaEntry 4 } + + + ospfAreaBdrRtrCount 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." + ::= { ospfAreaEntry 5 } + + ospfAsBdrRtrCount 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." + ::= { ospfAreaEntry 6 } + + + ospfAreaLsaCount OBJECT-TYPE + SYNTAX Gauge32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of link-state advertisements + in this area's link-state database, excluding + AS External LSA's." + ::= { ospfAreaEntry 7 } + + + ospfAreaLsaCksumSum OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The 32-bit unsigned sum of the link-state ad- + vertisements' LS checksums contained in this + area's link-state database. This sum excludes + external (LS type 5) link-state advertisements. + The sum can be used to determine if there has + been a change in a router's link state data- + base, and to compare the link-state database of + two routers." + DEFVAL { 0 } + ::= { ospfAreaEntry 8 } + + ospfAreaSummary OBJECT-TYPE + SYNTAX INTEGER { + noAreaSummary (1), + sendAreaSummary (2) + } + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The variable ospfAreaSummary controls the im- + port of summary LSAs into stub areas. It has + no effect on other areas. + + If it is noAreaSummary, the router will neither + originate nor propagate summary 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 summary LSAs." + DEFVAL { noAreaSummary } + ::= { ospfAreaEntry 9 } + + + ospfAreaStatus OBJECT-TYPE + SYNTAX RowStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "This variable displays the status of the en- + try. Setting it to 'invalid' has the effect of + rendering it inoperative. The internal effect + (row removal) is implementation dependent." + ::= { ospfAreaEntry 10 } + + +-- OSPF Area Default Metric Table + +-- The OSPF Area Default Metric Table describes the metrics +-- that a default Area Border Router will advertise into a +-- Stub area. + + + ospfStubAreaTable OBJECT-TYPE + SYNTAX SEQUENCE OF OspfStubAreaEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The set of metrics that will be advertised by + a default Area Border Router into a stub area." + REFERENCE + "OSPF Version 2, Appendix C.2, Area Parameters" + ::= { ospf 3 } + + + ospfStubAreaEntry OBJECT-TYPE + SYNTAX OspfStubAreaEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The metric for a given Type of Service that + will be advertised by a default Area Border + Router into a stub area." + REFERENCE + "OSPF Version 2, Appendix C.2, Area Parameters" + INDEX { ospfStubAreaId, ospfStubTOS } + ::= { ospfStubAreaTable 1 } + +OspfStubAreaEntry ::= + SEQUENCE { + ospfStubAreaId + AreaID, + ospfStubTOS + TOSType, + ospfStubMetric + BigMetric, + ospfStubStatus + RowStatus, + ospfStubMetricType + INTEGER + } + + ospfStubAreaId OBJECT-TYPE + SYNTAX AreaID + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The 32 bit identifier for the Stub Area. On + creation, this can be derived from the in- + stance." + ::= { ospfStubAreaEntry 1 } + + + ospfStubTOS OBJECT-TYPE + SYNTAX TOSType + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The Type of Service associated with the + metric. On creation, this can be derived from + the instance." + ::= { ospfStubAreaEntry 2 } + + + ospfStubMetric OBJECT-TYPE + SYNTAX BigMetric + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The metric value applied at the indicated type + of service. By default, this equals the least + metric at the type of service among the inter- + faces to other areas." + ::= { ospfStubAreaEntry 3 } + + + ospfStubStatus OBJECT-TYPE + SYNTAX RowStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "This variable displays the status of the en- + try. Setting it to 'invalid' has the effect of + rendering it inoperative. The internal effect + (row removal) is implementation dependent." + ::= { ospfStubAreaEntry 4 } + + ospfStubMetricType OBJECT-TYPE + SYNTAX INTEGER { + ospfMetric (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 { ospfMetric } + ::= { ospfStubAreaEntry 5 } + +-- OSPF Link State Database + +-- The Link State Database contains the Link State +-- Advertisements from throughout the areas that the +-- device is attached to. + + + ospfLsdbTable OBJECT-TYPE + SYNTAX SEQUENCE OF OspfLsdbEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The OSPF Process's Link State Database." + REFERENCE + "OSPF Version 2, Section 12 Link State Adver- + tisements" + ::= { ospf 4 } + + + ospfLsdbEntry OBJECT-TYPE + SYNTAX OspfLsdbEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A single Link State Advertisement." + INDEX { ospfLsdbAreaId, ospfLsdbType, + ospfLsdbLsid, ospfLsdbRouterId } + ::= { ospfLsdbTable 1 } + +OspfLsdbEntry ::= + SEQUENCE { + ospfLsdbAreaId + AreaID, + ospfLsdbType + INTEGER, + ospfLsdbLsid + IpAddress, + ospfLsdbRouterId + RouterID, + ospfLsdbSequence + Integer32, + ospfLsdbAge + Integer32, + ospfLsdbChecksum + Integer32, + ospfLsdbAdvertisement + OCTET STRING + } + ospfLsdbAreaId 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" + ::= { ospfLsdbEntry 1 } + +-- External Link State Advertisements are permitted +-- for backward compatibility, but should be displayed in +-- the ospfExtLsdbTable rather than here. + + ospfLsdbType OBJECT-TYPE + SYNTAX INTEGER { + routerLink (1), + networkLink (2), + summaryLink (3), + asSummaryLink (4), + asExternalLink (5), -- but see ospfExtLsdbTable + multicastLink (6), + nssaExternalLink (7) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The type of the link state advertisement. + Each link state type has a separate advertise- + ment format." + REFERENCE + "OSPF Version 2, Appendix A.4.1 The Link State + Advertisement header" + ::= { ospfLsdbEntry 2 } + + ospfLsdbLsid OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The Link State ID is an LS Type Specific field + containing either a Router ID or an IP Address; + it identifies the piece of the routing domain + that is being described by the advertisement." + REFERENCE + "OSPF Version 2, Section 12.1.4 Link State ID" + ::= { ospfLsdbEntry 3 } + ospfLsdbRouterId 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" + ::= { ospfLsdbEntry 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. + + ospfLsdbSequence 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" + ::= { ospfLsdbEntry 5 } + + + ospfLsdbAge OBJECT-TYPE + SYNTAX Integer32 -- Should be 0..MaxAge + 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" + ::= { ospfLsdbEntry 6 } + + ospfLsdbChecksum 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" + ::= { ospfLsdbEntry 7 } + + + ospfLsdbAdvertisement OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (1..65535)) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The entire Link State Advertisement, including + its header." + REFERENCE + "OSPF Version 2, Section 12 Link State Adver- + tisements" + ::= { ospfLsdbEntry 8 } + + +-- Address Range Table + +-- The Address Range Table acts as an adjunct to the Area +-- Table; It describes those Address Range Summaries that +-- are configured to be propagated from an Area to reduce +-- the amount of information about it which is known beyond +-- its borders. + + ospfAreaRangeTable OBJECT-TYPE + SYNTAX SEQUENCE OF OspfAreaRangeEntry + MAX-ACCESS not-accessible + STATUS obsolete + DESCRIPTION + "A range if IP addresses specified by an IP + address/IP network mask pair. For example, + class B address range of X.X.X.X with a network + mask of 255.255.0.0 includes all IP addresses + from X.X.0.0 to X.X.255.255" + REFERENCE + "OSPF Version 2, Appendix C.2 Area parameters" + ::= { ospf 5 } + ospfAreaRangeEntry OBJECT-TYPE + SYNTAX OspfAreaRangeEntry + MAX-ACCESS not-accessible + STATUS obsolete + DESCRIPTION + "A range if IP addresses specified by an IP + address/IP network mask pair. For example, + class B address range of X.X.X.X with a network + mask of 255.255.0.0 includes all IP addresses + from X.X.0.0 to X.X.255.255" + REFERENCE + "OSPF Version 2, Appendix C.2 Area parameters" + INDEX { ospfAreaRangeAreaId, ospfAreaRangeNet } + ::= { ospfAreaRangeTable 1 } + +OspfAreaRangeEntry ::= + SEQUENCE { + ospfAreaRangeAreaId + AreaID, + ospfAreaRangeNet + IpAddress, + ospfAreaRangeMask + IpAddress, + ospfAreaRangeStatus + RowStatus, + ospfAreaRangeEffect + INTEGER + } + + ospfAreaRangeAreaId OBJECT-TYPE + SYNTAX AreaID + MAX-ACCESS read-only + STATUS obsolete + DESCRIPTION + "The Area the Address Range is to be found + within." + REFERENCE + "OSPF Version 2, Appendix C.2 Area parameters" + ::= { ospfAreaRangeEntry 1 } + + + ospfAreaRangeNet OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS obsolete + DESCRIPTION + "The IP Address of the Net or Subnet indicated + by the range." + REFERENCE + "OSPF Version 2, Appendix C.2 Area parameters" + ::= { ospfAreaRangeEntry 2 } + + + ospfAreaRangeMask OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-create + STATUS obsolete + DESCRIPTION + "The Subnet Mask that pertains to the Net or + Subnet." + REFERENCE + "OSPF Version 2, Appendix C.2 Area parameters" + ::= { ospfAreaRangeEntry 3 } + + ospfAreaRangeStatus OBJECT-TYPE + SYNTAX RowStatus + MAX-ACCESS read-create + STATUS obsolete + DESCRIPTION + "This variable displays the status of the en- + try. Setting it to 'invalid' has the effect of + rendering it inoperative. The internal effect + (row removal) is implementation dependent." + ::= { ospfAreaRangeEntry 4 } + + + ospfAreaRangeEffect OBJECT-TYPE + SYNTAX INTEGER { + advertiseMatching (1), + doNotAdvertiseMatching (2) + } + MAX-ACCESS read-create + STATUS obsolete + DESCRIPTION + "Subnets subsumed by ranges either trigger the + advertisement of the indicated summary (adver- + tiseMatching), or result in the subnet's not + being advertised at all outside the area." + DEFVAL { advertiseMatching } + ::= { ospfAreaRangeEntry 5 } + + + +-- 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. + + ospfHostTable OBJECT-TYPE + SYNTAX SEQUENCE OF OspfHostEntry + 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" + ::= { ospf 6 } + + + ospfHostEntry OBJECT-TYPE + SYNTAX OspfHostEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A metric to be advertised, for a given type of + service, when a given host is reachable." + INDEX { ospfHostIpAddress, ospfHostTOS } + ::= { ospfHostTable 1 } + +OspfHostEntry ::= + SEQUENCE { + ospfHostIpAddress + IpAddress, + ospfHostTOS + TOSType, + ospfHostMetric + Metric, + ospfHostStatus + RowStatus, + ospfHostAreaID + AreaID + } + + ospfHostIpAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The IP Address of the Host." + REFERENCE + "OSPF Version 2, Appendix C.6 Host route parame- + ters" + ::= { ospfHostEntry 1 } + + + ospfHostTOS OBJECT-TYPE + SYNTAX TOSType + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The Type of Service of the route being config- + ured." + REFERENCE + "OSPF Version 2, Appendix C.6 Host route parame- + ters" + ::= { ospfHostEntry 2 } + + + ospfHostMetric 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" + ::= { ospfHostEntry 3 } + + ospfHostStatus OBJECT-TYPE + SYNTAX RowStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "This variable displays the status of the en- + try. Setting it to 'invalid' has the effect of + rendering it inoperative. The internal effect + (row removal) is implementation dependent." + ::= { ospfHostEntry 4 } + + + ospfHostAreaID OBJECT-TYPE + SYNTAX AreaID + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The Area the Host Entry is to be found within. + By default, the area that a subsuming OSPF in- + terface is in, or 0.0.0.0" + REFERENCE + "OSPF Version 2, Appendix C.2 Area parameters" + ::= { ospfHostEntry 5 } + + +-- OSPF Interface Table + +-- The OSPF Interface Table augments the ipAddrTable +-- with OSPF specific information. + + ospfIfTable OBJECT-TYPE + SYNTAX SEQUENCE OF OspfIfEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The OSPF Interface Table describes the inter- + faces from the viewpoint of OSPF." + REFERENCE + "OSPF Version 2, Appendix C.3 Router interface + parameters" + ::= { ospf 7 } + + + ospfIfEntry OBJECT-TYPE + SYNTAX OspfIfEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The OSPF Interface Entry describes one inter- + face from the viewpoint of OSPF." + INDEX { ospfIfIpAddress, ospfAddressLessIf } + ::= { ospfIfTable 1 } + +OspfIfEntry ::= + SEQUENCE { + ospfIfIpAddress + IpAddress, + ospfAddressLessIf + Integer32, + ospfIfAreaId + AreaID, + ospfIfType + INTEGER, + ospfIfAdminStat + Status, + ospfIfRtrPriority + DesignatedRouterPriority, + ospfIfTransitDelay + UpToMaxAge, + ospfIfRetransInterval + UpToMaxAge, + ospfIfHelloInterval + HelloRange, + ospfIfRtrDeadInterval + PositiveInteger, + ospfIfPollInterval + PositiveInteger, + ospfIfState + INTEGER, + ospfIfDesignatedRouter + IpAddress, + ospfIfBackupDesignatedRouter + IpAddress, + ospfIfEvents + Counter32, + ospfIfAuthType + INTEGER, + ospfIfAuthKey + OCTET STRING, + ospfIfStatus + RowStatus, + ospfIfMulticastForwarding + INTEGER, + ospfIfDemand + TruthValue + } + + ospfIfIpAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The IP address of this OSPF interface." + ::= { ospfIfEntry 1 } + + ospfAddressLessIf OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "For the purpose of easing the instancing of + addressed and addressless interfaces; This + variable takes the value 0 on interfaces with + IP Addresses, and the corresponding value of + ifIndex for interfaces having no IP Address." + ::= { ospfIfEntry 2 } + ospfIfAreaId 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 OSPF backbone." + DEFVAL { '00000000'H } -- 0.0.0.0 + ::= { ospfIfEntry 3 } + + ospfIfType OBJECT-TYPE + SYNTAX INTEGER { + broadcast (1), + nbma (2), + pointToPoint (3), + pointToMultipoint (5) + } + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The OSPF interface type. + + By way of a default, this field may be intuited + from the corresponding value of ifType. Broad- + cast LANs, such as Ethernet and IEEE 802.5, + take the value 'broadcast', X.25 and similar + technologies take the value 'nbma', and links + that are definitively point to point take the + value 'pointToPoint'." + ::= { ospfIfEntry 4 } + + + ospfIfAdminStat OBJECT-TYPE + SYNTAX Status + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The OSPF 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 OSPF." + DEFVAL { enabled } + ::= { ospfIfEntry 5 } + + ospfIfRtrPriority 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 } + ::= { ospfIfEntry 6 } + + + ospfIfTransitDelay OBJECT-TYPE + SYNTAX UpToMaxAge + 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 } + ::= { ospfIfEntry 7 } + + + ospfIfRetransInterval OBJECT-TYPE + SYNTAX UpToMaxAge + 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 } + ::= { ospfIfEntry 8 } + + + ospfIfHelloInterval OBJECT-TYPE + SYNTAX HelloRange + 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 } + ::= { ospfIfEntry 9 } + + + ospfIfRtrDeadInterval OBJECT-TYPE + SYNTAX PositiveInteger + 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 } + ::= { ospfIfEntry 10 } + + + ospfIfPollInterval OBJECT-TYPE + SYNTAX PositiveInteger + 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 } + ::= { ospfIfEntry 11 } + + + ospfIfState 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 OSPF Interface State." + DEFVAL { down } + ::= { ospfIfEntry 12 } + + + ospfIfDesignatedRouter OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The IP Address of the Designated Router." + DEFVAL { '00000000'H } -- 0.0.0.0 + ::= { ospfIfEntry 13 } + + + ospfIfBackupDesignatedRouter OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The IP Address of the Backup Designated + Router." + DEFVAL { '00000000'H } -- 0.0.0.0 + ::= { ospfIfEntry 14 } + + ospfIfEvents 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." + ::= { ospfIfEntry 15 } + + + ospfIfAuthKey OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (0..256)) + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The Authentication Key. If the Area's Author- + ization Type is simplePassword, and the key + length is shorter than 8 octets, the agent will + left adjust and zero fill to 8 octets. + + Note that unauthenticated interfaces need no + authentication key, and simple password authen- + tication cannot use a key of more than 8 oc- + tets. Larger keys are useful only with authen- + tication mechanisms not specified in this docu- + ment. + + When read, ospfIfAuthKey always returns an Oc- + tet String of length zero." + REFERENCE + "OSPF Version 2, Section 9 The Interface Data + Structure" + DEFVAL { '0000000000000000'H } -- 0.0.0.0.0.0.0.0 + ::= { ospfIfEntry 16 } + + ospfIfStatus OBJECT-TYPE + SYNTAX RowStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "This variable displays the status of the en- + try. Setting it to 'invalid' has the effect of + rendering it inoperative. The internal effect + (row removal) is implementation dependent." + ::= { ospfIfEntry 17 } + + + ospfIfMulticastForwarding OBJECT-TYPE + SYNTAX INTEGER { + blocked (1), -- no multicast forwarding + multicast (2), -- using multicast address + unicast (3) -- to each OSPF 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 ospfMulticastForwarding to 0 effective- + ly disables all multicast forwarding." + DEFVAL { blocked } + ::= { ospfIfEntry 18 } + + + ospfIfDemand OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "Indicates whether Demand OSPF procedures (hel- + lo suppression to FULL neighbors and setting the + DoNotAge flag on proogated LSAs) should be per- + formed on this interface." + DEFVAL { false } + ::= { ospfIfEntry 19 } + + + ospfIfAuthType OBJECT-TYPE + SYNTAX INTEGER (0..255) + -- none (0), + -- simplePassword (1) + -- md5 (2) + -- reserved for specification by IANA (> 2) + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The authentication type specified for an in- + terface. Additional authentication types may + be assigned locally." + REFERENCE + "OSPF Version 2, Appendix E Authentication" + DEFVAL { 0 } -- no authentication, by default + ::= { ospfIfEntry 20 } + + +-- OSPF Interface Metric Table + +-- The Metric Table describes the metrics to be advertised +-- for a specified interface at the various types of service. +-- As such, this table is an adjunct of the OSPF Interface +-- Table. + +-- Types of service, as defined by RFC 791, have the ability +-- to request low delay, high bandwidth, or reliable linkage. + +-- For the purposes of this specification, the measure of +-- bandwidth + +-- Metric = 10^8 / ifSpeed + +-- is the default value. For multiple link interfaces, note +-- that ifSpeed is the sum of the individual link speeds. +-- This yields a number having the following typical values: + +-- Network Type/bit rate Metric + +-- >= 100 MBPS 1 +-- Ethernet/802.3 10 +-- E1 48 +-- T1 (ESF) 65 +-- 64 KBPS 1562 +-- 56 KBPS 1785 +-- 19.2 KBPS 5208 +-- 9.6 KBPS 10416 + +-- Routes that are not specified use the default (TOS 0) metric + + ospfIfMetricTable OBJECT-TYPE + SYNTAX SEQUENCE OF OspfIfMetricEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The TOS metrics for a non-virtual interface + identified by the interface index." + REFERENCE + "OSPF Version 2, Appendix C.3 Router interface + parameters" + ::= { ospf 8 } + + ospfIfMetricEntry OBJECT-TYPE + SYNTAX OspfIfMetricEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A particular TOS metric for a non-virtual in- + terface identified by the interface index." + REFERENCE + "OSPF Version 2, Appendix C.3 Router interface + parameters" + INDEX { ospfIfMetricIpAddress, + ospfIfMetricAddressLessIf, + ospfIfMetricTOS } + ::= { ospfIfMetricTable 1 } + +OspfIfMetricEntry ::= + SEQUENCE { + ospfIfMetricIpAddress + IpAddress, + ospfIfMetricAddressLessIf + Integer32, + ospfIfMetricTOS + TOSType, + ospfIfMetricValue + Metric, + ospfIfMetricStatus + RowStatus + } + + ospfIfMetricIpAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The IP address of this OSPF interface. On row + creation, this can be derived from the in- + stance." + ::= { ospfIfMetricEntry 1 } + + ospfIfMetricAddressLessIf OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "For the purpose of easing the instancing of + addressed and addressless interfaces; This + variable takes the value 0 on interfaces with + IP Addresses, and the value of ifIndex for in- + terfaces having no IP Address. On row crea- + tion, this can be derived from the instance." + ::= { ospfIfMetricEntry 2 } + + + ospfIfMetricTOS OBJECT-TYPE + SYNTAX TOSType + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The type of service metric being referenced. + On row creation, this can be derived from the + instance." + ::= { ospfIfMetricEntry 3 } + + + ospfIfMetricValue OBJECT-TYPE + SYNTAX Metric + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The metric of using this type of service on + this interface. The default value of the TOS 0 + Metric is 10^8 / ifSpeed." + ::= { ospfIfMetricEntry 4 } + + ospfIfMetricStatus OBJECT-TYPE + SYNTAX RowStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "This variable displays the status of the en- + try. Setting it to 'invalid' has the effect of + rendering it inoperative. The internal effect + (row removal) is implementation dependent." + ::= { ospfIfMetricEntry 5 } + + +-- OSPF Virtual Interface Table + +-- The Virtual Interface Table describes the virtual +-- links that the OSPF Process is configured to +-- carry on. + + ospfVirtIfTable OBJECT-TYPE + SYNTAX SEQUENCE OF OspfVirtIfEntry + 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" + ::= { ospf 9 } + + + ospfVirtIfEntry OBJECT-TYPE + SYNTAX OspfVirtIfEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Information about a single Virtual Interface." + INDEX { ospfVirtIfAreaId, ospfVirtIfNeighbor } + ::= { ospfVirtIfTable 1 } + +OspfVirtIfEntry ::= + SEQUENCE { + ospfVirtIfAreaId + AreaID, + ospfVirtIfNeighbor + RouterID, + ospfVirtIfTransitDelay + UpToMaxAge, + ospfVirtIfRetransInterval + UpToMaxAge, + ospfVirtIfHelloInterval + HelloRange, + ospfVirtIfRtrDeadInterval + PositiveInteger, + ospfVirtIfState + INTEGER, + ospfVirtIfEvents + Counter32, + ospfVirtIfAuthType + INTEGER, + ospfVirtIfAuthKey + OCTET STRING, + ospfVirtIfStatus + RowStatus + } + + ospfVirtIfAreaId 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" + ::= { ospfVirtIfEntry 1 } + + + ospfVirtIfNeighbor OBJECT-TYPE + SYNTAX RouterID + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The Router ID of the Virtual Neighbor." + ::= { ospfVirtIfEntry 2 } + + + ospfVirtIfTransitDelay OBJECT-TYPE + SYNTAX UpToMaxAge + 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 } + ::= { ospfVirtIfEntry 3 } + + + ospfVirtIfRetransInterval OBJECT-TYPE + SYNTAX UpToMaxAge + 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 } + ::= { ospfVirtIfEntry 4 } + + + ospfVirtIfHelloInterval OBJECT-TYPE + SYNTAX HelloRange + 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 } + ::= { ospfVirtIfEntry 5 } + + + ospfVirtIfRtrDeadInterval OBJECT-TYPE + SYNTAX PositiveInteger + 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 } + ::= { ospfVirtIfEntry 6 } + + + ospfVirtIfState OBJECT-TYPE + SYNTAX INTEGER { + down (1), -- these use the same encoding + pointToPoint (4) -- as the ospfIfTable + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "OSPF virtual interface states." + DEFVAL { down } + ::= { ospfVirtIfEntry 7 } + + + ospfVirtIfEvents OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of state changes or error events on + this Virtual Link" + ::= { ospfVirtIfEntry 8 } + + + ospfVirtIfAuthKey OBJECT-TYPE + SYNTAX OCTET STRING (SIZE(0..256)) + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "If Authentication Type is simplePassword, the + device will left adjust and zero fill to 8 oc- + tets. + + Note that unauthenticated interfaces need no + authentication key, and simple password authen- + tication cannot use a key of more than 8 oc- + tets. Larger keys are useful only with authen- + tication mechanisms not specified in this docu- + ment. + + When read, ospfVifAuthKey always returns a + string of length zero." + REFERENCE + "OSPF Version 2, Section 9 The Interface Data + Structure" + DEFVAL { '0000000000000000'H } -- 0.0.0.0.0.0.0.0 + ::= { ospfVirtIfEntry 9 } + + + ospfVirtIfStatus OBJECT-TYPE + SYNTAX RowStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "This variable displays the status of the en- + try. Setting it to 'invalid' has the effect of + rendering it inoperative. The internal effect + (row removal) is implementation dependent." + ::= { ospfVirtIfEntry 10 } + + + ospfVirtIfAuthType OBJECT-TYPE + SYNTAX INTEGER (0..255) + -- none (0), + -- simplePassword (1) + -- md5 (2) + -- reserved for specification by IANA (> 2) + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The authentication type specified for a virtu- + al interface. Additional authentication types + may be assigned locally." + REFERENCE + "OSPF Version 2, Appendix E Authentication" + DEFVAL { 0 } -- no authentication, by default + ::= { ospfVirtIfEntry 11 } + + +-- OSPF Neighbor Table + +-- The OSPF Neighbor Table describes all neighbors in +-- the locality of the subject router. + + ospfNbrTable OBJECT-TYPE + SYNTAX SEQUENCE OF OspfNbrEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table of non-virtual neighbor information." + REFERENCE + "OSPF Version 2, Section 10 The Neighbor Data + Structure" + ::= { ospf 10 } + + + ospfNbrEntry OBJECT-TYPE + SYNTAX OspfNbrEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The information regarding a single neighbor." + REFERENCE + "OSPF Version 2, Section 10 The Neighbor Data + Structure" + INDEX { ospfNbrIpAddr, ospfNbrAddressLessIndex } + ::= { ospfNbrTable 1 } + +OspfNbrEntry ::= + SEQUENCE { + ospfNbrIpAddr + IpAddress, + ospfNbrAddressLessIndex + InterfaceIndex, + ospfNbrRtrId + RouterID, + ospfNbrOptions + Integer32, + ospfNbrPriority + DesignatedRouterPriority, + ospfNbrState + INTEGER, + ospfNbrEvents + Counter32, + ospfNbrLsRetransQLen + Gauge32, + ospfNbmaNbrStatus + RowStatus, + ospfNbmaNbrPermanence + INTEGER, + ospfNbrHelloSuppressed + TruthValue + } + + ospfNbrIpAddr OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The IP address this neighbor is using in its + IP Source Address. Note that, on addressless + links, this will not be 0.0.0.0, but the ad- + dress of another of the neighbor's interfaces." + ::= { ospfNbrEntry 1 } + + + ospfNbrAddressLessIndex OBJECT-TYPE + SYNTAX InterfaceIndex + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "On an interface having an IP Address, zero. + On addressless interfaces, the corresponding + value of ifIndex in the Internet Standard MIB. + On row creation, this can be derived from the + instance." + ::= { ospfNbrEntry 2 } + + + ospfNbrRtrId 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." + DEFVAL { '00000000'H } -- 0.0.0.0 + ::= { ospfNbrEntry 3 } + + + ospfNbrOptions OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "A Bit Mask corresponding to the neighbor's op- + tions field. + + Bit 0, if set, indicates that the system will + operate on Type of Service metrics other than + TOS 0. If zero, the neighbor will ignore all + metrics except the TOS 0 metric. + + Bit 1, if set, indicates that the associated + area accepts and operates on external informa- + tion; if zero, it is a stub area. + + Bit 2, if set, indicates that the system is ca- + pable of routing IP Multicast datagrams; i.e., + that it implements the Multicast Extensions to + OSPF. + + Bit 3, if set, indicates that the associated + area is an NSSA. These areas are capable of + carrying type 7 external advertisements, which + are translated into type 5 external advertise- + ments at NSSA borders." + REFERENCE + "OSPF Version 2, Section 12.1.2 Options" + DEFVAL { 0 } + ::= { ospfNbrEntry 4 } + + + ospfNbrPriority 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 } + ::= { ospfNbrEntry 5 } + + + ospfNbrState 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" + DEFVAL { down } + ::= { ospfNbrEntry 6 } + + + ospfNbrEvents 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." + ::= { ospfNbrEntry 7 } + + + ospfNbrLsRetransQLen OBJECT-TYPE + SYNTAX Gauge32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The current length of the retransmission + queue." + ::= { ospfNbrEntry 8 } + + + ospfNbmaNbrStatus OBJECT-TYPE + SYNTAX RowStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "This variable displays the status of the en- + try. Setting it to 'invalid' has the effect of + rendering it inoperative. The internal effect + (row removal) is implementation dependent." + ::= { ospfNbrEntry 9 } + + + ospfNbmaNbrPermanence OBJECT-TYPE + SYNTAX INTEGER { + dynamic (1), -- learned through protocol + permanent (2) -- configured address + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "This variable displays the status of the en- + try. 'dynamic' and 'permanent' refer to how + the neighbor became known." + DEFVAL { permanent } + ::= { ospfNbrEntry 10 } + + + ospfNbrHelloSuppressed OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Indicates whether Hellos are being suppressed + to the neighbor" + ::= { ospfNbrEntry 11 } + + +-- OSPF Virtual Neighbor Table + +-- This table describes all virtual neighbors. +-- Since Virtual Links are configured in the +-- virtual interface table, this table is read-only. + + ospfVirtNbrTable OBJECT-TYPE + SYNTAX SEQUENCE OF OspfVirtNbrEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table of virtual neighbor information." + REFERENCE + "OSPF Version 2, Section 15 Virtual Links" + ::= { ospf 11 } + + + ospfVirtNbrEntry OBJECT-TYPE + SYNTAX OspfVirtNbrEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Virtual neighbor information." + INDEX { ospfVirtNbrArea, ospfVirtNbrRtrId } + ::= { ospfVirtNbrTable 1 } + +OspfVirtNbrEntry ::= + SEQUENCE { + ospfVirtNbrArea + AreaID, + ospfVirtNbrRtrId + RouterID, + ospfVirtNbrIpAddr + IpAddress, + ospfVirtNbrOptions + Integer32, + ospfVirtNbrState + INTEGER, + ospfVirtNbrEvents + Counter32, + ospfVirtNbrLsRetransQLen + Gauge32, + ospfVirtNbrHelloSuppressed + TruthValue + } + + ospfVirtNbrArea OBJECT-TYPE + SYNTAX AreaID + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The Transit Area Identifier." + ::= { ospfVirtNbrEntry 1 } + + + ospfVirtNbrRtrId OBJECT-TYPE + SYNTAX RouterID + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "A 32-bit integer uniquely identifying the + neighboring router in the Autonomous System." + ::= { ospfVirtNbrEntry 2 } + + + ospfVirtNbrIpAddr OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The IP address this Virtual Neighbor is us- + ing." + ::= { ospfVirtNbrEntry 3 } + + + ospfVirtNbrOptions OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "A Bit Mask corresponding to the neighbor's op- + tions field. + + Bit 1, if set, indicates that the system will + operate on Type of Service metrics other than + TOS 0. If zero, the neighbor will ignore all + metrics except the TOS 0 metric. + + Bit 2, if set, indicates that the system is + Network Multicast capable; ie, that it imple- + ments OSPF Multicast Routing." + ::= { ospfVirtNbrEntry 4 } + ospfVirtNbrState 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." + ::= { ospfVirtNbrEntry 5 } + + + ospfVirtNbrEvents 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." + ::= { ospfVirtNbrEntry 6 } + + + ospfVirtNbrLsRetransQLen OBJECT-TYPE + SYNTAX Gauge32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The current length of the retransmission + queue." + ::= { ospfVirtNbrEntry 7 } + + + ospfVirtNbrHelloSuppressed OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Indicates whether Hellos are being suppressed + to the neighbor" + ::= { ospfVirtNbrEntry 8 } + +-- OSPF Link State Database, External + +-- The Link State Database contains the Link State +-- Advertisements from throughout the areas that the +-- device is attached to. + +-- This table is identical to the OSPF LSDB Table in +-- format, but contains only External Link State +-- Advertisements. The purpose is to allow external +-- LSAs to be displayed once for the router rather +-- than once in each non-stub area. + + ospfExtLsdbTable OBJECT-TYPE + SYNTAX SEQUENCE OF OspfExtLsdbEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The OSPF Process's Links State Database." + REFERENCE + "OSPF Version 2, Section 12 Link State Adver- + tisements" + ::= { ospf 12 } + + + ospfExtLsdbEntry OBJECT-TYPE + SYNTAX OspfExtLsdbEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A single Link State Advertisement." + INDEX { ospfExtLsdbType, ospfExtLsdbLsid, ospfExtLsdbRouterId } + ::= { ospfExtLsdbTable 1 } + +OspfExtLsdbEntry ::= + SEQUENCE { + ospfExtLsdbType + INTEGER, + ospfExtLsdbLsid + IpAddress, + ospfExtLsdbRouterId + RouterID, + ospfExtLsdbSequence + Integer32, + ospfExtLsdbAge + Integer32, + ospfExtLsdbChecksum + Integer32, + ospfExtLsdbAdvertisement + OCTET STRING + } + + ospfExtLsdbType OBJECT-TYPE + SYNTAX INTEGER { + asExternalLink (5) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The type of the link state advertisement. + Each link state type has a separate advertise- + ment format." + REFERENCE + "OSPF Version 2, Appendix A.4.1 The Link State + Advertisement header" + ::= { ospfExtLsdbEntry 1 } + + + ospfExtLsdbLsid OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The Link State ID is an LS Type Specific field + containing either a Router ID or an IP Address; + it identifies the piece of the routing domain + that is being described by the advertisement." + REFERENCE + "OSPF Version 2, Section 12.1.4 Link State ID" + ::= { ospfExtLsdbEntry 2 } + + + ospfExtLsdbRouterId 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" + ::= { ospfExtLsdbEntry 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. + ospfExtLsdbSequence 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" + ::= { ospfExtLsdbEntry 4 } + + + ospfExtLsdbAge OBJECT-TYPE + SYNTAX Integer32 -- Should be 0..MaxAge + 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" + ::= { ospfExtLsdbEntry 5 } + + + ospfExtLsdbChecksum 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" + ::= { ospfExtLsdbEntry 6 } + + + ospfExtLsdbAdvertisement OBJECT-TYPE + SYNTAX OCTET STRING (SIZE(36)) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The entire Link State Advertisement, including + its header." + REFERENCE + "OSPF Version 2, Section 12 Link State Adver- + tisements" + ::= { ospfExtLsdbEntry 7 } + + +-- OSPF Use of the CIDR Route Table + +ospfRouteGroup OBJECT IDENTIFIER ::= { ospf 13 } + +-- The IP Forwarding Table defines a number of objects for use by +-- the routing protocol to externalize its information. Most of +-- the variables (ipForwardDest, ipForwardMask, ipForwardPolicy, +-- ipForwardNextHop, ipForwardIfIndex, ipForwardType, +-- ipForwardProto, ipForwardAge, and ipForwardNextHopAS) are +-- defined there. + +-- Those that leave some discretion are defined here. + +-- ipCidrRouteProto is, of course, ospf (13). + +-- ipCidrRouteAge is the time since the route was first calculated, +-- as opposed to the time since the last SPF run. + +-- ipCidrRouteInfo is an OBJECT IDENTIFIER for use by the routing +-- protocol. The following values shall be found there depending +-- on the way the route was calculated. + +ospfIntraArea OBJECT IDENTIFIER ::= { ospfRouteGroup 1 } +ospfInterArea OBJECT IDENTIFIER ::= { ospfRouteGroup 2 } +ospfExternalType1 OBJECT IDENTIFIER ::= { ospfRouteGroup 3 } +ospfExternalType2 OBJECT IDENTIFIER ::= { ospfRouteGroup 4 } + +-- ipCidrRouteMetric1 is, by definition, the primary routing +-- metric. Therefore, it should be the metric that route +-- selection is based on. For intra-area and inter-area routes, +-- it is an OSPF metric. For External Type 1 (comparable value) +-- routes, it is an OSPF metric plus the External Metric. For +-- external Type 2 (non-comparable value) routes, it is the +-- external metric. + +-- ipCidrRouteMetric2 is, by definition, a secondary routing +-- metric. Therefore, it should be the metric that breaks a tie +-- among routes having equal metric1 values and the same +-- calculation rule. For intra-area, inter-area routes, and +-- External Type 1 (comparable value) routes, it is unused. For +-- external Type 2 (non-comparable value) routes, it is the metric +-- to the AS border router. + +-- ipCidrRouteMetric3, ipCidrRouteMetric4, and ipCidrRouteMetric5 are +-- unused. + +-- +-- The OSPF Area Aggregate Table +-- +-- This table replaces the OSPF Area Summary Table, being an +-- extension of that for CIDR routers. + + ospfAreaAggregateTable OBJECT-TYPE + SYNTAX SEQUENCE OF OspfAreaAggregateEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A range of IP addresses specified by an IP + address/IP network mask pair. For example, + class B address range of X.X.X.X with a network + mask of 255.255.0.0 includes all IP addresses + from X.X.0.0 to X.X.255.255. Note that if + ranges are configured such that one range sub- + sumes another range (e.g., 10.0.0.0 mask + 255.0.0.0 and 10.1.0.0 mask 255.255.0.0), the + most specific match is the preferred one." + REFERENCE + "OSPF Version 2, Appendix C.2 Area parameters" + ::= { ospf 14 } + + + ospfAreaAggregateEntry OBJECT-TYPE + SYNTAX OspfAreaAggregateEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A range of IP addresses specified by an IP + address/IP network mask pair. For example, + class B address range of X.X.X.X with a network + mask of 255.255.0.0 includes all IP addresses + from X.X.0.0 to X.X.255.255. Note that if + ranges are range configured such that one range + subsumes another range (e.g., 10.0.0.0 mask + 255.0.0.0 and 10.1.0.0 mask 255.255.0.0), the + most specific match is the preferred one." + REFERENCE + "OSPF Version 2, Appendix C.2 Area parameters" + INDEX { ospfAreaAggregateAreaID, ospfAreaAggregateLsdbType, + ospfAreaAggregateNet, ospfAreaAggregateMask } + ::= { ospfAreaAggregateTable 1 } + + +OspfAreaAggregateEntry ::= + SEQUENCE { + ospfAreaAggregateAreaID + AreaID, + ospfAreaAggregateLsdbType + INTEGER, + ospfAreaAggregateNet + IpAddress, + ospfAreaAggregateMask + IpAddress, + ospfAreaAggregateStatus + RowStatus, + ospfAreaAggregateEffect + INTEGER + } + + ospfAreaAggregateAreaID 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" + ::= { ospfAreaAggregateEntry 1 } + + + ospfAreaAggregateLsdbType OBJECT-TYPE + SYNTAX INTEGER { + summaryLink (3), + nssaExternalLink (7) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The type of the Address Aggregate. This field + specifies the Lsdb type that this Address Ag- + gregate applies to." + REFERENCE + "OSPF Version 2, Appendix A.4.1 The Link State + Advertisement header" + ::= { ospfAreaAggregateEntry 2 } + + + ospfAreaAggregateNet OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The IP Address of the Net or Subnet indicated + by the range." + REFERENCE + "OSPF Version 2, Appendix C.2 Area parameters" + ::= { ospfAreaAggregateEntry 3 } + + + ospfAreaAggregateMask OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The Subnet Mask that pertains to the Net or + Subnet." + REFERENCE + "OSPF Version 2, Appendix C.2 Area parameters" + ::= { ospfAreaAggregateEntry 4 } + + + ospfAreaAggregateStatus OBJECT-TYPE + SYNTAX RowStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "This variable displays the status of the en- + try. Setting it to 'invalid' has the effect of + rendering it inoperative. The internal effect + (row removal) is implementation dependent." + ::= { ospfAreaAggregateEntry 5 } + + + ospfAreaAggregateEffect OBJECT-TYPE + SYNTAX INTEGER { + advertiseMatching (1), + doNotAdvertiseMatching (2) + } + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "Subnets subsumed by ranges either trigger the + advertisement of the indicated aggregate (ad- + vertiseMatching), or result in the subnet's not + being advertised at all outside the area." + DEFVAL { advertiseMatching } + ::= { ospfAreaAggregateEntry 6 } + + +-- conformance information + +ospfConformance OBJECT IDENTIFIER ::= { ospf 15 } + +ospfGroups OBJECT IDENTIFIER ::= { ospfConformance 1 } +ospfCompliances OBJECT IDENTIFIER ::= { ospfConformance 2 } + +-- compliance statements + + ospfCompliance MODULE-COMPLIANCE + STATUS current + DESCRIPTION + "The compliance statement " + MODULE -- this module + MANDATORY-GROUPS { + ospfBasicGroup, + ospfAreaGroup, + ospfStubAreaGroup, + ospfIfGroup, + ospfIfMetricGroup, + ospfVirtIfGroup, + ospfNbrGroup, + ospfVirtNbrGroup, + ospfAreaAggregateGroup + } + ::= { ospfCompliances 1 } + + +-- units of conformance + + ospfBasicGroup OBJECT-GROUP + OBJECTS { + ospfRouterId, + ospfAdminStat, + ospfVersionNumber, + ospfAreaBdrRtrStatus, + ospfASBdrRtrStatus, + ospfExternLsaCount, + ospfExternLsaCksumSum, + ospfTOSSupport, + ospfOriginateNewLsas, + ospfRxNewLsas, + ospfExtLsdbLimit, + ospfMulticastExtensions, + ospfExitOverflowInterval, + ospfDemandExtensions + } + STATUS current + DESCRIPTION + "These objects are required for OSPF systems." + ::= { ospfGroups 1 } + + + ospfAreaGroup OBJECT-GROUP + OBJECTS { + ospfAreaId, + ospfImportAsExtern, + ospfSpfRuns, + ospfAreaBdrRtrCount, + ospfAsBdrRtrCount, + ospfAreaLsaCount, + ospfAreaLsaCksumSum, + ospfAreaSummary, + ospfAreaStatus + } + STATUS current + DESCRIPTION + "These objects are required for OSPF systems + supporting areas." + ::= { ospfGroups 2 } + + + ospfStubAreaGroup OBJECT-GROUP + OBJECTS { + ospfStubAreaId, + ospfStubTOS, + ospfStubMetric, + ospfStubStatus, + ospfStubMetricType + } + STATUS current + DESCRIPTION + "These objects are required for OSPF systems + supporting stub areas." + ::= { ospfGroups 3 } + + + ospfLsdbGroup OBJECT-GROUP + OBJECTS { + ospfLsdbAreaId, + ospfLsdbType, + ospfLsdbLsid, + ospfLsdbRouterId, + ospfLsdbSequence, + ospfLsdbAge, + ospfLsdbChecksum, + ospfLsdbAdvertisement + } + STATUS current + DESCRIPTION + "These objects are required for OSPF systems + that display their link state database." + ::= { ospfGroups 4 } + + + ospfAreaRangeGroup OBJECT-GROUP + OBJECTS { + ospfAreaRangeAreaId, + ospfAreaRangeNet, + ospfAreaRangeMask, + ospfAreaRangeStatus, + ospfAreaRangeEffect + } + STATUS obsolete + DESCRIPTION + "These objects are required for non-CIDR OSPF + systems that support multiple areas." + ::= { ospfGroups 5 } + + + ospfHostGroup OBJECT-GROUP + OBJECTS { + ospfHostIpAddress, + ospfHostTOS, + ospfHostMetric, + ospfHostStatus, + ospfHostAreaID + } + STATUS current + DESCRIPTION + "These objects are required for OSPF systems + that support attached hosts." + ::= { ospfGroups 6 } + + + ospfIfGroup OBJECT-GROUP + OBJECTS { + ospfIfIpAddress, + ospfAddressLessIf, + ospfIfAreaId, + ospfIfType, + ospfIfAdminStat, + ospfIfRtrPriority, + ospfIfTransitDelay, + ospfIfRetransInterval, + ospfIfHelloInterval, + ospfIfRtrDeadInterval, + ospfIfPollInterval, + ospfIfState, + ospfIfDesignatedRouter, + ospfIfBackupDesignatedRouter, + ospfIfEvents, + ospfIfAuthType, + ospfIfAuthKey, + ospfIfStatus, + ospfIfMulticastForwarding, + ospfIfDemand + } + STATUS current + DESCRIPTION + "These objects are required for OSPF systems." + ::= { ospfGroups 7 } + + + ospfIfMetricGroup OBJECT-GROUP + OBJECTS { + ospfIfMetricIpAddress, + ospfIfMetricAddressLessIf, + ospfIfMetricTOS, + ospfIfMetricValue, + ospfIfMetricStatus + } + STATUS current + DESCRIPTION + "These objects are required for OSPF systems." + ::= { ospfGroups 8 } + + + ospfVirtIfGroup OBJECT-GROUP + OBJECTS { + ospfVirtIfAreaId, + ospfVirtIfNeighbor, + ospfVirtIfTransitDelay, + ospfVirtIfRetransInterval, + ospfVirtIfHelloInterval, + ospfVirtIfRtrDeadInterval, + ospfVirtIfState, + ospfVirtIfEvents, + ospfVirtIfAuthType, + ospfVirtIfAuthKey, + ospfVirtIfStatus + } + STATUS current + DESCRIPTION + "These objects are required for OSPF systems." + ::= { ospfGroups 9 } + + + ospfNbrGroup OBJECT-GROUP + OBJECTS { + ospfNbrIpAddr, + ospfNbrAddressLessIndex, + ospfNbrRtrId, + ospfNbrOptions, + ospfNbrPriority, + ospfNbrState, + ospfNbrEvents, + ospfNbrLsRetransQLen, + ospfNbmaNbrStatus, + ospfNbmaNbrPermanence, + ospfNbrHelloSuppressed + } + STATUS current + DESCRIPTION + "These objects are required for OSPF systems." + ::= { ospfGroups 10 } + + + ospfVirtNbrGroup OBJECT-GROUP + OBJECTS { + ospfVirtNbrArea, + ospfVirtNbrRtrId, + ospfVirtNbrIpAddr, + ospfVirtNbrOptions, + ospfVirtNbrState, + ospfVirtNbrEvents, + ospfVirtNbrLsRetransQLen, + ospfVirtNbrHelloSuppressed + } + STATUS current + DESCRIPTION + "These objects are required for OSPF systems." + ::= { ospfGroups 11 } + + + ospfExtLsdbGroup OBJECT-GROUP + OBJECTS { + ospfExtLsdbType, + ospfExtLsdbLsid, + ospfExtLsdbRouterId, + ospfExtLsdbSequence, + ospfExtLsdbAge, + ospfExtLsdbChecksum, + ospfExtLsdbAdvertisement + } + STATUS current + DESCRIPTION + "These objects are required for OSPF systems + that display their link state database." + ::= { ospfGroups 12 } + + + ospfAreaAggregateGroup OBJECT-GROUP + OBJECTS { + ospfAreaAggregateAreaID, + ospfAreaAggregateLsdbType, + ospfAreaAggregateNet, + ospfAreaAggregateMask, + ospfAreaAggregateStatus, + ospfAreaAggregateEffect + } + STATUS current + DESCRIPTION + "These objects are required for OSPF systems." + ::= { ospfGroups 13 } + +END diff --git a/ospfd/OSPF-TRAP-MIB.txt b/ospfd/OSPF-TRAP-MIB.txt new file mode 100644 index 0000000..8a3ab99 --- /dev/null +++ b/ospfd/OSPF-TRAP-MIB.txt @@ -0,0 +1,443 @@ +OSPF-TRAP-MIB DEFINITIONS ::= BEGIN + + IMPORTS + MODULE-IDENTITY, OBJECT-TYPE, NOTIFICATION-TYPE, IpAddress + FROM SNMPv2-SMI + MODULE-COMPLIANCE, OBJECT-GROUP + FROM SNMPv2-CONF + ospfRouterId, ospfIfIpAddress, ospfAddressLessIf, ospfIfState, + ospfVirtIfAreaId, ospfVirtIfNeighbor, ospfVirtIfState, + ospfNbrIpAddr, ospfNbrAddressLessIndex, ospfNbrRtrId, + ospfNbrState, ospfVirtNbrArea, ospfVirtNbrRtrId, ospfVirtNbrState, + ospfLsdbType, ospfLsdbLsid, ospfLsdbRouterId, ospfLsdbAreaId, + ospfExtLsdbLimit, ospf + FROM OSPF-MIB; + + ospfTrap MODULE-IDENTITY + LAST-UPDATED "9501201225Z" -- Fri Jan 20 12:25:50 PST 1995 + ORGANIZATION "IETF OSPF Working Group" + CONTACT-INFO + " Fred Baker + Postal: Cisco Systems + 519 Lado Drive + Santa Barbara, California 93111 + Tel: +1 805 681 0115 + E-Mail: fred@cisco.com + + Rob Coltun + Postal: RainbowBridge Communications + Tel: (301) 340-9416 + E-Mail: rcoltun@rainbow-bridge.com" + DESCRIPTION + "The MIB module to describe traps for the OSPF + Version 2 Protocol." + ::= { ospf 16 } + +-- Trap Support Objects + +-- The following are support objects for the OSPF traps. + +ospfTrapControl OBJECT IDENTIFIER ::= { ospfTrap 1 } +ospfTraps OBJECT IDENTIFIER ::= { ospfTrap 2 } + + ospfSetTrap OBJECT-TYPE + SYNTAX OCTET STRING (SIZE(4)) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "A four-octet string serving as a bit map for + the trap events defined by the OSPF traps. This + object is used to enable and disable specific + OSPF traps where a 1 in the bit field + represents enabled. The right-most bit (least + significant) represents trap 0." + ::= { ospfTrapControl 1 } + + + ospfConfigErrorType OBJECT-TYPE + SYNTAX INTEGER { + badVersion (1), + areaMismatch (2), + unknownNbmaNbr (3), -- Router is Dr eligible + unknownVirtualNbr (4), + authTypeMismatch(5), + authFailure (6), + netMaskMismatch (7), + helloIntervalMismatch (8), + deadIntervalMismatch (9), + optionMismatch (10) } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Potential types of configuration conflicts. + Used by the ospfConfigError and ospfConfigVir- + tError traps." + ::= { ospfTrapControl 2 } + + + ospfPacketType OBJECT-TYPE + SYNTAX INTEGER { + hello (1), + dbDescript (2), + lsReq (3), + lsUpdate (4), + lsAck (5) } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "OSPF packet types." + ::= { ospfTrapControl 3 } + + + ospfPacketSrc OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The IP address of an inbound packet that can- + not be identified by a neighbor instance." + ::= { ospfTrapControl 4 } + + +-- Traps + + + ospfIfStateChange NOTIFICATION-TYPE + OBJECTS { + ospfRouterId, -- The originator of the trap + ospfIfIpAddress, + ospfAddressLessIf, + ospfIfState -- The new state + } + STATUS current + DESCRIPTION + "An ospfIfStateChange trap signifies that there + has been a change in the state of a non-virtual + OSPF interface. This trap 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)." + ::= { ospfTraps 16 } + + + ospfVirtIfStateChange NOTIFICATION-TYPE + OBJECTS { + ospfRouterId, -- The originator of the trap + ospfVirtIfAreaId, + ospfVirtIfNeighbor, + ospfVirtIfState -- The new state + } + STATUS current + DESCRIPTION + "An ospfIfStateChange trap signifies that there + has been a change in the state of an OSPF vir- + tual interface. + This trap should be generated when the inter- + face state regresses (e.g., goes from Point- + to-Point to Down) or progresses to a terminal + state (i.e., Point-to-Point)." + ::= { ospfTraps 1 } + + + ospfNbrStateChange NOTIFICATION-TYPE + OBJECTS { + ospfRouterId, -- The originator of the trap + ospfNbrIpAddr, + ospfNbrAddressLessIndex, + ospfNbrRtrId, + ospfNbrState -- The new state + } + STATUS current + DESCRIPTION + "An ospfNbrStateChange trap signifies that + there has been a change in the state of a non- + virtual OSPF neighbor. This trap 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 an neighbor transitions + from or to Full on non-broadcast multi-access + and broadcast networks, the trap should be gen- + erated by the designated router. A designated + router transitioning to Down will be noted by + ospfIfStateChange." + ::= { ospfTraps 2 } + + + ospfVirtNbrStateChange NOTIFICATION-TYPE + OBJECTS { + ospfRouterId, -- The originator of the trap + ospfVirtNbrArea, + ospfVirtNbrRtrId, + ospfVirtNbrState -- The new state + } + STATUS current + DESCRIPTION + "An ospfIfStateChange trap signifies that there + has been a change in the state of an OSPF vir- + tual neighbor. This trap 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)." + ::= { ospfTraps 3 } + ospfIfConfigError NOTIFICATION-TYPE + OBJECTS { + ospfRouterId, -- The originator of the trap + ospfIfIpAddress, + ospfAddressLessIf, + ospfPacketSrc, -- The source IP address + ospfConfigErrorType, -- Type of error + ospfPacketType + } + STATUS current + DESCRIPTION + "An ospfIfConfigError trap signifies that a + packet has been received on a non-virtual in- + terface from a router whose configuration + parameters conflict with this router's confi- + guration parameters. Note that the event op- + tionMismatch should cause a trap only if it + prevents an adjacency from forming." + ::= { ospfTraps 4 } + + + ospfVirtIfConfigError NOTIFICATION-TYPE + OBJECTS { + ospfRouterId, -- The originator of the trap + ospfVirtIfAreaId, + ospfVirtIfNeighbor, + ospfConfigErrorType, -- Type of error + ospfPacketType + } + STATUS current + DESCRIPTION + "An ospfConfigError trap signifies that a pack- + et 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 trap only if it prevents an ad- + jacency from forming." + ::= { ospfTraps 5 } + + + ospfIfAuthFailure NOTIFICATION-TYPE + OBJECTS { + ospfRouterId, -- The originator of the trap + ospfIfIpAddress, + ospfAddressLessIf, + ospfPacketSrc, -- The source IP address + ospfConfigErrorType, -- authTypeMismatch or + -- authFailure + ospfPacketType + } + STATUS current + DESCRIPTION + "An ospfIfAuthFailure trap signifies that a + packet has been received on a non-virtual in- + terface from a router whose authentication key + or authentication type conflicts with this + router's authentication key or authentication + type." + ::= { ospfTraps 6 } + + + ospfVirtIfAuthFailure NOTIFICATION-TYPE + OBJECTS { + ospfRouterId, -- The originator of the trap + ospfVirtIfAreaId, + ospfVirtIfNeighbor, + ospfConfigErrorType, -- authTypeMismatch or + -- authFailure + ospfPacketType + } + STATUS current + DESCRIPTION + "An ospfVirtIfAuthFailure trap signifies that a + packet has been received on a virtual interface + from a router whose authentication key or au- + thentication type conflicts with this router's + authentication key or authentication type." + ::= { ospfTraps 7 } + + + ospfIfRxBadPacket NOTIFICATION-TYPE + OBJECTS { + ospfRouterId, -- The originator of the trap + ospfIfIpAddress, + ospfAddressLessIf, + ospfPacketSrc, -- The source IP address + ospfPacketType + } + STATUS current + DESCRIPTION + "An ospfIfRxBadPacket trap signifies that an + OSPF packet has been received on a non-virtual + interface that cannot be parsed." + ::= { ospfTraps 8 } + + ospfVirtIfRxBadPacket NOTIFICATION-TYPE + OBJECTS { + ospfRouterId, -- The originator of the trap + ospfVirtIfAreaId, + ospfVirtIfNeighbor, + ospfPacketType + } + STATUS current + DESCRIPTION + "An ospfRxBadPacket trap signifies that an OSPF + packet has been received on a virtual interface + that cannot be parsed." + ::= { ospfTraps 9 } + + + ospfTxRetransmit NOTIFICATION-TYPE + OBJECTS { + ospfRouterId, -- The originator of the trap + ospfIfIpAddress, + ospfAddressLessIf, + ospfNbrRtrId, -- Destination + ospfPacketType, + ospfLsdbType, + ospfLsdbLsid, + ospfLsdbRouterId + } + STATUS current + DESCRIPTION + "An ospfTxRetransmit trap signifies than an + OSPF packet has been retransmitted on a non- + virtual interface. All packets that may be re- + transmitted are associated with an LSDB entry. + The LS type, LS ID, and Router ID are used to + identify the LSDB entry." + ::= { ospfTraps 10 } + + + ospfVirtIfTxRetransmit NOTIFICATION-TYPE + OBJECTS { + ospfRouterId, -- The originator of the trap + ospfVirtIfAreaId, + ospfVirtIfNeighbor, + ospfPacketType, + ospfLsdbType, + ospfLsdbLsid, + ospfLsdbRouterId + } + STATUS current + DESCRIPTION + "An ospfTxRetransmit trap signifies than an + OSPF packet has been retransmitted on a virtual + interface. All packets that may be retransmit- + ted are associated with an LSDB entry. The LS + type, LS ID, and Router ID are used to identify + the LSDB entry." + ::= { ospfTraps 11 } + + + ospfOriginateLsa NOTIFICATION-TYPE + OBJECTS { + ospfRouterId, -- The originator of the trap + ospfLsdbAreaId, -- 0.0.0.0 for AS Externals + ospfLsdbType, + ospfLsdbLsid, + ospfLsdbRouterId + } + STATUS current + DESCRIPTION + "An ospfOriginateLsa trap signifies that a new + LSA has been originated by this router. This + trap should not be invoked for simple refreshes + of LSAs (which happesn every 30 minutes), but + instead will only be invoked when an LSA is + (re)originated due to a topology change. Addi- + tionally, this trap does not include LSAs that + are being flushed because they have reached + MaxAge." + ::= { ospfTraps 12 } + + + ospfMaxAgeLsa NOTIFICATION-TYPE + OBJECTS { + ospfRouterId, -- The originator of the trap + ospfLsdbAreaId, -- 0.0.0.0 for AS Externals + ospfLsdbType, + ospfLsdbLsid, + ospfLsdbRouterId + } + STATUS current + DESCRIPTION + "An ospfMaxAgeLsa trap signifies that one of + the LSA in the router's link-state database has + aged to MaxAge." + ::= { ospfTraps 13 } + + + ospfLsdbOverflow NOTIFICATION-TYPE + OBJECTS { + ospfRouterId, -- The originator of the trap + ospfExtLsdbLimit + } + STATUS current + DESCRIPTION + "An ospfLsdbOverflow trap signifies that the + number of LSAs in the router's link-state data- + base has exceeded ospfExtLsdbLimit." + ::= { ospfTraps 14 } + + + ospfLsdbApproachingOverflow NOTIFICATION-TYPE + OBJECTS { + ospfRouterId, -- The originator of the trap + ospfExtLsdbLimit + } + STATUS current + DESCRIPTION + "An ospfLsdbApproachingOverflow trap signifies + that the number of LSAs in the router's link- + state database has exceeded ninety percent of + ospfExtLsdbLimit." + ::= { ospfTraps 15 } + + +-- conformance information + +ospfTrapConformance OBJECT IDENTIFIER ::= { ospfTrap 3 } + +ospfTrapGroups OBJECT IDENTIFIER ::= { ospfTrapConformance 1 } +ospfTrapCompliances OBJECT IDENTIFIER ::= { ospfTrapConformance 2 } + +-- compliance statements + + ospfTrapCompliance MODULE-COMPLIANCE + STATUS current + DESCRIPTION + "The compliance statement " + MODULE -- this module + MANDATORY-GROUPS { ospfTrapControlGroup } + + + GROUP ospfTrapControlGroup + DESCRIPTION + "This group is optional but recommended for all + OSPF systems" + ::= { ospfTrapCompliances 1 } + + +-- units of conformance + + ospfTrapControlGroup OBJECT-GROUP + OBJECTS { + ospfSetTrap, + ospfConfigErrorType, + ospfPacketType, + ospfPacketSrc + } + STATUS current + DESCRIPTION + "These objects are required to control traps + from OSPF systems." + ::= { ospfTrapGroups 1 } + + +END diff --git a/ospfd/ospf_abr.c b/ospfd/ospf_abr.c new file mode 100644 index 0000000..95c2d46 --- /dev/null +++ b/ospfd/ospf_abr.c @@ -0,0 +1,1887 @@ +/* + * OSPF ABR functions. + * Copyright (C) 1999, 2000 Alex Zinin, 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. + */ + + +#include + +#include "thread.h" +#include "memory.h" +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "vty.h" +#include "filter.h" +#include "plist.h" +#include "log.h" + +#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_spf.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_ia.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_abr.h" +#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) +{ + struct ospf_area_range *range; + + range = XCALLOC (MTYPE_OSPF_AREA_RANGE, sizeof (struct ospf_area_range)); + range->addr = p->prefix; + range->masklen = p->prefixlen; + range->cost_config = OSPF_AREA_RANGE_COST_UNSPEC; + + return range; +} + +static void +ospf_area_range_free (struct ospf_area_range *range) +{ + XFREE (MTYPE_OSPF_AREA_RANGE, range); +} + +static void +ospf_area_range_add (struct ospf_area *area, struct ospf_area_range *range) +{ + struct route_node *rn; + struct prefix_ipv4 p; + + p.family = AF_INET; + p.prefixlen = range->masklen; + p.prefix = range->addr; + + rn = route_node_get (area->ranges, (struct prefix *)&p); + if (rn->info) + route_unlock_node (rn); + else + rn->info = range; +} + +static void +ospf_area_range_delete (struct ospf_area *area, struct route_node *rn) +{ + struct ospf_area_range *range = rn->info; + + if (range->specifics != 0) + ospf_delete_discard_route (area->ospf->new_table, + (struct prefix_ipv4 *) &rn->p); + + ospf_area_range_free (range); + rn->info = NULL; + route_unlock_node (rn); + route_unlock_node (rn); +} + +struct ospf_area_range * +ospf_area_range_lookup (struct ospf_area *area, struct prefix_ipv4 *p) +{ + struct route_node *rn; + + rn = route_node_lookup (area->ranges, (struct prefix *)p); + if (rn) + { + route_unlock_node (rn); + return rn->info; + } + return NULL; +} + +struct ospf_area_range * +ospf_area_range_lookup_next (struct ospf_area *area, + struct in_addr *range_net, + int first) +{ + struct route_node *rn; + struct prefix_ipv4 p; + struct ospf_area_range *find; + + p.family = AF_INET; + p.prefixlen = IPV4_MAX_BITLEN; + p.prefix = *range_net; + + if (first) + rn = route_top (area->ranges); + else + { + rn = route_node_get (area->ranges, (struct prefix *) &p); + rn = route_next (rn); + } + + for (; rn; rn = route_next (rn)) + if (rn->info) + break; + + if (rn && rn->info) + { + find = rn->info; + *range_net = rn->p.u.prefix4; + route_unlock_node (rn); + return find; + } + return NULL; +} + +static struct ospf_area_range * +ospf_area_range_match (struct ospf_area *area, struct prefix_ipv4 *p) +{ + struct route_node *node; + + node = route_node_match (area->ranges, (struct prefix *) p); + if (node) + { + route_unlock_node (node); + return node->info; + } + return NULL; +} + +struct ospf_area_range * +ospf_area_range_match_any (struct ospf *ospf, struct prefix_ipv4 *p) +{ + struct ospf_area_range *range; + struct ospf_area *area; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area)) + if ((range = ospf_area_range_match (area, p))) + return range; + + return NULL; +} + +int +ospf_area_range_active (struct ospf_area_range *range) +{ + return range->specifics; +} + +static int +ospf_area_actively_attached (struct ospf_area *area) +{ + return area->act_ints; +} + +int +ospf_area_range_set (struct ospf *ospf, struct in_addr area_id, + struct prefix_ipv4 *p, int advertise) +{ + struct ospf_area *area; + struct ospf_area_range *range; + int ret = OSPF_AREA_ID_FORMAT_ADDRESS; + + area = ospf_area_get (ospf, area_id, ret); + if (area == NULL) + return 0; + + range = ospf_area_range_lookup (area, p); + if (range != NULL) + { + if ((CHECK_FLAG (range->flags, OSPF_AREA_RANGE_ADVERTISE) + && !CHECK_FLAG (advertise, OSPF_AREA_RANGE_ADVERTISE)) + || (!CHECK_FLAG (range->flags, OSPF_AREA_RANGE_ADVERTISE) + && CHECK_FLAG (advertise, OSPF_AREA_RANGE_ADVERTISE))) + ospf_schedule_abr_task (ospf); + } + else + { + range = ospf_area_range_new (p); + ospf_area_range_add (area, range); + ospf_schedule_abr_task (ospf); + } + + if (CHECK_FLAG (advertise, OSPF_AREA_RANGE_ADVERTISE)) + SET_FLAG (range->flags, OSPF_AREA_RANGE_ADVERTISE); + else + UNSET_FLAG (range->flags, OSPF_AREA_RANGE_ADVERTISE); + + return 1; +} + +int +ospf_area_range_cost_set (struct ospf *ospf, struct in_addr area_id, + struct prefix_ipv4 *p, u_int32_t cost) +{ + struct ospf_area *area; + struct ospf_area_range *range; + int ret = OSPF_AREA_ID_FORMAT_ADDRESS; + + area = ospf_area_get (ospf, area_id, ret); + if (area == NULL) + return 0; + + range = ospf_area_range_lookup (area, p); + if (range == NULL) + return 0; + + if (range->cost_config != cost) + { + range->cost_config = cost; + if (ospf_area_range_active (range)) + ospf_schedule_abr_task (ospf); + } + + return 1; +} + +int +ospf_area_range_unset (struct ospf *ospf, struct in_addr area_id, + struct prefix_ipv4 *p) +{ + struct ospf_area *area; + struct route_node *rn; + + area = ospf_area_lookup_by_area_id (ospf, area_id); + if (area == NULL) + return 0; + + rn = route_node_lookup (area->ranges, (struct prefix*)p); + if (rn == NULL) + return 0; + + if (ospf_area_range_active (rn->info)) + ospf_schedule_abr_task (ospf); + + ospf_area_range_delete (area, rn); + + return 1; +} + +int +ospf_area_range_substitute_set (struct ospf *ospf, struct in_addr area_id, + struct prefix_ipv4 *p, struct prefix_ipv4 *s) +{ + struct ospf_area *area; + struct ospf_area_range *range; + int ret = OSPF_AREA_ID_FORMAT_ADDRESS; + + area = ospf_area_get (ospf, area_id, ret); + range = ospf_area_range_lookup (area, p); + + if (range != NULL) + { + if (!CHECK_FLAG (range->flags, OSPF_AREA_RANGE_ADVERTISE) || + !CHECK_FLAG (range->flags, OSPF_AREA_RANGE_SUBSTITUTE)) + ospf_schedule_abr_task (ospf); + } + else + { + range = ospf_area_range_new (p); + ospf_area_range_add (area, range); + ospf_schedule_abr_task (ospf); + } + + SET_FLAG (range->flags, OSPF_AREA_RANGE_ADVERTISE); + SET_FLAG (range->flags, OSPF_AREA_RANGE_SUBSTITUTE); + range->subst_addr = s->prefix; + range->subst_masklen = s->prefixlen; + + return 1; +} + +int +ospf_area_range_substitute_unset (struct ospf *ospf, struct in_addr area_id, + struct prefix_ipv4 *p) +{ + struct ospf_area *area; + struct ospf_area_range *range; + + 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) + return 0; + + if (CHECK_FLAG (range->flags, OSPF_AREA_RANGE_SUBSTITUTE)) + if (ospf_area_range_active (range)) + ospf_schedule_abr_task (ospf); + + UNSET_FLAG (range->flags, OSPF_AREA_RANGE_SUBSTITUTE); + range->subst_addr.s_addr = 0; + range->subst_masklen = 0; + + return 1; +} + +int +ospf_act_bb_connection (struct ospf *ospf) +{ + if (ospf->backbone == NULL) + return 0; + + return ospf->backbone->full_nbrs; +} + +/* Determine whether this router is elected translator or not for area */ +static int +ospf_abr_nssa_am_elected (struct ospf_area *area) +{ + struct route_node *rn; + struct ospf_lsa *lsa; + struct router_lsa *rlsa; + struct in_addr *best = NULL; + + LSDB_LOOP ( ROUTER_LSDB (area), rn, lsa) + { + /* sanity checks */ + if (!lsa + || (lsa->data->type != OSPF_ROUTER_LSA) + || IS_LSA_SELF (lsa)) + continue; + + rlsa = (struct router_lsa *) lsa->data; + + /* ignore non-ABR routers */ + if (!IS_ROUTER_LSA_BORDER (rlsa)) + continue; + + /* Router has Nt flag - always translate */ + if (IS_ROUTER_LSA_NT (rlsa)) + { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_abr_nssa_am_elected: " + "router %s asserts Nt", + inet_ntoa (lsa->data->id) ); + return 0; + } + + if (best == NULL) + best = &lsa->data->id; + else + if (IPV4_ADDR_CMP (&best->s_addr, &lsa->data->id.s_addr) < 0) + best = &lsa->data->id; + } + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_abr_nssa_am_elected: best electable ABR is: %s", + (best) ? inet_ntoa (*best) : "" ); + + if (best == NULL) + return 1; + + if (IPV4_ADDR_CMP (&best->s_addr, &area->ospf->router_id.s_addr) < 0) + return 1; + else + return 0; +} + +/* Check NSSA ABR status + * assumes there are nssa areas + */ +static void +ospf_abr_nssa_check_status (struct ospf *ospf) +{ + struct ospf_area *area; + struct listnode *lnode, *nnode; + + for (ALL_LIST_ELEMENTS (ospf->areas, lnode, nnode, area)) + { + u_char old_state = area->NSSATranslatorState; + + if (area->external_routing != OSPF_AREA_NSSA) + continue; + + if (IS_DEBUG_OSPF (nssa, NSSA)) + zlog_debug ("ospf_abr_nssa_check_status: " + "checking area %s", + inet_ntoa (area->area_id)); + + if (!IS_OSPF_ABR (area->ospf)) + { + if (IS_DEBUG_OSPF (nssa, NSSA)) + zlog_debug ("ospf_abr_nssa_check_status: " + "not ABR"); + area->NSSATranslatorState = OSPF_NSSA_TRANSLATE_DISABLED; + } + else + { + switch (area->NSSATranslatorRole) + { + case OSPF_NSSA_ROLE_NEVER: + /* We never Translate Type-7 LSA. */ + /* TODO: check previous state and flush? */ + if (IS_DEBUG_OSPF (nssa, NSSA)) + zlog_debug ("ospf_abr_nssa_check_status: " + "never translate"); + area->NSSATranslatorState = OSPF_NSSA_TRANSLATE_DISABLED; + break; + + case OSPF_NSSA_ROLE_ALWAYS: + /* We always translate if we are an ABR + * TODO: originate new LSAs if state change? + * or let the nssa abr task take care of it? + */ + if (IS_DEBUG_OSPF (nssa, NSSA)) + zlog_debug ("ospf_abr_nssa_check_status: " + "translate always"); + area->NSSATranslatorState = OSPF_NSSA_TRANSLATE_ENABLED; + break; + + case OSPF_NSSA_ROLE_CANDIDATE: + /* We are a candidate for Translation */ + if (ospf_abr_nssa_am_elected (area) > 0) + { + area->NSSATranslatorState = OSPF_NSSA_TRANSLATE_ENABLED; + if (IS_DEBUG_OSPF (nssa, NSSA)) + zlog_debug ("ospf_abr_nssa_check_status: " + "elected translator"); + } + else + { + area->NSSATranslatorState = OSPF_NSSA_TRANSLATE_DISABLED; + if (IS_DEBUG_OSPF (nssa, NSSA)) + zlog_debug ("ospf_abr_nssa_check_status: " "not elected"); + } + break; + } + } + /* RFC3101, 3.1: + * All NSSA border routers must set the E-bit in the Type-1 router-LSAs + * of their directly attached non-stub areas, even when they are not + * translating. + */ + if (old_state != area->NSSATranslatorState) + { + if (old_state == OSPF_NSSA_TRANSLATE_DISABLED) + ospf_asbr_status_update (ospf, ++ospf->redistribute); + else if (area->NSSATranslatorState == OSPF_NSSA_TRANSLATE_DISABLED) + ospf_asbr_status_update (ospf, --ospf->redistribute); + } + } +} + +/* Check area border router status. */ +void +ospf_check_abr_status (struct ospf *ospf) +{ + struct ospf_area *area; + struct listnode *node, *nnode; + int bb_configured = 0; + int bb_act_attached = 0; + int areas_configured = 0; + int areas_act_attached = 0; + u_char new_flags = ospf->flags; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_check_abr_status(): Start"); + + for (ALL_LIST_ELEMENTS (ospf->areas, node, nnode, area)) + { + if (listcount (area->oiflist)) + { + areas_configured++; + + if (OSPF_IS_AREA_BACKBONE (area)) + bb_configured = 1; + } + + if (ospf_area_actively_attached (area)) + { + areas_act_attached++; + + if (OSPF_IS_AREA_BACKBONE (area)) + bb_act_attached = 1; + } + } + + if (IS_DEBUG_OSPF_EVENT) + { + zlog_debug ("ospf_check_abr_status(): looked through areas"); + zlog_debug ("ospf_check_abr_status(): bb_configured: %d", bb_configured); + zlog_debug ("ospf_check_abr_status(): bb_act_attached: %d", + bb_act_attached); + zlog_debug ("ospf_check_abr_status(): areas_configured: %d", + areas_configured); + zlog_debug ("ospf_check_abr_status(): areas_act_attached: %d", + areas_act_attached); + } + + switch (ospf->abr_type) + { + case OSPF_ABR_SHORTCUT: + case OSPF_ABR_STAND: + if (areas_act_attached > 1) + SET_FLAG (new_flags, OSPF_FLAG_ABR); + else + UNSET_FLAG (new_flags, OSPF_FLAG_ABR); + break; + + case OSPF_ABR_IBM: + if ((areas_act_attached > 1) && bb_configured) + SET_FLAG (new_flags, OSPF_FLAG_ABR); + else + UNSET_FLAG (new_flags, OSPF_FLAG_ABR); + break; + + case OSPF_ABR_CISCO: + if ((areas_configured > 1) && bb_act_attached) + SET_FLAG (new_flags, OSPF_FLAG_ABR); + else + UNSET_FLAG (new_flags, OSPF_FLAG_ABR); + break; + default: + break; + } + + if (new_flags != ospf->flags) + { + 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; + ospf_router_lsa_update (ospf); + } +} + +static void +ospf_abr_update_aggregate (struct ospf_area_range *range, + struct ospf_route *or, struct ospf_area *area) +{ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_update_aggregate(): Start"); + + 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", + range->cost_config); + + range->cost = range->cost_config; + } + else + { + if (range->specifics == 0) + { + 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(): update to %d", or->cost); + + range->cost = or->cost; + } + } + + range->specifics++; +} + +static void +set_metric (struct ospf_lsa *lsa, u_int32_t metric) +{ + struct summary_lsa *header; + u_char *mp; + metric = htonl (metric); + mp = (u_char *) &metric; + mp++; + header = (struct summary_lsa *) lsa->data; + memcpy(header->metric, mp, 3); +} + +/* ospf_abr_translate_nssa */ +static int +ospf_abr_translate_nssa (struct ospf_area *area, struct ospf_lsa *lsa) +{ + /* Incoming Type-7 or later aggregated Type-7 + * + * LSA is skipped if P-bit is off. + * LSA is aggregated if within range. + * + * The Type-7 is translated, Installed/Approved as a Type-5 into + * global LSDB, then Flooded through AS + * + * Later, any Unapproved Translated Type-5's are flushed/discarded + */ + + struct ospf_lsa *old = NULL, + *new = NULL; + struct as_external_lsa *ext7; + struct prefix_ipv4 p; + + if (! CHECK_FLAG (lsa->data->options, OSPF_OPTION_NP)) + { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_abr_translate_nssa(): LSA Id %s, P-bit off, NO Translation", + inet_ntoa (lsa->data->id)); + return 1; + } + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_abr_translate_nssa(): LSA Id %s, TRANSLATING 7 to 5", + inet_ntoa (lsa->data->id)); + + ext7 = (struct as_external_lsa *)(lsa->data); + p.prefix = lsa->data->id; + p.prefixlen = ip_masklen (ext7->mask); + + if (ext7->e[0].fwd_addr.s_addr == OSPF_DEFAULT_DESTINATION) + { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_abr_translate_nssa(): LSA Id %s, " + "Forward address is 0, NO Translation", + inet_ntoa (lsa->data->id)); + return 1; + } + + /* try find existing AS-External LSA for this prefix */ + + old = ospf_external_info_find_lsa (area->ospf, &p); + + if (old) + { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_abr_translate_nssa(): " + "found old translated LSA Id %s, refreshing", + inet_ntoa (old->data->id)); + + /* refresh */ + new = ospf_translated_nssa_refresh (area->ospf, lsa, old); + if (!new) + { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_abr_translate_nssa(): " + "could not refresh translated LSA Id %s", + inet_ntoa (old->data->id)); + } + } + else + { + /* no existing external route for this LSA Id + * originate translated LSA + */ + + if ((new = ospf_translated_nssa_originate (area->ospf, lsa)) + == NULL) + { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_abr_translate_nssa(): Could not translate " + "Type-7 for %s to Type-5", + inet_ntoa (lsa->data->id)); + return 1; + } + } + + /* Area where Aggregate testing will be inserted, just like summary + advertisements */ + /* ospf_abr_check_nssa_range (p_arg, lsa-> cost, lsa -> area); */ + + return 0; +} + +static void +ospf_abr_translate_nssa_range (struct prefix_ipv4 *p, u_int32_t cost) +{ + /* The Type-7 is created from the aggregated prefix and forwarded + for lsa installation and flooding... to be added... */ +} + +void +ospf_abr_announce_network_to_area (struct prefix_ipv4 *p, u_int32_t cost, + struct ospf_area *area) +{ + 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); + if (old) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_network_to_area(): old summary found"); + + sl = (struct summary_lsa *) old->data; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_network_to_area(): " + "old metric: %d, new metric: %d", + 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) + zlog_debug ("ospf_abr_announce_network_to_area(): " + "old summary approved"); + SET_FLAG (old->flags, OSPF_LSA_APPROVED); + } + else + { + /* LSA is changed, refresh it */ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_network_to_area(): " + "refreshing summary"); + set_metric (old, full_cost); + lsa = ospf_lsa_refresh (area->ospf, old); + + if (!lsa) + { + char buf[INET_ADDRSTRLEN + 3]; /* ipv4 and /XX */ + + prefix2str ((struct prefix *) p, buf, sizeof(buf)); + zlog_warn ("%s: Could not refresh %s to %s", + __func__, + buf, + inet_ntoa (area->area_id)); + return; + } + + SET_FLAG (lsa->flags, OSPF_LSA_APPROVED); + /* This will flood through area. */ + } + } + else + { + 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, full_cost, area); + /* This will flood through area. */ + + if (!lsa) + { + char buf[INET_ADDRSTRLEN + 3]; /* ipv4 and /XX */ + + prefix2str ((struct prefix *)p, buf, sizeof(buf)); + zlog_warn ("%s: Could not originate %s to %s", + __func__, + buf, + inet_ntoa (area->area_id)); + return; + } + + SET_FLAG (lsa->flags, OSPF_LSA_APPROVED); + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_network_to_area(): " + "flooding new version of summary"); + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_network_to_area(): Stop"); +} + +static int +ospf_abr_nexthops_belong_to_area (struct ospf_route *or, + struct ospf_area *area) +{ + struct listnode *node, *nnode; + struct ospf_path *path; + struct ospf_interface *oi; + + for (ALL_LIST_ELEMENTS_RO (or->paths, node, path)) + for (ALL_LIST_ELEMENTS_RO (area->oiflist, nnode, oi)) + if (oi->ifp && oi->ifp->ifindex == path->ifindex) + return 1; + + return 0; +} + +static int +ospf_abr_should_accept (struct prefix_ipv4 *p, struct ospf_area *area) +{ + if (IMPORT_NAME (area)) + { + if (IMPORT_LIST (area) == NULL) + IMPORT_LIST (area) = access_list_lookup (AFI_IP, IMPORT_NAME (area)); + + if (IMPORT_LIST (area)) + if (access_list_apply (IMPORT_LIST (area), p) == FILTER_DENY) + return 0; + } + + return 1; +} + +static int +ospf_abr_plist_in_check (struct ospf_area *area, struct ospf_route *or, + struct prefix_ipv4 *p) +{ + if (PREFIX_NAME_IN (area)) + { + if (PREFIX_LIST_IN (area) == NULL) + PREFIX_LIST_IN (area) = prefix_list_lookup (AFI_IP, + PREFIX_NAME_IN (area)); + if (PREFIX_LIST_IN (area)) + if (prefix_list_apply (PREFIX_LIST_IN (area), p) != PREFIX_PERMIT) + return 0; + } + return 1; +} + +static int +ospf_abr_plist_out_check (struct ospf_area *area, struct ospf_route *or, + struct prefix_ipv4 *p) +{ + if (PREFIX_NAME_OUT (area)) + { + if (PREFIX_LIST_OUT (area) == NULL) + PREFIX_LIST_OUT (area) = prefix_list_lookup (AFI_IP, + PREFIX_NAME_OUT (area)); + if (PREFIX_LIST_OUT (area)) + if (prefix_list_apply (PREFIX_LIST_OUT (area), p) != PREFIX_PERMIT) + return 0; + } + return 1; +} + +static void +ospf_abr_announce_network (struct ospf *ospf, + struct prefix_ipv4 *p, struct ospf_route *or) +{ + struct ospf_area_range *range; + struct ospf_area *area, *or_area; + struct listnode *node; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_network(): Start"); + + or_area = ospf_area_lookup_by_area_id (ospf, or->u.std.area_id); + assert (or_area); + + for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_network(): looking at area %s", + inet_ntoa (area->area_id)); + + if (IPV4_ADDR_SAME (&or->u.std.area_id, &area->area_id)) + continue; + + if (ospf_abr_nexthops_belong_to_area (or, area)) + continue; + + if (!ospf_abr_should_accept (p, area)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_network(): " + "prefix %s/%d was denied by import-list", + inet_ntoa (p->prefix), p->prefixlen); + continue; + } + + if (!ospf_abr_plist_in_check (area, or, p)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_network(): " + "prefix %s/%d was denied by prefix-list", + inet_ntoa (p->prefix), p->prefixlen); + continue; + } + + if (area->external_routing != OSPF_AREA_DEFAULT && area->no_summary) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_network(): " + "area %s is stub and no_summary", + inet_ntoa (area->area_id)); + continue; + } + + if (or->path_type == OSPF_PATH_INTER_AREA) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_network(): this is " + "inter-area route to %s/%d", + inet_ntoa (p->prefix), p->prefixlen); + + if (!OSPF_IS_AREA_BACKBONE (area)) + ospf_abr_announce_network_to_area (p, or->cost, area); + } + + if (or->path_type == OSPF_PATH_INTRA_AREA) + { + if (IS_DEBUG_OSPF_EVENT) + 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, area); + else + ospf_abr_announce_network_to_area (p, or->cost, area); + } + } +} + +static int +ospf_abr_should_announce (struct ospf *ospf, + struct prefix_ipv4 *p, struct ospf_route *or) +{ + struct ospf_area *area; + + area = ospf_area_lookup_by_area_id (ospf, or->u.std.area_id); + + assert (area); + + if (EXPORT_NAME (area)) + { + if (EXPORT_LIST (area) == NULL) + EXPORT_LIST (area) = access_list_lookup (AFI_IP, EXPORT_NAME (area)); + + if (EXPORT_LIST (area)) + if (access_list_apply (EXPORT_LIST (area), p) == FILTER_DENY) + return 0; + } + + return 1; +} + +static void +ospf_abr_process_nssa_translates (struct ospf *ospf) +{ + /* Scan through all NSSA_LSDB records for all areas; + + If P-bit is on, translate all Type-7's to 5's and aggregate or + flood install as approved in Type-5 LSDB with XLATE Flag on + later, do same for all aggregates... At end, DISCARD all + remaining UNAPPROVED Type-5's (Aggregate is for future ) */ + struct listnode *node; + struct ospf_area *area; + struct route_node *rn; + struct ospf_lsa *lsa; + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_abr_process_nssa_translates(): Start"); + + for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area)) + { + if (! area->NSSATranslatorState) + continue; /* skip if not translator */ + + if (area->external_routing != OSPF_AREA_NSSA) + continue; /* skip if not Nssa Area */ + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_abr_process_nssa_translates(): " + "looking at area %s", inet_ntoa (area->area_id)); + + LSDB_LOOP (NSSA_LSDB (area), rn, lsa) + ospf_abr_translate_nssa (area, lsa); + } + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_abr_process_nssa_translates(): Stop"); + +} + +static void +ospf_abr_process_network_rt (struct ospf *ospf, + struct route_table *rt) +{ + struct ospf_area *area; + struct ospf_route *or; + struct route_node *rn; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_process_network_rt(): Start"); + + for (rn = route_top (rt); rn; rn = route_next (rn)) + { + if ((or = rn->info) == NULL) + continue; + + if (!(area = ospf_area_lookup_by_area_id (ospf, or->u.std.area_id))) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_process_network_rt(): area %s no longer exists", + inet_ntoa (or->u.std.area_id)); + continue; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_process_network_rt(): this is a route to %s/%d", + inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen); + if (or->path_type >= OSPF_PATH_TYPE1_EXTERNAL) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_process_network_rt(): " + "this is an External router, skipping"); + continue; + } + + if (or->cost >= OSPF_LS_INFINITY) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_process_network_rt():" + " this route's cost is infinity, skipping"); + continue; + } + + if (or->type == OSPF_DESTINATION_DISCARD) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_process_network_rt():" + " this is a discard entry, skipping"); + continue; + } + + if (or->path_type == OSPF_PATH_INTRA_AREA && + !ospf_abr_should_announce (ospf, (struct prefix_ipv4 *) &rn->p, or)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("ospf_abr_process_network_rt(): denied by export-list"); + continue; + } + + if (or->path_type == OSPF_PATH_INTRA_AREA && + !ospf_abr_plist_out_check (area, or, (struct prefix_ipv4 *) &rn->p)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("ospf_abr_process_network_rt(): denied by prefix-list"); + continue; + } + + if ((or->path_type == OSPF_PATH_INTER_AREA) && + !OSPF_IS_AREA_ID_BACKBONE (or->u.std.area_id)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_process_network_rt():" + " this is route is not backbone one, skipping"); + continue; + } + + + if ((ospf->abr_type == OSPF_ABR_CISCO) || + (ospf->abr_type == OSPF_ABR_IBM)) + + if (!ospf_act_bb_connection (ospf) && + or->path_type != OSPF_PATH_INTRA_AREA) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_process_network_rt(): ALT ABR: " + "No BB connection, skip not intra-area routes"); + continue; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_process_network_rt(): announcing"); + ospf_abr_announce_network (ospf, (struct prefix_ipv4 *)&rn->p, or); + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_process_network_rt(): Stop"); +} + +static void +ospf_abr_announce_rtr_to_area (struct prefix_ipv4 *p, u_int32_t cost, + struct ospf_area *area) +{ + struct ospf_lsa *lsa, *old = NULL; + struct summary_lsa *slsa = NULL; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_rtr_to_area(): Start"); + + old = ospf_lsa_lookup_by_prefix (area->lsdb, OSPF_ASBR_SUMMARY_LSA, + p, area->ospf->router_id); + if (old) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_rtr_to_area(): old summary found"); + slsa = (struct summary_lsa *) old->data; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_network_to_area(): " + "old metric: %d, new metric: %d", + 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"); + SET_FLAG (old->flags, OSPF_LSA_APPROVED); + } + else + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_rtr_to_area(): 2.2"); + + if (old) + { + set_metric (old, cost); + lsa = ospf_lsa_refresh (area->ospf, old); + } + else + lsa = ospf_summary_asbr_lsa_originate (p, cost, area); + if (!lsa) + { + char buf[INET_ADDRSTRLEN + 3]; /* ipv4 and /XX */ + + prefix2str ((struct prefix *)p, buf, sizeof(buf)); + zlog_warn ("%s: Could not refresh/originate %s to %s", + __func__, + buf, + inet_ntoa (area->area_id)); + return; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_rtr_to_area(): " + "flooding new version of summary"); + + /* + zlog_info ("ospf_abr_announce_rtr_to_area(): creating new summary"); + lsa = ospf_summary_asbr_lsa (p, cost, area, old); */ + + SET_FLAG (lsa->flags, OSPF_LSA_APPROVED); + /* ospf_flood_through_area (area, NULL, lsa);*/ + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_rtr_to_area(): Stop"); +} + + +static void +ospf_abr_announce_rtr (struct ospf *ospf, + struct prefix_ipv4 *p, struct ospf_route *or) +{ + struct listnode *node; + struct ospf_area *area; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_rtr(): Start"); + + for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_rtr(): looking at area %s", + inet_ntoa (area->area_id)); + + if (IPV4_ADDR_SAME (&or->u.std.area_id, &area->area_id)) + continue; + + if (ospf_abr_nexthops_belong_to_area (or, area)) + continue; + + if (area->external_routing != OSPF_AREA_DEFAULT) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_rtr(): " + "area %s doesn't support external routing", + inet_ntoa(area->area_id)); + continue; + } + + if (or->path_type == OSPF_PATH_INTER_AREA) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_rtr(): " + "this is inter-area route to %s", inet_ntoa (p->prefix)); + if (!OSPF_IS_AREA_BACKBONE (area)) + ospf_abr_announce_rtr_to_area (p, or->cost, area); + } + + if (or->path_type == OSPF_PATH_INTRA_AREA) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_rtr(): " + "this is intra-area route to %s", inet_ntoa (p->prefix)); + ospf_abr_announce_rtr_to_area (p, or->cost, area); + } + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_rtr(): Stop"); +} + +static void +ospf_abr_process_router_rt (struct ospf *ospf, struct route_table *rt) +{ + struct ospf_route *or; + struct route_node *rn; + struct list *l; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_process_router_rt(): Start"); + + for (rn = route_top (rt); rn; rn = route_next (rn)) + { + struct listnode *node, *nnode; + char flag = 0; + struct ospf_route *best = NULL; + + if (rn->info == NULL) + continue; + + l = rn->info; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_process_router_rt(): this is a route to %s", + inet_ntoa (rn->p.u.prefix4)); + + for (ALL_LIST_ELEMENTS (l, node, nnode, or)) + { + if (!ospf_area_lookup_by_area_id (ospf, or->u.std.area_id)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_process_router_rt(): area %s no longer exists", + inet_ntoa (or->u.std.area_id)); + continue; + } + + + if (!CHECK_FLAG (or->u.std.flags, ROUTER_LSA_EXTERNAL)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_process_router_rt(): " + "This is not an ASBR, skipping"); + continue; + } + + if (!flag) + { + best = ospf_find_asbr_route (ospf, rt, + (struct prefix_ipv4 *) &rn->p); + flag = 1; + } + + if (best == NULL) + continue; + + if (or != best) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_process_router_rt(): " + "This route is not the best among possible, skipping"); + continue; + } + + if (or->path_type == OSPF_PATH_INTER_AREA && + !OSPF_IS_AREA_ID_BACKBONE (or->u.std.area_id)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_process_router_rt(): " + "This route is not a backbone one, skipping"); + continue; + } + + if (or->cost >= OSPF_LS_INFINITY) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_process_router_rt(): " + "This route has LS_INFINITY metric, skipping"); + continue; + } + + if (ospf->abr_type == OSPF_ABR_CISCO + || ospf->abr_type == OSPF_ABR_IBM) + if (!ospf_act_bb_connection (ospf) + && or->path_type != OSPF_PATH_INTRA_AREA) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("ospf_abr_process_network_rt(): ALT ABR: " + "No BB connection, skip not intra-area routes"); + continue; + } + + ospf_abr_announce_rtr (ospf, (struct prefix_ipv4 *) &rn->p, or); + + } + + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_process_router_rt(): Stop"); +} + +static void +ospf_abr_unapprove_translates (struct ospf *ospf) /* For NSSA Translations */ +{ + struct ospf_lsa *lsa; + struct route_node *rn; + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_abr_unapprove_translates(): Start"); + + /* NSSA Translator is not checked, because it may have gone away, + and we would want to flush any residuals anyway */ + + LSDB_LOOP (EXTERNAL_LSDB (ospf), rn, lsa) + if (CHECK_FLAG (lsa->flags, OSPF_LSA_LOCAL_XLT)) + { + UNSET_FLAG (lsa->flags, OSPF_LSA_APPROVED); + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_abr_unapprove_translates(): " + "approved unset on link id %s", + inet_ntoa (lsa->data->id)); + } + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_abr_unapprove_translates(): Stop"); +} + +static void +ospf_abr_unapprove_summaries (struct ospf *ospf) +{ + struct listnode *node; + struct ospf_area *area; + struct route_node *rn; + struct ospf_lsa *lsa; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_unapprove_summaries(): Start"); + + for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_unapprove_summaries(): " + "considering area %s", + inet_ntoa (area->area_id)); + LSDB_LOOP (SUMMARY_LSDB (area), rn, lsa) + if (ospf_lsa_is_self_originated (ospf, lsa)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_unapprove_summaries(): " + "approved unset on summary link id %s", + inet_ntoa (lsa->data->id)); + UNSET_FLAG (lsa->flags, OSPF_LSA_APPROVED); + } + + LSDB_LOOP (ASBR_SUMMARY_LSDB (area), rn, lsa) + if (ospf_lsa_is_self_originated (ospf, lsa)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_unapprove_summaries(): " + "approved unset on asbr-summary link id %s", + inet_ntoa (lsa->data->id)); + UNSET_FLAG (lsa->flags, OSPF_LSA_APPROVED); + } + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_unapprove_summaries(): Stop"); +} + +static void +ospf_abr_prepare_aggregates (struct ospf *ospf) +{ + struct listnode *node; + struct route_node *rn; + struct ospf_area_range *range; + struct ospf_area *area; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_prepare_aggregates(): Start"); + + for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area)) + { + for (rn = route_top (area->ranges); rn; rn = route_next (rn)) + if ((range = rn->info) != NULL) + { + range->cost = 0; + range->specifics = 0; + } + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_prepare_aggregates(): Stop"); +} + +static void +ospf_abr_announce_aggregates (struct ospf *ospf) +{ + struct ospf_area *area, *ar; + struct ospf_area_range *range; + struct route_node *rn; + struct prefix p; + struct listnode *node, *n; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_aggregates(): Start"); + + for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_aggregates(): looking at area %s", + inet_ntoa (area->area_id)); + + for (rn = route_top (area->ranges); rn; rn = route_next (rn)) + if ((range = rn->info)) + { + if (!CHECK_FLAG (range->flags, OSPF_AREA_RANGE_ADVERTISE)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_aggregates():" + " discarding suppress-ranges"); + continue; + } + + p.family = AF_INET; + p.u.prefix4 = range->addr; + p.prefixlen = range->masklen; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_aggregates():" + " this is range: %s/%d", + inet_ntoa (p.u.prefix4), p.prefixlen); + + if (CHECK_FLAG (range->flags, OSPF_AREA_RANGE_SUBSTITUTE)) + { + p.family = AF_INET; + p.u.prefix4 = range->subst_addr; + p.prefixlen = range->subst_masklen; + } + + if (range->specifics) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_aggregates(): active range"); + + for (ALL_LIST_ELEMENTS_RO (ospf->areas, n, ar)) + { + if (ar == area) + continue; + + /* We do not check nexthops here, because + intra-area routes can be associated with + one area only */ + + /* backbone routes are not summarized + when announced into transit areas */ + + if (ospf_area_is_transit (ar) && + OSPF_IS_AREA_BACKBONE (area)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_aggregates(): Skipping " + "announcement of BB aggregate into" + " a transit area"); + continue; + } + ospf_abr_announce_network_to_area ((struct prefix_ipv4 *)&p, range->cost, ar); + } + } + } + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_aggregates(): Stop"); +} + +static void +ospf_abr_send_nssa_aggregates (struct ospf *ospf) /* temporarily turned off */ +{ + struct listnode *node; /*, n; */ + struct ospf_area *area; /*, *ar; */ + struct route_node *rn; + struct ospf_area_range *range; + struct prefix_ipv4 p; + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_abr_send_nssa_aggregates(): Start"); + + for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area)) + { + if (! area->NSSATranslatorState) + continue; + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_abr_send_nssa_aggregates(): looking at area %s", + inet_ntoa (area->area_id)); + + for (rn = route_top (area->ranges); rn; rn = route_next (rn)) + { + if (rn->info == NULL) + continue; + + range = rn->info; + + if (!CHECK_FLAG (range->flags, OSPF_AREA_RANGE_ADVERTISE)) + { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_abr_send_nssa_aggregates():" + " discarding suppress-ranges"); + continue; + } + + p.family = AF_INET; + p.prefix = range->addr; + p.prefixlen = range->masklen; + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_abr_send_nssa_aggregates():" + " this is range: %s/%d", + inet_ntoa (p.prefix), p.prefixlen); + + if (CHECK_FLAG (range->flags, OSPF_AREA_RANGE_SUBSTITUTE)) + { + p.family = AF_INET; + p.prefix = range->subst_addr; + p.prefixlen = range->subst_masklen; + } + + if (range->specifics) + { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_abr_send_nssa_aggregates(): active range"); + + /* Fetch LSA-Type-7 from aggregate prefix, and then + * translate, Install (as Type-5), Approve, and Flood + */ + ospf_abr_translate_nssa_range (&p, range->cost); + } + } /* all area ranges*/ + } /* all areas */ + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_abr_send_nssa_aggregates(): Stop"); +} + +static void +ospf_abr_announce_stub_defaults (struct ospf *ospf) +{ + struct listnode *node; + struct ospf_area *area; + struct prefix_ipv4 p; + + if (! IS_OSPF_ABR (ospf)) + return; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_stub_defaults(): Start"); + + p.family = AF_INET; + p.prefix.s_addr = OSPF_DEFAULT_DESTINATION; + p.prefixlen = 0; + + for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_stub_defaults(): looking at area %s", + inet_ntoa (area->area_id)); + + if ( (area->external_routing != OSPF_AREA_STUB) + && (area->external_routing != OSPF_AREA_NSSA) + ) + continue; + + if (OSPF_IS_AREA_BACKBONE (area)) + continue; /* Sanity Check */ + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_stub_defaults(): " + "announcing 0.0.0.0/0 to area %s", + inet_ntoa (area->area_id)); + ospf_abr_announce_network_to_area (&p, area->default_cost, area); + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_stub_defaults(): Stop"); +} + +static int +ospf_abr_remove_unapproved_translates_apply (struct ospf *ospf, + struct ospf_lsa *lsa) +{ + if (CHECK_FLAG (lsa->flags, OSPF_LSA_LOCAL_XLT) + && ! CHECK_FLAG (lsa->flags, OSPF_LSA_APPROVED)) + { + zlog_info ("ospf_abr_remove_unapproved_translates(): " + "removing unapproved translates, ID: %s", + inet_ntoa (lsa->data->id)); + + /* FLUSH THROUGHOUT AS */ + ospf_lsa_flush_as (ospf, lsa); + + /* DISCARD from LSDB */ + } + return 0; +} + +static void +ospf_abr_remove_unapproved_translates (struct ospf *ospf) +{ + struct route_node *rn; + struct ospf_lsa *lsa; + + /* All AREA PROCESS should have APPROVED necessary LSAs */ + /* Remove any left over and not APPROVED */ + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_abr_remove_unapproved_translates(): Start"); + + LSDB_LOOP (EXTERNAL_LSDB (ospf), rn, lsa) + ospf_abr_remove_unapproved_translates_apply (ospf, lsa); + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_abr_remove_unapproved_translates(): Stop"); +} + +static void +ospf_abr_remove_unapproved_summaries (struct ospf *ospf) +{ + struct listnode *node; + struct ospf_area *area; + struct route_node *rn; + struct ospf_lsa *lsa; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_remove_unapproved_summaries(): Start"); + + for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_remove_unapproved_summaries(): " + "looking at area %s", inet_ntoa (area->area_id)); + + LSDB_LOOP (SUMMARY_LSDB (area), rn, lsa) + if (ospf_lsa_is_self_originated (ospf, lsa)) + if (!CHECK_FLAG (lsa->flags, OSPF_LSA_APPROVED)) + ospf_lsa_flush_area (lsa, area); + + LSDB_LOOP (ASBR_SUMMARY_LSDB (area), rn, lsa) + if (ospf_lsa_is_self_originated (ospf, lsa)) + if (!CHECK_FLAG (lsa->flags, OSPF_LSA_APPROVED)) + ospf_lsa_flush_area (lsa, area); + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_remove_unapproved_summaries(): Stop"); +} + +static void +ospf_abr_manage_discard_routes (struct ospf *ospf) +{ + struct listnode *node, *nnode; + struct route_node *rn; + struct ospf_area *area; + struct ospf_area_range *range; + + for (ALL_LIST_ELEMENTS (ospf->areas, node, nnode, area)) + for (rn = route_top (area->ranges); rn; rn = route_next (rn)) + if ((range = rn->info) != NULL) + if (CHECK_FLAG (range->flags, OSPF_AREA_RANGE_ADVERTISE)) + { + if (range->specifics) + ospf_add_discard_route (ospf->new_table, area, + (struct prefix_ipv4 *) &rn->p); + else + ospf_delete_discard_route (ospf->new_table, + (struct prefix_ipv4 *) &rn->p); + } +} + +/* This is the function taking care about ABR NSSA, i.e. NSSA + Translator, -LSA aggregation and flooding. For all NSSAs + + Any SELF-AS-LSA is in the Type-5 LSDB and Type-7 LSDB. These LSA's + are refreshed from the Type-5 LSDB, installed into the Type-7 LSDB + with the P-bit set. + + Any received Type-5s are legal for an ABR, else illegal for IR. + Received Type-7s are installed, by area, with incoming P-bit. They + are flooded; if the Elected NSSA Translator, then P-bit off. + + Additionally, this ABR will place "translated type-7's" into the + Type-5 LSDB in order to keep track of APPROVAL or not. + + It will scan through every area, looking for Type-7 LSAs with P-Bit + SET. The Type-7's are either AS-FLOODED & 5-INSTALLED or + AGGREGATED. Later, the AGGREGATED LSAs are AS-FLOODED & + 5-INSTALLED. + + 5-INSTALLED is into the Type-5 LSDB; Any UNAPPROVED Type-5 LSAs + left over are FLUSHED and DISCARDED. + + For External Calculations, any NSSA areas use the Type-7 AREA-LSDB, + any ABR-non-NSSA areas use the Type-5 GLOBAL-LSDB. */ + +static void +ospf_abr_nssa_task (struct ospf *ospf) /* called only if any_nssa */ +{ + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("Check for NSSA-ABR Tasks():"); + + if (! IS_OSPF_ABR (ospf)) + return; + + if (! ospf->anyNSSA) + return; + + /* Each area must confirm TranslatorRole */ + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_abr_nssa_task(): Start"); + + /* For all Global Entries flagged "local-translate", unset APPROVED */ + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_abr_nssa_task(): unapprove translates"); + + ospf_abr_unapprove_translates (ospf); + + /* RESET all Ranges in every Area, same as summaries */ + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_abr_nssa_task(): NSSA initialize aggregates"); + ospf_abr_prepare_aggregates (ospf); /*TURNED OFF just for now */ + + /* For all NSSAs, Type-7s, translate to 5's, INSTALL/FLOOD, or + * Aggregate as Type-7 + * Install or Approve in Type-5 Global LSDB + */ + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_abr_nssa_task(): process translates"); + ospf_abr_process_nssa_translates (ospf); + + /* Translate/Send any "ranged" aggregates, and also 5-Install and + * Approve + * Scan Type-7's for aggregates, translate to Type-5's, + * Install/Flood/Approve + */ + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("ospf_abr_nssa_task(): send NSSA aggregates"); + ospf_abr_send_nssa_aggregates (ospf); /*TURNED OFF FOR NOW */ + + /* Send any NSSA defaults as Type-5 + *if (IS_DEBUG_OSPF_NSSA) + * zlog_debug ("ospf_abr_nssa_task(): announce nssa defaults"); + *ospf_abr_announce_nssa_defaults (ospf); + * havnt a clue what above is supposed to do. + */ + + /* Flush any unapproved previous translates from Global Data Base */ + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_abr_nssa_task(): remove unapproved translates"); + ospf_abr_remove_unapproved_translates (ospf); + + ospf_abr_manage_discard_routes (ospf); /* same as normal...discard */ + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_abr_nssa_task(): Stop"); +} + +/* This is the function taking care about ABR stuff, i.e. + summary-LSA origination and flooding. */ +void +ospf_abr_task (struct ospf *ospf) +{ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_task(): Start"); + + if (ospf->new_table == NULL || ospf->new_rtrs == NULL) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_task(): Routing tables are not yet ready"); + return; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_task(): unapprove summaries"); + ospf_abr_unapprove_summaries (ospf); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_task(): prepare aggregates"); + ospf_abr_prepare_aggregates (ospf); + + if (IS_OSPF_ABR (ospf)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_task(): process network RT"); + ospf_abr_process_network_rt (ospf, ospf->new_table); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_task(): process router RT"); + ospf_abr_process_router_rt (ospf, ospf->new_rtrs); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_task(): announce aggregates"); + ospf_abr_announce_aggregates (ospf); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_task(): announce stub defaults"); + ospf_abr_announce_stub_defaults (ospf); + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_task(): remove unapproved summaries"); + ospf_abr_remove_unapproved_summaries (ospf); + + ospf_abr_manage_discard_routes (ospf); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_task(): Stop"); +} + +static int +ospf_abr_task_timer (struct thread *thread) +{ + struct ospf *ospf = THREAD_ARG (thread); + + ospf->t_abr_task = 0; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Running ABR task on timer"); + + ospf_check_abr_status (ospf); + ospf_abr_nssa_check_status (ospf); + + ospf_abr_task (ospf); + ospf_abr_nssa_task (ospf); /* if nssa-abr, then scan Type-7 LSDB */ + + return 0; +} + +void +ospf_schedule_abr_task (struct ospf *ospf) +{ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Scheduling ABR task"); + + if (ospf->t_abr_task == NULL) + ospf->t_abr_task = thread_add_timer (master, ospf_abr_task_timer, + ospf, OSPF_ABR_TASK_DELAY); +} diff --git a/ospfd/ospf_abr.h b/ospfd/ospf_abr.h new file mode 100644 index 0000000..e367e44 --- /dev/null +++ b/ospfd/ospf_abr.h @@ -0,0 +1,94 @@ +/* + * OSPF ABR functions. + * Copyright (C) 1999 Alex Zinin + * + * 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_ABR_H +#define _ZEBRA_OSPF_ABR_H + +#define OSPF_ABR_TASK_DELAY 7 + +#define OSPF_AREA_RANGE_ADVERTISE (1 << 0) +#define OSPF_AREA_RANGE_SUBSTITUTE (1 << 1) + +/* Area range. */ +struct ospf_area_range +{ + /* Area range address. */ + struct in_addr addr; + + /* Area range masklen. */ + u_char masklen; + + /* Flags. */ + u_char flags; + + /* Number of more specific prefixes. */ + int specifics; + + /* Addr and masklen to substitute. */ + struct in_addr subst_addr; + u_char subst_masklen; + + /* Range cost. */ + u_int32_t cost; + + /* Configured range cost. */ + u_int32_t cost_config; +#define OSPF_AREA_RANGE_COST_UNSPEC -1U +}; + +/* Prototypes. */ +extern struct ospf_area_range *ospf_area_range_lookup (struct ospf_area *, + struct prefix_ipv4 *); + +extern struct ospf_area_range *ospf_some_area_range_match (struct prefix_ipv4 + *); + +extern struct ospf_area_range *ospf_area_range_lookup_next (struct ospf_area + *, + struct in_addr *, + int); + +extern int ospf_area_range_set (struct ospf *, struct in_addr, + struct prefix_ipv4 *, int); +extern int ospf_area_range_cost_set (struct ospf *, struct in_addr, + struct prefix_ipv4 *, u_int32_t); +extern int ospf_area_range_unset (struct ospf *, struct in_addr, + struct prefix_ipv4 *); +extern int ospf_area_range_substitute_set (struct ospf *, struct in_addr, + struct prefix_ipv4 *, + struct prefix_ipv4 *); +extern int ospf_area_range_substitute_unset (struct ospf *, struct in_addr, + struct prefix_ipv4 *); +extern struct ospf_area_range *ospf_area_range_match_any (struct ospf *, + struct prefix_ipv4 + *); +extern int ospf_area_range_active (struct ospf_area_range *); +extern int ospf_act_bb_connection (struct ospf *); + +extern void ospf_check_abr_status (struct ospf *); +extern void ospf_abr_task (struct ospf *); +extern void ospf_schedule_abr_task (struct ospf *); + +extern void ospf_abr_announce_network_to_area (struct prefix_ipv4 *, + u_int32_t, + struct ospf_area *); +#endif /* _ZEBRA_OSPF_ABR_H */ diff --git a/ospfd/ospf_api.c b/ospfd/ospf_api.c new file mode 100644 index 0000000..e92f162 --- /dev/null +++ b/ospfd/ospf_api.c @@ -0,0 +1,649 @@ +/* + * API message handling module for OSPF daemon and client. + * Copyright (C) 2001, 2002 Ralph Keller + * + * 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 SUPPORT_OSPF_API + +#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 "buffer.h" +#include "network.h" + +#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_api.h" + + +/* For debugging only, will be removed */ +void +api_opaque_lsa_print (struct lsa_header *data) +{ + struct opaque_lsa + { + struct lsa_header header; + u_char mydata[]; + }; + + struct opaque_lsa *olsa; + int opaquelen; + int i; + + ospf_lsa_header_dump (data); + + olsa = (struct opaque_lsa *) data; + + opaquelen = ntohs (data->length) - OSPF_LSA_HEADER_SIZE; + zlog_debug ("apiserver_lsa_print: opaquelen=%d\n", opaquelen); + + for (i = 0; i < opaquelen; i++) + { + zlog_debug ("0x%x ", olsa->mydata[i]); + } + zlog_debug ("\n"); +} + +/* ----------------------------------------------------------- + * Generic messages + * ----------------------------------------------------------- + */ + +struct msg * +msg_new (u_char msgtype, void *msgbody, u_int32_t seqnum, u_int16_t msglen) +{ + struct msg *new; + + new = XCALLOC (MTYPE_OSPF_API_MSG, sizeof (struct msg)); + + new->hdr.version = OSPF_API_VERSION; + new->hdr.msgtype = msgtype; + new->hdr.msglen = htons (msglen); + new->hdr.msgseq = htonl (seqnum); + + new->s = stream_new (msglen); + assert (new->s); + stream_put (new->s, msgbody, msglen); + + return new; +} + + +/* Duplicate a message by copying content. */ +struct msg * +msg_dup (struct msg *msg) +{ + struct msg *new; + + assert (msg); + + new = msg_new (msg->hdr.msgtype, STREAM_DATA (msg->s), + ntohl (msg->hdr.msgseq), ntohs (msg->hdr.msglen)); + return new; +} + + +/* XXX only for testing, will be removed */ + +struct nametab { + int value; + const char *name; +}; + +const char * +ospf_api_typename (int msgtype) +{ + struct nametab NameTab[] = { + { MSG_REGISTER_OPAQUETYPE, "Register opaque-type", }, + { MSG_UNREGISTER_OPAQUETYPE, "Unregister opaque-type", }, + { MSG_REGISTER_EVENT, "Register event", }, + { MSG_SYNC_LSDB, "Sync LSDB", }, + { MSG_ORIGINATE_REQUEST, "Originate request", }, + { MSG_DELETE_REQUEST, "Delete request", }, + { MSG_REPLY, "Reply", }, + { MSG_READY_NOTIFY, "Ready notify", }, + { MSG_LSA_UPDATE_NOTIFY, "LSA update notify", }, + { MSG_LSA_DELETE_NOTIFY, "LSA delete notify", }, + { MSG_NEW_IF, "New interface", }, + { MSG_DEL_IF, "Del interface", }, + { MSG_ISM_CHANGE, "ISM change", }, + { MSG_NSM_CHANGE, "NSM change", }, + }; + + int i, n = array_size(NameTab); + const char *name = NULL; + + for (i = 0; i < n; i++) + { + if (NameTab[i].value == msgtype) + { + name = NameTab[i].name; + break; + } + } + + return name ? name : "?"; +} + +const char * +ospf_api_errname (int errcode) +{ + struct nametab NameTab[] = { + { OSPF_API_OK, "OK", }, + { OSPF_API_NOSUCHINTERFACE, "No such interface", }, + { OSPF_API_NOSUCHAREA, "No such area", }, + { OSPF_API_NOSUCHLSA, "No such LSA", }, + { OSPF_API_ILLEGALLSATYPE, "Illegal LSA type", }, + { OSPF_API_OPAQUETYPEINUSE, "Opaque type in use", }, + { OSPF_API_OPAQUETYPENOTREGISTERED, "Opaque type not registered", }, + { OSPF_API_NOTREADY, "Not ready", }, + { OSPF_API_NOMEMORY, "No memory", }, + { OSPF_API_ERROR, "Other error", }, + { OSPF_API_UNDEF, "Undefined", }, + }; + + int i, n = array_size(NameTab); + const char *name = NULL; + + for (i = 0; i < n; i++) + { + if (NameTab[i].value == errcode) + { + name = NameTab[i].name; + break; + } + } + + return name ? name : "?"; +} + +void +msg_print (struct msg *msg) +{ + if (!msg) + { + zlog_debug ("msg_print msg=NULL!\n"); + return; + } + +#ifdef ORIGINAL_CODING + zlog_debug + ("msg=%p msgtype=%d msglen=%d msgseq=%d streamdata=%p streamsize=%lu\n", + msg, msg->hdr.msgtype, ntohs (msg->hdr.msglen), ntohl (msg->hdr.msgseq), + STREAM_DATA (msg->s), STREAM_SIZE (msg->s)); +#else /* ORIGINAL_CODING */ + /* API message common header part. */ + zlog_debug + ("API-msg [%s]: type(%d),len(%d),seq(%lu),data(%p),size(%zd)", + ospf_api_typename (msg->hdr.msgtype), msg->hdr.msgtype, + ntohs (msg->hdr.msglen), (unsigned long) ntohl (msg->hdr.msgseq), + STREAM_DATA (msg->s), STREAM_SIZE (msg->s)); + + /* API message body part. */ +#ifdef ndef + /* Generic Hex/Ascii dump */ + DumpBuf (STREAM_DATA (msg->s), STREAM_SIZE (msg->s)); /* Sorry, deleted! */ +#else /* ndef */ + /* Message-type dependent dump function. */ +#endif /* ndef */ + + return; +#endif /* ORIGINAL_CODING */ +} + +void +msg_free (struct msg *msg) +{ + if (msg->s) + stream_free (msg->s); + + XFREE (MTYPE_OSPF_API_MSG, msg); +} + + +/* Set sequence number of message */ +void +msg_set_seq (struct msg *msg, u_int32_t seqnr) +{ + assert (msg); + msg->hdr.msgseq = htonl (seqnr); +} + +/* Get sequence number of message */ +u_int32_t +msg_get_seq (struct msg *msg) +{ + assert (msg); + return ntohl (msg->hdr.msgseq); +} + +/* ----------------------------------------------------------- + * Message fifo queues + * ----------------------------------------------------------- + */ + +struct msg_fifo * +msg_fifo_new () +{ + return XCALLOC (MTYPE_OSPF_API_FIFO, sizeof (struct msg_fifo)); +} + +/* Add new message to fifo. */ +void +msg_fifo_push (struct msg_fifo *fifo, struct msg *msg) +{ + if (fifo->tail) + fifo->tail->next = msg; + else + fifo->head = msg; + + fifo->tail = msg; + fifo->count++; +} + + +/* Remove first message from fifo. */ +struct msg * +msg_fifo_pop (struct msg_fifo *fifo) +{ + struct msg *msg; + + msg = fifo->head; + if (msg) + { + fifo->head = msg->next; + + if (fifo->head == NULL) + fifo->tail = NULL; + + fifo->count--; + } + return msg; +} + +/* Return first fifo entry but do not remove it. */ +struct msg * +msg_fifo_head (struct msg_fifo *fifo) +{ + return fifo->head; +} + +/* Flush message fifo. */ +void +msg_fifo_flush (struct msg_fifo *fifo) +{ + struct msg *op; + struct msg *next; + + for (op = fifo->head; op; op = next) + { + next = op->next; + msg_free (op); + } + + fifo->head = fifo->tail = NULL; + fifo->count = 0; +} + +/* Free API message fifo. */ +void +msg_fifo_free (struct msg_fifo *fifo) +{ + msg_fifo_flush (fifo); + + XFREE (MTYPE_OSPF_API_FIFO, fifo); +} + +struct msg * +msg_read (int fd) +{ + struct msg *msg; + struct apimsghdr hdr; + u_char buf[OSPF_API_MAX_MSG_SIZE]; + int bodylen; + int rlen; + + /* Read message header */ + rlen = readn (fd, (u_char *) &hdr, sizeof (struct apimsghdr)); + + if (rlen < 0) + { + zlog_warn ("msg_read: readn %s", safe_strerror (errno)); + return NULL; + } + else if (rlen == 0) + { + zlog_warn ("msg_read: Connection closed by peer"); + return NULL; + } + else if (rlen != sizeof (struct apimsghdr)) + { + zlog_warn ("msg_read: Cannot read message header!"); + return NULL; + } + + /* Check version of API protocol */ + if (hdr.version != OSPF_API_VERSION) + { + zlog_warn ("msg_read: OSPF API protocol version mismatch"); + return NULL; + } + + /* Determine body length. */ + bodylen = ntohs (hdr.msglen); + if (bodylen > 0) + { + + /* Read message body */ + rlen = readn (fd, buf, bodylen); + if (rlen < 0) + { + zlog_warn ("msg_read: readn %s", safe_strerror (errno)); + return NULL; + } + else if (rlen == 0) + { + zlog_warn ("msg_read: Connection closed by peer"); + return NULL; + } + else if (rlen != bodylen) + { + zlog_warn ("msg_read: Cannot read message body!"); + return NULL; + } + } + + /* Allocate new message */ + msg = msg_new (hdr.msgtype, buf, ntohl (hdr.msgseq), ntohs (hdr.msglen)); + + return msg; +} + +int +msg_write (int fd, struct msg *msg) +{ + u_char buf[OSPF_API_MAX_MSG_SIZE]; + int l; + int wlen; + + assert (msg); + assert (msg->s); + + /* Length of message including header */ + l = sizeof (struct apimsghdr) + ntohs (msg->hdr.msglen); + + /* Make contiguous memory buffer for message */ + memcpy (buf, &msg->hdr, sizeof (struct apimsghdr)); + memcpy (buf + sizeof (struct apimsghdr), STREAM_DATA (msg->s), + ntohs (msg->hdr.msglen)); + + wlen = writen (fd, buf, l); + if (wlen < 0) + { + zlog_warn ("msg_write: writen %s", safe_strerror (errno)); + return -1; + } + else if (wlen == 0) + { + zlog_warn ("msg_write: Connection closed by peer"); + return -1; + } + else if (wlen != l) + { + zlog_warn ("msg_write: Cannot write API message"); + return -1; + } + return 0; +} + +/* ----------------------------------------------------------- + * Specific messages + * ----------------------------------------------------------- + */ + +struct msg * +new_msg_register_opaque_type (u_int32_t seqnum, u_char ltype, u_char otype) +{ + struct msg_register_opaque_type rmsg; + + rmsg.lsatype = ltype; + rmsg.opaquetype = otype; + memset (&rmsg.pad, 0, sizeof (rmsg.pad)); + + return msg_new (MSG_REGISTER_OPAQUETYPE, &rmsg, seqnum, + sizeof (struct msg_register_opaque_type)); +} + +struct msg * +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; + size_t len; + + emsg = (struct msg_register_event *) buf; + len = sizeof (struct msg_register_event) + + filter->num_areas * sizeof (struct in_addr); + 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); +} + +struct msg * +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; + size_t len; + + smsg = (struct msg_sync_lsdb *) buf; + len = sizeof (struct msg_sync_lsdb) + + filter->num_areas * sizeof (struct in_addr); + 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); +} + + +struct msg * +new_msg_originate_request (u_int32_t seqnum, + struct in_addr ifaddr, + struct in_addr area_id, struct lsa_header *data) +{ + struct msg_originate_request *omsg; + size_t omsglen; + char buf[OSPF_API_MAX_MSG_SIZE]; + + omsg = (struct msg_originate_request *) buf; + omsg->ifaddr = ifaddr; + omsg->area_id = area_id; + + 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); +} + +struct msg * +new_msg_delete_request (u_int32_t seqnum, + struct in_addr area_id, u_char lsa_type, + u_char opaque_type, u_int32_t opaque_id) +{ + struct msg_delete_request dmsg; + dmsg.area_id = area_id; + dmsg.lsa_type = lsa_type; + dmsg.opaque_type = opaque_type; + dmsg.opaque_id = htonl (opaque_id); + memset (&dmsg.pad, 0, sizeof (dmsg.pad)); + + return msg_new (MSG_DELETE_REQUEST, &dmsg, seqnum, + sizeof (struct msg_delete_request)); +} + + +struct msg * +new_msg_reply (u_int32_t seqnr, u_char rc) +{ + struct msg *msg; + struct msg_reply rmsg; + + /* Set return code */ + rmsg.errcode = rc; + memset (&rmsg.pad, 0, sizeof (rmsg.pad)); + + msg = msg_new (MSG_REPLY, &rmsg, seqnr, sizeof (struct msg_reply)); + + return msg; +} + +struct msg * +new_msg_ready_notify (u_int32_t seqnr, u_char lsa_type, + u_char opaque_type, struct in_addr addr) +{ + struct msg_ready_notify rmsg; + + rmsg.lsa_type = lsa_type; + rmsg.opaque_type = opaque_type; + memset (&rmsg.pad, 0, sizeof (rmsg.pad)); + rmsg.addr = addr; + + return msg_new (MSG_READY_NOTIFY, &rmsg, seqnr, + sizeof (struct msg_ready_notify)); +} + +struct msg * +new_msg_new_if (u_int32_t seqnr, + struct in_addr ifaddr, struct in_addr area_id) +{ + struct msg_new_if nmsg; + + nmsg.ifaddr = ifaddr; + nmsg.area_id = area_id; + + return msg_new (MSG_NEW_IF, &nmsg, seqnr, sizeof (struct msg_new_if)); +} + +struct msg * +new_msg_del_if (u_int32_t seqnr, struct in_addr ifaddr) +{ + struct msg_del_if dmsg; + + dmsg.ifaddr = ifaddr; + + return msg_new (MSG_DEL_IF, &dmsg, seqnr, sizeof (struct msg_del_if)); +} + +struct msg * +new_msg_ism_change (u_int32_t seqnr, struct in_addr ifaddr, + struct in_addr area_id, u_char status) +{ + struct msg_ism_change imsg; + + imsg.ifaddr = ifaddr; + imsg.area_id = area_id; + imsg.status = status; + memset (&imsg.pad, 0, sizeof (imsg.pad)); + + return msg_new (MSG_ISM_CHANGE, &imsg, seqnr, + sizeof (struct msg_ism_change)); +} + +struct msg * +new_msg_nsm_change (u_int32_t seqnr, struct in_addr ifaddr, + struct in_addr nbraddr, + struct in_addr router_id, u_char status) +{ + struct msg_nsm_change nmsg; + + nmsg.ifaddr = ifaddr; + nmsg.nbraddr = nbraddr; + nmsg.router_id = router_id; + nmsg.status = status; + memset (&nmsg.pad, 0, sizeof (nmsg.pad)); + + return msg_new (MSG_NSM_CHANGE, &nmsg, seqnr, + sizeof (struct msg_nsm_change)); +} + +struct msg * +new_msg_lsa_change_notify (u_char msgtype, + u_int32_t seqnum, + struct in_addr ifaddr, + struct in_addr area_id, + u_char is_self_originated, struct lsa_header *data) +{ + u_char buf[OSPF_API_MAX_MSG_SIZE]; + struct msg_lsa_change_notify *nmsg; + size_t len; + + assert (data); + + nmsg = (struct msg_lsa_change_notify *) buf; + nmsg->ifaddr = ifaddr; + nmsg->area_id = area_id; + nmsg->is_self_originated = is_self_originated; + memset (&nmsg->pad, 0, sizeof (nmsg->pad)); + + 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); +} + +#endif /* SUPPORT_OSPF_API */ diff --git a/ospfd/ospf_api.h b/ospfd/ospf_api.h new file mode 100644 index 0000000..eb5ad0a --- /dev/null +++ b/ospfd/ospf_api.h @@ -0,0 +1,361 @@ +/* + * API message handling module for OSPF daemon and client. + * Copyright (C) 2001, 2002 Ralph Keller + * + * 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. + */ + + +/* This file is used both by the OSPFd and client applications to + define message formats used for communication. */ + +#ifndef _OSPF_API_H +#define _OSPF_API_H + +#define OSPF_API_VERSION 1 + +/* MTYPE definition is not reflected to "memory.h". */ +#define MTYPE_OSPF_API_MSG MTYPE_TMP +#define MTYPE_OSPF_API_FIFO MTYPE_TMP + +/* Default API server port to accept connection request from client-side. */ +/* This value could be overridden by "ospfapi" entry in "/etc/services". */ +#define OSPF_API_SYNC_PORT 2607 + +/* ----------------------------------------------------------- + * Generic messages + * ----------------------------------------------------------- + */ + +/* Message header structure, fields are in network byte order and + aligned to four octets. */ +struct apimsghdr +{ + u_char version; /* OSPF API protocol version */ + u_char msgtype; /* Type of message */ + u_int16_t msglen; /* Length of message w/o header */ + u_int32_t msgseq; /* Sequence number */ +}; + +/* Message representation with header and body */ +struct msg +{ + struct msg *next; /* to link into fifo */ + + /* Message header */ + struct apimsghdr hdr; + + /* Message body */ + struct stream *s; +}; + +/* Prototypes for generic messages. */ +extern struct msg *msg_new (u_char msgtype, void *msgbody, + u_int32_t seqnum, u_int16_t msglen); +extern struct msg *msg_dup (struct msg *msg); +extern void msg_print (struct msg *msg); /* XXX debug only */ +extern void msg_free (struct msg *msg); +struct msg *msg_read (int fd); +extern int msg_write (int fd, struct msg *msg); + +/* For requests, the message sequence number is between MIN_SEQ and + MAX_SEQ. For notifications, the sequence number is 0. */ + +#define MIN_SEQ 1 +#define MAX_SEQ 2147483647 + +extern void msg_set_seq (struct msg *msg, u_int32_t seqnr); +extern u_int32_t msg_get_seq (struct msg *msg); + +/* ----------------------------------------------------------- + * Message fifo queues + * ----------------------------------------------------------- + */ + +/* Message queue structure. */ +struct msg_fifo +{ + unsigned long count; + + struct msg *head; + struct msg *tail; +}; + +/* Prototype for message fifo queues. */ +extern struct msg_fifo *msg_fifo_new (void); +extern void msg_fifo_push (struct msg_fifo *, struct msg *msg); +extern struct msg *msg_fifo_pop (struct msg_fifo *fifo); +extern struct msg *msg_fifo_head (struct msg_fifo *fifo); +extern void msg_fifo_flush (struct msg_fifo *fifo); +extern void msg_fifo_free (struct msg_fifo *fifo); + +/* ----------------------------------------------------------- + * Specific message type and format definitions + * ----------------------------------------------------------- + */ + +/* Messages to OSPF daemon. */ +#define MSG_REGISTER_OPAQUETYPE 1 +#define MSG_UNREGISTER_OPAQUETYPE 2 +#define MSG_REGISTER_EVENT 3 +#define MSG_SYNC_LSDB 4 +#define MSG_ORIGINATE_REQUEST 5 +#define MSG_DELETE_REQUEST 6 + +/* Messages from OSPF daemon. */ +#define MSG_REPLY 10 +#define MSG_READY_NOTIFY 11 +#define MSG_LSA_UPDATE_NOTIFY 12 +#define MSG_LSA_DELETE_NOTIFY 13 +#define MSG_NEW_IF 14 +#define MSG_DEL_IF 15 +#define MSG_ISM_CHANGE 16 +#define MSG_NSM_CHANGE 17 + +struct msg_register_opaque_type +{ + u_char lsatype; + u_char opaquetype; + u_char pad[2]; /* padding */ +}; + +struct msg_unregister_opaque_type +{ + u_char lsatype; + u_char opaquetype; + u_char pad[2]; /* padding */ +}; + +/* Power2 is needed to convert LSA types into bit positions, + * see typemask below. Type definition starts at 1, so + * Power2[0] is not used. */ + + +#ifdef ORIGINAL_CODING +static const u_int16_t + Power2[] = { 0x0, 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, + 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000 +}; +#else +static const u_int16_t + Power2[] = { 0, (1 << 0), (1 << 1), (1 << 2), (1 << 3), (1 << 4), + (1 << 5), (1 << 6), (1 << 7), (1 << 8), (1 << 9), + (1 << 10), (1 << 11), (1 << 12), (1 << 13), (1 << 14), + (1 << 15) +}; +#endif /* ORIGINAL_CODING */ + +struct lsa_filter_type +{ + u_int16_t typemask; /* bitmask for selecting LSA types (1..16) */ + u_char origin; /* selects according to origin. */ +#define NON_SELF_ORIGINATED 0 +#define SELF_ORIGINATED (OSPF_LSA_SELF) +#define ANY_ORIGIN 2 + + u_char num_areas; /* number of areas in the filter. */ + /* areas, if any, go here. */ +}; + +struct msg_register_event +{ + struct lsa_filter_type filter; +}; + +struct msg_sync_lsdb +{ + struct lsa_filter_type filter; +}; + +struct msg_originate_request +{ + /* Used for LSA type 9 otherwise ignored */ + struct in_addr ifaddr; + + /* Used for LSA type 10 otherwise ignored */ + struct in_addr area_id; + + /* LSA header and LSA-specific part */ + struct lsa_header data; +}; + +struct msg_delete_request +{ + struct in_addr area_id; /* "0.0.0.0" for AS-external opaque LSAs */ + u_char lsa_type; + u_char opaque_type; + u_char pad[2]; /* padding */ + u_int32_t opaque_id; +}; + +struct msg_reply +{ + signed char errcode; +#define OSPF_API_OK 0 +#define OSPF_API_NOSUCHINTERFACE (-1) +#define OSPF_API_NOSUCHAREA (-2) +#define OSPF_API_NOSUCHLSA (-3) +#define OSPF_API_ILLEGALLSATYPE (-4) +#define OSPF_API_OPAQUETYPEINUSE (-5) +#define OSPF_API_OPAQUETYPENOTREGISTERED (-6) +#define OSPF_API_NOTREADY (-7) +#define OSPF_API_NOMEMORY (-8) +#define OSPF_API_ERROR (-9) +#define OSPF_API_UNDEF (-10) + u_char pad[3]; /* padding to four byte alignment */ +}; + +/* Message to tell client application that it ospf daemon is + * ready to accept opaque LSAs for a given interface or area. */ + +struct msg_ready_notify +{ + u_char lsa_type; + u_char opaque_type; + u_char pad[2]; /* padding */ + struct in_addr addr; /* interface address or area address */ +}; + +/* These messages have a dynamic length depending on the embodied LSA. + They are aligned to four octets. msg_lsa_change_notify is used for + both LSA update and LSAs delete. */ + +struct msg_lsa_change_notify +{ + /* Used for LSA type 9 otherwise ignored */ + struct in_addr ifaddr; + /* Area ID. Not valid for AS-External and Opaque11 LSAs. */ + struct in_addr area_id; + u_char is_self_originated; /* 1 if self originated. */ + u_char pad[3]; + struct lsa_header data; +}; + +struct msg_new_if +{ + struct in_addr ifaddr; /* interface IP address */ + struct in_addr area_id; /* area this interface belongs to */ +}; + +struct msg_del_if +{ + struct in_addr ifaddr; /* interface IP address */ +}; + +struct msg_ism_change +{ + struct in_addr ifaddr; /* interface IP address */ + struct in_addr area_id; /* area this interface belongs to */ + u_char status; /* interface status (up/down) */ + u_char pad[3]; /* not used */ +}; + +struct msg_nsm_change +{ + struct in_addr ifaddr; /* attached interface */ + struct in_addr nbraddr; /* Neighbor interface address */ + struct in_addr router_id; /* Router ID of neighbor */ + u_char status; /* NSM status */ + u_char pad[3]; +}; + +/* We make use of a union to define a structure that covers all + possible API messages. This allows us to find out how much memory + needs to be reserved for the largest API message. */ +struct apimsg +{ + struct apimsghdr hdr; + union + { + struct msg_register_opaque_type register_opaque_type; + struct msg_register_event register_event; + struct msg_sync_lsdb sync_lsdb; + struct msg_originate_request originate_request; + struct msg_delete_request delete_request; + struct msg_reply reply; + struct msg_ready_notify ready_notify; + struct msg_new_if new_if; + struct msg_del_if del_if; + struct msg_ism_change ism_change; + struct msg_nsm_change nsm_change; + struct msg_lsa_change_notify lsa_change_notify; + } + u; +}; + +#define OSPF_API_MAX_MSG_SIZE (sizeof(struct apimsg) + OSPF_MAX_LSA_SIZE) + +/* ----------------------------------------------------------- + * Prototypes for specific messages + * ----------------------------------------------------------- + */ + +/* For debugging only. */ +extern void api_opaque_lsa_print (struct lsa_header *data); + +/* Messages sent by client */ +extern struct msg *new_msg_register_opaque_type (u_int32_t seqnum, + u_char ltype, u_char otype); +extern struct msg *new_msg_register_event (u_int32_t seqnum, + struct lsa_filter_type *filter); +extern struct msg *new_msg_sync_lsdb (u_int32_t seqnum, + struct lsa_filter_type *filter); +extern struct msg *new_msg_originate_request (u_int32_t seqnum, + struct in_addr ifaddr, + struct in_addr area_id, + struct lsa_header *data); +extern struct msg *new_msg_delete_request (u_int32_t seqnum, + struct in_addr area_id, + u_char lsa_type, + u_char opaque_type, + u_int32_t opaque_id); + +/* Messages sent by OSPF daemon */ +extern struct msg *new_msg_reply (u_int32_t seqnum, u_char rc); + +extern struct msg *new_msg_ready_notify (u_int32_t seqnr, u_char lsa_type, + u_char opaque_type, + struct in_addr addr); + +extern struct msg *new_msg_new_if (u_int32_t seqnr, + struct in_addr ifaddr, + struct in_addr area); + +extern struct msg *new_msg_del_if (u_int32_t seqnr, struct in_addr ifaddr); + +extern struct msg *new_msg_ism_change (u_int32_t seqnr, struct in_addr ifaddr, + struct in_addr area, u_char status); + +extern struct msg *new_msg_nsm_change (u_int32_t seqnr, struct in_addr ifaddr, + struct in_addr nbraddr, + struct in_addr router_id, + u_char status); + +/* msgtype is MSG_LSA_UPDATE_NOTIFY or MSG_LSA_DELETE_NOTIFY */ +extern struct msg *new_msg_lsa_change_notify (u_char msgtype, + u_int32_t seqnum, + struct in_addr ifaddr, + struct in_addr area_id, + u_char is_self_originated, + struct lsa_header *data); + +/* string printing functions */ +extern const char *ospf_api_errname (int errcode); +extern const char *ospf_api_typename (int msgtype); + +#endif /* _OSPF_API_H */ diff --git a/ospfd/ospf_apiserver.c b/ospfd/ospf_apiserver.c new file mode 100644 index 0000000..aac8ef4 --- /dev/null +++ b/ospfd/ospf_apiserver.c @@ -0,0 +1,2641 @@ +/* + * Server side of OSPF API. + * Copyright (C) 2001, 2002 Ralph Keller + * + * 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 SUPPORT_OSPF_API + +#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 "buffer.h" + +#include + +#include "ospfd/ospfd.h" /* for "struct thread_master" */ +#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_api.h" +#include "ospfd/ospf_apiserver.h" + +/* This is an implementation of an API to the OSPF daemon that allows + * external applications to access the OSPF daemon through socket + * connections. The application can use this API to inject its own + * opaque LSAs and flood them to other OSPF daemons. Other OSPF + * daemons then receive these LSAs and inform applications through the + * API by sending a corresponding message. The application can also + * register to receive all LSA types (in addition to opaque types) and + * use this information to reconstruct the OSPF's LSDB. The OSPF + * daemon supports multiple applications concurrently. */ + +/* List of all active connections. */ +struct list *apiserver_list; + +/* ----------------------------------------------------------- + * Functions to lookup interfaces + * ----------------------------------------------------------- + */ + +struct ospf_interface * +ospf_apiserver_if_lookup_by_addr (struct in_addr address) +{ + struct listnode *node, *nnode; + struct ospf_interface *oi; + struct ospf *ospf; + + if (!(ospf = ospf_lookup ())) + return NULL; + + for (ALL_LIST_ELEMENTS (ospf->oiflist, node, nnode, oi)) + if (oi->type != OSPF_IFTYPE_VIRTUALLINK) + if (IPV4_ADDR_SAME (&address, &oi->address->u.prefix4)) + return oi; + + return NULL; +} + +struct ospf_interface * +ospf_apiserver_if_lookup_by_ifp (struct interface *ifp) +{ + struct listnode *node, *nnode; + struct ospf_interface *oi; + struct ospf *ospf; + + if (!(ospf = ospf_lookup ())) + return NULL; + + for (ALL_LIST_ELEMENTS (ospf->oiflist, node, nnode, oi)) + if (oi->ifp == ifp) + return oi; + + return NULL; +} + +/* ----------------------------------------------------------- + * Initialization + * ----------------------------------------------------------- + */ + +unsigned short +ospf_apiserver_getport (void) +{ + struct servent *sp = getservbyname ("ospfapi", "tcp"); + + return sp ? ntohs (sp->s_port) : OSPF_API_SYNC_PORT; +} + +/* Initialize OSPF API module. Invoked from ospf_opaque_init() */ +int +ospf_apiserver_init (void) +{ + int fd; + int rc = -1; + + /* Create new socket for synchronous messages. */ + fd = ospf_apiserver_serv_sock_family (ospf_apiserver_getport (), AF_INET); + + if (fd < 0) + goto out; + + /* Schedule new thread that handles accepted connections. */ + ospf_apiserver_event (OSPF_APISERVER_ACCEPT, fd, NULL); + + /* Initialize list that keeps track of all connections. */ + apiserver_list = list_new (); + + /* Register opaque-independent call back functions. These functions + are invoked on ISM, NSM changes and LSA update and LSA deletes */ + rc = + ospf_register_opaque_functab (0 /* all LSAs */, + 0 /* all opaque types */, + ospf_apiserver_new_if, + ospf_apiserver_del_if, + ospf_apiserver_ism_change, + ospf_apiserver_nsm_change, + NULL, + NULL, + NULL, + NULL, /* ospf_apiserver_show_info */ + NULL, /* originator_func */ + NULL, /* ospf_apiserver_lsa_refresher */ + ospf_apiserver_lsa_update, + ospf_apiserver_lsa_delete); + if (rc != 0) + { + zlog_warn ("ospf_apiserver_init: Failed to register opaque type [0/0]"); + } + + rc = 0; + +out: + return rc; +} + +/* Terminate OSPF API module. */ +void +ospf_apiserver_term (void) +{ + struct ospf_apiserver *apiserv; + + /* Unregister wildcard [0/0] type */ + ospf_delete_opaque_functab (0 /* all LSAs */, + 0 /* all opaque types */); + + /* + * Free all client instances. ospf_apiserver_free removes the node + * from the list, so we examine the head of the list anew each time. + */ + while ( apiserver_list && + (apiserv = listgetdata (listhead (apiserver_list))) != NULL) + ospf_apiserver_free (apiserv); + + /* Free client list itself */ + if (apiserver_list) + list_delete (apiserver_list); + + /* Free wildcard list */ + /* XXX */ +} + +static struct ospf_apiserver * +lookup_apiserver (u_char lsa_type, u_char opaque_type) +{ + struct listnode *n1, *n2; + struct registered_opaque_type *r; + struct ospf_apiserver *apiserv, *found = NULL; + + /* XXX: this approaches O(n**2) */ + for (ALL_LIST_ELEMENTS_RO (apiserver_list, n1, apiserv)) + { + for (ALL_LIST_ELEMENTS_RO (apiserv->opaque_types, n2, r)) + if (r->lsa_type == lsa_type && r->opaque_type == opaque_type) + { + found = apiserv; + goto out; + } + } +out: + return found; +} + +static struct ospf_apiserver * +lookup_apiserver_by_lsa (struct ospf_lsa *lsa) +{ + struct lsa_header *lsah = lsa->data; + struct ospf_apiserver *found = NULL; + + if (IS_OPAQUE_LSA (lsah->type)) + { + found = lookup_apiserver (lsah->type, + GET_OPAQUE_TYPE (ntohl (lsah->id.s_addr))); + } + return found; +} + +/* ----------------------------------------------------------- + * Followings are functions to manage client connections. + * ----------------------------------------------------------- + */ +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", (void *)lsa, + dump_lsa_key (lsa), lsa->lsdb->total); + return 0; +} + +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", (void *)lsa, + dump_lsa_key (lsa), lsa->lsdb->total); + return 0; +} + +/* Allocate new connection structure. */ +struct ospf_apiserver * +ospf_apiserver_new (int fd_sync, int fd_async) +{ + struct ospf_apiserver *new = + XMALLOC (MTYPE_OSPF_APISERVER, sizeof (struct ospf_apiserver)); + + new->filter = + XMALLOC (MTYPE_OSPF_APISERVER_MSGFILTER, sizeof (struct lsa_filter_type)); + + new->fd_sync = fd_sync; + new->fd_async = fd_async; + + /* list of registered opaque types that application uses */ + new->opaque_types = list_new (); + + /* Initialize temporary strage for LSA instances to be refreshed. */ + memset (&new->reserve, 0, sizeof (struct ospf_lsdb)); + ospf_lsdb_init (&new->reserve); + + new->reserve.new_lsa_hook = ospf_apiserver_new_lsa_hook; /* debug */ + new->reserve.del_lsa_hook = ospf_apiserver_del_lsa_hook; /* debug */ + + new->out_sync_fifo = msg_fifo_new (); + new->out_async_fifo = msg_fifo_new (); + new->t_sync_read = NULL; +#ifdef USE_ASYNC_READ + new->t_async_read = NULL; +#endif /* USE_ASYNC_READ */ + new->t_sync_write = NULL; + new->t_async_write = NULL; + + new->filter->typemask = 0; /* filter all LSAs */ + new->filter->origin = ANY_ORIGIN; + new->filter->num_areas = 0; + + return new; +} + +void +ospf_apiserver_event (enum event event, int fd, + struct ospf_apiserver *apiserv) +{ + switch (event) + { + case OSPF_APISERVER_ACCEPT: + (void)thread_add_read (master, ospf_apiserver_accept, apiserv, fd); + break; + case OSPF_APISERVER_SYNC_READ: + apiserv->t_sync_read = + thread_add_read (master, ospf_apiserver_read, apiserv, fd); + break; +#ifdef USE_ASYNC_READ + case OSPF_APISERVER_ASYNC_READ: + apiserv->t_async_read = + thread_add_read (master, ospf_apiserver_read, apiserv, fd); + break; +#endif /* USE_ASYNC_READ */ + case OSPF_APISERVER_SYNC_WRITE: + if (!apiserv->t_sync_write) + { + apiserv->t_sync_write = + thread_add_write (master, ospf_apiserver_sync_write, apiserv, fd); + } + break; + case OSPF_APISERVER_ASYNC_WRITE: + if (!apiserv->t_async_write) + { + apiserv->t_async_write = + thread_add_write (master, ospf_apiserver_async_write, apiserv, fd); + } + break; + } +} + +/* Free instance. First unregister all opaque types used by + application, flush opaque LSAs injected by application + from network and close connection. */ +void +ospf_apiserver_free (struct ospf_apiserver *apiserv) +{ + struct listnode *node; + + /* Cancel read and write threads. */ + if (apiserv->t_sync_read) + { + thread_cancel (apiserv->t_sync_read); + } +#ifdef USE_ASYNC_READ + if (apiserv->t_async_read) + { + thread_cancel (apiserv->t_async_read); + } +#endif /* USE_ASYNC_READ */ + if (apiserv->t_sync_write) + { + thread_cancel (apiserv->t_sync_write); + } + + if (apiserv->t_async_write) + { + thread_cancel (apiserv->t_async_write); + } + + /* Unregister all opaque types that application registered + and flush opaque LSAs if still in LSDB. */ + + while ((node = listhead (apiserv->opaque_types)) != NULL) + { + struct registered_opaque_type *regtype = listgetdata(node); + + ospf_apiserver_unregister_opaque_type (apiserv, regtype->lsa_type, + regtype->opaque_type); + + } + + /* Close connections to OSPFd. */ + if (apiserv->fd_sync > 0) + { + close (apiserv->fd_sync); + } + + if (apiserv->fd_async > 0) + { + close (apiserv->fd_async); + } + + /* Free fifos */ + msg_fifo_free (apiserv->out_sync_fifo); + msg_fifo_free (apiserv->out_async_fifo); + + /* Clear temporary strage for LSA instances to be refreshed. */ + ospf_lsdb_delete_all (&apiserv->reserve); + ospf_lsdb_cleanup (&apiserv->reserve); + + /* Remove from the list of active clients. */ + listnode_delete (apiserver_list, apiserv); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("API: Delete apiserv(%p), total#(%d)", + (void *)apiserv, apiserver_list->count); + + /* And free instance. */ + XFREE (MTYPE_OSPF_APISERVER, apiserv); +} + +int +ospf_apiserver_read (struct thread *thread) +{ + struct ospf_apiserver *apiserv; + struct msg *msg; + int fd; + int rc = -1; + enum event event; + + apiserv = THREAD_ARG (thread); + fd = THREAD_FD (thread); + + if (fd == apiserv->fd_sync) + { + event = OSPF_APISERVER_SYNC_READ; + apiserv->t_sync_read = NULL; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("API: ospf_apiserver_read: Peer: %s/%u", + inet_ntoa (apiserv->peer_sync.sin_addr), + ntohs (apiserv->peer_sync.sin_port)); + } +#ifdef USE_ASYNC_READ + else if (fd == apiserv->fd_async) + { + event = OSPF_APISERVER_ASYNC_READ; + apiserv->t_async_read = NULL; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("API: ospf_apiserver_read: Peer: %s/%u", + inet_ntoa (apiserv->peer_async.sin_addr), + ntohs (apiserv->peer_async.sin_port)); + } +#endif /* USE_ASYNC_READ */ + else + { + zlog_warn ("ospf_apiserver_read: Unknown fd(%d)", fd); + ospf_apiserver_free (apiserv); + goto out; + } + + /* Read message from fd. */ + msg = msg_read (fd); + if (msg == NULL) + { + zlog_warn + ("ospf_apiserver_read: read failed on fd=%d, closing connection", fd); + + /* Perform cleanup. */ + ospf_apiserver_free (apiserv); + goto out; + } + + if (IS_DEBUG_OSPF_EVENT) + msg_print (msg); + + /* Dispatch to corresponding message handler. */ + rc = ospf_apiserver_handle_msg (apiserv, msg); + + /* Prepare for next message, add read thread. */ + ospf_apiserver_event (event, fd, apiserv); + + msg_free (msg); + +out: + return rc; +} + +int +ospf_apiserver_sync_write (struct thread *thread) +{ + struct ospf_apiserver *apiserv; + struct msg *msg; + int fd; + int rc = -1; + + apiserv = THREAD_ARG (thread); + assert (apiserv); + fd = THREAD_FD (thread); + + apiserv->t_sync_write = NULL; + + /* Sanity check */ + if (fd != apiserv->fd_sync) + { + zlog_warn ("ospf_apiserver_sync_write: Unknown fd=%d", fd); + goto out; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("API: ospf_apiserver_sync_write: Peer: %s/%u", + inet_ntoa (apiserv->peer_sync.sin_addr), + ntohs (apiserv->peer_sync.sin_port)); + + /* Check whether there is really a message in the fifo. */ + msg = msg_fifo_pop (apiserv->out_sync_fifo); + if (!msg) + { + zlog_warn ("API: ospf_apiserver_sync_write: No message in Sync-FIFO?"); + return 0; + } + + if (IS_DEBUG_OSPF_EVENT) + msg_print (msg); + + rc = msg_write (fd, msg); + + /* Once a message is dequeued, it should be freed anyway. */ + msg_free (msg); + + if (rc < 0) + { + zlog_warn + ("ospf_apiserver_sync_write: write failed on fd=%d", fd); + goto out; + } + + + /* If more messages are in sync message fifo, schedule write thread. */ + if (msg_fifo_head (apiserv->out_sync_fifo)) + { + ospf_apiserver_event (OSPF_APISERVER_SYNC_WRITE, apiserv->fd_sync, + apiserv); + } + + out: + + if (rc < 0) + { + /* Perform cleanup and disconnect with peer */ + ospf_apiserver_free (apiserv); + } + + return rc; +} + + +int +ospf_apiserver_async_write (struct thread *thread) +{ + struct ospf_apiserver *apiserv; + struct msg *msg; + int fd; + int rc = -1; + + apiserv = THREAD_ARG (thread); + assert (apiserv); + fd = THREAD_FD (thread); + + apiserv->t_async_write = NULL; + + /* Sanity check */ + if (fd != apiserv->fd_async) + { + zlog_warn ("ospf_apiserver_async_write: Unknown fd=%d", fd); + goto out; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("API: ospf_apiserver_async_write: Peer: %s/%u", + inet_ntoa (apiserv->peer_async.sin_addr), + ntohs (apiserv->peer_async.sin_port)); + + /* Check whether there is really a message in the fifo. */ + msg = msg_fifo_pop (apiserv->out_async_fifo); + if (!msg) + { + zlog_warn ("API: ospf_apiserver_async_write: No message in Async-FIFO?"); + return 0; + } + + if (IS_DEBUG_OSPF_EVENT) + msg_print (msg); + + rc = msg_write (fd, msg); + + /* Once a message is dequeued, it should be freed anyway. */ + msg_free (msg); + + if (rc < 0) + { + zlog_warn + ("ospf_apiserver_async_write: write failed on fd=%d", fd); + goto out; + } + + + /* If more messages are in async message fifo, schedule write thread. */ + if (msg_fifo_head (apiserv->out_async_fifo)) + { + ospf_apiserver_event (OSPF_APISERVER_ASYNC_WRITE, apiserv->fd_async, + apiserv); + } + + out: + + if (rc < 0) + { + /* Perform cleanup and disconnect with peer */ + ospf_apiserver_free (apiserv); + } + + return rc; +} + + +int +ospf_apiserver_serv_sock_family (unsigned short port, int family) +{ + union sockunion su; + int accept_sock; + int rc; + + memset (&su, 0, sizeof (union sockunion)); + su.sa.sa_family = family; + + /* Make new socket */ + accept_sock = sockunion_stream_socket (&su); + if (accept_sock < 0) + return accept_sock; + + /* This is a server, so reuse address and port */ + sockopt_reuseaddr (accept_sock); + sockopt_reuseport (accept_sock); + + /* Bind socket to address and given port. */ + rc = sockunion_bind (accept_sock, &su, port, NULL); + if (rc < 0) + { + close (accept_sock); /* Close socket */ + return rc; + } + + /* Listen socket under queue length 3. */ + rc = listen (accept_sock, 3); + if (rc < 0) + { + zlog_warn ("ospf_apiserver_serv_sock_family: listen: %s", + safe_strerror (errno)); + close (accept_sock); /* Close socket */ + return rc; + } + return accept_sock; +} + + +/* Accept connection request from external applications. For each + accepted connection allocate own connection instance. */ +int +ospf_apiserver_accept (struct thread *thread) +{ + int accept_sock; + int new_sync_sock; + int new_async_sock; + union sockunion su; + struct ospf_apiserver *apiserv; + struct sockaddr_in peer_async; + struct sockaddr_in peer_sync; + unsigned int peerlen; + int ret; + + /* THREAD_ARG (thread) is NULL */ + accept_sock = THREAD_FD (thread); + + /* Keep hearing on socket for further connections. */ + ospf_apiserver_event (OSPF_APISERVER_ACCEPT, accept_sock, NULL); + + memset (&su, 0, sizeof (union sockunion)); + /* Accept connection for synchronous messages */ + new_sync_sock = sockunion_accept (accept_sock, &su); + if (new_sync_sock < 0) + { + zlog_warn ("ospf_apiserver_accept: accept: %s", safe_strerror (errno)); + return -1; + } + + /* Get port address and port number of peer to make reverse connection. + The reverse channel uses the port number of the peer port+1. */ + + memset(&peer_sync, 0, sizeof(struct sockaddr_in)); + peerlen = sizeof (struct sockaddr_in); + + ret = getpeername (new_sync_sock, (struct sockaddr *)&peer_sync, &peerlen); + if (ret < 0) + { + zlog_warn ("ospf_apiserver_accept: getpeername: %s", safe_strerror (errno)); + close (new_sync_sock); + return -1; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("API: ospf_apiserver_accept: New peer: %s/%u", + inet_ntoa (peer_sync.sin_addr), ntohs (peer_sync.sin_port)); + + /* Create new socket for asynchronous messages. */ + peer_async = peer_sync; + peer_async.sin_port = htons(ntohs(peer_sync.sin_port) + 1); + + /* Check if remote port number to make reverse connection is valid one. */ + if (ntohs (peer_async.sin_port) == ospf_apiserver_getport ()) + { + zlog_warn ("API: ospf_apiserver_accept: Peer(%s/%u): Invalid async port number?", + inet_ntoa (peer_async.sin_addr), ntohs (peer_async.sin_port)); + close (new_sync_sock); + return -1; + } + + new_async_sock = socket (AF_INET, SOCK_STREAM, 0); + if (new_async_sock < 0) + { + zlog_warn ("ospf_apiserver_accept: socket: %s", safe_strerror (errno)); + close (new_sync_sock); + return -1; + } + + ret = connect (new_async_sock, (struct sockaddr *) &peer_async, + sizeof (struct sockaddr_in)); + + if (ret < 0) + { + zlog_warn ("ospf_apiserver_accept: connect: %s", safe_strerror (errno)); + close (new_sync_sock); + close (new_async_sock); + return -1; + } + +#ifdef USE_ASYNC_READ +#else /* USE_ASYNC_READ */ + /* Make the asynchronous channel write-only. */ + ret = shutdown (new_async_sock, SHUT_RD); + if (ret < 0) + { + zlog_warn ("ospf_apiserver_accept: shutdown: %s", safe_strerror (errno)); + close (new_sync_sock); + close (new_async_sock); + return -1; + } +#endif /* USE_ASYNC_READ */ + + /* Allocate new server-side connection structure */ + apiserv = ospf_apiserver_new (new_sync_sock, new_async_sock); + + /* Add to active connection list */ + listnode_add (apiserver_list, apiserv); + apiserv->peer_sync = peer_sync; + apiserv->peer_async = peer_async; + + /* And add read threads for new connection */ + ospf_apiserver_event (OSPF_APISERVER_SYNC_READ, new_sync_sock, apiserv); +#ifdef USE_ASYNC_READ + ospf_apiserver_event (OSPF_APISERVER_ASYNC_READ, new_async_sock, apiserv); +#endif /* USE_ASYNC_READ */ + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("API: New apiserv(%p), total#(%d)", + (void *)apiserv, apiserver_list->count); + + return 0; +} + + +/* ----------------------------------------------------------- + * Send reply with return code to client application + * ----------------------------------------------------------- + */ + +static int +ospf_apiserver_send_msg (struct ospf_apiserver *apiserv, struct msg *msg) +{ + struct msg_fifo *fifo; + struct msg *msg2; + enum event event; + int fd; + + switch (msg->hdr.msgtype) + { + case MSG_REPLY: + fifo = apiserv->out_sync_fifo; + fd = apiserv->fd_sync; + event = OSPF_APISERVER_SYNC_WRITE; + break; + case MSG_READY_NOTIFY: + case MSG_LSA_UPDATE_NOTIFY: + case MSG_LSA_DELETE_NOTIFY: + case MSG_NEW_IF: + case MSG_DEL_IF: + case MSG_ISM_CHANGE: + case MSG_NSM_CHANGE: + fifo = apiserv->out_async_fifo; + fd = apiserv->fd_async; + event = OSPF_APISERVER_ASYNC_WRITE; + break; + default: + zlog_warn ("ospf_apiserver_send_msg: Unknown message type %d", + msg->hdr.msgtype); + return -1; + } + + /* Make a copy of the message and put in the fifo. Once the fifo + gets drained by the write thread, the message will be freed. */ + /* NB: Given "msg" is untouched in this function. */ + msg2 = msg_dup (msg); + + /* Enqueue message into corresponding fifo queue */ + msg_fifo_push (fifo, msg2); + + /* Schedule write thread */ + ospf_apiserver_event (event, fd, apiserv); + return 0; +} + +int +ospf_apiserver_send_reply (struct ospf_apiserver *apiserv, u_int32_t seqnr, + u_char rc) +{ + struct msg *msg = new_msg_reply (seqnr, rc); + int ret; + + if (!msg) + { + zlog_warn ("ospf_apiserver_send_reply: msg_new failed"); +#ifdef NOTYET + /* Cannot allocate new message. What should we do? */ + ospf_apiserver_free (apiserv); +#endif + return -1; + } + + ret = ospf_apiserver_send_msg (apiserv, msg); + msg_free (msg); + return ret; +} + + +/* ----------------------------------------------------------- + * Generic message dispatching handler function + * ----------------------------------------------------------- + */ + +int +ospf_apiserver_handle_msg (struct ospf_apiserver *apiserv, struct msg *msg) +{ + int rc; + + /* Call corresponding message handler function. */ + switch (msg->hdr.msgtype) + { + case MSG_REGISTER_OPAQUETYPE: + rc = ospf_apiserver_handle_register_opaque_type (apiserv, msg); + break; + case MSG_UNREGISTER_OPAQUETYPE: + rc = ospf_apiserver_handle_unregister_opaque_type (apiserv, msg); + break; + case MSG_REGISTER_EVENT: + rc = ospf_apiserver_handle_register_event (apiserv, msg); + break; + case MSG_SYNC_LSDB: + rc = ospf_apiserver_handle_sync_lsdb (apiserv, msg); + break; + case MSG_ORIGINATE_REQUEST: + rc = ospf_apiserver_handle_originate_request (apiserv, msg); + break; + case MSG_DELETE_REQUEST: + rc = ospf_apiserver_handle_delete_request (apiserv, msg); + break; + default: + zlog_warn ("ospf_apiserver_handle_msg: Unknown message type: %d", + msg->hdr.msgtype); + rc = -1; + } + return rc; +} + + +/* ----------------------------------------------------------- + * Following are functions for opaque type registration + * ----------------------------------------------------------- + */ + +int +ospf_apiserver_register_opaque_type (struct ospf_apiserver *apiserv, + u_char lsa_type, u_char opaque_type) +{ + struct registered_opaque_type *regtype; + int (*originator_func) (void *arg); + int rc; + + switch (lsa_type) + { + case OSPF_OPAQUE_LINK_LSA: + originator_func = ospf_apiserver_lsa9_originator; + break; + case OSPF_OPAQUE_AREA_LSA: + originator_func = ospf_apiserver_lsa10_originator; + break; + case OSPF_OPAQUE_AS_LSA: + originator_func = ospf_apiserver_lsa11_originator; + break; + default: + zlog_warn ("ospf_apiserver_register_opaque_type: lsa_type(%d)", + lsa_type); + return OSPF_API_ILLEGALLSATYPE; + } + + + /* Register opaque function table */ + /* NB: Duplicated registration will be detected inside the function. */ + rc = + ospf_register_opaque_functab (lsa_type, opaque_type, + NULL, /* ospf_apiserver_new_if */ + NULL, /* ospf_apiserver_del_if */ + NULL, /* ospf_apiserver_ism_change */ + NULL, /* ospf_apiserver_nsm_change */ + NULL, + NULL, + NULL, + ospf_apiserver_show_info, + originator_func, + ospf_apiserver_lsa_refresher, + NULL, /* ospf_apiserver_lsa_update */ + NULL /* ospf_apiserver_lsa_delete */); + + if (rc != 0) + { + zlog_warn ("Failed to register opaque type [%d/%d]", + lsa_type, opaque_type); + return OSPF_API_OPAQUETYPEINUSE; + } + + /* Remember the opaque type that application registers so when + connection shuts down, we can flush all LSAs of this opaque + type. */ + + regtype = + XCALLOC (MTYPE_OSPF_APISERVER, sizeof (struct registered_opaque_type)); + regtype->lsa_type = lsa_type; + regtype->opaque_type = opaque_type; + + /* Add to list of registered opaque types */ + listnode_add (apiserv->opaque_types, regtype); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("API: Add LSA-type(%d)/Opaque-type(%d) into" + " apiserv(%p), total#(%d)", + lsa_type, opaque_type, (void *)apiserv, + listcount (apiserv->opaque_types)); + + return 0; +} + +int +ospf_apiserver_unregister_opaque_type (struct ospf_apiserver *apiserv, + u_char lsa_type, u_char opaque_type) +{ + struct listnode *node, *nnode; + struct registered_opaque_type *regtype; + + for (ALL_LIST_ELEMENTS (apiserv->opaque_types, node, nnode, regtype)) + { + /* Check if we really registered this opaque type */ + if (regtype->lsa_type == lsa_type && + regtype->opaque_type == opaque_type) + { + + /* Yes, we registered this opaque type. Flush + all existing opaque LSAs of this type */ + + ospf_apiserver_flush_opaque_lsa (apiserv, lsa_type, opaque_type); + ospf_delete_opaque_functab (lsa_type, opaque_type); + + /* Remove from list of registered opaque types */ + listnode_delete (apiserv->opaque_types, regtype); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("API: Del LSA-type(%d)/Opaque-type(%d)" + " from apiserv(%p), total#(%d)", + lsa_type, opaque_type, (void *)apiserv, + listcount (apiserv->opaque_types)); + + return 0; + } + } + + /* Opaque type is not registered */ + zlog_warn ("Failed to unregister opaque type [%d/%d]", + lsa_type, opaque_type); + return OSPF_API_OPAQUETYPENOTREGISTERED; +} + + +static int +apiserver_is_opaque_type_registered (struct ospf_apiserver *apiserv, + u_char lsa_type, u_char opaque_type) +{ + struct listnode *node, *nnode; + struct registered_opaque_type *regtype; + + /* XXX: how many types are there? if few, why not just a bitmap? */ + for (ALL_LIST_ELEMENTS (apiserv->opaque_types, node, nnode, regtype)) + { + /* Check if we really registered this opaque type */ + if (regtype->lsa_type == lsa_type && + regtype->opaque_type == opaque_type) + { + /* Yes registered */ + return 1; + } + } + /* Not registered */ + return 0; +} + +int +ospf_apiserver_handle_register_opaque_type (struct ospf_apiserver *apiserv, + struct msg *msg) +{ + struct msg_register_opaque_type *rmsg; + u_char lsa_type; + u_char opaque_type; + int rc = 0; + + /* Extract parameters from register opaque type message */ + rmsg = (struct msg_register_opaque_type *) STREAM_DATA (msg->s); + + lsa_type = rmsg->lsatype; + opaque_type = rmsg->opaquetype; + + rc = ospf_apiserver_register_opaque_type (apiserv, lsa_type, opaque_type); + + /* Send a reply back to client including return code */ + rc = ospf_apiserver_send_reply (apiserv, ntohl (msg->hdr.msgseq), rc); + if (rc < 0) + goto out; + + /* Now inform application about opaque types that are ready */ + switch (lsa_type) + { + case OSPF_OPAQUE_LINK_LSA: + ospf_apiserver_notify_ready_type9 (apiserv); + break; + case OSPF_OPAQUE_AREA_LSA: + ospf_apiserver_notify_ready_type10 (apiserv); + break; + case OSPF_OPAQUE_AS_LSA: + ospf_apiserver_notify_ready_type11 (apiserv); + break; + } +out: + return rc; +} + + +/* Notify specific client about all opaque types 9 that are ready. */ +void +ospf_apiserver_notify_ready_type9 (struct ospf_apiserver *apiserv) +{ + struct listnode *node, *nnode; + struct listnode *node2, *nnode2; + struct ospf *ospf; + struct ospf_interface *oi; + struct registered_opaque_type *r; + + ospf = ospf_lookup (); + + for (ALL_LIST_ELEMENTS (ospf->oiflist, node, nnode, oi)) + { + /* Check if this interface is indeed ready for type 9 */ + if (!ospf_apiserver_is_ready_type9 (oi)) + continue; + + /* Check for registered opaque type 9 types */ + /* XXX: loop-de-loop - optimise me */ + for (ALL_LIST_ELEMENTS (apiserv->opaque_types, node2, nnode2, r)) + { + struct msg *msg; + + if (r->lsa_type == OSPF_OPAQUE_LINK_LSA) + { + + /* Yes, this opaque type is ready */ + msg = new_msg_ready_notify (0, OSPF_OPAQUE_LINK_LSA, + r->opaque_type, + oi->address->u.prefix4); + if (!msg) + { + zlog_warn ("apiserver_notify_ready_type9: msg_new failed"); +#ifdef NOTYET + /* Cannot allocate new message. What should we do? */ + ospf_apiserver_free (apiserv); +#endif + goto out; + } + ospf_apiserver_send_msg (apiserv, msg); + msg_free (msg); + } + } + } + +out: + return; +} + + +/* Notify specific client about all opaque types 10 that are ready. */ +void +ospf_apiserver_notify_ready_type10 (struct ospf_apiserver *apiserv) +{ + struct listnode *node, *nnode; + struct listnode *node2, *nnode2; + struct ospf *ospf; + struct ospf_area *area; + + ospf = ospf_lookup (); + + for (ALL_LIST_ELEMENTS (ospf->areas, node, nnode, area)) + { + struct registered_opaque_type *r; + + if (!ospf_apiserver_is_ready_type10 (area)) + { + continue; + } + + /* Check for registered opaque type 10 types */ + /* XXX: loop in loop - optimise me */ + for (ALL_LIST_ELEMENTS (apiserv->opaque_types, node2, nnode2, r)) + { + struct msg *msg; + + if (r->lsa_type == OSPF_OPAQUE_AREA_LSA) + { + /* Yes, this opaque type is ready */ + msg = + new_msg_ready_notify (0, OSPF_OPAQUE_AREA_LSA, + r->opaque_type, area->area_id); + if (!msg) + { + zlog_warn ("apiserver_notify_ready_type10: msg_new failed"); +#ifdef NOTYET + /* Cannot allocate new message. What should we do? */ + ospf_apiserver_free (apiserv); +#endif + goto out; + } + ospf_apiserver_send_msg (apiserv, msg); + msg_free (msg); + } + } + } + +out: + return; +} + +/* Notify specific client about all opaque types 11 that are ready */ +void +ospf_apiserver_notify_ready_type11 (struct ospf_apiserver *apiserv) +{ + struct listnode *node, *nnode; + struct ospf *ospf; + struct registered_opaque_type *r; + + ospf = ospf_lookup (); + + /* Can type 11 be originated? */ + if (!ospf_apiserver_is_ready_type11 (ospf)) + goto out; + + /* Check for registered opaque type 11 types */ + for (ALL_LIST_ELEMENTS (apiserv->opaque_types, node, nnode, r)) + { + struct msg *msg; + struct in_addr noarea_id = { .s_addr = 0L }; + + if (r->lsa_type == OSPF_OPAQUE_AS_LSA) + { + /* Yes, this opaque type is ready */ + msg = new_msg_ready_notify (0, OSPF_OPAQUE_AS_LSA, + r->opaque_type, noarea_id); + + if (!msg) + { + zlog_warn ("apiserver_notify_ready_type11: msg_new failed"); +#ifdef NOTYET + /* Cannot allocate new message. What should we do? */ + ospf_apiserver_free (apiserv); +#endif + goto out; + } + ospf_apiserver_send_msg (apiserv, msg); + msg_free (msg); + } + } + +out: + return; +} + +int +ospf_apiserver_handle_unregister_opaque_type (struct ospf_apiserver *apiserv, + struct msg *msg) +{ + struct msg_unregister_opaque_type *umsg; + u_char ltype; + u_char otype; + int rc = 0; + + /* Extract parameters from unregister opaque type message */ + umsg = (struct msg_unregister_opaque_type *) STREAM_DATA (msg->s); + + ltype = umsg->lsatype; + otype = umsg->opaquetype; + + rc = ospf_apiserver_unregister_opaque_type (apiserv, ltype, otype); + + /* Send a reply back to client including return code */ + rc = ospf_apiserver_send_reply (apiserv, ntohl (msg->hdr.msgseq), rc); + + return rc; +} + + +/* ----------------------------------------------------------- + * Following are functions for event (filter) registration. + * ----------------------------------------------------------- + */ +int +ospf_apiserver_handle_register_event (struct ospf_apiserver *apiserv, + struct msg *msg) +{ + struct msg_register_event *rmsg; + int rc; + u_int32_t seqnum; + + rmsg = (struct msg_register_event *) STREAM_DATA (msg->s); + + /* Get request sequence number */ + seqnum = msg_get_seq (msg); + + /* Free existing filter in apiserv. */ + XFREE (MTYPE_OSPF_APISERVER_MSGFILTER, apiserv->filter); + /* Alloc new space for filter. */ + + apiserv->filter = XMALLOC (MTYPE_OSPF_APISERVER_MSGFILTER, + ntohs (msg->hdr.msglen)); + if (apiserv->filter) + { + /* copy it over. */ + memcpy (apiserv->filter, &rmsg->filter, ntohs (msg->hdr.msglen)); + rc = OSPF_API_OK; + } + else + { + rc = OSPF_API_NOMEMORY; + } + /* Send a reply back to client with return code */ + rc = ospf_apiserver_send_reply (apiserv, seqnum, rc); + return rc; +} + + +/* ----------------------------------------------------------- + * Followings are functions for LSDB synchronization. + * ----------------------------------------------------------- + */ + +static int +apiserver_sync_callback (struct ospf_lsa *lsa, void *p_arg, int int_arg) +{ + struct ospf_apiserver *apiserv; + int seqnum; + struct msg *msg; + struct param_t + { + struct ospf_apiserver *apiserv; + struct lsa_filter_type *filter; + } + *param; + int rc = -1; + + /* Sanity check */ + assert (lsa->data); + assert (p_arg); + + param = (struct param_t *) p_arg; + apiserv = param->apiserv; + seqnum = (u_int32_t) int_arg; + + /* Check origin in filter. */ + if ((param->filter->origin == ANY_ORIGIN) || + (param->filter->origin == (lsa->flags & OSPF_LSA_SELF))) + { + + /* Default area for AS-External and Opaque11 LSAs */ + struct in_addr area_id = { .s_addr = 0L }; + + /* Default interface for non Opaque9 LSAs */ + struct in_addr ifaddr = { .s_addr = 0L }; + + if (lsa->area) + { + area_id = lsa->area->area_id; + } + if (lsa->data->type == OSPF_OPAQUE_LINK_LSA) + { + ifaddr = lsa->oi->address->u.prefix4; + } + + msg = new_msg_lsa_change_notify (MSG_LSA_UPDATE_NOTIFY, + seqnum, + ifaddr, area_id, + lsa->flags & OSPF_LSA_SELF, lsa->data); + if (!msg) + { + zlog_warn ("apiserver_sync_callback: new_msg_update failed"); +#ifdef NOTYET + /* Cannot allocate new message. What should we do? */ +/* ospf_apiserver_free (apiserv);*//* Do nothing here XXX */ +#endif + goto out; + } + + /* Send LSA */ + ospf_apiserver_send_msg (apiserv, msg); + msg_free (msg); + } + rc = 0; + +out: + return rc; +} + +int +ospf_apiserver_handle_sync_lsdb (struct ospf_apiserver *apiserv, + struct msg *msg) +{ + struct listnode *node, *nnode; + u_int32_t seqnum; + int rc = 0; + struct msg_sync_lsdb *smsg; + struct ospf_apiserver_param_t + { + struct ospf_apiserver *apiserv; + struct lsa_filter_type *filter; + } param; + u_int16_t mask; + struct route_node *rn; + struct ospf_lsa *lsa; + struct ospf *ospf; + struct ospf_area *area; + + ospf = ospf_lookup (); + + /* Get request sequence number */ + seqnum = msg_get_seq (msg); + /* Set sync msg. */ + smsg = (struct msg_sync_lsdb *) STREAM_DATA (msg->s); + + /* Set parameter struct. */ + param.apiserv = apiserv; + param.filter = &smsg->filter; + + /* Remember mask. */ + mask = ntohs (smsg->filter.typemask); + + /* Iterate over all areas. */ + for (ALL_LIST_ELEMENTS (ospf->areas, node, nnode, area)) + { + int i; + u_int32_t *area_id = NULL; + + /* Compare area_id with area_ids in sync request. */ + if ((i = smsg->filter.num_areas) > 0) + { + /* Let area_id point to the list of area IDs, + * which is at the end of smsg->filter. */ + area_id = (u_int32_t *) (&smsg->filter + 1); + while (i) + { + if (*area_id == area->area_id.s_addr) + { + break; + } + i--; + area_id++; + } + } + else + { + i = 1; + } + + /* If area was found, then i>0 here. */ + if (i) + { + /* Check msg type. */ + if (mask & Power2[OSPF_ROUTER_LSA]) + LSDB_LOOP (ROUTER_LSDB (area), rn, lsa) + apiserver_sync_callback(lsa, (void *) ¶m, seqnum); + if (mask & Power2[OSPF_NETWORK_LSA]) + LSDB_LOOP (NETWORK_LSDB (area), rn, lsa) + apiserver_sync_callback(lsa, (void *) ¶m, seqnum); + if (mask & Power2[OSPF_SUMMARY_LSA]) + LSDB_LOOP (SUMMARY_LSDB (area), rn, lsa) + apiserver_sync_callback(lsa, (void *) ¶m, seqnum); + if (mask & Power2[OSPF_ASBR_SUMMARY_LSA]) + LSDB_LOOP (ASBR_SUMMARY_LSDB (area), rn, lsa) + apiserver_sync_callback(lsa, (void *) ¶m, seqnum); + if (mask & Power2[OSPF_OPAQUE_LINK_LSA]) + LSDB_LOOP (OPAQUE_LINK_LSDB (area), rn, lsa) + apiserver_sync_callback(lsa, (void *) ¶m, seqnum); + if (mask & Power2[OSPF_OPAQUE_AREA_LSA]) + LSDB_LOOP (OPAQUE_AREA_LSDB (area), rn, lsa) + apiserver_sync_callback(lsa, (void *) ¶m, seqnum); + } + } + + /* For AS-external LSAs */ + if (ospf->lsdb) + { + if (mask & Power2[OSPF_AS_EXTERNAL_LSA]) + LSDB_LOOP (EXTERNAL_LSDB (ospf), rn, lsa) + apiserver_sync_callback(lsa, (void *) ¶m, seqnum); + } + + /* For AS-external opaque LSAs */ + if (ospf->lsdb) + { + if (mask & Power2[OSPF_OPAQUE_AS_LSA]) + LSDB_LOOP (OPAQUE_AS_LSDB (ospf), rn, lsa) + apiserver_sync_callback(lsa, (void *) ¶m, seqnum); + } + + /* Send a reply back to client with return code */ + rc = ospf_apiserver_send_reply (apiserv, seqnum, rc); + return rc; +} + + +/* ----------------------------------------------------------- + * Followings are functions to originate or update LSA + * from an application. + * ----------------------------------------------------------- + */ + +/* Create a new internal opaque LSA by taking prototype and filling in + missing fields such as age, sequence number, advertising router, + checksum and so on. The interface parameter is used for type 9 + LSAs, area parameter for type 10. Type 11 LSAs do neither need area + nor interface. */ + +struct ospf_lsa * +ospf_apiserver_opaque_lsa_new (struct ospf_area *area, + struct ospf_interface *oi, + struct lsa_header *protolsa) +{ + struct stream *s; + struct lsa_header *newlsa; + struct ospf_lsa *new = NULL; + u_char options = 0x0; + u_int16_t length; + + struct ospf *ospf; + + ospf = ospf_lookup(); + assert(ospf); + + /* Create a stream for internal opaque LSA */ + if ((s = stream_new (OSPF_MAX_LSA_SIZE)) == NULL) + { + zlog_warn ("ospf_apiserver_opaque_lsa_new: stream_new failed"); + return NULL; + } + + newlsa = (struct lsa_header *) STREAM_DATA (s); + + /* XXX If this is a link-local LSA or an AS-external LSA, how do we + have to set options? */ + + if (area) + { + options = LSA_OPTIONS_GET (area); + options |= LSA_OPTIONS_NSSA_GET (area); + } + + options |= OSPF_OPTION_O; /* Don't forget to set option bit */ + + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + { + zlog_debug ("LSA[Type%d:%s]: Creating an Opaque-LSA instance", + protolsa->type, inet_ntoa (protolsa->id)); + } + + /* Set opaque-LSA header fields. */ + lsa_header_set (s, options, protolsa->type, protolsa->id, + ospf->router_id); + + /* Set opaque-LSA body fields. */ + stream_put (s, ((u_char *) protolsa) + sizeof (struct lsa_header), + ntohs (protolsa->length) - sizeof (struct lsa_header)); + + /* Determine length of LSA. */ + length = stream_get_endp (s); + newlsa->length = htons (length); + + /* Create OSPF LSA. */ + if ((new = ospf_lsa_new ()) == NULL) + { + zlog_warn ("ospf_apiserver_opaque_lsa_new: ospf_lsa_new() ?"); + stream_free (s); + return NULL; + } + + if ((new->data = ospf_lsa_data_new (length)) == NULL) + { + zlog_warn ("ospf_apiserver_opaque_lsa_new: ospf_lsa_data_new() ?"); + ospf_lsa_unlock (&new); + stream_free (s); + return NULL; + } + + new->area = area; + new->oi = oi; + + SET_FLAG (new->flags, OSPF_LSA_SELF); + memcpy (new->data, newlsa, length); + stream_free (s); + + return new; +} + + +int +ospf_apiserver_is_ready_type9 (struct ospf_interface *oi) +{ + /* Type 9 opaque LSA can be originated if there is at least one + active opaque-capable neighbor attached to the outgoing + interface. */ + + return (ospf_nbr_count_opaque_capable (oi) > 0); +} + +int +ospf_apiserver_is_ready_type10 (struct ospf_area *area) +{ + /* Type 10 opaque LSA can be originated if there is at least one + interface belonging to the area that has an active opaque-capable + neighbor. */ + struct listnode *node, *nnode; + struct ospf_interface *oi; + + for (ALL_LIST_ELEMENTS (area->oiflist, node, nnode, oi)) + /* Is there an active neighbor attached to this interface? */ + if (ospf_apiserver_is_ready_type9 (oi)) + return 1; + + /* No active neighbor in area */ + return 0; +} + +int +ospf_apiserver_is_ready_type11 (struct ospf *ospf) +{ + /* Type 11 opaque LSA can be originated if there is at least one interface + that has an active opaque-capable neighbor. */ + struct listnode *node, *nnode; + struct ospf_interface *oi; + + for (ALL_LIST_ELEMENTS (ospf->oiflist, node, nnode, oi)) + /* Is there an active neighbor attached to this interface? */ + if (ospf_apiserver_is_ready_type9 (oi)) + return 1; + + /* No active neighbor at all */ + return 0; +} + + +int +ospf_apiserver_handle_originate_request (struct ospf_apiserver *apiserv, + struct msg *msg) +{ + struct msg_originate_request *omsg; + struct lsa_header *data; + struct ospf_lsa *new; + struct ospf_lsa *old; + struct ospf_area *area = NULL; + struct ospf_interface *oi = NULL; + struct ospf_lsdb *lsdb = NULL; + struct ospf *ospf; + int lsa_type, opaque_type; + int ready = 0; + int rc = 0; + + ospf = ospf_lookup(); + + /* Extract opaque LSA data from message */ + omsg = (struct msg_originate_request *) STREAM_DATA (msg->s); + data = &omsg->data; + + /* Determine interface for type9 or area for type10 LSAs. */ + switch (data->type) + { + case OSPF_OPAQUE_LINK_LSA: + oi = ospf_apiserver_if_lookup_by_addr (omsg->ifaddr); + if (!oi) + { + zlog_warn ("apiserver_originate: unknown interface %s", + inet_ntoa (omsg->ifaddr)); + rc = OSPF_API_NOSUCHINTERFACE; + goto out; + } + area = oi->area; + lsdb = area->lsdb; + break; + case OSPF_OPAQUE_AREA_LSA: + area = ospf_area_lookup_by_area_id (ospf, omsg->area_id); + if (!area) + { + zlog_warn ("apiserver_originate: unknown area %s", + inet_ntoa (omsg->area_id)); + rc = OSPF_API_NOSUCHAREA; + goto out; + } + lsdb = area->lsdb; + break; + case OSPF_OPAQUE_AS_LSA: + lsdb = ospf->lsdb; + break; + default: + /* We can only handle opaque types here */ + zlog_warn ("apiserver_originate: Cannot originate non-opaque LSA type %d", + data->type); + rc = OSPF_API_ILLEGALLSATYPE; + goto out; + } + + /* Check if we registered this opaque type */ + lsa_type = data->type; + opaque_type = GET_OPAQUE_TYPE (ntohl (data->id.s_addr)); + + if (!apiserver_is_opaque_type_registered (apiserv, lsa_type, opaque_type)) + { + zlog_warn ("apiserver_originate: LSA-type(%d)/Opaque-type(%d): Not registered", lsa_type, opaque_type); + rc = OSPF_API_OPAQUETYPENOTREGISTERED; + goto out; + } + + /* Make sure that the neighbors are ready before we can originate */ + switch (data->type) + { + case OSPF_OPAQUE_LINK_LSA: + ready = ospf_apiserver_is_ready_type9 (oi); + break; + case OSPF_OPAQUE_AREA_LSA: + ready = ospf_apiserver_is_ready_type10 (area); + break; + case OSPF_OPAQUE_AS_LSA: + ready = ospf_apiserver_is_ready_type11 (ospf); + break; + default: + break; + } + + if (!ready) + { + zlog_warn ("Neighbors not ready to originate type %d", data->type); + rc = OSPF_API_NOTREADY; + goto out; + } + + /* Create OSPF's internal opaque LSA representation */ + new = ospf_apiserver_opaque_lsa_new (area, oi, data); + if (!new) + { + rc = OSPF_API_NOMEMORY; /* XXX */ + goto out; + } + + /* Determine if LSA is new or an update for an existing one. */ + old = ospf_lsdb_lookup (lsdb, new); + + if (!old) + { + /* New LSA install in LSDB. */ + rc = ospf_apiserver_originate1 (new); + } + else + { + /* + * Keep the new LSA instance in the "waiting place" until the next + * refresh timing. If several LSA update requests for the same LSID + * have issued by peer, the last one takes effect. + */ + new->lsdb = &apiserv->reserve; + ospf_lsdb_add (&apiserv->reserve, new); + + /* Kick the scheduler function. */ + ospf_opaque_lsa_refresh_schedule (old); + } + +out: + + /* Send a reply back to client with return code */ + rc = ospf_apiserver_send_reply (apiserv, ntohl (msg->hdr.msgseq), rc); + return rc; +} + + +/* ----------------------------------------------------------- + * Flood an LSA within its flooding scope. + * ----------------------------------------------------------- + */ + +/* XXX We can probably use ospf_flood_through instead of this function + but then we need the neighbor parameter. If we set nbr to + NULL then ospf_flood_through crashes due to dereferencing NULL. */ + +void +ospf_apiserver_flood_opaque_lsa (struct ospf_lsa *lsa) +{ + assert (lsa); + + switch (lsa->data->type) + { + case OSPF_OPAQUE_LINK_LSA: + /* Increment counters? XXX */ + + /* Flood LSA through local network. */ + ospf_flood_through_area (lsa->area, NULL /*nbr */ , lsa); + break; + case OSPF_OPAQUE_AREA_LSA: + /* Update LSA origination count. */ + assert (lsa->area); + lsa->area->ospf->lsa_originate_count++; + + /* Flood LSA through area. */ + ospf_flood_through_area (lsa->area, NULL /*nbr */ , lsa); + break; + case OSPF_OPAQUE_AS_LSA: + { + struct ospf *ospf; + + ospf = ospf_lookup(); + assert(ospf); + + /* Increment counters? XXX */ + + /* Flood LSA through AS. */ + ospf_flood_through_as (ospf, NULL /*nbr */ , lsa); + break; + } + } +} + +int +ospf_apiserver_originate1 (struct ospf_lsa *lsa) +{ + struct ospf *ospf; + + ospf = ospf_lookup(); + assert(ospf); + + /* Install this LSA into LSDB. */ + if (ospf_lsa_install (ospf, lsa->oi, lsa) == NULL) + { + zlog_warn ("ospf_apiserver_originate1: ospf_lsa_install failed"); + return -1; + } + + /* Flood LSA within scope */ + +#ifdef NOTYET + /* + * NB: Modified version of "ospf_flood_though ()" accepts NULL "inbr" + * parameter, and thus it does not cause SIGSEGV error. + */ + ospf_flood_through (NULL /*nbr */ , lsa); +#else /* NOTYET */ + + ospf_apiserver_flood_opaque_lsa (lsa); +#endif /* NOTYET */ + + return 0; +} + + +/* Opaque LSAs of type 9 on a specific interface can now be + originated. Tell clients that registered type 9. */ +int +ospf_apiserver_lsa9_originator (void *arg) +{ + struct ospf_interface *oi; + + oi = (struct ospf_interface *) arg; + if (listcount (apiserver_list) > 0) { + ospf_apiserver_clients_notify_ready_type9 (oi); + } + return 0; +} + +int +ospf_apiserver_lsa10_originator (void *arg) +{ + struct ospf_area *area; + + area = (struct ospf_area *) arg; + if (listcount (apiserver_list) > 0) { + ospf_apiserver_clients_notify_ready_type10 (area); + } + return 0; +} + +int +ospf_apiserver_lsa11_originator (void *arg) +{ + struct ospf *ospf; + + ospf = (struct ospf *) arg; + if (listcount (apiserver_list) > 0) { + ospf_apiserver_clients_notify_ready_type11 (ospf); + } + return 0; +} + + +/* Periodically refresh opaque LSAs so that they do not expire in + other routers. */ +struct ospf_lsa * +ospf_apiserver_lsa_refresher (struct ospf_lsa *lsa) +{ + struct ospf_apiserver *apiserv; + struct ospf_lsa *new = NULL; + struct ospf * ospf; + + ospf = ospf_lookup(); + assert(ospf); + + apiserv = lookup_apiserver_by_lsa (lsa); + if (!apiserv) + { + zlog_warn ("ospf_apiserver_lsa_refresher: LSA[%s]: No apiserver?", dump_lsa_key (lsa)); + lsa->data->ls_age = htons (OSPF_LSA_MAXAGE); /* Flush it anyway. */ + } + + if (IS_LSA_MAXAGE (lsa)) + { + ospf_opaque_lsa_flush_schedule (lsa); + goto out; + } + + /* Check if updated version of LSA instance has already prepared. */ + new = ospf_lsdb_lookup (&apiserv->reserve, lsa); + if (!new) + { + /* This is a periodic refresh, driven by core OSPF mechanism. */ + new = ospf_apiserver_opaque_lsa_new (lsa->area, lsa->oi, lsa->data); + if (!new) + { + zlog_warn ("ospf_apiserver_lsa_refresher: Cannot create a new LSA?"); + goto out; + } + } + else + { + /* This is a forcible refresh, requested by OSPF-API client. */ + ospf_lsdb_delete (&apiserv->reserve, new); + new->lsdb = NULL; + } + + /* Increment sequence number */ + new->data->ls_seqnum = lsa_seqnum_increment (lsa); + + /* New LSA is in same area. */ + new->area = lsa->area; + SET_FLAG (new->flags, OSPF_LSA_SELF); + + /* Install LSA into LSDB. */ + if (ospf_lsa_install (ospf, new->oi, new) == NULL) + { + zlog_warn ("ospf_apiserver_lsa_refresher: ospf_lsa_install failed"); + ospf_lsa_unlock (&new); + goto out; + } + + /* Flood updated LSA through interface, area or AS */ + +#ifdef NOTYET + ospf_flood_through (NULL /*nbr */ , new); +#endif /* NOTYET */ + ospf_apiserver_flood_opaque_lsa (new); + + /* Debug logging. */ + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + { + zlog_debug ("LSA[Type%d:%s]: Refresh Opaque LSA", + new->data->type, inet_ntoa (new->data->id)); + ospf_lsa_header_dump (new->data); + } + +out: + return new; +} + + +/* ----------------------------------------------------------- + * Followings are functions to delete LSAs + * ----------------------------------------------------------- + */ + +int +ospf_apiserver_handle_delete_request (struct ospf_apiserver *apiserv, + struct msg *msg) +{ + struct msg_delete_request *dmsg; + struct ospf_lsa *old; + struct ospf_area *area = NULL; + struct in_addr id; + int lsa_type, opaque_type; + int rc = 0; + struct ospf * ospf; + + ospf = ospf_lookup(); + assert(ospf); + + /* Extract opaque LSA from message */ + dmsg = (struct msg_delete_request *) STREAM_DATA (msg->s); + + /* Lookup area for link-local and area-local opaque LSAs */ + switch (dmsg->lsa_type) + { + case OSPF_OPAQUE_LINK_LSA: + case OSPF_OPAQUE_AREA_LSA: + area = ospf_area_lookup_by_area_id (ospf, dmsg->area_id); + if (!area) + { + zlog_warn ("ospf_apiserver_lsa_delete: unknown area %s", + inet_ntoa (dmsg->area_id)); + rc = OSPF_API_NOSUCHAREA; + goto out; + } + break; + case OSPF_OPAQUE_AS_LSA: + /* AS-external opaque LSAs have no designated area */ + area = NULL; + break; + default: + zlog_warn + ("ospf_apiserver_lsa_delete: Cannot delete non-opaque LSA type %d", + dmsg->lsa_type); + rc = OSPF_API_ILLEGALLSATYPE; + goto out; + } + + /* Check if we registered this opaque type */ + lsa_type = dmsg->lsa_type; + opaque_type = dmsg->opaque_type; + + if (!apiserver_is_opaque_type_registered (apiserv, lsa_type, opaque_type)) + { + zlog_warn ("ospf_apiserver_lsa_delete: LSA-type(%d)/Opaque-type(%d): Not registered", lsa_type, opaque_type); + rc = OSPF_API_OPAQUETYPENOTREGISTERED; + goto out; + } + + /* opaque_id is in network byte order */ + id.s_addr = htonl (SET_OPAQUE_LSID (dmsg->opaque_type, + ntohl (dmsg->opaque_id))); + + /* + * Even if the target LSA has once scheduled to flush, it remains in + * the LSDB until it is finally handled by the maxage remover thread. + * Therefore, the lookup function below may return non-NULL result. + */ + old = ospf_lsa_lookup (area, dmsg->lsa_type, id, ospf->router_id); + if (!old) + { + zlog_warn ("ospf_apiserver_lsa_delete: LSA[Type%d:%s] not in LSDB", + dmsg->lsa_type, inet_ntoa (id)); + rc = OSPF_API_NOSUCHLSA; + goto out; + } + + /* Schedule flushing of LSA from LSDB */ + /* NB: Multiple scheduling will produce a warning message, but harmless. */ + ospf_opaque_lsa_flush_schedule (old); + +out: + + /* Send reply back to client including return code */ + rc = ospf_apiserver_send_reply (apiserv, ntohl (msg->hdr.msgseq), rc); + return rc; +} + +/* Flush self-originated opaque LSA */ +static int +apiserver_flush_opaque_type_callback (struct ospf_lsa *lsa, + void *p_arg, int int_arg) +{ + struct param_t + { + struct ospf_apiserver *apiserv; + u_char lsa_type; + u_char opaque_type; + } + *param; + + /* Sanity check */ + assert (lsa->data); + assert (p_arg); + param = (struct param_t *) p_arg; + + /* If LSA matches type and opaque type then delete it */ + if (IS_LSA_SELF (lsa) && lsa->data->type == param->lsa_type + && GET_OPAQUE_TYPE (ntohl (lsa->data->id.s_addr)) == param->opaque_type) + { + ospf_opaque_lsa_flush_schedule (lsa); + } + return 0; +} + +/* Delete self-originated opaque LSAs of a given opaque type. This + function is called when an application unregisters a given opaque + type or a connection to an application closes and all those opaque + LSAs need to be flushed the LSDB. */ +void +ospf_apiserver_flush_opaque_lsa (struct ospf_apiserver *apiserv, + u_char lsa_type, u_char opaque_type) +{ + struct param_t + { + struct ospf_apiserver *apiserv; + u_char lsa_type; + u_char opaque_type; + } param; + struct listnode *node, *nnode; + struct ospf * ospf; + struct ospf_area *area; + + ospf = ospf_lookup(); + assert(ospf); + + /* Set parameter struct. */ + param.apiserv = apiserv; + param.lsa_type = lsa_type; + param.opaque_type = opaque_type; + + switch (lsa_type) + { + struct route_node *rn; + struct ospf_lsa *lsa; + + case OSPF_OPAQUE_LINK_LSA: + for (ALL_LIST_ELEMENTS (ospf->areas, node, nnode, area)) + LSDB_LOOP (OPAQUE_LINK_LSDB (area), rn, lsa) + apiserver_flush_opaque_type_callback(lsa, (void *) ¶m, 0); + break; + case OSPF_OPAQUE_AREA_LSA: + for (ALL_LIST_ELEMENTS (ospf->areas, node, nnode, area)) + LSDB_LOOP (OPAQUE_AREA_LSDB (area), rn, lsa) + apiserver_flush_opaque_type_callback(lsa, (void *) ¶m, 0); + break; + case OSPF_OPAQUE_AS_LSA: + LSDB_LOOP (OPAQUE_LINK_LSDB (ospf), rn, lsa) + apiserver_flush_opaque_type_callback(lsa, (void *) ¶m, 0); + break; + default: + break; + } + return; +} + + +/* ----------------------------------------------------------- + * Followings are callback functions to handle opaque types + * ----------------------------------------------------------- + */ + +int +ospf_apiserver_new_if (struct interface *ifp) +{ + struct ospf_interface *oi; + + /* For some strange reason it seems possible that we are invoked + with an interface that has no name. This seems to happen during + initialization. Return if this happens */ + + if (ifp->name[0] == '\0') { + /* interface has empty name */ + zlog_warn ("ospf_apiserver_new_if: interface has no name?"); + return 0; + } + + /* zlog_warn for debugging */ + zlog_warn ("ospf_apiserver_new_if"); + zlog_warn ("ifp name=%s status=%d index=%d", ifp->name, ifp->status, + ifp->ifindex); + + if (ifp->name[0] == '\0') { + /* interface has empty name */ + zlog_warn ("ospf_apiserver_new_if: interface has no name?"); + return 0; + } + + oi = ospf_apiserver_if_lookup_by_ifp (ifp); + + if (!oi) { + /* This interface is known to Zebra but not to OSPF daemon yet. */ + zlog_warn ("ospf_apiserver_new_if: interface %s not known to OSPFd?", + ifp->name); + return 0; + } + + assert (oi); + + /* New interface added to OSPF, tell clients about it */ + if (listcount (apiserver_list) > 0) { + ospf_apiserver_clients_notify_new_if (oi); + } + return 0; +} + +int +ospf_apiserver_del_if (struct interface *ifp) +{ + struct ospf_interface *oi; + + /* zlog_warn for debugging */ + zlog_warn ("ospf_apiserver_del_if"); + zlog_warn ("ifp name=%s status=%d index=%d\n", ifp->name, ifp->status, + ifp->ifindex); + + oi = ospf_apiserver_if_lookup_by_ifp (ifp); + + if (!oi) { + /* This interface is known to Zebra but not to OSPF daemon + anymore. No need to tell clients about it */ + return 0; + } + + /* Interface deleted, tell clients about it */ + if (listcount (apiserver_list) > 0) { + ospf_apiserver_clients_notify_del_if (oi); + } + return 0; +} + +void +ospf_apiserver_ism_change (struct ospf_interface *oi, int old_state) +{ + /* Tell clients about interface change */ + + /* zlog_warn for debugging */ + zlog_warn ("ospf_apiserver_ism_change"); + if (listcount (apiserver_list) > 0) { + ospf_apiserver_clients_notify_ism_change (oi); + } + + zlog_warn ("oi->ifp->name=%s", oi->ifp->name); + zlog_warn ("old_state=%d", old_state); + zlog_warn ("oi->state=%d", oi->state); +} + +void +ospf_apiserver_nsm_change (struct ospf_neighbor *nbr, int old_status) +{ + /* Neighbor status changed, tell clients about it */ + zlog_warn ("ospf_apiserver_nsm_change"); + if (listcount (apiserver_list) > 0) { + ospf_apiserver_clients_notify_nsm_change (nbr); + } +} + +void +ospf_apiserver_show_info (struct vty *vty, struct ospf_lsa *lsa) +{ + struct opaque_lsa + { + struct lsa_header header; + u_char data[1]; /* opaque data have variable length. This is start + address */ + }; + struct opaque_lsa *olsa; + int opaquelen; + + olsa = (struct opaque_lsa *) lsa->data; + + if (VALID_OPAQUE_INFO_LEN (lsa->data)) + opaquelen = ntohs (lsa->data->length) - OSPF_LSA_HEADER_SIZE; + else + opaquelen = 0; + + /* Output information about opaque LSAs */ + if (vty != NULL) + { + int i; + vty_out (vty, " Added using OSPF API: %u octets of opaque data %s%s", + opaquelen, + VALID_OPAQUE_INFO_LEN (lsa->data) ? "" : "(Invalid length?)", + VTY_NEWLINE); + vty_out (vty, " Opaque data: "); + + for (i = 0; i < opaquelen; i++) + { + vty_out (vty, "0x%x ", olsa->data[i]); + } + vty_out (vty, "%s", VTY_NEWLINE); + } + else + { + int i; + zlog_debug (" Added using OSPF API: %u octets of opaque data %s", + opaquelen, + VALID_OPAQUE_INFO_LEN (lsa-> + data) ? "" : "(Invalid length?)"); + zlog_debug (" Opaque data: "); + + for (i = 0; i < opaquelen; i++) + { + zlog_debug ("0x%x ", olsa->data[i]); + } + zlog_debug ("\n"); + } + return; +} + +/* ----------------------------------------------------------- + * Followings are functions to notify clients about events + * ----------------------------------------------------------- + */ + +/* Send a message to all clients. This is useful for messages + that need to be notified to all clients (such as interface + changes) */ + +void +ospf_apiserver_clients_notify_all (struct msg *msg) +{ + struct listnode *node, *nnode; + struct ospf_apiserver *apiserv; + + /* Send message to all clients */ + for (ALL_LIST_ELEMENTS (apiserver_list, node, nnode, apiserv)) + ospf_apiserver_send_msg (apiserv, msg); +} + +/* An interface is now ready to accept opaque LSAs. Notify all + clients that registered to use this opaque type */ +void +ospf_apiserver_clients_notify_ready_type9 (struct ospf_interface *oi) +{ + struct listnode *node, *nnode; + struct msg *msg; + struct ospf_apiserver *apiserv; + + assert (oi); + if (!oi->address) + { + zlog_warn ("Interface has no address?"); + return; + } + + if (!ospf_apiserver_is_ready_type9 (oi)) + { + zlog_warn ("Interface not ready for type 9?"); + return; + } + + for (ALL_LIST_ELEMENTS (apiserver_list, node, nnode, apiserv)) + { + struct listnode *node2, *nnode2; + struct registered_opaque_type *r; + + for (ALL_LIST_ELEMENTS (apiserv->opaque_types, node2, nnode2, r)) + { + if (r->lsa_type == OSPF_OPAQUE_LINK_LSA) + { + msg = new_msg_ready_notify (0, OSPF_OPAQUE_LINK_LSA, + r->opaque_type, + oi->address->u.prefix4); + if (!msg) + { + zlog_warn + ("ospf_apiserver_clients_notify_ready_type9: new_msg_ready_notify failed"); +#ifdef NOTYET + /* Cannot allocate new message. What should we do? */ + ospf_apiserver_free (apiserv); +#endif + goto out; + } + + ospf_apiserver_send_msg (apiserv, msg); + msg_free (msg); + } + } + } + +out: + return; +} + +void +ospf_apiserver_clients_notify_ready_type10 (struct ospf_area *area) +{ + struct listnode *node, *nnode; + struct msg *msg; + struct ospf_apiserver *apiserv; + + assert (area); + + if (!ospf_apiserver_is_ready_type10 (area)) + { + zlog_warn ("Area not ready for type 10?"); + return; + } + + for (ALL_LIST_ELEMENTS (apiserver_list, node, nnode, apiserv)) + { + struct listnode *node2, *nnode2; + struct registered_opaque_type *r; + + for (ALL_LIST_ELEMENTS (apiserv->opaque_types, node2, nnode2, r)) + { + if (r->lsa_type == OSPF_OPAQUE_AREA_LSA) + { + msg = new_msg_ready_notify (0, OSPF_OPAQUE_AREA_LSA, + r->opaque_type, area->area_id); + if (!msg) + { + zlog_warn + ("ospf_apiserver_clients_notify_ready_type10: new_msg_ready_nofity failed"); +#ifdef NOTYET + /* Cannot allocate new message. What should we do? */ + ospf_apiserver_free (apiserv); +#endif + goto out; + } + + ospf_apiserver_send_msg (apiserv, msg); + msg_free (msg); + } + } + } + +out: + return; +} + + +void +ospf_apiserver_clients_notify_ready_type11 (struct ospf *top) +{ + struct listnode *node, *nnode; + struct msg *msg; + struct in_addr id_null = { .s_addr = 0L }; + struct ospf_apiserver *apiserv; + + assert (top); + + if (!ospf_apiserver_is_ready_type11 (top)) + { + zlog_warn ("AS not ready for type 11?"); + return; + } + + for (ALL_LIST_ELEMENTS (apiserver_list, node, nnode, apiserv)) + { + struct listnode *node2, *nnode2; + struct registered_opaque_type *r; + + for (ALL_LIST_ELEMENTS (apiserv->opaque_types, node2, nnode2, r)) + { + if (r->lsa_type == OSPF_OPAQUE_AS_LSA) + { + msg = new_msg_ready_notify (0, OSPF_OPAQUE_AS_LSA, + r->opaque_type, id_null); + if (!msg) + { + zlog_warn + ("ospf_apiserver_clients_notify_ready_type11: new_msg_ready_notify failed"); +#ifdef NOTYET + /* Cannot allocate new message. What should we do? */ + ospf_apiserver_free (apiserv); +#endif + goto out; + } + + ospf_apiserver_send_msg (apiserv, msg); + msg_free (msg); + } + } + } + +out: + return; +} + +void +ospf_apiserver_clients_notify_new_if (struct ospf_interface *oi) +{ + struct msg *msg; + + msg = new_msg_new_if (0, oi->address->u.prefix4, oi->area->area_id); + if (msg != NULL) + { + ospf_apiserver_clients_notify_all (msg); + msg_free (msg); + } +} + +void +ospf_apiserver_clients_notify_del_if (struct ospf_interface *oi) +{ + struct msg *msg; + + msg = new_msg_del_if (0, oi->address->u.prefix4); + if (msg != NULL) + { + ospf_apiserver_clients_notify_all (msg); + msg_free (msg); + } +} + +void +ospf_apiserver_clients_notify_ism_change (struct ospf_interface *oi) +{ + struct msg *msg; + struct in_addr ifaddr = { .s_addr = 0L }; + struct in_addr area_id = { .s_addr = 0L }; + + assert (oi); + assert (oi->ifp); + + if (oi->address) + { + ifaddr = oi->address->u.prefix4; + } + if (oi->area) + { + area_id = oi->area->area_id; + } + + msg = new_msg_ism_change (0, ifaddr, area_id, oi->state); + if (!msg) + { + zlog_warn ("apiserver_clients_notify_ism_change: msg_new failed"); + return; + } + + ospf_apiserver_clients_notify_all (msg); + msg_free (msg); +} + +void +ospf_apiserver_clients_notify_nsm_change (struct ospf_neighbor *nbr) +{ + struct msg *msg; + struct in_addr ifaddr = { .s_addr = 0L }; + struct in_addr nbraddr = { .s_addr = 0L }; + + assert (nbr); + + if (nbr->oi) + { + ifaddr = nbr->oi->address->u.prefix4; + } + + nbraddr = nbr->address.u.prefix4; + + msg = new_msg_nsm_change (0, ifaddr, nbraddr, nbr->router_id, nbr->state); + if (!msg) + { + zlog_warn ("apiserver_clients_notify_nsm_change: msg_new failed"); + return; + } + + ospf_apiserver_clients_notify_all (msg); + msg_free (msg); +} + +static void +apiserver_clients_lsa_change_notify (u_char msgtype, struct ospf_lsa *lsa) +{ + struct msg *msg; + struct listnode *node, *nnode; + struct ospf_apiserver *apiserv; + + /* Default area for AS-External and Opaque11 LSAs */ + struct in_addr area_id = { .s_addr = 0L }; + + /* Default interface for non Opaque9 LSAs */ + struct in_addr ifaddr = { .s_addr = 0L }; + + if (lsa->area) + { + area_id = lsa->area->area_id; + } + if (lsa->data->type == OSPF_OPAQUE_LINK_LSA) + { + assert (lsa->oi); + ifaddr = lsa->oi->address->u.prefix4; + } + + /* Prepare message that can be sent to clients that have a matching + filter */ + msg = new_msg_lsa_change_notify (msgtype, 0L, /* no sequence number */ + ifaddr, area_id, + lsa->flags & OSPF_LSA_SELF, lsa->data); + if (!msg) + { + zlog_warn ("apiserver_clients_lsa_change_notify: msg_new failed"); + return; + } + + /* Now send message to all clients with a matching filter */ + for (ALL_LIST_ELEMENTS (apiserver_list, node, nnode, apiserv)) + { + struct lsa_filter_type *filter; + u_int16_t mask; + u_int32_t *area; + int i; + + /* Check filter for this client. */ + filter = apiserv->filter; + + /* Check area IDs in case of non AS-E LSAs. + * If filter has areas (num_areas > 0), + * then one of the areas must match the area ID of this LSA. */ + + i = filter->num_areas; + if ((lsa->data->type == OSPF_AS_EXTERNAL_LSA) || + (lsa->data->type == OSPF_OPAQUE_AS_LSA)) + { + i = 0; + } + + if (i > 0) + { + area = (u_int32_t *) (filter + 1); + while (i) + { + if (*area == area_id.s_addr) + { + break; + } + i--; + area++; + } + } + else + { + i = 1; + } + + if (i > 0) + { + /* Area match. Check LSA type. */ + mask = ntohs (filter->typemask); + + if (mask & Power2[lsa->data->type]) + { + /* Type also matches. Check origin. */ + if ((filter->origin == ANY_ORIGIN) || + (filter->origin == IS_LSA_SELF (lsa))) + { + ospf_apiserver_send_msg (apiserv, msg); + } + } + } + } + /* Free message since it is not used anymore */ + msg_free (msg); +} + + +/* ------------------------------------------------------------- + * Followings are hooks invoked when LSAs are updated or deleted + * ------------------------------------------------------------- + */ + + +static int +apiserver_notify_clients_lsa (u_char msgtype, struct ospf_lsa *lsa) +{ + struct msg *msg; + /* default area for AS-External and Opaque11 LSAs */ + struct in_addr area_id = { .s_addr = 0L }; + + /* default interface for non Opaque9 LSAs */ + struct in_addr ifaddr = { .s_addr = 0L }; + + /* Only notify this update if the LSA's age is smaller than + MAXAGE. Otherwise clients would see LSA updates with max age just + before they are deleted from the LSDB. LSA delete messages have + MAXAGE too but should not be filtered. */ + if (IS_LSA_MAXAGE(lsa) && (msgtype == MSG_LSA_UPDATE_NOTIFY)) { + return 0; + } + + if (lsa->area) + { + area_id = lsa->area->area_id; + } + if (lsa->data->type == OSPF_OPAQUE_LINK_LSA) + { + ifaddr = lsa->oi->address->u.prefix4; + } + msg = new_msg_lsa_change_notify (msgtype, 0L, /* no sequence number */ + ifaddr, area_id, + lsa->flags & OSPF_LSA_SELF, lsa->data); + if (!msg) + { + zlog_warn ("notify_clients_lsa: msg_new failed"); + return -1; + } + /* Notify all clients that new LSA is added/updated */ + apiserver_clients_lsa_change_notify (msgtype, lsa); + + /* Clients made their own copies of msg so we can free msg here */ + msg_free (msg); + + return 0; +} + +int +ospf_apiserver_lsa_update (struct ospf_lsa *lsa) +{ + return apiserver_notify_clients_lsa (MSG_LSA_UPDATE_NOTIFY, lsa); +} + +int +ospf_apiserver_lsa_delete (struct ospf_lsa *lsa) +{ + return apiserver_notify_clients_lsa (MSG_LSA_DELETE_NOTIFY, lsa); +} + +#endif /* SUPPORT_OSPF_API */ + diff --git a/ospfd/ospf_apiserver.h b/ospfd/ospf_apiserver.h new file mode 100644 index 0000000..b60f56b --- /dev/null +++ b/ospfd/ospf_apiserver.h @@ -0,0 +1,201 @@ +/* + * Server side of OSPF API. + * Copyright (C) 2001, 2002 Ralph Keller + * + * 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 _OSPF_APISERVER_H +#define _OSPF_APISERVER_H + +/* MTYPE definition is not reflected to "memory.h". */ +#define MTYPE_OSPF_APISERVER MTYPE_TMP +#define MTYPE_OSPF_APISERVER_MSGFILTER MTYPE_TMP + +/* List of opaque types that application registered */ +struct registered_opaque_type +{ + u_char lsa_type; + u_char opaque_type; +}; + + +/* Server instance for each accepted client connection. */ +struct ospf_apiserver +{ + /* Socket connections for synchronous commands and asynchronous + notifications */ + int fd_sync; /* synchronous requests */ + struct sockaddr_in peer_sync; + + int fd_async; /* asynchronous notifications */ + struct sockaddr_in peer_async; + + /* List of all opaque types that application registers to use. Using + a single connection with the OSPF daemon, multiple + pairs can be registered. However, each + combination can only be registered once by all applications. */ + struct list *opaque_types; /* of type registered_opaque_type */ + + /* Temporary storage for LSA instances to be refreshed. */ + struct ospf_lsdb reserve; + + /* filter for LSA update/delete notifies */ + struct lsa_filter_type *filter; + + /* Fifo buffers for outgoing messages */ + struct msg_fifo *out_sync_fifo; + struct msg_fifo *out_async_fifo; + + /* Read and write threads */ + struct thread *t_sync_read; +#ifdef USE_ASYNC_READ + struct thread *t_async_read; +#endif /* USE_ASYNC_READ */ + struct thread *t_sync_write; + struct thread *t_async_write; +}; + +enum event +{ + OSPF_APISERVER_ACCEPT, + OSPF_APISERVER_SYNC_READ, +#ifdef USE_ASYNC_READ + OSPF_APISERVER_ASYNC_READ, +#endif /* USE_ASYNC_READ */ + OSPF_APISERVER_SYNC_WRITE, + OSPF_APISERVER_ASYNC_WRITE +}; + +/* ----------------------------------------------------------- + * Followings are functions to manage client connections. + * ----------------------------------------------------------- + */ + +extern unsigned short ospf_apiserver_getport (void); +extern int ospf_apiserver_init (void); +extern void ospf_apiserver_term (void); +extern struct ospf_apiserver *ospf_apiserver_new (int fd_sync, int fd_async); +extern void ospf_apiserver_free (struct ospf_apiserver *apiserv); +extern void ospf_apiserver_event (enum event event, int fd, + struct ospf_apiserver *apiserv); +extern int ospf_apiserver_serv_sock_family (unsigned short port, int family); +extern int ospf_apiserver_accept (struct thread *thread); +extern int ospf_apiserver_read (struct thread *thread); +extern int ospf_apiserver_sync_write (struct thread *thread); +extern int ospf_apiserver_async_write (struct thread *thread); +extern int ospf_apiserver_send_reply (struct ospf_apiserver *apiserv, + u_int32_t seqnr, u_char rc); + +/* ----------------------------------------------------------- + * Followings are message handler functions + * ----------------------------------------------------------- + */ + +extern int ospf_apiserver_lsa9_originator (void *arg); +extern int ospf_apiserver_lsa10_originator (void *arg); +extern int ospf_apiserver_lsa11_originator (void *arg); + +extern void ospf_apiserver_clients_notify_all (struct msg *msg); + +extern void ospf_apiserver_clients_notify_ready_type9 (struct ospf_interface *oi); +extern void ospf_apiserver_clients_notify_ready_type10 (struct ospf_area *area); +extern void ospf_apiserver_clients_notify_ready_type11 (struct ospf *top); + +extern void ospf_apiserver_clients_notify_new_if (struct ospf_interface *oi); +extern void ospf_apiserver_clients_notify_del_if (struct ospf_interface *oi); +extern void ospf_apiserver_clients_notify_ism_change (struct ospf_interface *oi); +extern void ospf_apiserver_clients_notify_nsm_change (struct ospf_neighbor *nbr); + +extern int ospf_apiserver_is_ready_type9 (struct ospf_interface *oi); +extern int ospf_apiserver_is_ready_type10 (struct ospf_area *area); +extern int ospf_apiserver_is_ready_type11 (struct ospf *ospf); + +extern void ospf_apiserver_notify_ready_type9 (struct ospf_apiserver *apiserv); +extern void ospf_apiserver_notify_ready_type10 (struct ospf_apiserver *apiserv); +extern void ospf_apiserver_notify_ready_type11 (struct ospf_apiserver *apiserv); + +extern int ospf_apiserver_handle_msg (struct ospf_apiserver *apiserv, + struct msg *msg); +extern int ospf_apiserver_handle_register_opaque_type (struct ospf_apiserver + *apiserv, struct msg *msg); +extern int ospf_apiserver_handle_unregister_opaque_type (struct ospf_apiserver + *apiserv, struct msg *msg); +extern int ospf_apiserver_handle_register_event (struct ospf_apiserver *apiserv, + struct msg *msg); +extern int ospf_apiserver_handle_originate_request (struct ospf_apiserver *apiserv, + struct msg *msg); +extern int ospf_apiserver_handle_delete_request (struct ospf_apiserver *apiserv, + struct msg *msg); +extern int ospf_apiserver_handle_sync_lsdb (struct ospf_apiserver *apiserv, + struct msg *msg); + + +/* ----------------------------------------------------------- + * Followings are functions for LSA origination/deletion + * ----------------------------------------------------------- + */ + +extern int ospf_apiserver_register_opaque_type (struct ospf_apiserver *apiserver, + u_char lsa_type, u_char opaque_type); +extern int ospf_apiserver_unregister_opaque_type (struct ospf_apiserver *apiserver, + u_char lsa_type, + u_char opaque_type); +extern struct ospf_lsa *ospf_apiserver_opaque_lsa_new (struct ospf_area *area, + struct ospf_interface *oi, + struct lsa_header *protolsa); +extern struct ospf_interface *ospf_apiserver_if_lookup_by_addr (struct in_addr + address); +extern struct ospf_interface *ospf_apiserver_if_lookup_by_ifp (struct interface + *ifp); +extern int ospf_apiserver_originate1 (struct ospf_lsa *lsa); +extern void ospf_apiserver_flood_opaque_lsa (struct ospf_lsa *lsa); + + +/* ----------------------------------------------------------- + * Followings are callback functions to handle opaque types + * ----------------------------------------------------------- + */ + +extern int ospf_apiserver_new_if (struct interface *ifp); +extern int ospf_apiserver_del_if (struct interface *ifp); +extern void ospf_apiserver_ism_change (struct ospf_interface *oi, int old_status); +extern void ospf_apiserver_nsm_change (struct ospf_neighbor *nbr, int old_status); +extern void ospf_apiserver_config_write_router (struct vty *vty); +extern void ospf_apiserver_config_write_if (struct vty *vty, struct interface *ifp); +extern void ospf_apiserver_show_info (struct vty *vty, struct ospf_lsa *lsa); +extern int ospf_ospf_apiserver_lsa_originator (void *arg); +extern struct ospf_lsa *ospf_apiserver_lsa_refresher (struct ospf_lsa *lsa); +extern void ospf_apiserver_flush_opaque_lsa (struct ospf_apiserver *apiserv, + u_char lsa_type, u_char opaque_type); + +/* ----------------------------------------------------------- + * Followings are hooks when LSAs are updated or deleted + * ----------------------------------------------------------- + */ + + +/* Hooks that are invoked from ospf opaque module */ + +extern int ospf_apiserver_lsa_update (struct ospf_lsa *lsa); +extern int ospf_apiserver_lsa_delete (struct ospf_lsa *lsa); + +extern void ospf_apiserver_clients_lsa_change_notify (u_char msgtype, + struct ospf_lsa *lsa); + +#endif /* _OSPF_APISERVER_H */ diff --git a/ospfd/ospf_asbr.c b/ospfd/ospf_asbr.c new file mode 100644 index 0000000..0a411e4 --- /dev/null +++ b/ospfd/ospf_asbr.c @@ -0,0 +1,294 @@ +/* + * OSPF AS Boundary Router functions. + * Copyright (C) 1999, 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. + */ + +#include + +#include "thread.h" +#include "memory.h" +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "vty.h" +#include "filter.h" +#include "log.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_spf.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_route.h" +#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) +{ + struct route_node *rn; + struct ospf_route *or; + + rn = route_node_lookup (ospf->old_external_route, (struct prefix *) p); + if (rn) + if ((or = rn->info)) + { + zlog_info ("Route[%s/%d]: external path deleted", + inet_ntoa (p->prefix), p->prefixlen); + + /* Remove route from zebra. */ + if (or->type == OSPF_DESTINATION_NETWORK) + ospf_zebra_delete ((struct prefix_ipv4 *) &rn->p, or); + + ospf_route_free (or); + rn->info = NULL; + + route_unlock_node (rn); + route_unlock_node (rn); + return; + } + + zlog_info ("Route[%s/%d]: no such external path", + inet_ntoa (p->prefix), p->prefixlen); +} + +/* Lookup external route. */ +struct ospf_route * +ospf_external_route_lookup (struct ospf *ospf, + struct prefix_ipv4 *p) +{ + struct route_node *rn; + + rn = route_node_lookup (ospf->old_external_route, (struct prefix *) p); + if (rn) + { + route_unlock_node (rn); + if (rn->info) + return rn->info; + } + + zlog_warn ("Route[%s/%d]: lookup, no such prefix", + inet_ntoa (p->prefix), p->prefixlen); + + return NULL; +} + + +/* Add an External info for AS-external-LSA. */ +struct external_info * +ospf_external_info_new (u_char type) +{ + struct external_info *new; + + new = (struct external_info *) + XCALLOC (MTYPE_OSPF_EXTERNAL_INFO, sizeof (struct external_info)); + new->type = type; + + ospf_reset_route_map_set_values (&new->route_map_set); + return new; +} + +static void +ospf_external_info_free (struct external_info *ei) +{ + XFREE (MTYPE_OSPF_EXTERNAL_INFO, ei); +} + +void +ospf_reset_route_map_set_values (struct route_map_set_values *values) +{ + values->metric = -1; + values->metric_type = -1; +} + +int +ospf_route_map_set_compare (struct route_map_set_values *values1, + struct route_map_set_values *values2) +{ + return values1->metric == values2->metric && + values1->metric_type == values2->metric_type; +} + +/* Add an External info for AS-external-LSA. */ +struct external_info * +ospf_external_info_add (u_char type, struct prefix_ipv4 p, + ifindex_t ifindex, struct in_addr nexthop, + route_tag_t tag) +{ + struct external_info *new; + struct route_node *rn; + + /* Initialize route table. */ + if (EXTERNAL_INFO (type) == NULL) + EXTERNAL_INFO (type) = route_table_init (); + + rn = route_node_get (EXTERNAL_INFO (type), (struct prefix *) &p); + /* If old info exists, -- discard new one or overwrite with new one? */ + if (rn) + if (rn->info) + { + route_unlock_node (rn); + zlog_warn ("Redistribute[%s]: %s/%d already exists, discard.", + ospf_redist_string(type), + inet_ntoa (p.prefix), p.prefixlen); + /* XFREE (MTYPE_OSPF_TMP, rn->info); */ + return rn->info; + } + + /* Create new External info instance. */ + new = ospf_external_info_new (type); + new->p = p; + new->ifindex = ifindex; + new->nexthop = nexthop; + new->tag = tag; + + if (rn) + rn->info = new; + + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + zlog_debug ("Redistribute[%s]: %s/%d external info created.", + ospf_redist_string(type), + inet_ntoa (p.prefix), p.prefixlen); + return new; +} + +void +ospf_external_info_delete (u_char type, struct prefix_ipv4 p) +{ + struct route_node *rn; + + rn = route_node_lookup (EXTERNAL_INFO (type), (struct prefix *) &p); + if (rn) + { + ospf_external_info_free (rn->info); + rn->info = NULL; + route_unlock_node (rn); + route_unlock_node (rn); + } +} + +struct external_info * +ospf_external_info_lookup (u_char type, struct prefix_ipv4 *p) +{ + struct route_node *rn; + rn = route_node_lookup (EXTERNAL_INFO (type), (struct prefix *) p); + if (rn) + { + route_unlock_node (rn); + if (rn->info) + return rn->info; + } + + return NULL; +} + +struct ospf_lsa * +ospf_external_info_find_lsa (struct ospf *ospf, + struct prefix_ipv4 *p) +{ + struct ospf_lsa *lsa; + struct as_external_lsa *al; + struct in_addr mask, id; + + lsa = ospf_lsdb_lookup_by_id (ospf->lsdb, OSPF_AS_EXTERNAL_LSA, + p->prefix, ospf->router_id); + + if (!lsa) + return NULL; + + al = (struct as_external_lsa *) lsa->data; + + masklen2ip (p->prefixlen, &mask); + + if (mask.s_addr != al->mask.s_addr) + { + id.s_addr = p->prefix.s_addr | (~mask.s_addr); + lsa = ospf_lsdb_lookup_by_id (ospf->lsdb, OSPF_AS_EXTERNAL_LSA, + id, ospf->router_id); + if (!lsa) + return NULL; + } + + return lsa; +} + + +/* Update ASBR status. */ +void +ospf_asbr_status_update (struct ospf *ospf, u_char status) +{ + zlog_info ("ASBR[Status:%d]: Update", status); + + /* ASBR on. */ + if (status) + { + /* Already ASBR. */ + if (IS_OSPF_ASBR (ospf)) + { + zlog_info ("ASBR[Status:%d]: Already ASBR", status); + return; + } + SET_FLAG (ospf->flags, OSPF_FLAG_ASBR); + } + else + { + /* Already non ASBR. */ + if (! IS_OSPF_ASBR (ospf)) + { + zlog_info ("ASBR[Status:%d]: Already non ASBR", status); + return; + } + UNSET_FLAG (ospf->flags, OSPF_FLAG_ASBR); + } + + /* Transition from/to status ASBR, schedule timer. */ + ospf_spf_calculate_schedule (ospf, SPF_FLAG_ASBR_STATUS_CHANGE); + ospf_router_lsa_update (ospf); +} + +void +ospf_redistribute_withdraw (struct ospf *ospf, u_char type) +{ + struct route_node *rn; + struct external_info *ei; + + /* Delete external info for specified type. */ + if (EXTERNAL_INFO (type)) + for (rn = route_top (EXTERNAL_INFO (type)); rn; rn = route_next (rn)) + if ((ei = rn->info)) + if (ospf_external_info_find_lsa (ospf, &ei->p)) + { + if (is_prefix_default (&ei->p) && + ospf->default_originate != DEFAULT_ORIGINATE_NONE) + continue; + ospf_external_lsa_flush (ospf, type, &ei->p, + ei->ifindex /*, ei->nexthop */); + + ospf_external_info_free (ei); + route_unlock_node (rn); + rn->info = NULL; + } +} diff --git a/ospfd/ospf_asbr.h b/ospfd/ospf_asbr.h new file mode 100644 index 0000000..0fc3302 --- /dev/null +++ b/ospfd/ospf_asbr.h @@ -0,0 +1,81 @@ +/* + * OSPF AS Boundary Router functions. + * Copyright (C) 1999, 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 _ZEBRA_OSPF_ASBR_H +#define _ZEBRA_OSPF_ASBR_H + +struct route_map_set_values +{ + int32_t metric; + int32_t metric_type; +}; + +/* Redistributed external information. */ +struct external_info +{ + /* Type of source protocol. */ + u_char type; + + /* Prefix. */ + struct prefix_ipv4 p; + + /* Interface index. */ + ifindex_t ifindex; + + /* Nexthop address. */ + struct in_addr nexthop; + + /* Additional Route tag: this is the wire type */ + u_int32_t tag; + + struct route_map_set_values route_map_set; +#define ROUTEMAP_METRIC(E) (E)->route_map_set.metric +#define ROUTEMAP_METRIC_TYPE(E) (E)->route_map_set.metric_type +}; + +#define OSPF_ASBR_CHECK_DELAY 30 + +extern void ospf_external_route_remove (struct ospf *, struct prefix_ipv4 *); +extern struct external_info *ospf_external_info_new (u_char); +extern void ospf_reset_route_map_set_values (struct route_map_set_values *); +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, + 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 *); +extern struct ospf_route *ospf_external_route_lookup (struct ospf *, + struct prefix_ipv4 *); +extern void ospf_asbr_status_update (struct ospf *, u_char); + +extern void ospf_redistribute_withdraw (struct ospf *, u_char); +extern void ospf_asbr_check (void); +extern void ospf_schedule_asbr_check (void); +extern void ospf_asbr_route_install_lsa (struct ospf_lsa *); +extern struct ospf_lsa *ospf_external_info_find_lsa (struct ospf *, + struct prefix_ipv4 *p); + +#endif /* _ZEBRA_OSPF_ASBR_H */ diff --git a/ospfd/ospf_ase.c b/ospfd/ospf_ase.c new file mode 100644 index 0000000..fe40b10 --- /dev/null +++ b/ospfd/ospf_ase.c @@ -0,0 +1,867 @@ +/* + * OSPF AS external route calculation. + * Copyright (C) 1999, 2000 Alex Zinin, 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. + */ + +#include + +#include "thread.h" +#include "memory.h" +#include "hash.h" +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "vty.h" +#include "log.h" + +#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_spf.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_ase.h" +#include "ospfd/ospf_zebra.h" +#include "ospfd/ospf_dump.h" + +struct ospf_route * +ospf_find_asbr_route (struct ospf *ospf, + struct route_table *rtrs, struct prefix_ipv4 *asbr) +{ + struct route_node *rn; + struct ospf_route *or, *best = NULL; + struct listnode *node; + struct list *chosen; + + /* Sanity check. */ + if (rtrs == NULL) + return NULL; + + rn = route_node_lookup (rtrs, (struct prefix *) asbr); + if (! rn) + return NULL; + + route_unlock_node (rn); + + chosen = list_new (); + + /* First try to find intra-area non-bb paths. */ + if (!CHECK_FLAG (ospf->config, OSPF_RFC1583_COMPATIBLE)) + for (ALL_LIST_ELEMENTS_RO ((struct list *) rn->info, node, or)) + if (or->cost < OSPF_LS_INFINITY) + if (!OSPF_IS_AREA_ID_BACKBONE (or->u.std.area_id) && + or->path_type == OSPF_PATH_INTRA_AREA) + listnode_add (chosen, or); + + /* If none is found -- look through all. */ + if (listcount (chosen) == 0) + { + list_free (chosen); + chosen = rn->info; + } + + /* Now find the route with least cost. */ + for (ALL_LIST_ELEMENTS_RO (chosen, node, or)) + if (or->cost < OSPF_LS_INFINITY) + { + if (best == NULL) + best = or; + else if (best->cost > or->cost) + best = or; + else if (best->cost == or->cost && + IPV4_ADDR_CMP (&best->u.std.area_id, + &or->u.std.area_id) < 0) + best = or; + } + + if (chosen != rn->info) + list_delete (chosen); + + return best; +} + +struct ospf_route * +ospf_find_asbr_route_through_area (struct route_table *rtrs, + struct prefix_ipv4 *asbr, + struct ospf_area *area) +{ + struct route_node *rn; + + /* Sanity check. */ + if (rtrs == NULL) + return NULL; + + rn = route_node_lookup (rtrs, (struct prefix *) asbr); + + if (rn) + { + struct listnode *node; + struct ospf_route *or; + + route_unlock_node (rn); + + for (ALL_LIST_ELEMENTS_RO ((struct list *) rn->info, node, or)) + if (IPV4_ADDR_SAME (&or->u.std.area_id, &area->area_id)) + return or; + } + + return NULL; +} + +static void +ospf_ase_complete_direct_routes (struct ospf_route *ro, struct in_addr nexthop) +{ + struct listnode *node; + struct ospf_path *op; + + for (ALL_LIST_ELEMENTS_RO (ro->paths, node, op)) + if (op->nexthop.s_addr == 0) + op->nexthop.s_addr = nexthop.s_addr; +} + +static int +ospf_ase_forward_address_check (struct ospf *ospf, struct in_addr fwd_addr) +{ + struct listnode *ifn; + struct ospf_interface *oi; + + for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, ifn, oi)) + if (if_is_operative (oi->ifp)) + if (oi->type != OSPF_IFTYPE_VIRTUALLINK) + if (IPV4_ADDR_SAME (&oi->address->u.prefix4, &fwd_addr)) + return 0; + + return 1; +} + +#if 0 +/* Calculate ASBR route. */ +static struct ospf_route * +ospf_ase_calculate_asbr_route (struct ospf *ospf, + struct route_table *rt_network, + struct route_table *rt_router, + struct as_external_lsa *al) +{ + struct prefix_ipv4 asbr; + struct ospf_route *asbr_route; + struct route_node *rn; + + /* Find ASBR route from Router routing table. */ + asbr.family = AF_INET; + asbr.prefix = al->header.adv_router; + asbr.prefixlen = IPV4_MAX_BITLEN; + apply_mask_ipv4 (&asbr); + + asbr_route = ospf_find_asbr_route (ospf, rt_router, &asbr); + + if (asbr_route == NULL) + { + if (IS_DEBUG_OSPF (lsa, LSA)) + zlog_debug ("ospf_ase_calculate(): Route to ASBR %s not found", + inet_ntoa (asbr.prefix)); + return NULL; + } + + if (!(asbr_route->u.std.flags & ROUTER_LSA_EXTERNAL)) + { + if (IS_DEBUG_OSPF (lsa, LSA)) + zlog_debug ("ospf_ase_calculate(): Originating router is not an ASBR"); + return NULL; + } + + if (al->e[0].fwd_addr.s_addr != 0) + { + if (IS_DEBUG_OSPF (lsa, LSA)) + zlog_debug ("ospf_ase_calculate(): " + "Forwarding address is not 0.0.0.0."); + + if (! ospf_ase_forward_address_check (ospf, al->e[0].fwd_addr)) + { + if (IS_DEBUG_OSPF (lsa, LSA)) + zlog_debug ("ospf_ase_calculate(): " + "Forwarding address is one of our addresses, Ignore."); + return NULL; + } + + if (IS_DEBUG_OSPF (lsa, LSA)) + zlog_debug ("ospf_ase_calculate(): " + "Looking up in the Network Routing Table."); + + /* Looking up the path to the fwd_addr from Network route. */ + asbr.family = AF_INET; + asbr.prefix = al->e[0].fwd_addr; + asbr.prefixlen = IPV4_MAX_BITLEN; + + rn = route_node_match (rt_network, (struct prefix *) &asbr); + + if (rn == NULL) + { + if (IS_DEBUG_OSPF (lsa, LSA)) + zlog_debug ("ospf_ase_calculate(): " + "Couldn't find a route to the forwarding address."); + return NULL; + } + + route_unlock_node (rn); + + if ((asbr_route = rn->info) == NULL) + { + if (IS_DEBUG_OSPF (lsa, LSA)) + zlog_debug ("ospf_ase_calculate(): " + "Somehow OSPF route to ASBR is lost"); + return NULL; + } + } + + return asbr_route; +} +#endif + +static struct ospf_route * +ospf_ase_calculate_new_route (struct ospf_lsa *lsa, + struct ospf_route *asbr_route, u_int32_t metric) +{ + struct as_external_lsa *al; + struct ospf_route *new; + + al = (struct as_external_lsa *) lsa->data; + + new = ospf_route_new (); + + /* Set redistributed type -- does make sense? */ + /* new->type = type; */ + new->id = al->header.id; + new->mask = al->mask; + + if (!IS_EXTERNAL_METRIC (al->e[0].tos)) + { + if (IS_DEBUG_OSPF (lsa, LSA)) + zlog_debug ("Route[External]: type-1 created."); + new->path_type = OSPF_PATH_TYPE1_EXTERNAL; + new->cost = asbr_route->cost + metric; /* X + Y */ + } + else + { + if (IS_DEBUG_OSPF (lsa, LSA)) + zlog_debug ("Route[External]: type-2 created."); + new->path_type = OSPF_PATH_TYPE2_EXTERNAL; + new->cost = asbr_route->cost; /* X */ + new->u.ext.type2_cost = metric; /* Y */ + } + + new->type = OSPF_DESTINATION_NETWORK; + new->u.ext.origin = lsa; + new->u.ext.tag = ntohl (al->e[0].route_tag); + new->u.ext.asbr = asbr_route; + + assert (new != asbr_route); + + return new; +} + +#define OSPF_ASE_CALC_INTERVAL 1 + +int +ospf_ase_calculate_route (struct ospf *ospf, struct ospf_lsa * lsa) +{ + u_int32_t metric; + struct as_external_lsa *al; + struct ospf_route *asbr_route; + struct prefix_ipv4 asbr, p; + struct route_node *rn; + struct ospf_route *new, *or; + int ret; + + assert (lsa); + al = (struct as_external_lsa *) lsa->data; + + if (lsa->data->type == OSPF_AS_NSSA_LSA) + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_ase_calc(): Processing Type-7"); + + /* Stay away from any Local Translated Type-7 LSAs */ + if (CHECK_FLAG (lsa->flags, OSPF_LSA_LOCAL_XLT)) + { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_ase_calc(): Rejecting Local Xlt'd"); + return 0; + } + + if (IS_DEBUG_OSPF (lsa, LSA)) + zlog_debug ("Route[External]: Calculate AS-external-LSA to %s/%d", + inet_ntoa (al->header.id), ip_masklen (al->mask)); + /* (1) If the cost specified by the LSA is LSInfinity, or if the + LSA's LS age is equal to MaxAge, then examine the next LSA. */ + if ((metric = GET_METRIC (al->e[0].metric)) >= OSPF_LS_INFINITY) + { + if (IS_DEBUG_OSPF (lsa, LSA)) + zlog_debug ("Route[External]: Metric is OSPF_LS_INFINITY"); + return 0; + } + if (IS_LSA_MAXAGE (lsa)) + { + if (IS_DEBUG_OSPF (lsa, LSA)) + zlog_debug ("Route[External]: AS-external-LSA is MAXAGE"); + return 0; + } + + /* (2) If the LSA was originated by the calculating router itself, + examine the next LSA. */ + if (IS_LSA_SELF (lsa)) + { + if (IS_DEBUG_OSPF (lsa, LSA)) + zlog_debug ("Route[External]: AS-external-LSA is self originated"); + return 0; + } + + /* (3) Call the destination described by the LSA N. N's address is + obtained by masking the LSA's Link State ID with the + network/subnet mask contained in the body of the LSA. Look + up the routing table entries (potentially one per attached + area) for the AS boundary router (ASBR) that originated the + LSA. If no entries exist for router ASBR (i.e., ASBR is + unreachable), do nothing with this LSA and consider the next + in the list. */ + + asbr.family = AF_INET; + asbr.prefix = al->header.adv_router; + asbr.prefixlen = IPV4_MAX_BITLEN; + apply_mask_ipv4 (&asbr); + + asbr_route = ospf_find_asbr_route (ospf, ospf->new_rtrs, &asbr); + if (asbr_route == NULL) + { + if (IS_DEBUG_OSPF (lsa, LSA)) + zlog_debug ("Route[External]: Can't find originating ASBR route"); + return 0; + } + if (!(asbr_route->u.std.flags & ROUTER_LSA_EXTERNAL)) + { + if (IS_DEBUG_OSPF (lsa, LSA)) + zlog_debug ("Route[External]: Originating router is not an ASBR"); + return 0; + } + + /* Else, this LSA describes an AS external path to destination + N. Examine the forwarding address specified in the AS- + external-LSA. This indicates the IP address to which + packets for the destination should be forwarded. */ + + if (al->e[0].fwd_addr.s_addr == 0) + { + /* If the forwarding address is set to 0.0.0.0, packets should + be sent to the ASBR itself. Among the multiple routing table + entries for the ASBR, select the preferred entry as follows. + If RFC1583Compatibility is set to "disabled", prune the set + of routing table entries for the ASBR as described in + Section 16.4.1. In any case, among the remaining routing + table entries, select the routing table entry with the least + cost; when there are multiple least cost routing table + entries the entry whose associated area has the largest OSPF + Area ID (when considered as an unsigned 32-bit integer) is + chosen. */ + + /* asbr_route already contains the requested route */ + } + else + { + /* If the forwarding address is non-zero, look up the + forwarding address in the routing table.[24] The matching + routing table entry must specify an intra-area or inter-area + path; if no such path exists, do nothing with the LSA and + consider the next in the list. */ + if (! ospf_ase_forward_address_check (ospf, al->e[0].fwd_addr)) + { + if (IS_DEBUG_OSPF (lsa, LSA)) + zlog_debug ("Route[External]: Forwarding address is our router " + "address"); + return 0; + } + + asbr.family = AF_INET; + asbr.prefix = al->e[0].fwd_addr; + asbr.prefixlen = IPV4_MAX_BITLEN; + + rn = route_node_match (ospf->new_table, (struct prefix *) &asbr); + + if (rn == NULL || (asbr_route = rn->info) == NULL) + { + if (IS_DEBUG_OSPF (lsa, LSA)) + zlog_debug ("Route[External]: Can't find route to forwarding " + "address"); + if (rn) + route_unlock_node (rn); + return 0; + } + + route_unlock_node (rn); + } + + /* (4) Let X be the cost specified by the preferred routing table + entry for the ASBR/forwarding address, and Y the cost + specified in the LSA. X is in terms of the link state + metric, and Y is a type 1 or 2 external metric. */ + + + /* (5) Look up the routing table entry for the destination N. If + no entry exists for N, install the AS external path to N, + with next hop equal to the list of next hops to the + forwarding address, and advertising router equal to ASBR. + If the external metric type is 1, then the path-type is set + to type 1 external and the cost is equal to X+Y. If the + external metric type is 2, the path-type is set to type 2 + external, the link state component of the route's cost is X, + and the type 2 cost is Y. */ + new = ospf_ase_calculate_new_route (lsa, asbr_route, metric); + + /* (6) Compare the AS external path described by the LSA with the + existing paths in N's routing table entry, as follows. If + the new path is preferred, it replaces the present paths in + N's routing table entry. If the new path is of equal + preference, it is added to N's routing table entry's list of + paths. */ + + /* Set prefix. */ + p.family = AF_INET; + p.prefix = al->header.id; + p.prefixlen = ip_masklen (al->mask); + + /* if there is a Intra/Inter area route to the N + do not install external route */ + if ((rn = route_node_lookup (ospf->new_table, + (struct prefix *) &p))) + { + route_unlock_node(rn); + if (rn->info == NULL) + zlog_info ("Route[External]: rn->info NULL"); + if (new) + ospf_route_free (new); + return 0; + } + /* Find a route to the same dest */ + /* If there is no route, create new one. */ + if ((rn = route_node_lookup (ospf->new_external_route, + (struct prefix *) &p))) + route_unlock_node(rn); + + if (!rn || (or = rn->info) == NULL) + { + if (IS_DEBUG_OSPF (lsa, LSA)) + zlog_debug ("Route[External]: Adding a new route %s/%d", + inet_ntoa (p.prefix), p.prefixlen); + + ospf_route_add (ospf->new_external_route, &p, new, asbr_route); + + if (al->e[0].fwd_addr.s_addr) + ospf_ase_complete_direct_routes (new, al->e[0].fwd_addr); + return 0; + } + else + { + /* (a) Intra-area and inter-area paths are always preferred + over AS external paths. + + (b) Type 1 external paths are always preferred over type 2 + external paths. When all paths are type 2 external + paths, the paths with the smallest advertised type 2 + metric are always preferred. */ + ret = ospf_route_cmp (ospf, new, or); + + /* (c) If the new AS external path is still indistinguishable + from the current paths in the N's routing table entry, + and RFC1583Compatibility is set to "disabled", select + the preferred paths based on the intra-AS paths to the + ASBR/forwarding addresses, as specified in Section + 16.4.1. + + (d) If the new AS external path is still indistinguishable + from the current paths in the N's routing table entry, + select the preferred path based on a least cost + comparison. Type 1 external paths are compared by + looking at the sum of the distance to the forwarding + address and the advertised type 1 metric (X+Y). Type 2 + external paths advertising equal type 2 metrics are + compared by looking at the distance to the forwarding + addresses. + */ + /* New route is better */ + if (ret < 0) + { + if (IS_DEBUG_OSPF (lsa, LSA)) + zlog_debug ("Route[External]: New route is better"); + ospf_route_subst (rn, new, asbr_route); + if (al->e[0].fwd_addr.s_addr) + ospf_ase_complete_direct_routes (new, al->e[0].fwd_addr); + or = new; + new = NULL; + } + /* Old route is better */ + else if (ret > 0) + { + if (IS_DEBUG_OSPF (lsa, LSA)) + zlog_debug ("Route[External]: Old route is better"); + /* do nothing */ + } + /* Routes are equal */ + else + { + if (IS_DEBUG_OSPF (lsa, LSA)) + zlog_debug ("Route[External]: Routes are equal"); + ospf_route_copy_nexthops (or, asbr_route->paths); + if (al->e[0].fwd_addr.s_addr) + ospf_ase_complete_direct_routes (or, al->e[0].fwd_addr); + } + } + /* Make sure setting newly calculated ASBR route.*/ + or->u.ext.asbr = asbr_route; + if (new) + ospf_route_free (new); + + lsa->route = or; + return 0; +} + +static int +ospf_ase_route_match_same (struct route_table *rt, struct prefix *prefix, + struct ospf_route *newor) +{ + struct route_node *rn; + struct ospf_route *or; + struct ospf_path *op; + struct ospf_path *newop; + struct listnode *n1; + struct listnode *n2; + + if (! rt || ! prefix) + return 0; + + rn = route_node_lookup (rt, prefix); + if (! rn) + return 0; + + route_unlock_node (rn); + + or = rn->info; + if (or->path_type != newor->path_type) + return 0; + + switch (or->path_type) + { + case OSPF_PATH_TYPE1_EXTERNAL: + if (or->cost != newor->cost) + return 0; + break; + case OSPF_PATH_TYPE2_EXTERNAL: + if ((or->cost != newor->cost) || + (or->u.ext.type2_cost != newor->u.ext.type2_cost)) + return 0; + break; + default: + assert (0); + return 0; + } + + if (or->paths->count != newor->paths->count) + return 0; + + /* Check each path. */ + for (n1 = listhead (or->paths), n2 = listhead (newor->paths); + n1 && n2; n1 = listnextnode (n1), n2 = listnextnode (n2)) + { + op = listgetdata (n1); + newop = listgetdata (n2); + + if (! IPV4_ADDR_SAME (&op->nexthop, &newop->nexthop)) + return 0; + if (op->ifindex != newop->ifindex) + return 0; + } + + if (or->u.ext.tag != newor->u.ext.tag) + return 0; + + return 1; +} + +static int +ospf_ase_compare_tables (struct route_table *new_external_route, + struct route_table *old_external_route) +{ + struct route_node *rn, *new_rn; + struct ospf_route *or; + + /* Remove deleted routes */ + for (rn = route_top (old_external_route); rn; rn = route_next (rn)) + if ((or = rn->info)) + { + if (! (new_rn = route_node_lookup (new_external_route, &rn->p))) + ospf_zebra_delete ((struct prefix_ipv4 *) &rn->p, or); + else + route_unlock_node (new_rn); + } + + + /* Install new routes */ + for (rn = route_top (new_external_route); rn; rn = route_next (rn)) + if ((or = rn->info) != NULL) + if (! ospf_ase_route_match_same (old_external_route, &rn->p, or)) + ospf_zebra_add ((struct prefix_ipv4 *) &rn->p, or); + + return 0; +} + +static int +ospf_ase_calculate_timer (struct thread *t) +{ + struct ospf *ospf; + struct ospf_lsa *lsa; + 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; + + if (ospf->ase_calc) + { + 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); + + /* This version simple adds to the table all NSSA areas */ + if (ospf->anyNSSA) + for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area)) + { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_ase_calculate_timer(): looking at area %s", + inet_ntoa (area->area_id)); + + if (area->external_routing == OSPF_AREA_NSSA) + LSDB_LOOP (NSSA_LSDB (area), rn, lsa) + ospf_ase_calculate_route (ospf, lsa); + } + /* kevinm: And add the NSSA routes in ospf_top */ + LSDB_LOOP (NSSA_LSDB (ospf),rn,lsa) + ospf_ase_calculate_route(ospf,lsa); + + /* Compare old and new external routing table and install the + difference info zebra/kernel */ + ospf_ase_compare_tables (ospf->new_external_route, + ospf->old_external_route); + + /* Delete old external routing table */ + 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; +} + +void +ospf_ase_calculate_schedule (struct ospf *ospf) +{ + if (ospf == NULL) + return; + + ospf->ase_calc = 1; +} + +void +ospf_ase_calculate_timer_add (struct ospf *ospf) +{ + if (ospf == NULL) + return; + + if (! ospf->t_ase_calc) + ospf->t_ase_calc = thread_add_timer (master, ospf_ase_calculate_timer, + ospf, OSPF_ASE_CALC_INTERVAL); +} + +void +ospf_ase_register_external_lsa (struct ospf_lsa *lsa, struct ospf *top) +{ + struct route_node *rn; + struct prefix_ipv4 p; + struct list *lst; + struct as_external_lsa *al; + + al = (struct as_external_lsa *) lsa->data; + p.family = AF_INET; + p.prefix = lsa->data->id; + p.prefixlen = ip_masklen (al->mask); + apply_mask_ipv4 (&p); + + 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 */ + listnode_add (lst, ospf_lsa_lock (lsa)); /* external_lsas lst */ +} + +void +ospf_ase_unregister_external_lsa (struct ospf_lsa *lsa, struct ospf *top) +{ + struct route_node *rn; + struct prefix_ipv4 p; + struct list *lst; + struct as_external_lsa *al; + + al = (struct as_external_lsa *) lsa->data; + p.family = AF_INET; + p.prefix = lsa->data->id; + p.prefixlen = ip_masklen (al->mask); + apply_mask_ipv4 (&p); + + rn = route_node_lookup (top->external_lsas, (struct prefix *) &p); + + if (rn) { + lst = rn->info; + listnode_delete (lst, lsa); + ospf_lsa_unlock (&lsa); /* external_lsas list */ + route_unlock_node (rn); + } +} + +void +ospf_ase_external_lsas_finish (struct route_table *rt) +{ + struct route_node *rn; + struct ospf_lsa *lsa; + struct list *lst; + struct listnode *node, *nnode; + + for (rn = route_top (rt); rn; rn = route_next (rn)) + if ((lst = rn->info) != NULL) + { + for (ALL_LIST_ELEMENTS (lst, node, nnode, lsa)) + ospf_lsa_unlock (&lsa); /* external_lsas lst */ + list_delete (lst); + } + + route_table_finish (rt); +} + +void +ospf_ase_incremental_update (struct ospf *ospf, struct ospf_lsa *lsa) +{ + struct list *lsas; + struct listnode *node; + struct route_node *rn, *rn2; + struct prefix_ipv4 p; + struct route_table *tmp_old; + struct as_external_lsa *al; + + al = (struct as_external_lsa *) lsa->data; + p.family = AF_INET; + p.prefix = lsa->data->id; + p.prefixlen = ip_masklen (al->mask); + apply_mask_ipv4 (&p); + + /* if new_table is NULL, there was no spf calculation, thus + incremental update is unneeded */ + if (!ospf->new_table) + return; + + /* If there is already an intra-area or inter-area route + to the destination, no recalculation is necessary + (internal routes take precedence). */ + + rn = route_node_lookup (ospf->new_table, (struct prefix *) &p); + if (rn) + { + route_unlock_node (rn); + if (rn->info) + return; + } + + rn = route_node_lookup (ospf->external_lsas, (struct prefix *) &p); + assert (rn); + assert (rn->info); + lsas = rn->info; + route_unlock_node (rn); + + for (ALL_LIST_ELEMENTS_RO (lsas, node, lsa)) + ospf_ase_calculate_route (ospf, lsa); + + /* prepare temporary old routing table for compare */ + tmp_old = route_table_init (); + rn = route_node_lookup (ospf->old_external_route, (struct prefix *) &p); + if (rn && rn->info) + { + rn2 = route_node_get (tmp_old, (struct prefix *) &p); + rn2->info = rn->info; + route_unlock_node (rn); + } + + /* install changes to zebra */ + ospf_ase_compare_tables (ospf->new_external_route, tmp_old); + + /* update ospf->old_external_route table */ + if (rn && rn->info) + ospf_route_free ((struct ospf_route *) rn->info); + + rn2 = route_node_lookup (ospf->new_external_route, (struct prefix *) &p); + /* if new route exists, install it to ospf->old_external_route */ + if (rn2 && rn2->info) + { + if (!rn) + rn = route_node_get (ospf->old_external_route, (struct prefix *) &p); + rn->info = rn2->info; + } + else + { + /* remove route node from ospf->old_external_route */ + if (rn) + { + rn->info = NULL; + route_unlock_node (rn); + } + } + + if (rn2) + { + /* rn2->info is stored in route node of ospf->old_external_route */ + rn2->info = NULL; + route_unlock_node (rn2); + route_unlock_node (rn2); + } + + route_table_finish (tmp_old); +} diff --git a/ospfd/ospf_ase.h b/ospfd/ospf_ase.h new file mode 100644 index 0000000..e6a1b2f --- /dev/null +++ b/ospfd/ospf_ase.h @@ -0,0 +1,47 @@ +/* + * OSPF AS External route calculation. + * Copyright (C) 1999, 2000 Alex Zinin, 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 _ZEBRA_OSPF_ASE_H +#define _ZEBRA_OSPF_ASE_H + + +extern struct ospf_route *ospf_find_asbr_route (struct ospf *, + struct route_table *, + struct prefix_ipv4 *); +extern struct ospf_route *ospf_find_asbr_route_through_area (struct + route_table *, + struct + prefix_ipv4 *, + struct ospf_area + *); + +extern int ospf_ase_calculate_route (struct ospf *, struct ospf_lsa *); +extern void ospf_ase_calculate_schedule (struct ospf *); +extern void ospf_ase_calculate_timer_add (struct ospf *); + +extern void ospf_ase_external_lsas_finish (struct route_table *); +extern void ospf_ase_incremental_update (struct ospf *, struct ospf_lsa *); +extern void ospf_ase_register_external_lsa (struct ospf_lsa *, struct ospf *); +extern void ospf_ase_unregister_external_lsa (struct ospf_lsa *, + struct ospf *); + +#endif /* _ZEBRA_OSPF_ASE_H */ diff --git a/ospfd/ospf_dump.c b/ospfd/ospf_dump.c new file mode 100644 index 0000000..be9fa2a --- /dev/null +++ b/ospfd/ospf_dump.c @@ -0,0 +1,1748 @@ +/* + * OSPFd dump routine. + * Copyright (C) 1999, 2000 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. + */ + +#include + +#include "linklist.h" +#include "thread.h" +#include "prefix.h" +#include "command.h" +#include "stream.h" +#include "log.h" +#include "sockopt.h" + +#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_dump.h" +#include "ospfd/ospf_packet.h" +#include "ospfd/ospf_network.h" + +const struct message ospf_ism_state_msg[] = +{ + { ISM_DependUpon, "DependUpon" }, + { ISM_Down, "Down" }, + { ISM_Loopback, "Loopback" }, + { ISM_Waiting, "Waiting" }, + { ISM_PointToPoint, "Point-To-Point" }, + { ISM_DROther, "DROther" }, + { ISM_Backup, "Backup" }, + { ISM_DR, "DR" }, +}; +const int ospf_ism_state_msg_max = OSPF_ISM_STATE_MAX; + +const struct message ospf_nsm_state_msg[] = +{ + { NSM_DependUpon, "DependUpon" }, + { NSM_Deleted, "Deleted" }, + { NSM_Down, "Down" }, + { NSM_Attempt, "Attempt" }, + { NSM_Init, "Init" }, + { NSM_TwoWay, "2-Way" }, + { NSM_ExStart, "ExStart" }, + { NSM_Exchange, "Exchange" }, + { NSM_Loading, "Loading" }, + { NSM_Full, "Full" }, +}; +const int ospf_nsm_state_msg_max = OSPF_NSM_STATE_MAX; + +const struct message ospf_lsa_type_msg[] = +{ + { OSPF_UNKNOWN_LSA, "unknown" }, + { OSPF_ROUTER_LSA, "router-LSA" }, + { OSPF_NETWORK_LSA, "network-LSA" }, + { OSPF_SUMMARY_LSA, "summary-LSA" }, + { OSPF_ASBR_SUMMARY_LSA, "summary-LSA" }, + { OSPF_AS_EXTERNAL_LSA, "AS-external-LSA" }, + { OSPF_GROUP_MEMBER_LSA, "GROUP MEMBER LSA" }, + { OSPF_AS_NSSA_LSA, "NSSA-LSA" }, + { 8, "Type-8 LSA" }, + { OSPF_OPAQUE_LINK_LSA, "Link-Local Opaque-LSA" }, + { OSPF_OPAQUE_AREA_LSA, "Area-Local Opaque-LSA" }, + { OSPF_OPAQUE_AS_LSA, "AS-external Opaque-LSA" }, +}; +const int ospf_lsa_type_msg_max = OSPF_MAX_LSA; + +const struct message ospf_link_state_id_type_msg[] = +{ + { OSPF_UNKNOWN_LSA, "(unknown)" }, + { OSPF_ROUTER_LSA, "" }, + { OSPF_NETWORK_LSA, "(address of Designated Router)" }, + { OSPF_SUMMARY_LSA, "(summary Network Number)" }, + { OSPF_ASBR_SUMMARY_LSA, "(AS Boundary Router address)" }, + { OSPF_AS_EXTERNAL_LSA, "(External Network Number)" }, + { OSPF_GROUP_MEMBER_LSA, "(Group membership information)" }, + { OSPF_AS_NSSA_LSA, "(External Network Number for NSSA)" }, + { 8, "(Type-8 LSID)" }, + { OSPF_OPAQUE_LINK_LSA, "(Link-Local Opaque-Type/ID)" }, + { OSPF_OPAQUE_AREA_LSA, "(Area-Local Opaque-Type/ID)" }, + { OSPF_OPAQUE_AS_LSA, "(AS-external Opaque-Type/ID)" }, +}; +const int ospf_link_state_id_type_msg_max = OSPF_MAX_LSA; + +const struct message ospf_network_type_msg[] = +{ + { OSPF_IFTYPE_NONE, "NONE" }, + { OSPF_IFTYPE_POINTOPOINT, "Point-to-Point" }, + { OSPF_IFTYPE_BROADCAST, "Broadcast" }, + { OSPF_IFTYPE_NBMA, "NBMA" }, + { OSPF_IFTYPE_POINTOMULTIPOINT, "Point-to-MultiPoint" }, + { OSPF_IFTYPE_VIRTUALLINK, "Virtual-Link" }, +}; +const int ospf_network_type_msg_max = OSPF_IFTYPE_MAX; + +/* AuType */ +const struct message ospf_auth_type_str[] = +{ + { OSPF_AUTH_NULL, "Null" }, + { OSPF_AUTH_SIMPLE, "Simple" }, + { OSPF_AUTH_CRYPTOGRAPHIC, "Cryptographic" }, +}; +const size_t ospf_auth_type_str_max = sizeof (ospf_auth_type_str) / + sizeof (ospf_auth_type_str[0]); + +/* Configuration debug option variables. */ +unsigned long conf_debug_ospf_packet[5] = {0, 0, 0, 0, 0}; +unsigned long conf_debug_ospf_event = 0; +unsigned long conf_debug_ospf_ism = 0; +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}; +unsigned long term_debug_ospf_event = 0; +unsigned long term_debug_ospf_ism = 0; +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) +{ + return (route_type == ZEBRA_ROUTE_MAX) ? + "Default" : zebra_route_string(route_type); +} + +#define OSPF_AREA_STRING_MAXLEN 16 +const char * +ospf_area_name_string (struct ospf_area *area) +{ + static char buf[OSPF_AREA_STRING_MAXLEN] = ""; + u_int32_t area_id; + + if (!area) + return "-"; + + area_id = ntohl (area->area_id.s_addr); + snprintf (buf, OSPF_AREA_STRING_MAXLEN, "%d.%d.%d.%d", + (area_id >> 24) & 0xff, (area_id >> 16) & 0xff, + (area_id >> 8) & 0xff, area_id & 0xff); + return buf; +} + +#define OSPF_AREA_DESC_STRING_MAXLEN 23 +const char * +ospf_area_desc_string (struct ospf_area *area) +{ + static char buf[OSPF_AREA_DESC_STRING_MAXLEN] = ""; + u_char type; + + if (!area) + return "(incomplete)"; + + type = area->external_routing; + switch (type) + { + case OSPF_AREA_NSSA: + snprintf (buf, OSPF_AREA_DESC_STRING_MAXLEN, "%s [NSSA]", + ospf_area_name_string (area)); + break; + case OSPF_AREA_STUB: + snprintf (buf, OSPF_AREA_DESC_STRING_MAXLEN, "%s [Stub]", + ospf_area_name_string (area)); + break; + default: + return ospf_area_name_string (area); + } + + return buf; +} + +#define OSPF_IF_STRING_MAXLEN 40 +const char * +ospf_if_name_string (struct ospf_interface *oi) +{ + static char buf[OSPF_IF_STRING_MAXLEN] = ""; + u_int32_t ifaddr; + + if (!oi) + return "inactive"; + + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) + return oi->ifp->name; + + ifaddr = ntohl (oi->address->u.prefix4.s_addr); + snprintf (buf, OSPF_IF_STRING_MAXLEN, + "%s:%d.%d.%d.%d", oi->ifp->name, + (ifaddr >> 24) & 0xff, (ifaddr >> 16) & 0xff, + (ifaddr >> 8) & 0xff, ifaddr & 0xff); + return buf; +} + + +void +ospf_nbr_state_message (struct ospf_neighbor *nbr, char *buf, size_t size) +{ + int state; + struct ospf_interface *oi = nbr->oi; + + if (IPV4_ADDR_SAME (&DR (oi), &nbr->address.u.prefix4)) + state = ISM_DR; + else if (IPV4_ADDR_SAME (&BDR (oi), &nbr->address.u.prefix4)) + state = ISM_Backup; + else + state = ISM_DROther; + + memset (buf, 0, size); + + snprintf (buf, size, "%s/%s", + LOOKUP (ospf_nsm_state_msg, nbr->state), + LOOKUP (ospf_ism_state_msg, state)); +} + +const char * +ospf_timeval_dump (struct timeval *t, char *buf, size_t size) +{ + /* Making formatted timer strings. */ +#define MINUTE_IN_SECONDS 60 +#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, us; + + if (!t) + return "inactive"; + + w = d = h = m = s = ms = us = 0; + memset (buf, 0, size); + + us = t->tv_usec; + if (us >= 1000) + { + ms = us / 1000; + us %= 1000; + } + + if (ms >= 1000) + { + t->tv_sec += ms / 1000; + ms %= 1000; + } + + if (t->tv_sec > WEEK_IN_SECONDS) + { + w = t->tv_sec / WEEK_IN_SECONDS; + t->tv_sec -= w * WEEK_IN_SECONDS; + } + + if (t->tv_sec > DAY_IN_SECONDS) + { + d = t->tv_sec / DAY_IN_SECONDS; + t->tv_sec -= d * DAY_IN_SECONDS; + } + + if (t->tv_sec >= HOUR_IN_SECONDS) + { + h = t->tv_sec / HOUR_IN_SECONDS; + t->tv_sec -= h * HOUR_IN_SECONDS; + } + + if (t->tv_sec >= MINUTE_IN_SECONDS) + { + m = t->tv_sec / MINUTE_IN_SECONDS; + t->tv_sec -= m * MINUTE_IN_SECONDS; + } + + if (w > 99) + snprintf (buf, size, "%ldw%1ldd", w, d); + else if (w) + snprintf (buf, size, "%ldw%1ldd%02ldh", w, d, h); + else if (d) + snprintf (buf, size, "%1ldd%02ldh%02ldm", d, h, m); + else if (h) + snprintf (buf, size, "%ldh%02ldm%02lds", h, m, (long)t->tv_sec); + else if (m) + 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 usecs", (long)t->tv_usec); + + return buf; +} + +const char * +ospf_timer_dump (struct thread *t, char *buf, size_t size) +{ + struct timeval result; + if (!t) + return "inactive"; + + result = tv_sub (t->u.sands, recent_relative_time()); + return ospf_timeval_dump (&result, buf, size); +} + +#define OSPF_OPTION_STR_MAXLEN 24 + +char * +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|%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_MT) ? "M/T" : "-"); + + return buf; +} + +static void +ospf_packet_hello_dump (struct stream *s, u_int16_t length) +{ + struct ospf_hello *hello; + int i; + + hello = (struct ospf_hello *) STREAM_PNT (s); + + zlog_debug ("Hello"); + zlog_debug (" NetworkMask %s", inet_ntoa (hello->network_mask)); + zlog_debug (" HelloInterval %d", ntohs (hello->hello_interval)); + zlog_debug (" Options %d (%s)", hello->options, + ospf_options_dump (hello->options)); + zlog_debug (" RtrPriority %d", hello->priority); + zlog_debug (" RtrDeadInterval %ld", (u_long)ntohl (hello->dead_interval)); + zlog_debug (" DRouter %s", inet_ntoa (hello->d_router)); + zlog_debug (" BDRouter %s", inet_ntoa (hello->bd_router)); + + length -= OSPF_HEADER_SIZE + OSPF_HELLO_MIN_SIZE; + zlog_debug (" # Neighbors %d", length / 4); + for (i = 0; length > 0; i++, length -= sizeof (struct in_addr)) + zlog_debug (" Neighbor %s", inet_ntoa (hello->neighbors[i])); +} + +static char * +ospf_dd_flags_dump (u_char flags, char *buf, size_t size) +{ + memset (buf, 0, size); + + snprintf (buf, size, "%s|%s|%s", + (flags & OSPF_DD_FLAG_I) ? "I" : "-", + (flags & OSPF_DD_FLAG_M) ? "M" : "-", + (flags & OSPF_DD_FLAG_MS) ? "MS" : "-"); + + return buf; +} + +void +ospf_lsa_header_dump (struct lsa_header *lsah) +{ + const char *lsah_type = LOOKUP (ospf_lsa_type_msg, lsah->type); + + zlog_debug (" LSA Header"); + zlog_debug (" LS age %d", ntohs (lsah->ls_age)); + zlog_debug (" Options %d (%s)", lsah->options, + ospf_options_dump (lsah->options)); + zlog_debug (" LS type %d (%s)", lsah->type, + (lsah->type ? lsah_type : "unknown type")); + zlog_debug (" Link State ID %s", inet_ntoa (lsah->id)); + zlog_debug (" Advertising Router %s", inet_ntoa (lsah->adv_router)); + zlog_debug (" LS sequence number 0x%lx", (u_long)ntohl (lsah->ls_seqnum)); + zlog_debug (" LS checksum 0x%x", ntohs (lsah->checksum)); + zlog_debug (" length %d", ntohs (lsah->length)); +} + +static char * +ospf_router_lsa_flags_dump (u_char flags, char *buf, size_t size) +{ + memset (buf, 0, size); + + snprintf (buf, size, "%s|%s|%s", + (flags & ROUTER_LSA_VIRTUAL) ? "V" : "-", + (flags & ROUTER_LSA_EXTERNAL) ? "E" : "-", + (flags & ROUTER_LSA_BORDER) ? "B" : "-"); + + return buf; +} + +static void +ospf_router_lsa_dump (struct stream *s, u_int16_t length) +{ + char buf[BUFSIZ]; + struct router_lsa *rl; + int i, len; + + rl = (struct router_lsa *) STREAM_PNT (s); + + zlog_debug (" Router-LSA"); + zlog_debug (" flags %s", + ospf_router_lsa_flags_dump (rl->flags, buf, BUFSIZ)); + zlog_debug (" # links %d", ntohs (rl->links)); + + len = ntohs (rl->header.length) - OSPF_LSA_HEADER_SIZE - 4; + for (i = 0; len > 0; i++) + { + zlog_debug (" Link ID %s", inet_ntoa (rl->link[i].link_id)); + zlog_debug (" Link Data %s", inet_ntoa (rl->link[i].link_data)); + zlog_debug (" Type %d", (u_char) rl->link[i].type); + zlog_debug (" TOS %d", (u_char) rl->link[i].tos); + zlog_debug (" metric %d", ntohs (rl->link[i].metric)); + + len -= 12; + } +} + +static void +ospf_network_lsa_dump (struct stream *s, u_int16_t length) +{ + struct network_lsa *nl; + int i, cnt; + + nl = (struct network_lsa *) STREAM_PNT (s); + cnt = (ntohs (nl->header.length) - (OSPF_LSA_HEADER_SIZE + 4)) / 4; + + zlog_debug (" Network-LSA"); + /* + zlog_debug ("LSA total size %d", ntohs (nl->header.length)); + zlog_debug ("Network-LSA size %d", + ntohs (nl->header.length) - OSPF_LSA_HEADER_SIZE); + */ + zlog_debug (" Network Mask %s", inet_ntoa (nl->mask)); + zlog_debug (" # Attached Routers %d", cnt); + for (i = 0; i < cnt; i++) + zlog_debug (" Attached Router %s", inet_ntoa (nl->routers[i])); +} + +static void +ospf_summary_lsa_dump (struct stream *s, u_int16_t length) +{ + struct summary_lsa *sl; + int size; + int i; + + sl = (struct summary_lsa *) STREAM_PNT (s); + + zlog_debug (" Summary-LSA"); + zlog_debug (" Network Mask %s", inet_ntoa (sl->mask)); + + size = ntohs (sl->header.length) - OSPF_LSA_HEADER_SIZE - 4; + for (i = 0; size > 0; size -= 4, i++) + zlog_debug (" TOS=%d metric %d", sl->tos, + GET_METRIC (sl->metric)); +} + +static void +ospf_as_external_lsa_dump (struct stream *s, u_int16_t length) +{ + struct as_external_lsa *al; + int size; + int i; + + al = (struct as_external_lsa *) STREAM_PNT (s); + zlog_debug (" %s", ospf_lsa_type_msg[al->header.type].str); + zlog_debug (" Network Mask %s", inet_ntoa (al->mask)); + + size = ntohs (al->header.length) - OSPF_LSA_HEADER_SIZE -4; + for (i = 0; size > 0; size -= 12, i++) + { + zlog_debug (" bit %s TOS=%d metric %d", + 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 %u", al->e[i].route_tag); + } +} + +static void +ospf_lsa_header_list_dump (struct stream *s, u_int16_t length) +{ + struct lsa_header *lsa; + + zlog_debug (" # LSA Headers %d", length / OSPF_LSA_HEADER_SIZE); + + /* LSA Headers. */ + while (length > 0) + { + lsa = (struct lsa_header *) STREAM_PNT (s); + ospf_lsa_header_dump (lsa); + + stream_forward_getp (s, OSPF_LSA_HEADER_SIZE); + length -= OSPF_LSA_HEADER_SIZE; + } +} + +static void +ospf_packet_db_desc_dump (struct stream *s, u_int16_t length) +{ + struct ospf_db_desc *dd; + char dd_flags[8]; + + u_int32_t gp; + + gp = stream_get_getp (s); + dd = (struct ospf_db_desc *) STREAM_PNT (s); + + zlog_debug ("Database Description"); + zlog_debug (" Interface MTU %d", ntohs (dd->mtu)); + zlog_debug (" Options %d (%s)", dd->options, + ospf_options_dump (dd->options)); + zlog_debug (" Flags %d (%s)", dd->flags, + ospf_dd_flags_dump (dd->flags, dd_flags, sizeof dd_flags)); + zlog_debug (" Sequence Number 0x%08lx", (u_long)ntohl (dd->dd_seqnum)); + + length -= OSPF_HEADER_SIZE + OSPF_DB_DESC_MIN_SIZE; + + stream_forward_getp (s, OSPF_DB_DESC_MIN_SIZE); + + ospf_lsa_header_list_dump (s, length); + + stream_set_getp (s, gp); +} + +static void +ospf_packet_ls_req_dump (struct stream *s, u_int16_t length) +{ + u_int32_t sp; + u_int32_t ls_type; + struct in_addr ls_id; + struct in_addr adv_router; + + sp = stream_get_getp (s); + + length -= OSPF_HEADER_SIZE; + + zlog_debug ("Link State Request"); + zlog_debug (" # Requests %d", length / 12); + + for (; length > 0; length -= 12) + { + ls_type = stream_getl (s); + ls_id.s_addr = stream_get_ipv4 (s); + adv_router.s_addr = stream_get_ipv4 (s); + + zlog_debug (" LS type %d", ls_type); + zlog_debug (" Link State ID %s", inet_ntoa (ls_id)); + zlog_debug (" Advertising Router %s", + inet_ntoa (adv_router)); + } + + stream_set_getp (s, sp); +} + +static void +ospf_packet_ls_upd_dump (struct stream *s, u_int16_t length) +{ + u_int32_t sp; + struct lsa_header *lsa; + int lsa_len; + u_int32_t count; + + length -= OSPF_HEADER_SIZE; + + sp = stream_get_getp (s); + + count = stream_getl (s); + length -= 4; + + zlog_debug ("Link State Update"); + zlog_debug (" # LSAs %d", count); + + while (length > 0 && count > 0) + { + if (length < OSPF_HEADER_SIZE || length % 4 != 0) + { + zlog_debug (" Remaining %d bytes; Incorrect length.", length); + break; + } + + lsa = (struct lsa_header *) STREAM_PNT (s); + lsa_len = ntohs (lsa->length); + ospf_lsa_header_dump (lsa); + + switch (lsa->type) + { + case OSPF_ROUTER_LSA: + ospf_router_lsa_dump (s, length); + break; + case OSPF_NETWORK_LSA: + ospf_network_lsa_dump (s, length); + break; + case OSPF_SUMMARY_LSA: + case OSPF_ASBR_SUMMARY_LSA: + ospf_summary_lsa_dump (s, length); + break; + case OSPF_AS_EXTERNAL_LSA: + ospf_as_external_lsa_dump (s, length); + break; + case OSPF_AS_NSSA_LSA: + ospf_as_external_lsa_dump (s, length); + break; + case OSPF_OPAQUE_LINK_LSA: + case OSPF_OPAQUE_AREA_LSA: + case OSPF_OPAQUE_AS_LSA: + ospf_opaque_lsa_dump (s, length); + break; + default: + break; + } + + stream_forward_getp (s, lsa_len); + length -= lsa_len; + count--; + } + + stream_set_getp (s, sp); +} + +static void +ospf_packet_ls_ack_dump (struct stream *s, u_int16_t length) +{ + u_int32_t sp; + + length -= OSPF_HEADER_SIZE; + sp = stream_get_getp (s); + + zlog_debug ("Link State Acknowledgment"); + ospf_lsa_header_list_dump (s, length); + + stream_set_getp (s, sp); +} + +/* Expects header to be in host order */ +void +ospf_ip_header_dump (struct ip *iph) +{ + /* IP Header dump. */ + zlog_debug ("ip_v %d", iph->ip_v); + zlog_debug ("ip_hl %d", iph->ip_hl); + zlog_debug ("ip_tos %d", iph->ip_tos); + zlog_debug ("ip_len %d", iph->ip_len); + zlog_debug ("ip_id %u", (u_int32_t) iph->ip_id); + zlog_debug ("ip_off %u", (u_int32_t) iph->ip_off); + zlog_debug ("ip_ttl %d", iph->ip_ttl); + zlog_debug ("ip_p %d", iph->ip_p); + zlog_debug ("ip_sum 0x%x", (u_int32_t) iph->ip_sum); + zlog_debug ("ip_src %s", inet_ntoa (iph->ip_src)); + zlog_debug ("ip_dst %s", inet_ntoa (iph->ip_dst)); +} + +static void +ospf_header_dump (struct ospf_header *ospfh) +{ + char buf[9]; + u_int16_t auth_type = ntohs (ospfh->auth_type); + + zlog_debug ("Header"); + zlog_debug (" Version %d", ospfh->version); + zlog_debug (" Type %d (%s)", ospfh->type, + LOOKUP (ospf_packet_type_str, ospfh->type)); + zlog_debug (" Packet Len %d", ntohs (ospfh->length)); + zlog_debug (" Router ID %s", inet_ntoa (ospfh->router_id)); + zlog_debug (" Area ID %s", inet_ntoa (ospfh->area_id)); + zlog_debug (" Checksum 0x%x", ntohs (ospfh->checksum)); + zlog_debug (" AuType %s", LOOKUP (ospf_auth_type_str, auth_type)); + + switch (auth_type) + { + case OSPF_AUTH_NULL: + break; + case OSPF_AUTH_SIMPLE: + memset (buf, 0, 9); + strncpy (buf, (char *) ospfh->u.auth_data, 8); + zlog_debug (" Simple Password %s", buf); + break; + case OSPF_AUTH_CRYPTOGRAPHIC: + zlog_debug (" Cryptographic Authentication"); + zlog_debug (" Key ID %d", ospfh->u.crypt.key_id); + zlog_debug (" Auth Data Len %d", ospfh->u.crypt.auth_data_len); + zlog_debug (" Sequence number %ld", + (u_long)ntohl (ospfh->u.crypt.crypt_seqnum)); + break; + default: + zlog_debug ("* This is not supported authentication type"); + break; + } + +} + +void +ospf_packet_dump (struct stream *s) +{ + struct ospf_header *ospfh; + unsigned long gp; + + /* Preserve pointer. */ + gp = stream_get_getp (s); + + /* OSPF Header dump. */ + ospfh = (struct ospf_header *) STREAM_PNT (s); + + /* Until detail flag is set, return. */ + if (!(term_debug_ospf_packet[ospfh->type - 1] & OSPF_DEBUG_DETAIL)) + return; + + /* Show OSPF header detail. */ + ospf_header_dump (ospfh); + stream_forward_getp (s, OSPF_HEADER_SIZE); + + switch (ospfh->type) + { + case OSPF_MSG_HELLO: + ospf_packet_hello_dump (s, ntohs (ospfh->length)); + break; + case OSPF_MSG_DB_DESC: + ospf_packet_db_desc_dump (s, ntohs (ospfh->length)); + break; + case OSPF_MSG_LS_REQ: + ospf_packet_ls_req_dump (s, ntohs (ospfh->length)); + break; + case OSPF_MSG_LS_UPD: + ospf_packet_ls_upd_dump (s, ntohs (ospfh->length)); + break; + case OSPF_MSG_LS_ACK: + ospf_packet_ls_ack_dump (s, ntohs (ospfh->length)); + break; + default: + break; + } + + stream_set_getp (s, gp); +} + + +/* + [no] debug ospf packet (hello|dd|ls-request|ls-update|ls-ack|all) + [send|recv [detail]] +*/ +DEFUN (debug_ospf_packet, + debug_ospf_packet_all_cmd, + "debug ospf packet (hello|dd|ls-request|ls-update|ls-ack|all)", + DEBUG_STR + OSPF_STR + "OSPF packets\n" + "OSPF Hello\n" + "OSPF Database Description\n" + "OSPF Link State Request\n" + "OSPF Link State Update\n" + "OSPF Link State Acknowledgment\n" + "OSPF all packets\n") +{ + int type = 0; + int flag = 0; + int i; + + assert (argc > 0); + + /* Check packet type. */ + if (strncmp (argv[0], "h", 1) == 0) + type = OSPF_DEBUG_HELLO; + else if (strncmp (argv[0], "d", 1) == 0) + type = OSPF_DEBUG_DB_DESC; + else if (strncmp (argv[0], "ls-r", 4) == 0) + type = OSPF_DEBUG_LS_REQ; + else if (strncmp (argv[0], "ls-u", 4) == 0) + type = OSPF_DEBUG_LS_UPD; + else if (strncmp (argv[0], "ls-a", 4) == 0) + type = OSPF_DEBUG_LS_ACK; + else if (strncmp (argv[0], "a", 1) == 0) + type = OSPF_DEBUG_ALL; + + /* Default, both send and recv. */ + if (argc == 1) + flag = OSPF_DEBUG_SEND | OSPF_DEBUG_RECV; + + /* send or recv. */ + if (argc >= 2) + { + if (strncmp (argv[1], "s", 1) == 0) + flag = OSPF_DEBUG_SEND; + else if (strncmp (argv[1], "r", 1) == 0) + flag = OSPF_DEBUG_RECV; + else if (strncmp (argv[1], "d", 1) == 0) + flag = OSPF_DEBUG_SEND | OSPF_DEBUG_RECV | OSPF_DEBUG_DETAIL; + } + + /* detail. */ + if (argc == 3) + if (strncmp (argv[2], "d", 1) == 0) + flag |= OSPF_DEBUG_DETAIL; + + for (i = 0; i < 5; i++) + if (type & (0x01 << i)) + { + if (vty->node == CONFIG_NODE) + DEBUG_PACKET_ON (i, flag); + else + TERM_DEBUG_PACKET_ON (i, flag); + } + + return CMD_SUCCESS; +} + +ALIAS (debug_ospf_packet, + debug_ospf_packet_send_recv_cmd, + "debug ospf packet (hello|dd|ls-request|ls-update|ls-ack|all) (send|recv|detail)", + "Debugging functions\n" + "OSPF information\n" + "OSPF packets\n" + "OSPF Hello\n" + "OSPF Database Description\n" + "OSPF Link State Request\n" + "OSPF Link State Update\n" + "OSPF Link State Acknowledgment\n" + "OSPF all packets\n" + "Packet sent\n" + "Packet received\n" + "Detail information\n") + +ALIAS (debug_ospf_packet, + debug_ospf_packet_send_recv_detail_cmd, + "debug ospf packet (hello|dd|ls-request|ls-update|ls-ack|all) (send|recv) (detail|)", + "Debugging functions\n" + "OSPF information\n" + "OSPF packets\n" + "OSPF Hello\n" + "OSPF Database Description\n" + "OSPF Link State Request\n" + "OSPF Link State Update\n" + "OSPF Link State Acknowledgment\n" + "OSPF all packets\n" + "Packet sent\n" + "Packet received\n" + "Detail Information\n") + + +DEFUN (no_debug_ospf_packet, + no_debug_ospf_packet_all_cmd, + "no debug ospf packet (hello|dd|ls-request|ls-update|ls-ack|all)", + NO_STR + DEBUG_STR + OSPF_STR + "OSPF packets\n" + "OSPF Hello\n" + "OSPF Database Description\n" + "OSPF Link State Request\n" + "OSPF Link State Update\n" + "OSPF Link State Acknowledgment\n" + "OSPF all packets\n") +{ + int type = 0; + int flag = 0; + int i; + + assert (argc > 0); + + /* Check packet type. */ + if (strncmp (argv[0], "h", 1) == 0) + type = OSPF_DEBUG_HELLO; + else if (strncmp (argv[0], "d", 1) == 0) + type = OSPF_DEBUG_DB_DESC; + else if (strncmp (argv[0], "ls-r", 4) == 0) + type = OSPF_DEBUG_LS_REQ; + else if (strncmp (argv[0], "ls-u", 4) == 0) + type = OSPF_DEBUG_LS_UPD; + else if (strncmp (argv[0], "ls-a", 4) == 0) + type = OSPF_DEBUG_LS_ACK; + else if (strncmp (argv[0], "a", 1) == 0) + type = OSPF_DEBUG_ALL; + + /* Default, both send and recv. */ + if (argc == 1) + flag = OSPF_DEBUG_SEND | OSPF_DEBUG_RECV | OSPF_DEBUG_DETAIL ; + + /* send or recv. */ + if (argc == 2) + { + if (strncmp (argv[1], "s", 1) == 0) + flag = OSPF_DEBUG_SEND | OSPF_DEBUG_DETAIL; + 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_SEND | OSPF_DEBUG_RECV | OSPF_DEBUG_DETAIL; + } + + /* detail. */ + if (argc == 3) + if (strncmp (argv[2], "d", 1) == 0) + flag = OSPF_DEBUG_DETAIL; + + for (i = 0; i < 5; i++) + if (type & (0x01 << i)) + { + if (vty->node == CONFIG_NODE) + DEBUG_PACKET_OFF (i, flag); + else + TERM_DEBUG_PACKET_OFF (i, flag); + } + +#ifdef DEBUG + /* + for (i = 0; i < 5; i++) + zlog_debug ("flag[%d] = %d", i, ospf_debug_packet[i]); + */ +#endif /* DEBUG */ + + return CMD_SUCCESS; +} + +ALIAS (no_debug_ospf_packet, + no_debug_ospf_packet_send_recv_cmd, + "no debug ospf packet (hello|dd|ls-request|ls-update|ls-ack|all) (send|recv|detail)", + NO_STR + "Debugging functions\n" + "OSPF information\n" + "OSPF packets\n" + "OSPF Hello\n" + "OSPF Database Description\n" + "OSPF Link State Request\n" + "OSPF Link State Update\n" + "OSPF Link State Acknowledgment\n" + "OSPF all packets\n" + "Packet sent\n" + "Packet received\n" + "Detail Information\n") + +ALIAS (no_debug_ospf_packet, + no_debug_ospf_packet_send_recv_detail_cmd, + "no debug ospf packet (hello|dd|ls-request|ls-update|ls-ack|all) (send|recv) (detail|)", + NO_STR + "Debugging functions\n" + "OSPF information\n" + "OSPF packets\n" + "OSPF Hello\n" + "OSPF Database Description\n" + "OSPF Link State Request\n" + "OSPF Link State Update\n" + "OSPF Link State Acknowledgment\n" + "OSPF all packets\n" + "Packet sent\n" + "Packet received\n" + "Detail Information\n") + + +DEFUN (debug_ospf_ism, + debug_ospf_ism_cmd, + "debug ospf ism", + DEBUG_STR + OSPF_STR + "OSPF Interface State Machine\n") +{ + if (vty->node == CONFIG_NODE) + { + if (argc == 0) + DEBUG_ON (ism, ISM); + else if (argc == 1) + { + if (strncmp (argv[0], "s", 1) == 0) + DEBUG_ON (ism, ISM_STATUS); + else if (strncmp (argv[0], "e", 1) == 0) + DEBUG_ON (ism, ISM_EVENTS); + else if (strncmp (argv[0], "t", 1) == 0) + DEBUG_ON (ism, ISM_TIMERS); + } + + return CMD_SUCCESS; + } + + /* ENABLE_NODE. */ + if (argc == 0) + TERM_DEBUG_ON (ism, ISM); + else if (argc == 1) + { + if (strncmp (argv[0], "s", 1) == 0) + TERM_DEBUG_ON (ism, ISM_STATUS); + else if (strncmp (argv[0], "e", 1) == 0) + TERM_DEBUG_ON (ism, ISM_EVENTS); + else if (strncmp (argv[0], "t", 1) == 0) + TERM_DEBUG_ON (ism, ISM_TIMERS); + } + + return CMD_SUCCESS; +} + +ALIAS (debug_ospf_ism, + debug_ospf_ism_sub_cmd, + "debug ospf ism (status|events|timers)", + DEBUG_STR + OSPF_STR + "OSPF Interface State Machine\n" + "ISM Status Information\n" + "ISM Event Information\n" + "ISM TImer Information\n") + +DEFUN (no_debug_ospf_ism, + no_debug_ospf_ism_cmd, + "no debug ospf ism", + NO_STR + DEBUG_STR + OSPF_STR + "OSPF Interface State Machine") +{ + if (vty->node == CONFIG_NODE) + { + if (argc == 0) + DEBUG_OFF (ism, ISM); + else if (argc == 1) + { + if (strncmp (argv[0], "s", 1) == 0) + DEBUG_OFF (ism, ISM_STATUS); + else if (strncmp (argv[0], "e", 1) == 0) + DEBUG_OFF (ism, ISM_EVENTS); + else if (strncmp (argv[0], "t", 1) == 0) + DEBUG_OFF (ism, ISM_TIMERS); + } + return CMD_SUCCESS; + } + + /* ENABLE_NODE. */ + if (argc == 0) + TERM_DEBUG_OFF (ism, ISM); + else if (argc == 1) + { + if (strncmp (argv[0], "s", 1) == 0) + TERM_DEBUG_OFF (ism, ISM_STATUS); + else if (strncmp (argv[0], "e", 1) == 0) + TERM_DEBUG_OFF (ism, ISM_EVENTS); + else if (strncmp (argv[0], "t", 1) == 0) + TERM_DEBUG_OFF (ism, ISM_TIMERS); + } + + return CMD_SUCCESS; +} + +ALIAS (no_debug_ospf_ism, + no_debug_ospf_ism_sub_cmd, + "no debug ospf ism (status|events|timers)", + NO_STR + "Debugging functions\n" + "OSPF information\n" + "OSPF Interface State Machine\n" + "ISM Status Information\n" + "ISM Event Information\n" + "ISM Timer Information\n") + + +DEFUN (debug_ospf_nsm, + debug_ospf_nsm_cmd, + "debug ospf nsm", + DEBUG_STR + OSPF_STR + "OSPF Neighbor State Machine\n") +{ + if (vty->node == CONFIG_NODE) + { + if (argc == 0) + DEBUG_ON (nsm, NSM); + else if (argc == 1) + { + if (strncmp (argv[0], "s", 1) == 0) + DEBUG_ON (nsm, NSM_STATUS); + else if (strncmp (argv[0], "e", 1) == 0) + DEBUG_ON (nsm, NSM_EVENTS); + else if (strncmp (argv[0], "t", 1) == 0) + DEBUG_ON (nsm, NSM_TIMERS); + } + + return CMD_SUCCESS; + } + + /* ENABLE_NODE. */ + if (argc == 0) + TERM_DEBUG_ON (nsm, NSM); + else if (argc == 1) + { + if (strncmp (argv[0], "s", 1) == 0) + TERM_DEBUG_ON (nsm, NSM_STATUS); + else if (strncmp (argv[0], "e", 1) == 0) + TERM_DEBUG_ON (nsm, NSM_EVENTS); + else if (strncmp (argv[0], "t", 1) == 0) + TERM_DEBUG_ON (nsm, NSM_TIMERS); + } + + return CMD_SUCCESS; +} + +ALIAS (debug_ospf_nsm, + debug_ospf_nsm_sub_cmd, + "debug ospf nsm (status|events|timers)", + DEBUG_STR + OSPF_STR + "OSPF Neighbor State Machine\n" + "NSM Status Information\n" + "NSM Event Information\n" + "NSM Timer Information\n") + +DEFUN (no_debug_ospf_nsm, + no_debug_ospf_nsm_cmd, + "no debug ospf nsm", + NO_STR + DEBUG_STR + OSPF_STR + "OSPF Neighbor State Machine") +{ + if (vty->node == CONFIG_NODE) + { + if (argc == 0) + DEBUG_OFF (nsm, NSM); + else if (argc == 1) + { + if (strncmp (argv[0], "s", 1) == 0) + DEBUG_OFF (nsm, NSM_STATUS); + else if (strncmp (argv[0], "e", 1) == 0) + DEBUG_OFF (nsm, NSM_EVENTS); + else if (strncmp (argv[0], "t", 1) == 0) + DEBUG_OFF (nsm, NSM_TIMERS); + } + + return CMD_SUCCESS; + } + + /* ENABLE_NODE. */ + if (argc == 0) + TERM_DEBUG_OFF (nsm, NSM); + else if (argc == 1) + { + if (strncmp (argv[0], "s", 1) == 0) + TERM_DEBUG_OFF (nsm, NSM_STATUS); + else if (strncmp (argv[0], "e", 1) == 0) + TERM_DEBUG_OFF (nsm, NSM_EVENTS); + else if (strncmp (argv[0], "t", 1) == 0) + TERM_DEBUG_OFF (nsm, NSM_TIMERS); + } + + return CMD_SUCCESS; +} + +ALIAS (no_debug_ospf_nsm, + no_debug_ospf_nsm_sub_cmd, + "no debug ospf nsm (status|events|timers)", + NO_STR + "Debugging functions\n" + "OSPF information\n" + "OSPF Interface State Machine\n" + "NSM Status Information\n" + "NSM Event Information\n" + "NSM Timer Information\n") + + +DEFUN (debug_ospf_lsa, + debug_ospf_lsa_cmd, + "debug ospf lsa", + DEBUG_STR + OSPF_STR + "OSPF Link State Advertisement\n") +{ + if (vty->node == CONFIG_NODE) + { + if (argc == 0) + DEBUG_ON (lsa, LSA); + else if (argc == 1) + { + if (strncmp (argv[0], "g", 1) == 0) + DEBUG_ON (lsa, LSA_GENERATE); + else if (strncmp (argv[0], "f", 1) == 0) + DEBUG_ON (lsa, LSA_FLOODING); + else if (strncmp (argv[0], "i", 1) == 0) + DEBUG_ON (lsa, LSA_INSTALL); + else if (strncmp (argv[0], "r", 1) == 0) + DEBUG_ON (lsa, LSA_REFRESH); + } + + return CMD_SUCCESS; + } + + /* ENABLE_NODE. */ + if (argc == 0) + TERM_DEBUG_ON (lsa, LSA); + else if (argc == 1) + { + if (strncmp (argv[0], "g", 1) == 0) + TERM_DEBUG_ON (lsa, LSA_GENERATE); + else if (strncmp (argv[0], "f", 1) == 0) + TERM_DEBUG_ON (lsa, LSA_FLOODING); + else if (strncmp (argv[0], "i", 1) == 0) + TERM_DEBUG_ON (lsa, LSA_INSTALL); + else if (strncmp (argv[0], "r", 1) == 0) + TERM_DEBUG_ON (lsa, LSA_REFRESH); + } + + return CMD_SUCCESS; +} + +ALIAS (debug_ospf_lsa, + debug_ospf_lsa_sub_cmd, + "debug ospf lsa (generate|flooding|install|refresh)", + DEBUG_STR + OSPF_STR + "OSPF Link State Advertisement\n" + "LSA Generation\n" + "LSA Flooding\n" + "LSA Install/Delete\n" + "LSA Refresh\n") + +DEFUN (no_debug_ospf_lsa, + no_debug_ospf_lsa_cmd, + "no debug ospf lsa", + NO_STR + DEBUG_STR + OSPF_STR + "OSPF Link State Advertisement\n") +{ + if (vty->node == CONFIG_NODE) + { + if (argc == 0) + DEBUG_OFF (lsa, LSA); + else if (argc == 1) + { + if (strncmp (argv[0], "g", 1) == 0) + DEBUG_OFF (lsa, LSA_GENERATE); + else if (strncmp (argv[0], "f", 1) == 0) + DEBUG_OFF (lsa, LSA_FLOODING); + else if (strncmp (argv[0], "i", 1) == 0) + DEBUG_OFF (lsa, LSA_INSTALL); + else if (strncmp (argv[0], "r", 1) == 0) + DEBUG_OFF (lsa, LSA_REFRESH); + } + + return CMD_SUCCESS; + } + + /* ENABLE_NODE. */ + if (argc == 0) + TERM_DEBUG_OFF (lsa, LSA); + else if (argc == 1) + { + if (strncmp (argv[0], "g", 1) == 0) + TERM_DEBUG_OFF (lsa, LSA_GENERATE); + else if (strncmp (argv[0], "f", 1) == 0) + TERM_DEBUG_OFF (lsa, LSA_FLOODING); + else if (strncmp (argv[0], "i", 1) == 0) + TERM_DEBUG_OFF (lsa, LSA_INSTALL); + else if (strncmp (argv[0], "r", 1) == 0) + TERM_DEBUG_OFF (lsa, LSA_REFRESH); + } + + return CMD_SUCCESS; +} + +ALIAS (no_debug_ospf_lsa, + no_debug_ospf_lsa_sub_cmd, + "no debug ospf lsa (generate|flooding|install|refresh)", + NO_STR + DEBUG_STR + OSPF_STR + "OSPF Link State Advertisement\n" + "LSA Generation\n" + "LSA Flooding\n" + "LSA Install/Delete\n" + "LSA Refres\n") + + +DEFUN (debug_ospf_zebra, + debug_ospf_zebra_cmd, + "debug ospf zebra", + DEBUG_STR + OSPF_STR + "OSPF Zebra information\n") +{ + if (vty->node == CONFIG_NODE) + { + if (argc == 0) + DEBUG_ON (zebra, ZEBRA); + else if (argc == 1) + { + if (strncmp (argv[0], "i", 1) == 0) + DEBUG_ON (zebra, ZEBRA_INTERFACE); + else if (strncmp (argv[0], "r", 1) == 0) + DEBUG_ON (zebra, ZEBRA_REDISTRIBUTE); + } + + return CMD_SUCCESS; + } + + /* ENABLE_NODE. */ + if (argc == 0) + TERM_DEBUG_ON (zebra, ZEBRA); + else if (argc == 1) + { + if (strncmp (argv[0], "i", 1) == 0) + TERM_DEBUG_ON (zebra, ZEBRA_INTERFACE); + else if (strncmp (argv[0], "r", 1) == 0) + TERM_DEBUG_ON (zebra, ZEBRA_REDISTRIBUTE); + } + + return CMD_SUCCESS; +} + +ALIAS (debug_ospf_zebra, + debug_ospf_zebra_sub_cmd, + "debug ospf zebra (interface|redistribute)", + DEBUG_STR + OSPF_STR + "OSPF Zebra information\n" + "Zebra interface\n" + "Zebra redistribute\n") + +DEFUN (no_debug_ospf_zebra, + no_debug_ospf_zebra_cmd, + "no debug ospf zebra", + NO_STR + DEBUG_STR + OSPF_STR + "OSPF Zebra information\n") +{ + if (vty->node == CONFIG_NODE) + { + if (argc == 0) + DEBUG_OFF (zebra, ZEBRA); + else if (argc == 1) + { + if (strncmp (argv[0], "i", 1) == 0) + DEBUG_OFF (zebra, ZEBRA_INTERFACE); + else if (strncmp (argv[0], "r", 1) == 0) + DEBUG_OFF (zebra, ZEBRA_REDISTRIBUTE); + } + + return CMD_SUCCESS; + } + + /* ENABLE_NODE. */ + if (argc == 0) + TERM_DEBUG_OFF (zebra, ZEBRA); + else if (argc == 1) + { + if (strncmp (argv[0], "i", 1) == 0) + TERM_DEBUG_OFF (zebra, ZEBRA_INTERFACE); + else if (strncmp (argv[0], "r", 1) == 0) + TERM_DEBUG_OFF (zebra, ZEBRA_REDISTRIBUTE); + } + + return CMD_SUCCESS; +} + +ALIAS (no_debug_ospf_zebra, + no_debug_ospf_zebra_sub_cmd, + "no debug ospf zebra (interface|redistribute)", + NO_STR + DEBUG_STR + OSPF_STR + "OSPF Zebra information\n" + "Zebra interface\n" + "Zebra redistribute\n") + +DEFUN (debug_ospf_event, + debug_ospf_event_cmd, + "debug ospf event", + DEBUG_STR + OSPF_STR + "OSPF event information\n") +{ + if (vty->node == CONFIG_NODE) + CONF_DEBUG_ON (event, EVENT); + TERM_DEBUG_ON (event, EVENT); + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf_event, + no_debug_ospf_event_cmd, + "no debug ospf event", + NO_STR + DEBUG_STR + OSPF_STR + "OSPF event information\n") +{ + if (vty->node == CONFIG_NODE) + CONF_DEBUG_OFF (event, EVENT); + TERM_DEBUG_OFF (event, EVENT); + return CMD_SUCCESS; +} + +DEFUN (debug_ospf_nssa, + debug_ospf_nssa_cmd, + "debug ospf nssa", + DEBUG_STR + OSPF_STR + "OSPF nssa information\n") +{ + if (vty->node == CONFIG_NODE) + CONF_DEBUG_ON (nssa, NSSA); + TERM_DEBUG_ON (nssa, NSSA); + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf_nssa, + no_debug_ospf_nssa_cmd, + "no debug ospf nssa", + NO_STR + DEBUG_STR + OSPF_STR + "OSPF nssa information\n") +{ + if (vty->node == CONFIG_NODE) + CONF_DEBUG_OFF (nssa, NSSA); + TERM_DEBUG_OFF (nssa, 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", + SHOW_STR + DEBUG_STR + OSPF_STR) +{ + int i; + + vty_out (vty, "OSPF debugging status:%s", VTY_NEWLINE); + + /* Show debug status for events. */ + if (IS_DEBUG_OSPF(event,EVENT)) + vty_out (vty, " OSPF event debugging is on%s", VTY_NEWLINE); + + /* Show debug status for ISM. */ + if (IS_DEBUG_OSPF (ism, ISM) == OSPF_DEBUG_ISM) + vty_out (vty, " OSPF ISM debugging is on%s", VTY_NEWLINE); + else + { + if (IS_DEBUG_OSPF (ism, ISM_STATUS)) + vty_out (vty, " OSPF ISM status debugging is on%s", VTY_NEWLINE); + if (IS_DEBUG_OSPF (ism, ISM_EVENTS)) + vty_out (vty, " OSPF ISM event debugging is on%s", VTY_NEWLINE); + if (IS_DEBUG_OSPF (ism, ISM_TIMERS)) + vty_out (vty, " OSPF ISM timer debugging is on%s", VTY_NEWLINE); + } + + /* Show debug status for NSM. */ + if (IS_DEBUG_OSPF (nsm, NSM) == OSPF_DEBUG_NSM) + vty_out (vty, " OSPF NSM debugging is on%s", VTY_NEWLINE); + else + { + if (IS_DEBUG_OSPF (nsm, NSM_STATUS)) + vty_out (vty, " OSPF NSM status debugging is on%s", VTY_NEWLINE); + if (IS_DEBUG_OSPF (nsm, NSM_EVENTS)) + vty_out (vty, " OSPF NSM event debugging is on%s", VTY_NEWLINE); + if (IS_DEBUG_OSPF (nsm, NSM_TIMERS)) + vty_out (vty, " OSPF NSM timer debugging is on%s", VTY_NEWLINE); + } + + /* Show debug status for OSPF Packets. */ + for (i = 0; i < 5; i++) + if (IS_DEBUG_OSPF_PACKET (i, SEND) && IS_DEBUG_OSPF_PACKET (i, RECV)) + { + vty_out (vty, " OSPF packet %s%s debugging is on%s", + LOOKUP (ospf_packet_type_str, i + 1), + IS_DEBUG_OSPF_PACKET (i, DETAIL) ? " detail" : "", + VTY_NEWLINE); + } + else + { + if (IS_DEBUG_OSPF_PACKET (i, SEND)) + vty_out (vty, " OSPF packet %s send%s debugging is on%s", + LOOKUP (ospf_packet_type_str, i + 1), + IS_DEBUG_OSPF_PACKET (i, DETAIL) ? " detail" : "", + VTY_NEWLINE); + if (IS_DEBUG_OSPF_PACKET (i, RECV)) + vty_out (vty, " OSPF packet %s receive%s debugging is on%s", + LOOKUP (ospf_packet_type_str, i + 1), + IS_DEBUG_OSPF_PACKET (i, DETAIL) ? " detail" : "", + VTY_NEWLINE); + } + + /* Show debug status for OSPF LSAs. */ + if (IS_DEBUG_OSPF (lsa, LSA) == OSPF_DEBUG_LSA) + vty_out (vty, " OSPF LSA debugging is on%s", VTY_NEWLINE); + else + { + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + vty_out (vty, " OSPF LSA generation debugging is on%s", VTY_NEWLINE); + if (IS_DEBUG_OSPF (lsa, LSA_FLOODING)) + vty_out (vty, " OSPF LSA flooding debugging is on%s", VTY_NEWLINE); + if (IS_DEBUG_OSPF (lsa, LSA_INSTALL)) + vty_out (vty, " OSPF LSA install debugging is on%s", VTY_NEWLINE); + if (IS_DEBUG_OSPF (lsa, LSA_REFRESH)) + vty_out (vty, " OSPF LSA refresh debugging is on%s", VTY_NEWLINE); + } + + /* Show debug status for Zebra. */ + if (IS_DEBUG_OSPF (zebra, ZEBRA) == OSPF_DEBUG_ZEBRA) + vty_out (vty, " OSPF Zebra debugging is on%s", VTY_NEWLINE); + else + { + if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE)) + vty_out (vty, " OSPF Zebra interface debugging is on%s", VTY_NEWLINE); + if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE)) + vty_out (vty, " OSPF Zebra redistribute debugging is on%s", VTY_NEWLINE); + } + + /* Show debug status for NSSA. */ + if (IS_DEBUG_OSPF (nssa, NSSA) == OSPF_DEBUG_NSSA) + vty_out (vty, " OSPF NSSA debugging is on%s", VTY_NEWLINE); + + return CMD_SUCCESS; +} + +/* Debug node. */ +static struct cmd_node debug_node = +{ + DEBUG_NODE, + "", + 1 /* VTYSH */ +}; + +static int +config_write_debug (struct vty *vty) +{ + int write = 0; + int i, r; + + const char *type_str[] = {"hello", "dd", "ls-request", "ls-update", "ls-ack"}; + const char *detail_str[] = {"", " send", " recv", "", " detail", + " send detail", " recv detail", " detail"}; + + /* debug ospf ism (status|events|timers). */ + if (IS_CONF_DEBUG_OSPF (ism, ISM) == OSPF_DEBUG_ISM) + vty_out (vty, "debug ospf ism%s", VTY_NEWLINE); + else + { + if (IS_CONF_DEBUG_OSPF (ism, ISM_STATUS)) + vty_out (vty, "debug ospf ism status%s", VTY_NEWLINE); + if (IS_CONF_DEBUG_OSPF (ism, ISM_EVENTS)) + vty_out (vty, "debug ospf ism event%s", VTY_NEWLINE); + if (IS_CONF_DEBUG_OSPF (ism, ISM_TIMERS)) + vty_out (vty, "debug ospf ism timer%s", VTY_NEWLINE); + } + + /* debug ospf nsm (status|events|timers). */ + if (IS_CONF_DEBUG_OSPF (nsm, NSM) == OSPF_DEBUG_NSM) + vty_out (vty, "debug ospf nsm%s", VTY_NEWLINE); + else + { + if (IS_CONF_DEBUG_OSPF (nsm, NSM_STATUS)) + vty_out (vty, "debug ospf nsm status%s", VTY_NEWLINE); + if (IS_CONF_DEBUG_OSPF (nsm, NSM_EVENTS)) + vty_out (vty, "debug ospf nsm event%s", VTY_NEWLINE); + if (IS_CONF_DEBUG_OSPF (nsm, NSM_TIMERS)) + vty_out (vty, "debug ospf nsm timer%s", VTY_NEWLINE); + } + + /* debug ospf lsa (generate|flooding|install|refresh). */ + if (IS_CONF_DEBUG_OSPF (lsa, LSA) == OSPF_DEBUG_LSA) + vty_out (vty, "debug ospf lsa%s", VTY_NEWLINE); + else + { + if (IS_CONF_DEBUG_OSPF (lsa, LSA_GENERATE)) + vty_out (vty, "debug ospf lsa generate%s", VTY_NEWLINE); + if (IS_CONF_DEBUG_OSPF (lsa, LSA_FLOODING)) + vty_out (vty, "debug ospf lsa flooding%s", VTY_NEWLINE); + if (IS_CONF_DEBUG_OSPF (lsa, LSA_INSTALL)) + vty_out (vty, "debug ospf lsa install%s", VTY_NEWLINE); + if (IS_CONF_DEBUG_OSPF (lsa, LSA_REFRESH)) + vty_out (vty, "debug ospf lsa refresh%s", VTY_NEWLINE); + + write = 1; + } + + /* debug ospf zebra (interface|redistribute). */ + if (IS_CONF_DEBUG_OSPF (zebra, ZEBRA) == OSPF_DEBUG_ZEBRA) + vty_out (vty, "debug ospf zebra%s", VTY_NEWLINE); + else + { + if (IS_CONF_DEBUG_OSPF (zebra, ZEBRA_INTERFACE)) + vty_out (vty, "debug ospf zebra interface%s", VTY_NEWLINE); + if (IS_CONF_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE)) + vty_out (vty, "debug ospf zebra redistribute%s", VTY_NEWLINE); + + write = 1; + } + + /* debug ospf event. */ + if (IS_CONF_DEBUG_OSPF (event, EVENT) == OSPF_DEBUG_EVENT) + { + vty_out (vty, "debug ospf event%s", VTY_NEWLINE); + write = 1; + } + + /* debug ospf nssa. */ + if (IS_CONF_DEBUG_OSPF (nssa, NSSA) == OSPF_DEBUG_NSSA) + { + vty_out (vty, "debug ospf nssa%s", VTY_NEWLINE); + write = 1; + } + + /* debug ospf packet all detail. */ + r = OSPF_DEBUG_SEND_RECV|OSPF_DEBUG_DETAIL; + for (i = 0; i < 5; i++) + r &= conf_debug_ospf_packet[i] & (OSPF_DEBUG_SEND_RECV|OSPF_DEBUG_DETAIL); + if (r == (OSPF_DEBUG_SEND_RECV|OSPF_DEBUG_DETAIL)) + { + vty_out (vty, "debug ospf packet all detail%s", VTY_NEWLINE); + return 1; + } + + /* debug ospf packet all. */ + r = OSPF_DEBUG_SEND_RECV; + for (i = 0; i < 5; i++) + r &= conf_debug_ospf_packet[i] & OSPF_DEBUG_SEND_RECV; + if (r == OSPF_DEBUG_SEND_RECV) + { + vty_out (vty, "debug ospf packet all%s", VTY_NEWLINE); + for (i = 0; i < 5; i++) + if (conf_debug_ospf_packet[i] & OSPF_DEBUG_DETAIL) + vty_out (vty, "debug ospf packet %s detail%s", + type_str[i], + VTY_NEWLINE); + return 1; + } + + /* debug ospf packet (hello|dd|ls-request|ls-update|ls-ack) + (send|recv) (detail). */ + for (i = 0; i < 5; i++) + { + if (conf_debug_ospf_packet[i] == 0) + continue; + + vty_out (vty, "debug ospf packet %s%s%s", + type_str[i], detail_str[conf_debug_ospf_packet[i]], + VTY_NEWLINE); + write = 1; + } + + return write; +} + +/* Initialize debug commands. */ +void +debug_init () +{ + install_node (&debug_node, config_write_debug); + + install_element (ENABLE_NODE, &show_debugging_ospf_cmd); + install_element (ENABLE_NODE, &debug_ospf_packet_send_recv_detail_cmd); + install_element (ENABLE_NODE, &debug_ospf_packet_send_recv_cmd); + install_element (ENABLE_NODE, &debug_ospf_packet_all_cmd); + install_element (ENABLE_NODE, &debug_ospf_ism_sub_cmd); + install_element (ENABLE_NODE, &debug_ospf_ism_cmd); + install_element (ENABLE_NODE, &debug_ospf_nsm_sub_cmd); + install_element (ENABLE_NODE, &debug_ospf_nsm_cmd); + install_element (ENABLE_NODE, &debug_ospf_lsa_sub_cmd); + install_element (ENABLE_NODE, &debug_ospf_lsa_cmd); + install_element (ENABLE_NODE, &debug_ospf_zebra_sub_cmd); + 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); + install_element (ENABLE_NODE, &no_debug_ospf_ism_sub_cmd); + install_element (ENABLE_NODE, &no_debug_ospf_ism_cmd); + install_element (ENABLE_NODE, &no_debug_ospf_nsm_sub_cmd); + install_element (ENABLE_NODE, &no_debug_ospf_nsm_cmd); + install_element (ENABLE_NODE, &no_debug_ospf_lsa_sub_cmd); + install_element (ENABLE_NODE, &no_debug_ospf_lsa_cmd); + install_element (ENABLE_NODE, &no_debug_ospf_zebra_sub_cmd); + 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); + install_element (CONFIG_NODE, &debug_ospf_packet_all_cmd); + install_element (CONFIG_NODE, &debug_ospf_ism_sub_cmd); + install_element (CONFIG_NODE, &debug_ospf_ism_cmd); + install_element (CONFIG_NODE, &debug_ospf_nsm_sub_cmd); + install_element (CONFIG_NODE, &debug_ospf_nsm_cmd); + install_element (CONFIG_NODE, &debug_ospf_lsa_sub_cmd); + install_element (CONFIG_NODE, &debug_ospf_lsa_cmd); + install_element (CONFIG_NODE, &debug_ospf_zebra_sub_cmd); + 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); + install_element (CONFIG_NODE, &no_debug_ospf_ism_sub_cmd); + install_element (CONFIG_NODE, &no_debug_ospf_ism_cmd); + install_element (CONFIG_NODE, &no_debug_ospf_nsm_sub_cmd); + install_element (CONFIG_NODE, &no_debug_ospf_nsm_cmd); + install_element (CONFIG_NODE, &no_debug_ospf_lsa_sub_cmd); + install_element (CONFIG_NODE, &no_debug_ospf_lsa_cmd); + install_element (CONFIG_NODE, &no_debug_ospf_zebra_sub_cmd); + 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 new file mode 100644 index 0000000..f843df0 --- /dev/null +++ b/ospfd/ospf_dump.h @@ -0,0 +1,148 @@ +/* + * OSPFd dump routine. + * Copyright (C) 1999 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 _ZEBRA_OSPF_DUMP_H +#define _ZEBRA_OSPF_DUMP_H + +/* Debug Flags. */ +#define OSPF_DEBUG_HELLO 0x01 +#define OSPF_DEBUG_DB_DESC 0x02 +#define OSPF_DEBUG_LS_REQ 0x04 +#define OSPF_DEBUG_LS_UPD 0x08 +#define OSPF_DEBUG_LS_ACK 0x10 +#define OSPF_DEBUG_ALL 0x1f + +#define OSPF_DEBUG_SEND 0x01 +#define OSPF_DEBUG_RECV 0x02 +#define OSPF_DEBUG_SEND_RECV 0x03 +#define OSPF_DEBUG_DETAIL 0x04 + +#define OSPF_DEBUG_ISM_STATUS 0x01 +#define OSPF_DEBUG_ISM_EVENTS 0x02 +#define OSPF_DEBUG_ISM_TIMERS 0x04 +#define OSPF_DEBUG_ISM 0x07 +#define OSPF_DEBUG_NSM_STATUS 0x01 +#define OSPF_DEBUG_NSM_EVENTS 0x02 +#define OSPF_DEBUG_NSM_TIMERS 0x04 +#define OSPF_DEBUG_NSM 0x07 + +#define OSPF_DEBUG_LSA_GENERATE 0x01 +#define OSPF_DEBUG_LSA_FLOODING 0x02 +#define OSPF_DEBUG_LSA_INSTALL 0x04 +#define OSPF_DEBUG_LSA_REFRESH 0x08 +#define OSPF_DEBUG_LSA 0x0F + +#define OSPF_DEBUG_ZEBRA_INTERFACE 0x01 +#define OSPF_DEBUG_ZEBRA_REDISTRIBUTE 0x02 +#define OSPF_DEBUG_ZEBRA 0x03 + +#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) +#define CONF_DEBUG_PACKET_OFF(a, b) conf_debug_ospf_packet[a] &= ~(b) +#define TERM_DEBUG_PACKET_ON(a, b) term_debug_ospf_packet[a] |= (b) +#define TERM_DEBUG_PACKET_OFF(a, b) term_debug_ospf_packet[a] &= ~(b) +#define DEBUG_PACKET_ON(a, b) \ + do { \ + CONF_DEBUG_PACKET_ON(a, b); \ + TERM_DEBUG_PACKET_ON(a, b); \ + } while (0) +#define DEBUG_PACKET_OFF(a, b) \ + do { \ + CONF_DEBUG_PACKET_OFF(a, b); \ + TERM_DEBUG_PACKET_OFF(a, b); \ + } while (0) + +#define CONF_DEBUG_ON(a, b) conf_debug_ospf_ ## a |= (OSPF_DEBUG_ ## b) +#define CONF_DEBUG_OFF(a, b) conf_debug_ospf_ ## a &= ~(OSPF_DEBUG_ ## b) +#define TERM_DEBUG_ON(a, b) term_debug_ospf_ ## a |= (OSPF_DEBUG_ ## b) +#define TERM_DEBUG_OFF(a, b) term_debug_ospf_ ## a &= ~(OSPF_DEBUG_ ## b) +#define DEBUG_ON(a, b) \ + do { \ + CONF_DEBUG_ON(a, b); \ + TERM_DEBUG_ON(a, b); \ + } while (0) +#define DEBUG_OFF(a, b) \ + do { \ + CONF_DEBUG_OFF(a, b); \ + TERM_DEBUG_OFF(a, b); \ + } while (0) + +/* Macro for checking debug option. */ +#define IS_DEBUG_OSPF_PACKET(a, b) \ + (term_debug_ospf_packet[a] & OSPF_DEBUG_ ## b) +#define IS_DEBUG_OSPF(a, b) \ + (term_debug_ospf_ ## a & OSPF_DEBUG_ ## b) +#define IS_DEBUG_OSPF_EVENT IS_DEBUG_OSPF(event,EVENT) + +#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) \ + (conf_debug_ospf_ ## a & OSPF_DEBUG_ ## b) + +#ifdef ORIGINAL_CODING +#else /* ORIGINAL_CODING */ +struct stream; +#endif /* ORIGINAL_CODING */ + +#define AREA_NAME(A) ospf_area_name_string ((A)) +#define IF_NAME(I) ospf_if_name_string ((I)) + +/* Extern debug flag. */ +extern unsigned long term_debug_ospf_packet[]; +extern unsigned long term_debug_ospf_event; +extern unsigned long term_debug_ospf_ism; +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[]; +extern const struct message ospf_auth_type_str[]; +extern const size_t ospf_auth_type_str_max; + +/* Prototypes. */ +extern const char *ospf_area_name_string (struct ospf_area *); +extern const char *ospf_area_desc_string (struct ospf_area *); +extern const char *ospf_if_name_string (struct ospf_interface *); +extern void ospf_nbr_state_message (struct ospf_neighbor *, char *, size_t); +extern char *ospf_options_dump (u_char); +extern const char *ospf_timer_dump (struct thread *, char *, size_t); +extern const char *ospf_timeval_dump (struct timeval *, char *, size_t); +extern void ospf_ip_header_dump (struct ip *); +extern void ospf_packet_dump (struct stream *); +extern void ospf_lsa_header_dump (struct lsa_header *); +extern void debug_init (void); + +/* Appropriate buffer size to use with ospf_timer_dump and ospf_timeval_dump: */ +#define OSPF_TIME_DUMP_SIZE 16 + +#endif /* _ZEBRA_OSPF_DUMP_H */ diff --git a/ospfd/ospf_flood.c b/ospfd/ospf_flood.c new file mode 100644 index 0000000..8de4974 --- /dev/null +++ b/ospfd/ospf_flood.c @@ -0,0 +1,997 @@ +/* + * OSPF Flooding -- RFC2328 Section 13. + * Copyright (C) 1999, 2000 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. + */ + +#include + +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "command.h" +#include "table.h" +#include "thread.h" +#include "memory.h" +#include "log.h" +#include "zclient.h" + +#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_spf.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_packet.h" +#include "ospfd/ospf_abr.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_zebra.h" +#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. */ +static void +ospf_flood_delayed_lsa_ack (struct ospf_neighbor *inbr, struct ospf_lsa *lsa) +{ + /* LSA is more recent than database copy, but was not + flooded back out receiving interface. Delayed + acknowledgment sent. If interface is in Backup state + delayed acknowledgment sent only if advertisement + received from Designated Router, otherwise do nothing See + RFC 2328 Section 13.5 */ + + /* Whether LSA is more recent or not, and whether this is in + response to the LSA being sent out recieving interface has been + worked out previously */ + + /* Deal with router as BDR */ + if (inbr->oi->state == ISM_Backup && ! NBR_IS_DR (inbr)) + return; + + /* Schedule a delayed LSA Ack to be sent */ + listnode_add (inbr->oi->ls_ack, ospf_lsa_lock (lsa)); /* delayed LSA Ack */ +} + +/* Check LSA is related to external info. */ +struct external_info * +ospf_external_info_check (struct ospf_lsa *lsa) +{ + struct as_external_lsa *al; + struct prefix_ipv4 p; + struct route_node *rn; + int type; + + al = (struct as_external_lsa *) lsa->data; + + p.family = AF_INET; + p.prefix = lsa->data->id; + p.prefixlen = ip_masklen (al->mask); + + for (type = 0; type <= ZEBRA_ROUTE_MAX; type++) + { + int redist_type = is_prefix_default (&p) ? DEFAULT_ROUTE : type; + if (ospf_is_type_redistributed (redist_type)) + if (EXTERNAL_INFO (type)) + { + rn = route_node_lookup (EXTERNAL_INFO (type), + (struct prefix *) &p); + if (rn) + { + route_unlock_node (rn); + if (rn->info != NULL) + return (struct external_info *) rn->info; + } + } + } + + return NULL; +} + +static void +ospf_process_self_originated_lsa (struct ospf *ospf, + struct ospf_lsa *new, struct ospf_area *area) +{ + struct ospf_interface *oi; + struct external_info *ei; + struct listnode *node; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("LSA[Type%d:%s]: Process self-originated LSA seq 0x%x", + new->data->type, inet_ntoa (new->data->id), + ntohl(new->data->ls_seqnum)); + + /* If we're here, we installed a self-originated LSA that we received + from a neighbor, i.e. it's more recent. We must see whether we want + to originate it. + If yes, we should use this LSA's sequence number and reoriginate + a new instance. + if not --- we must flush this LSA from the domain. */ + switch (new->data->type) + { + case OSPF_ROUTER_LSA: + /* Originate a new instance and schedule flooding */ + if (area->router_lsa_self) + area->router_lsa_self->data->ls_seqnum = new->data->ls_seqnum; + ospf_router_lsa_update_area (area); + return; + case OSPF_NETWORK_LSA: + case OSPF_OPAQUE_LINK_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 + schedule flooding. */ + + /* Look through all interfaces, not just area, since interface + could be moved from one area to another. */ + for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi)) + /* These are sanity check. */ + if (IPV4_ADDR_SAME (&oi->address->u.prefix4, &new->data->id)) + { + if (oi->area != area || + oi->type != OSPF_IFTYPE_BROADCAST || + !IPV4_ADDR_SAME (&oi->address->u.prefix4, &DR (oi))) + { + ospf_schedule_lsa_flush_area (area, new); + return; + } + + if (new->data->type == OSPF_OPAQUE_LINK_LSA) + { + ospf_opaque_lsa_refresh (new); + return; + } + + if (oi->network_lsa_self) + oi->network_lsa_self->data->ls_seqnum = new->data->ls_seqnum; + /* Schedule network-LSA origination. */ + ospf_network_lsa_update (oi); + return; + } + break; + case OSPF_SUMMARY_LSA: + case OSPF_ASBR_SUMMARY_LSA: + ospf_schedule_abr_task (ospf); + break; + case OSPF_AS_EXTERNAL_LSA : + case OSPF_AS_NSSA_LSA: + if ( (new->data->type == OSPF_AS_EXTERNAL_LSA) + && CHECK_FLAG (new->flags, OSPF_LSA_LOCAL_XLT)) + { + ospf_translated_nssa_refresh (ospf, NULL, new); + return; + } + ei = ospf_external_info_check (new); + if (ei) + ospf_external_lsa_refresh (ospf, new, ei, LSA_REFRESH_FORCE); + else + ospf_lsa_flush_as (ospf, new); + break; + 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; + default: + break; + } +} + +/* OSPF LSA flooding -- RFC2328 Section 13.(5). */ + +/* Now Updated for NSSA operation, as follows: + + + Type-5's have no change. Blocked to STUB or NSSA. + + Type-7's can be received, and if a DR + they will also flood the local NSSA Area as Type-7's + + If a Self-Originated LSA (now an ASBR), + The LSDB will be updated as Type-5's, (for continual re-fresh) + + If an NSSA-IR it is installed/flooded as Type-7, P-bit on. + if an NSSA-ABR it is installed/flooded as Type-7, P-bit off. + + Later, during the ABR TASK, if the ABR is the Elected NSSA + translator, then All Type-7s (with P-bit ON) are Translated to + Type-5's and flooded to all non-NSSA/STUB areas. + + During ASE Calculations, + non-ABRs calculate external routes from Type-7's + ABRs calculate external routes from Type-5's and non-self Type-7s +*/ +int +ospf_flood (struct ospf *ospf, struct ospf_neighbor *nbr, + struct ospf_lsa *current, struct ospf_lsa *new) +{ + struct ospf_interface *oi; + int lsa_ack_flag; + + /* Type-7 LSA's will be flooded throughout their native NSSA area, + but will also be flooded as Type-5's into ABR capable links. */ + + if (IS_DEBUG_OSPF_EVENT) + 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), + (void *)current, + dump_lsa_key (new)); + + lsa_ack_flag = 0; + oi = nbr->oi; + + /* If there is already a database copy, and if the + database copy was received via flooding and installed less + than MinLSArrival seconds ago, discard the new LSA + (without acknowledging it). */ + if (current != NULL) /* -- endo. */ + { + if (IS_LSA_SELF (current) + && (ntohs (current->data->ls_age) == 0 + && ntohl (current->data->ls_seqnum) == OSPF_INITIAL_SEQUENCE_NUMBER)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("LSA[Flooding]: Got a self-originated LSA, " + "while local one is initial instance."); + ; /* Accept this LSA for quick LSDB resynchronization. */ + } + else if (tv_cmp (tv_sub (recent_relative_time (), current->tv_recv), + msec2tv (ospf->min_ls_arrival)) < 0) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("LSA[Flooding]: LSA is received recently."); + return -1; + } + } + + /* Flood the new LSA out some subset of the router's interfaces. + In some cases (e.g., the state of the receiving interface is + DR and the LSA was received from a router other than the + Backup DR) the LSA will be flooded back out the receiving + interface. */ + lsa_ack_flag = ospf_flood_through (ospf, nbr, new); + + /* 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. */ + if (current) + { + switch (current->data->type) + { + case OSPF_AS_EXTERNAL_LSA: + case OSPF_OPAQUE_AS_LSA: + ospf_ls_retransmit_delete_nbr_as (ospf, current); + break; + default: + ospf_ls_retransmit_delete_nbr_area (nbr->oi->area, current); + break; + } + } + + /* Do some internal house keeping that is needed here */ + SET_FLAG (new->flags, OSPF_LSA_RECEIVED); + ospf_lsa_is_self_originated (ospf, new); /* Let it set the flag */ + + /* Install the new LSA in the link state database + (replacing the current database copy). This may cause the + routing table calculation to be scheduled. In addition, + timestamp the new LSA with the current time. The flooding + procedure cannot overwrite the newly installed LSA until + MinLSArrival seconds have elapsed. */ + + if (! (new = ospf_lsa_install (ospf, nbr->oi, new))) + return -1; /* unknown LSA type or any other error condition */ + + /* Acknowledge the receipt of the LSA by sending a Link State + Acknowledgment packet back out the receiving interface. */ + if (lsa_ack_flag) + ospf_flood_delayed_lsa_ack (nbr, new); + + /* If this new LSA indicates that it was originated by the + receiving router itself, the router must take special action, + either updating the LSA or in some cases flushing it from + the routing domain. */ + if (ospf_lsa_is_self_originated (ospf, new)) + ospf_process_self_originated_lsa (ospf, new, oi->area); + else + /* Update statistics value for OSPF-MIB. */ + ospf->rx_lsa_count++; + + return 0; +} + +/* OSPF LSA flooding -- RFC2328 Section 13.3. */ +static int +ospf_flood_through_interface (struct ospf_interface *oi, + struct ospf_neighbor *inbr, + struct ospf_lsa *lsa) +{ + struct ospf_neighbor *onbr; + struct route_node *rn; + int retx_flag; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_flood_through_interface(): " + "considering int %s, INBR(%s), LSA[%s]", + IF_NAME (oi), inbr ? inet_ntoa (inbr->router_id) : "NULL", + dump_lsa_key (lsa)); + + if (!ospf_if_is_enable (oi)) + return 0; + + /* Remember if new LSA is aded to a retransmit list. */ + retx_flag = 0; + + /* Each of the neighbors attached to this interface are examined, + to determine whether they must receive the new LSA. The following + steps are executed for each neighbor: */ + for (rn = route_top (oi->nbrs); rn; rn = route_next (rn)) + { + struct ospf_lsa *ls_req; + + if (rn->info == NULL) + continue; + + onbr = rn->info; + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_flood_through_interface(): considering nbr %s (%s)", + inet_ntoa (onbr->router_id), + LOOKUP (ospf_nsm_state_msg, onbr->state)); + + /* If the neighbor is in a lesser state than Exchange, it + does not participate in flooding, and the next neighbor + should be examined. */ + if (onbr->state < NSM_Exchange) + continue; + + /* If the adjacency is not yet full (neighbor state is + Exchange or Loading), examine the Link state request + list associated with this adjacency. If there is an + instance of the new LSA on the list, it indicates that + the neighboring router has an instance of the LSA + already. Compare the new LSA to the neighbor's copy: */ + if (onbr->state < NSM_Full) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_flood_through_interface(): nbr adj is not Full"); + ls_req = ospf_ls_request_lookup (onbr, lsa); + if (ls_req != NULL) + { + int ret; + + ret = ospf_lsa_more_recent (ls_req, lsa); + /* The new LSA is less recent. */ + if (ret > 0) + continue; + /* The two copies are the same instance, then delete + the LSA from the Link state request list. */ + else if (ret == 0) + { + ospf_ls_request_delete (onbr, ls_req); + ospf_check_nbr_loading (onbr); + continue; + } + /* The new LSA is more recent. Delete the LSA + from the Link state request list. */ + else + { + ospf_ls_request_delete (onbr, ls_req); + ospf_check_nbr_loading (onbr); + } + } + } + + if (IS_OPAQUE_LSA (lsa->data->type)) + { + if (! CHECK_FLAG (onbr->options, OSPF_OPTION_O)) + { + if (IS_DEBUG_OSPF (lsa, LSA_FLOODING)) + zlog_debug ("Skip this neighbor: Not Opaque-capable."); + continue; + } + } + + /* If the new LSA was received from this neighbor, + examine the next neighbor. */ +#ifdef ORIGINAL_CODING + if (inbr) + if (IPV4_ADDR_SAME (&inbr->router_id, &onbr->router_id)) + continue; +#else /* ORIGINAL_CODING */ + if (inbr) + { + /* + * Triggered by LSUpd message parser "ospf_ls_upd ()". + * E.g., all LSAs handling here is received via network. + */ + if (IPV4_ADDR_SAME (&inbr->router_id, &onbr->router_id)) + { + if (IS_DEBUG_OSPF (lsa, LSA_FLOODING)) + zlog_debug ("Skip this neighbor: inbr == onbr"); + continue; + } + } + else + { + /* + * Triggered by MaxAge remover, so far. + * NULL "inbr" means flooding starts from this node. + */ + if (IPV4_ADDR_SAME (&lsa->data->adv_router, &onbr->router_id)) + { + if (IS_DEBUG_OSPF (lsa, LSA_FLOODING)) + zlog_debug ("Skip this neighbor: lsah->adv_router == onbr"); + continue; + } + } +#endif /* ORIGINAL_CODING */ + + /* Add the new LSA to the Link state retransmission list + for the adjacency. The LSA will be retransmitted + at intervals until an acknowledgment is seen from + the neighbor. */ + ospf_ls_retransmit_add (onbr, lsa); + retx_flag = 1; + } + + /* If in the previous step, the LSA was NOT added to any of + the Link state retransmission lists, there is no need to + flood the LSA out the interface. */ + if (retx_flag == 0) + { + return (inbr && inbr->oi == oi); + } + + /* if we've received the lsa on this interface we need to perform + additional checking */ + if (inbr && (inbr->oi == oi)) + { + /* If the new LSA was received on this interface, and it was + received from either the Designated Router or the Backup + Designated Router, chances are that all the neighbors have + received the LSA already. */ + if (NBR_IS_DR (inbr) || NBR_IS_BDR (inbr)) + { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_flood_through_interface(): " + "DR/BDR NOT SEND to int %s", IF_NAME (oi)); + return 1; + } + + /* If the new LSA was received on this interface, and the + interface state is Backup, examine the next interface. The + Designated Router will do the flooding on this interface. + However, if the Designated Router fails the router will + end up retransmitting the updates. */ + + if (oi->state == ISM_Backup) + { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_flood_through_interface(): " + "ISM_Backup NOT SEND to int %s", IF_NAME (oi)); + return 1; + } + } + + /* The LSA must be flooded out the interface. Send a Link State + Update packet (including the new LSA as contents) out the + interface. The LSA's LS age must be incremented by InfTransDelay + (which must be > 0) when it is copied into the outgoing Link + State Update packet (until the LS age field reaches the maximum + value of MaxAge). */ + /* XXX HASSO: Is this IS_DEBUG_OSPF_NSSA really correct? */ + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_flood_through_interface(): " + "DR/BDR sending upd to int %s", IF_NAME (oi)); + + /* RFC2328 Section 13.3 + On non-broadcast networks, separate Link State Update + packets must be sent, as unicasts, to each adjacent neighbor + (i.e., those in state Exchange or greater). The destination + IP addresses for these packets are the neighbors' IP + addresses. */ + if (oi->type == OSPF_IFTYPE_NBMA) + { + struct route_node *rn; + struct ospf_neighbor *nbr; + + for (rn = route_top (oi->nbrs); rn; rn = route_next (rn)) + if ((nbr = rn->info) != NULL) + if (nbr != oi->nbr_self && nbr->state >= NSM_Exchange) + ospf_ls_upd_send_lsa (nbr, lsa, OSPF_SEND_PACKET_DIRECT); + } + else + ospf_ls_upd_send_lsa (oi->nbr_self, lsa, OSPF_SEND_PACKET_INDIRECT); + + return 0; +} + +int +ospf_flood_through_area (struct ospf_area *area, + struct ospf_neighbor *inbr, struct ospf_lsa *lsa) +{ + struct listnode *node, *nnode; + struct ospf_interface *oi; + int lsa_ack_flag = 0; + + /* All other types are specific to a single area (Area A). The + eligible interfaces are all those interfaces attaching to the + Area A. If Area A is the backbone, this includes all the virtual + links. */ + for (ALL_LIST_ELEMENTS (area->oiflist, node, nnode, oi)) + { + if (area->area_id.s_addr != OSPF_AREA_BACKBONE && + oi->type == OSPF_IFTYPE_VIRTUALLINK) + continue; + + if ((lsa->data->type == OSPF_OPAQUE_LINK_LSA) && (lsa->oi != oi)) + { + /* + * Link local scoped Opaque-LSA should only be flooded + * 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)", + (void *)lsa->oi, (void *)oi); + continue; + } + + if (ospf_flood_through_interface (oi, inbr, lsa)) + lsa_ack_flag = 1; + } + + return (lsa_ack_flag); +} + +int +ospf_flood_through_as (struct ospf *ospf, struct ospf_neighbor *inbr, + struct ospf_lsa *lsa) +{ + struct listnode *node; + struct ospf_area *area; + int lsa_ack_flag; + + lsa_ack_flag = 0; + + /* The incoming LSA is type 5 or type 7 (AS-EXTERNAL or AS-NSSA ) + + Divert the Type-5 LSA's to all non-NSSA/STUB areas + + Divert the Type-7 LSA's to all NSSA areas + + AS-external-LSAs are flooded throughout the entire AS, with the + exception of stub areas (see Section 3.6). The eligible + interfaces are all the router's interfaces, excluding virtual + links and those interfaces attaching to stub areas. */ + + if (CHECK_FLAG (lsa->flags, OSPF_LSA_LOCAL_XLT)) /* Translated from 7 */ + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("Flood/AS: NSSA TRANSLATED LSA"); + + for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area)) + { + int continue_flag = 0; + struct listnode *if_node; + struct ospf_interface *oi; + + switch (area->external_routing) + { + /* Don't send AS externals into stub areas. Various types + of support for partial stub areas can be implemented + here. NSSA's will receive Type-7's that have areas + matching the originl LSA. */ + case OSPF_AREA_NSSA: /* Sending Type 5 or 7 into NSSA area */ + /* Type-7, flood NSSA area */ + if (lsa->data->type == OSPF_AS_NSSA_LSA + && area == lsa->area) + /* We will send it. */ + continue_flag = 0; + else + continue_flag = 1; /* Skip this NSSA area for Type-5's et al */ + break; + + case OSPF_AREA_TYPE_MAX: + case OSPF_AREA_STUB: + continue_flag = 1; /* Skip this area. */ + break; + + case OSPF_AREA_DEFAULT: + default: + /* No Type-7 into normal area */ + if (lsa->data->type == OSPF_AS_NSSA_LSA) + continue_flag = 1; /* skip Type-7 */ + else + continue_flag = 0; /* Do this area. */ + break; + } + + /* Do continue for above switch. Saves a big if then mess */ + if (continue_flag) + continue; /* main for-loop */ + + /* send to every interface in this area */ + + for (ALL_LIST_ELEMENTS_RO (area->oiflist, if_node, oi)) + { + /* Skip virtual links */ + if (oi->type != OSPF_IFTYPE_VIRTUALLINK) + if (ospf_flood_through_interface (oi, inbr, lsa)) /* lsa */ + lsa_ack_flag = 1; + } + } /* main area for-loop */ + + return (lsa_ack_flag); +} + +int +ospf_flood_through (struct ospf *ospf, + struct ospf_neighbor *inbr, struct ospf_lsa *lsa) +{ + int lsa_ack_flag = 0; + + /* Type-7 LSA's for NSSA are flooded throughout the AS here, and + upon return are updated in the LSDB for Type-7's. Later, + re-fresh will re-send them (and also, if ABR, packet code will + translate to Type-5's) + + As usual, Type-5 LSA's (if not DISCARDED because we are STUB or + NSSA) are flooded throughout the AS, and are updated in the + global table. */ +#ifdef ORIGINAL_CODING + switch (lsa->data->type) + { + case OSPF_ROUTER_LSA: + case OSPF_NETWORK_LSA: + case OSPF_SUMMARY_LSA: + case OSPF_ASBR_SUMMARY_LSA: + case OSPF_OPAQUE_LINK_LSA: /* ospf_flood_through_interface ? */ + case OSPF_OPAQUE_AREA_LSA: + lsa_ack_flag = ospf_flood_through_area (inbr->oi->area, inbr, lsa); + break; + case OSPF_AS_EXTERNAL_LSA: /* Type-5 */ + case OSPF_OPAQUE_AS_LSA: + lsa_ack_flag = ospf_flood_through_as (ospf, inbr, lsa); + break; + /* Type-7 Only received within NSSA, then flooded */ + case OSPF_AS_NSSA_LSA: + /* Any P-bit was installed with the Type-7. */ + lsa_ack_flag = ospf_flood_through_area (inbr->oi->area, inbr, lsa); + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_flood_through: LOCAL NSSA FLOOD of Type-7."); + break; + default: + break; + } +#else /* ORIGINAL_CODING */ + /* + * At the common sub-sub-function "ospf_flood_through_interface()", + * a parameter "inbr" will be used to distinguish the called context + * whether the given LSA was received from the neighbor, or the + * flooding for the LSA starts from this node (e.g. the LSA was self- + * originated, or the LSA is going to be flushed from routing domain). + * + * So, for consistency reasons, this function "ospf_flood_through()" + * should also allow the usage that the given "inbr" parameter to be + * NULL. If we do so, corresponding AREA parameter should be referred + * by "lsa->area", instead of "inbr->oi->area". + */ + switch (lsa->data->type) + { + case OSPF_AS_EXTERNAL_LSA: /* Type-5 */ + case OSPF_OPAQUE_AS_LSA: + lsa_ack_flag = ospf_flood_through_as (ospf, inbr, lsa); + break; + /* Type-7 Only received within NSSA, then flooded */ + case OSPF_AS_NSSA_LSA: + /* Any P-bit was installed with the Type-7. */ + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_flood_through: LOCAL NSSA FLOOD of Type-7."); + /* Fallthrough */ + default: + lsa_ack_flag = ospf_flood_through_area (lsa->area, inbr, lsa); + break; + } +#endif /* ORIGINAL_CODING */ + + return (lsa_ack_flag); +} + + + +/* Management functions for neighbor's Link State Request list. */ +void +ospf_ls_request_add (struct ospf_neighbor *nbr, struct ospf_lsa *lsa) +{ + /* + * We cannot make use of the newly introduced callback function + * "lsdb->new_lsa_hook" to replace debug output below, just because + * it seems no simple and smart way to pass neighbor information to + * the common function "ospf_lsdb_add()" -- endo. + */ + if (IS_DEBUG_OSPF (lsa, LSA_FLOODING)) + zlog_debug ("RqstL(%lu)++, NBR(%s), LSA[%s]", + ospf_ls_request_count (nbr), + inet_ntoa (nbr->router_id), dump_lsa_key (lsa)); + + ospf_lsdb_add (&nbr->ls_req, lsa); +} + +unsigned long +ospf_ls_request_count (struct ospf_neighbor *nbr) +{ + return ospf_lsdb_count_all (&nbr->ls_req); +} + +int +ospf_ls_request_isempty (struct ospf_neighbor *nbr) +{ + return ospf_lsdb_isempty (&nbr->ls_req); +} + +/* Remove LSA from neighbor's ls-request list. */ +void +ospf_ls_request_delete (struct ospf_neighbor *nbr, struct ospf_lsa *lsa) +{ + if (nbr->ls_req_last == lsa) + { + ospf_lsa_unlock (&nbr->ls_req_last); + nbr->ls_req_last = NULL; + } + + if (IS_DEBUG_OSPF (lsa, LSA_FLOODING)) /* -- endo. */ + zlog_debug ("RqstL(%lu)--, NBR(%s), LSA[%s]", + ospf_ls_request_count (nbr), + inet_ntoa (nbr->router_id), dump_lsa_key (lsa)); + + ospf_lsdb_delete (&nbr->ls_req, lsa); +} + +/* Remove all LSA from neighbor's ls-requenst list. */ +void +ospf_ls_request_delete_all (struct ospf_neighbor *nbr) +{ + ospf_lsa_unlock (&nbr->ls_req_last); + nbr->ls_req_last = NULL; + ospf_lsdb_delete_all (&nbr->ls_req); +} + +/* Lookup LSA from neighbor's ls-request list. */ +struct ospf_lsa * +ospf_ls_request_lookup (struct ospf_neighbor *nbr, struct ospf_lsa *lsa) +{ + return ospf_lsdb_lookup (&nbr->ls_req, lsa); +} + +struct ospf_lsa * +ospf_ls_request_new (struct lsa_header *lsah) +{ + struct ospf_lsa *new; + + new = ospf_lsa_new (); + new->data = ospf_lsa_data_new (OSPF_LSA_HEADER_SIZE); + memcpy (new->data, lsah, OSPF_LSA_HEADER_SIZE); + + return new; +} + + +/* Management functions for neighbor's ls-retransmit list. */ +unsigned long +ospf_ls_retransmit_count (struct ospf_neighbor *nbr) +{ + return ospf_lsdb_count_all (&nbr->ls_rxmt); +} + +unsigned long +ospf_ls_retransmit_count_self (struct ospf_neighbor *nbr, int lsa_type) +{ + return ospf_lsdb_count_self (&nbr->ls_rxmt, lsa_type); +} + +int +ospf_ls_retransmit_isempty (struct ospf_neighbor *nbr) +{ + return ospf_lsdb_isempty (&nbr->ls_rxmt); +} + +/* Add LSA to be retransmitted to neighbor's ls-retransmit list. */ +void +ospf_ls_retransmit_add (struct ospf_neighbor *nbr, struct ospf_lsa *lsa) +{ + struct ospf_lsa *old; + + old = ospf_ls_retransmit_lookup (nbr, lsa); + + if (ospf_lsa_more_recent (old, lsa) < 0) + { + if (old) + { + old->retransmit_counter--; + ospf_lsdb_delete (&nbr->ls_rxmt, old); + } + lsa->retransmit_counter++; + /* + * We cannot make use of the newly introduced callback function + * "lsdb->new_lsa_hook" to replace debug output below, just because + * it seems no simple and smart way to pass neighbor information to + * the common function "ospf_lsdb_add()" -- endo. + */ + if (IS_DEBUG_OSPF (lsa, LSA_FLOODING)) + zlog_debug ("RXmtL(%lu)++, NBR(%s), LSA[%s]", + ospf_ls_retransmit_count (nbr), + inet_ntoa (nbr->router_id), dump_lsa_key (lsa)); + ospf_lsdb_add (&nbr->ls_rxmt, lsa); + } +} + +/* Remove LSA from neibghbor's ls-retransmit list. */ +void +ospf_ls_retransmit_delete (struct ospf_neighbor *nbr, struct ospf_lsa *lsa) +{ + if (ospf_ls_retransmit_lookup (nbr, lsa)) + { + lsa->retransmit_counter--; + if (IS_DEBUG_OSPF (lsa, LSA_FLOODING)) /* -- endo. */ + zlog_debug ("RXmtL(%lu)--, NBR(%s), LSA[%s]", + ospf_ls_retransmit_count (nbr), + inet_ntoa (nbr->router_id), dump_lsa_key (lsa)); + ospf_lsdb_delete (&nbr->ls_rxmt, lsa); + } +} + +/* Clear neighbor's ls-retransmit list. */ +void +ospf_ls_retransmit_clear (struct ospf_neighbor *nbr) +{ + struct ospf_lsdb *lsdb; + int i; + + lsdb = &nbr->ls_rxmt; + + for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++) + { + struct route_table *table = lsdb->type[i].db; + struct route_node *rn; + struct ospf_lsa *lsa; + + for (rn = route_top (table); rn; rn = route_next (rn)) + if ((lsa = rn->info) != NULL) + ospf_ls_retransmit_delete (nbr, lsa); + } + + ospf_lsa_unlock (&nbr->ls_req_last); + nbr->ls_req_last = NULL; +} + +/* Lookup LSA from neighbor's ls-retransmit list. */ +struct ospf_lsa * +ospf_ls_retransmit_lookup (struct ospf_neighbor *nbr, struct ospf_lsa *lsa) +{ + return ospf_lsdb_lookup (&nbr->ls_rxmt, lsa); +} + +static void +ospf_ls_retransmit_delete_nbr_if (struct ospf_interface *oi, + struct ospf_lsa *lsa) +{ + struct route_node *rn; + struct ospf_neighbor *nbr; + struct ospf_lsa *lsr; + + if (ospf_if_is_enable (oi)) + for (rn = route_top (oi->nbrs); rn; rn = route_next (rn)) + /* If LSA find in LS-retransmit list, then remove it. */ + if ((nbr = rn->info) != NULL) + { + lsr = ospf_ls_retransmit_lookup (nbr, lsa); + + /* If LSA find in ls-retransmit list, remove it. */ + if (lsr != NULL && lsr->data->ls_seqnum == lsa->data->ls_seqnum) + ospf_ls_retransmit_delete (nbr, lsr); + } +} + +void +ospf_ls_retransmit_delete_nbr_area (struct ospf_area *area, + struct ospf_lsa *lsa) +{ + struct listnode *node, *nnode; + struct ospf_interface *oi; + + for (ALL_LIST_ELEMENTS (area->oiflist, node, nnode, oi)) + ospf_ls_retransmit_delete_nbr_if (oi, lsa); +} + +void +ospf_ls_retransmit_delete_nbr_as (struct ospf *ospf, struct ospf_lsa *lsa) +{ + struct listnode *node, *nnode; + struct ospf_interface *oi; + + for (ALL_LIST_ELEMENTS (ospf->oiflist, node, nnode, oi)) + 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); +} + +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); +} + +void +ospf_lsa_flush (struct ospf *ospf, struct ospf_lsa *lsa) +{ + lsa->data->ls_age = htons (OSPF_LSA_MAXAGE); + + switch (lsa->data->type) + { + case OSPF_ROUTER_LSA: + case OSPF_NETWORK_LSA: + case OSPF_SUMMARY_LSA: + case OSPF_ASBR_SUMMARY_LSA: + case OSPF_AS_NSSA_LSA: + case OSPF_OPAQUE_LINK_LSA: + case OSPF_OPAQUE_AREA_LSA: + ospf_lsa_flush_area (lsa, lsa->area); + break; + case OSPF_AS_EXTERNAL_LSA: + case OSPF_OPAQUE_AS_LSA: + ospf_lsa_flush_as (ospf, lsa); + break; + default: + zlog_info ("%s: Unknown LSA type %u", __func__, lsa->data->type); + break; + } +} diff --git a/ospfd/ospf_flood.h b/ospfd/ospf_flood.h new file mode 100644 index 0000000..1ab11b8 --- /dev/null +++ b/ospfd/ospf_flood.h @@ -0,0 +1,74 @@ +/* + * OSPF Flooding -- RFC2328 Section 13. + * Copyright (C) 1999, 2000 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 _ZEBRA_OSPF_FLOOD_H +#define _ZEBRA_OSPF_FLOOD_H + +extern int ospf_flood (struct ospf *, struct ospf_neighbor *, + struct ospf_lsa *, struct ospf_lsa *); +extern int ospf_flood_through (struct ospf *, struct ospf_neighbor *, + struct ospf_lsa *); +extern int ospf_flood_through_area (struct ospf_area *, + struct ospf_neighbor *, + struct ospf_lsa *); +extern int ospf_flood_through_as (struct ospf *, struct ospf_neighbor *, + struct ospf_lsa *); + +extern unsigned long ospf_ls_request_count (struct ospf_neighbor *); +extern int ospf_ls_request_isempty (struct ospf_neighbor *); +extern struct ospf_lsa *ospf_ls_request_new (struct lsa_header *); +extern void ospf_ls_request_free (struct ospf_lsa *); +extern void ospf_ls_request_add (struct ospf_neighbor *, struct ospf_lsa *); +extern void ospf_ls_request_delete (struct ospf_neighbor *, + struct ospf_lsa *); +extern void ospf_ls_request_delete_all (struct ospf_neighbor *); +extern struct ospf_lsa *ospf_ls_request_lookup (struct ospf_neighbor *, + struct ospf_lsa *); + +extern unsigned long ospf_ls_retransmit_count (struct ospf_neighbor *); +extern unsigned long ospf_ls_retransmit_count_self (struct ospf_neighbor *, + int); +extern int ospf_ls_retransmit_isempty (struct ospf_neighbor *); +extern void ospf_ls_retransmit_add (struct ospf_neighbor *, + struct ospf_lsa *); +extern void ospf_ls_retransmit_delete (struct ospf_neighbor *, + struct ospf_lsa *); +extern void ospf_ls_retransmit_clear (struct ospf_neighbor *); +extern struct ospf_lsa *ospf_ls_retransmit_lookup (struct ospf_neighbor *, + struct ospf_lsa *); +extern void ospf_ls_retransmit_delete_nbr_area (struct ospf_area *, + struct ospf_lsa *); +extern void ospf_ls_retransmit_delete_nbr_as (struct ospf *, + struct ospf_lsa *); +extern void ospf_ls_retransmit_add_nbr_all (struct ospf_interface *, + struct ospf_lsa *); + +extern void ospf_flood_lsa_area (struct ospf_lsa *, struct ospf_area *); +extern void ospf_flood_lsa_as (struct ospf_lsa *); +extern void ospf_lsa_flush_area (struct ospf_lsa *, struct ospf_area *); +extern void ospf_lsa_flush_as (struct ospf *, struct ospf_lsa *); +extern void ospf_lsa_flush (struct ospf *, struct ospf_lsa *); +extern struct external_info *ospf_external_info_check (struct ospf_lsa *); + +extern void ospf_lsdb_init (struct ospf_lsdb *); + +#endif /* _ZEBRA_OSPF_FLOOD_H */ diff --git a/ospfd/ospf_ia.c b/ospfd/ospf_ia.c new file mode 100644 index 0000000..b2d0fae --- /dev/null +++ b/ospfd/ospf_ia.c @@ -0,0 +1,715 @@ +/* + * OSPF inter-area routing. + * Copyright (C) 1999, 2000 Alex Zinin, 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. + */ + + +#include + +#include "thread.h" +#include "memory.h" +#include "hash.h" +#include "linklist.h" +#include "prefix.h" +#include "table.h" +#include "log.h" + +#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_spf.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_ase.h" +#include "ospfd/ospf_abr.h" +#include "ospfd/ospf_ia.h" +#include "ospfd/ospf_dump.h" + +static struct ospf_route * +ospf_find_abr_route (struct route_table *rtrs, + struct prefix_ipv4 *abr, + struct ospf_area *area) +{ + struct route_node *rn; + struct ospf_route *or; + struct listnode *node; + + if ((rn = route_node_lookup (rtrs, (struct prefix *) abr)) == NULL) + return NULL; + + route_unlock_node (rn); + + for (ALL_LIST_ELEMENTS_RO ((struct list *) rn->info, node, or)) + if (IPV4_ADDR_SAME (&or->u.std.area_id, &area->area_id) + && (or->u.std.flags & ROUTER_LSA_BORDER)) + return or; + + return NULL; +} + +static void +ospf_ia_network_route (struct ospf *ospf, struct route_table *rt, + struct prefix_ipv4 *p, struct ospf_route *new_or, + struct ospf_route *abr_or) +{ + struct route_node *rn1; + struct ospf_route *or; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_ia_network_route(): processing summary route to %s/%d", + inet_ntoa (p->prefix), p->prefixlen); + + /* Find a route to the same dest */ + if ((rn1 = route_node_lookup (rt, (struct prefix *) p))) + { + int res; + + route_unlock_node (rn1); + + if ((or = rn1->info)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_ia_network_route(): " + "Found a route to the same network"); + /* Check the existing route. */ + if ((res = ospf_route_cmp (ospf, new_or, or)) < 0) + { + /* New route is better, so replace old one. */ + ospf_route_subst (rn1, new_or, abr_or); + } + else if (res == 0) + { + /* New and old route are equal, so next hops can be added. */ + route_lock_node (rn1); + ospf_route_copy_nexthops (or, abr_or->paths); + route_unlock_node (rn1); + + /* new route can be deleted, because existing route has been updated. */ + ospf_route_free (new_or); + } + else + { + /* New route is worse, so free it. */ + ospf_route_free (new_or); + return; + } + } /* if (or)*/ + } /*if (rn1)*/ + else + { /* no route */ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_ia_network_route(): add new route to %s/%d", + inet_ntoa (p->prefix), p->prefixlen); + ospf_route_add (rt, p, new_or, abr_or); + } +} + +static void +ospf_ia_router_route (struct ospf *ospf, struct route_table *rtrs, + struct prefix_ipv4 *p, + struct ospf_route *new_or, struct ospf_route *abr_or) +{ + struct ospf_route *or = NULL; + struct route_node *rn; + int ret; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_ia_router_route(): considering %s/%d", + inet_ntoa (p->prefix), p->prefixlen); + /* Find a route to the same dest */ + rn = route_node_get (rtrs, (struct prefix *) p); + + if (rn->info == NULL) + /* This is a new route */ + rn->info = list_new (); + else + { + struct ospf_area *or_area; + or_area = ospf_area_lookup_by_area_id (ospf, new_or->u.std.area_id); + assert (or_area); + /* This is an additional route */ + route_unlock_node (rn); + or = ospf_find_asbr_route_through_area (rtrs, p, or_area); + } + + if (or) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_ia_router_route(): " + "a route to the same ABR through the same area exists"); + /* New route is better */ + if ((ret = ospf_route_cmp (ospf, new_or, or)) < 0) + { + listnode_delete (rn->info, or); + ospf_route_free (or); + /* proceed down */ + } + /* Routes are the same */ + else if (ret == 0) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_ia_router_route(): merging the new route"); + + ospf_route_copy_nexthops (or, abr_or->paths); + ospf_route_free (new_or); + return; + } + /* New route is worse */ + else + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_ia_router_route(): skipping the new route"); + ospf_route_free (new_or); + return; + } + } + + ospf_route_copy_nexthops (new_or, abr_or->paths); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_ia_router_route(): adding the new route"); + + 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) +{ + struct ospf *ospf = area->ospf; + struct ospf_area_range *range; + struct ospf_route *abr_or, *new_or; + struct summary_lsa *sl; + struct prefix_ipv4 p, abr; + u_int32_t metric; + + if (lsa == NULL) + return 0; + + sl = (struct summary_lsa *) lsa->data; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("process_summary_lsa(): LS ID: %s", inet_ntoa (sl->header.id)); + + metric = GET_METRIC (sl->metric); + + if (metric == OSPF_LS_INFINITY) + return 0; + + if (IS_LSA_MAXAGE (lsa)) + return 0; + + if (ospf_lsa_is_self_originated (area->ospf, lsa)) + return 0; + + p.family = AF_INET; + p.prefix = sl->header.id; + + if (sl->header.type == OSPF_SUMMARY_LSA) + p.prefixlen = ip_masklen (sl->mask); + else + p.prefixlen = IPV4_MAX_BITLEN; + + apply_mask_ipv4 (&p); + + if (sl->header.type == OSPF_SUMMARY_LSA && + (range = ospf_area_range_match_any (ospf, &p)) && + ospf_area_range_active (range)) + return 0; + + /* XXX: This check seems dubious to me. If an ABR has already decided + * to consider summaries received in this area, then why would one wish + * to exclude default? + */ + if (IS_OSPF_ABR(ospf) && + ospf->abr_type != OSPF_ABR_STAND && + area->external_routing != OSPF_AREA_DEFAULT && + p.prefix.s_addr == OSPF_DEFAULT_DESTINATION && + p.prefixlen == 0) + return 0; /* Ignore summary default from a stub area */ + + abr.family = AF_INET; + abr.prefix = sl->header.adv_router; + abr.prefixlen = IPV4_MAX_BITLEN; + apply_mask_ipv4 (&abr); + + abr_or = ospf_find_abr_route (rtrs, &abr, area); + + if (abr_or == NULL) + return 0; + + new_or = ospf_route_new (); + new_or->type = OSPF_DESTINATION_NETWORK; + new_or->id = sl->header.id; + new_or->mask = sl->mask; + new_or->u.std.options = sl->header.options; + new_or->u.std.origin = (struct lsa_header *) sl; + new_or->cost = abr_or->cost + metric; + new_or->u.std.area_id = area->area_id; + new_or->u.std.external_routing = area->external_routing; + new_or->path_type = OSPF_PATH_INTER_AREA; + + if (sl->header.type == OSPF_SUMMARY_LSA) + ospf_ia_network_route (ospf, rt, &p, new_or, abr_or); + else + { + new_or->type = OSPF_DESTINATION_ROUTER; + new_or->u.std.flags = ROUTER_LSA_EXTERNAL; + ospf_ia_router_route (ospf, rtrs, &p, new_or, abr_or); + } + + return 0; +} + +static void +ospf_examine_summaries (struct ospf_area *area, + struct route_table *lsdb_rt, + struct route_table *rt, + struct route_table *rtrs) +{ + struct ospf_lsa *lsa; + struct route_node *rn; + + LSDB_LOOP (lsdb_rt, rn, lsa) + process_summary_lsa (area, rt, rtrs, lsa); +} + +int +ospf_area_is_transit (struct ospf_area *area) +{ + return (area->transit == OSPF_TRANSIT_TRUE) || + ospf_full_virtual_nbrs(area); /* Cisco forgets to set the V-bit :( */ +} + +static void +ospf_update_network_route (struct ospf *ospf, + struct route_table *rt, + struct route_table *rtrs, + struct summary_lsa *lsa, + struct prefix_ipv4 *p, + struct ospf_area *area) +{ + struct route_node *rn; + struct ospf_route *or, *abr_or, *new_or; + struct prefix_ipv4 abr; + u_int32_t cost; + + abr.family = AF_INET; + abr.prefix =lsa->header.adv_router; + abr.prefixlen = IPV4_MAX_BITLEN; + apply_mask_ipv4 (&abr); + + abr_or = ospf_find_abr_route (rtrs, &abr, area); + + if (abr_or == NULL) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_update_network_route(): can't find a route to the ABR"); + return; + } + + cost = abr_or->cost + GET_METRIC (lsa->metric); + + rn = route_node_lookup (rt, (struct prefix *) p); + + if (! rn) + { + if (ospf->abr_type != OSPF_ABR_SHORTCUT) + return; /* Standard ABR can update only already installed + backbone paths */ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_update_network_route(): " + "Allowing Shortcut ABR to add new route"); + new_or = ospf_route_new (); + new_or->type = OSPF_DESTINATION_NETWORK; + new_or->id = lsa->header.id; + new_or->mask = lsa->mask; + new_or->u.std.options = lsa->header.options; + new_or->u.std.origin = (struct lsa_header *) lsa; + new_or->cost = cost; + new_or->u.std.area_id = area->area_id; + new_or->u.std.external_routing = area->external_routing; + new_or->path_type = OSPF_PATH_INTER_AREA; + ospf_route_add (rt, p, new_or, abr_or); + + return; + } + else + { + route_unlock_node (rn); + if (rn->info == NULL) + return; + } + + or = rn->info; + + if (or->path_type != OSPF_PATH_INTRA_AREA && + or->path_type != OSPF_PATH_INTER_AREA) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_update_network_route(): ERR: path type is wrong"); + return; + } + + if (ospf->abr_type == OSPF_ABR_SHORTCUT) + { + if (or->path_type == OSPF_PATH_INTRA_AREA && + !OSPF_IS_AREA_ID_BACKBONE (or->u.std.area_id)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_update_network_route(): Shortcut: " + "this intra-area path is not backbone"); + return; + } + } + else /* Not Shortcut ABR */ + { + if (!OSPF_IS_AREA_ID_BACKBONE (or->u.std.area_id)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_update_network_route(): " + "route is not BB-associated"); + return; /* We can update only BB routes */ + } + } + + if (or->cost < cost) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_update_network_route(): new route is worse"); + return; + } + + if (or->cost == cost) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_update_network_route(): " + "new route is same distance, adding nexthops"); + ospf_route_copy_nexthops (or, abr_or->paths); + } + + if (or->cost > cost) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_update_network_route(): " + "new route is better, overriding nexthops"); + ospf_route_subst_nexthops (or, abr_or->paths); + or->cost = cost; + + if ((ospf->abr_type == OSPF_ABR_SHORTCUT) && + !OSPF_IS_AREA_ID_BACKBONE (or->u.std.area_id)) + { + or->path_type = OSPF_PATH_INTER_AREA; + or->u.std.area_id = area->area_id; + or->u.std.external_routing = area->external_routing; + /* Note that we can do this only in Shortcut ABR mode, + because standard ABR must leave the route type and area + unchanged + */ + } + } +} + +static void +ospf_update_router_route (struct ospf *ospf, + struct route_table *rtrs, + struct summary_lsa *lsa, + struct prefix_ipv4 *p, + struct ospf_area *area) +{ + struct ospf_route *or, *abr_or, *new_or; + struct prefix_ipv4 abr; + u_int32_t cost; + + abr.family = AF_INET; + abr.prefix = lsa->header.adv_router; + abr.prefixlen = IPV4_MAX_BITLEN; + apply_mask_ipv4 (&abr); + + abr_or = ospf_find_abr_route (rtrs, &abr, area); + + if (abr_or == NULL) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_update_router_route(): can't find a route to the ABR"); + return; + } + + cost = abr_or->cost + GET_METRIC (lsa->metric); + + /* First try to find a backbone path, + because standard ABR can update only BB-associated paths */ + + if ((ospf->backbone == NULL) && + (ospf->abr_type != OSPF_ABR_SHORTCUT)) + return; /* no BB area, not Shortcut ABR, exiting */ + + /* find the backbone route, if possible */ + if ((ospf->backbone == NULL) + || !(or = ospf_find_asbr_route_through_area (rtrs, p, ospf->backbone))) + { + if (ospf->abr_type != OSPF_ABR_SHORTCUT) + + /* route to ASBR through the BB not found + the router is not Shortcut ABR, exiting */ + + return; + else + /* We're a Shortcut ABR*/ + { + /* Let it either add a new router or update the route + through the same (non-BB) area. */ + + new_or = ospf_route_new (); + new_or->type = OSPF_DESTINATION_ROUTER; + new_or->id = lsa->header.id; + new_or->mask = lsa->mask; + new_or->u.std.options = lsa->header.options; + new_or->u.std.origin = (struct lsa_header *)lsa; + new_or->cost = cost; + new_or->u.std.area_id = area->area_id; + new_or->u.std.external_routing = area->external_routing; + new_or->path_type = OSPF_PATH_INTER_AREA; + new_or->u.std.flags = ROUTER_LSA_EXTERNAL; + ospf_ia_router_route (ospf, rtrs, p, new_or, abr_or); + + return; + } + } + + /* At this point the "or" is always bb-associated */ + + if (!(or->u.std.flags & ROUTER_LSA_EXTERNAL)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_upd_router_route(): the remote router is not an ASBR"); + return; + } + + if (or->path_type != OSPF_PATH_INTRA_AREA && + or->path_type != OSPF_PATH_INTER_AREA) + return; + + if (or->cost < cost) + return; + + else if (or->cost == cost) + ospf_route_copy_nexthops (or, abr_or->paths); + + else if (or->cost > cost) + { + ospf_route_subst_nexthops (or, abr_or->paths); + or->cost = cost; + + /* Even if the ABR runs in Shortcut mode, we can't change + the path type and area, because the "or" is always bb-associated + at this point and even Shortcut ABR can't change these attributes */ + } +} + +static int +process_transit_summary_lsa (struct ospf_area *area, struct route_table *rt, + struct route_table *rtrs, struct ospf_lsa *lsa) +{ + struct ospf *ospf = area->ospf; + struct summary_lsa *sl; + struct prefix_ipv4 p; + u_int32_t metric; + + if (lsa == NULL) + return 0; + + sl = (struct summary_lsa *) lsa->data; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("process_transit_summaries(): LS ID: %s", + inet_ntoa (lsa->data->id)); + metric = GET_METRIC (sl->metric); + + if (metric == OSPF_LS_INFINITY) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("process_transit_summaries(): metric is infinity, skip"); + return 0; + } + + if (IS_LSA_MAXAGE (lsa)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("process_transit_summaries(): This LSA is too old"); + return 0; + } + + if (ospf_lsa_is_self_originated (area->ospf, lsa)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("process_transit_summaries(): This LSA is mine, skip"); + return 0; + } + + p.family = AF_INET; + p.prefix = sl->header.id; + + if (sl->header.type == OSPF_SUMMARY_LSA) + p.prefixlen = ip_masklen (sl->mask); + else + p.prefixlen = IPV4_MAX_BITLEN; + + apply_mask_ipv4 (&p); + + if (sl->header.type == OSPF_SUMMARY_LSA) + ospf_update_network_route (ospf, rt, rtrs, sl, &p, area); + else + ospf_update_router_route (ospf, rtrs, sl, &p, area); + + return 0; +} + +static void +ospf_examine_transit_summaries (struct ospf_area *area, + struct route_table *lsdb_rt, + struct route_table *rt, + struct route_table *rtrs) +{ + struct ospf_lsa *lsa; + struct route_node *rn; + + LSDB_LOOP (lsdb_rt, rn, lsa) + process_transit_summary_lsa (area, rt, rtrs, lsa); +} + +void +ospf_ia_routing (struct ospf *ospf, + struct route_table *rt, + struct route_table *rtrs) +{ + struct ospf_area * area; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_ia_routing():start"); + + if (IS_OSPF_ABR (ospf)) + { + struct listnode *node; + struct ospf_area *area; + + switch (ospf->abr_type) + { + case OSPF_ABR_STAND: + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_ia_routing():Standard ABR"); + + if ((area = ospf->backbone)) + { + struct listnode *node; + + if (IS_DEBUG_OSPF_EVENT) + { + zlog_debug ("ospf_ia_routing():backbone area found"); + zlog_debug ("ospf_ia_routing():examining summaries"); + } + + OSPF_EXAMINE_SUMMARIES_ALL (area, rt, rtrs); + + for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area)) + if (area != ospf->backbone) + if (ospf_area_is_transit (area)) + OSPF_EXAMINE_TRANSIT_SUMMARIES_ALL (area, rt, rtrs); + } + else + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_ia_routing():backbone area NOT found"); + break; + case OSPF_ABR_IBM: + case OSPF_ABR_CISCO: + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_ia_routing():Alternative Cisco/IBM ABR"); + area = ospf->backbone; /* Find the BB */ + + /* If we have an active BB connection */ + if (area && ospf_act_bb_connection (ospf)) + { + if (IS_DEBUG_OSPF_EVENT) + { + zlog_debug ("ospf_ia_routing(): backbone area found"); + zlog_debug ("ospf_ia_routing(): examining BB summaries"); + } + + OSPF_EXAMINE_SUMMARIES_ALL (area, rt, rtrs); + + for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area)) + if (area != ospf->backbone) + if (ospf_area_is_transit (area)) + OSPF_EXAMINE_TRANSIT_SUMMARIES_ALL (area, rt, rtrs); + } + else + { /* No active BB connection--consider all areas */ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_ia_routing(): " + "Active BB connection not found"); + for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area)) + OSPF_EXAMINE_SUMMARIES_ALL (area, rt, rtrs); + } + break; + case OSPF_ABR_SHORTCUT: + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_ia_routing():Alternative Shortcut"); + area = ospf->backbone; /* Find the BB */ + + /* If we have an active BB connection */ + if (area && ospf_act_bb_connection (ospf)) + { + if (IS_DEBUG_OSPF_EVENT) + { + zlog_debug ("ospf_ia_routing(): backbone area found"); + zlog_debug ("ospf_ia_routing(): examining BB summaries"); + } + OSPF_EXAMINE_SUMMARIES_ALL (area, rt, rtrs); + } + + for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area)) + if (area != ospf->backbone) + if (ospf_area_is_transit (area) || + ((area->shortcut_configured != OSPF_SHORTCUT_DISABLE) && + ((ospf->backbone == NULL) || + ((area->shortcut_configured == OSPF_SHORTCUT_ENABLE) && + area->shortcut_capability)))) + OSPF_EXAMINE_TRANSIT_SUMMARIES_ALL (area, rt, rtrs); + break; + default: + break; + } + } + else + { + struct listnode *node; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_ia_routing():not ABR, considering all areas"); + + for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area)) + OSPF_EXAMINE_SUMMARIES_ALL (area, rt, rtrs); + } +} diff --git a/ospfd/ospf_ia.h b/ospfd/ospf_ia.h new file mode 100644 index 0000000..b65b938 --- /dev/null +++ b/ospfd/ospf_ia.h @@ -0,0 +1,43 @@ +/* + * OSPF inter-area routing. + * Copyright (C) 1999, 2000 Alex Zinin, 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 _ZEBRA_OSPF_IA_H +#define _ZEBRA_OSPF_IA_H + +/* Macros. */ +#define OSPF_EXAMINE_SUMMARIES_ALL(A,N,R) \ + { \ + ospf_examine_summaries ((A), SUMMARY_LSDB ((A)), (N), (R)); \ + ospf_examine_summaries ((A), ASBR_SUMMARY_LSDB ((A)), (N), (R)); \ + } + +#define OSPF_EXAMINE_TRANSIT_SUMMARIES_ALL(A,N,R) \ + { \ + ospf_examine_transit_summaries ((A), SUMMARY_LSDB ((A)), (N), (R)); \ + ospf_examine_transit_summaries ((A), ASBR_SUMMARY_LSDB ((A)), (N), (R)); \ + } + +extern void ospf_ia_routing (struct ospf *, struct route_table *, + struct route_table *); +extern int ospf_area_is_transit (struct ospf_area *); + +#endif /* _ZEBRA_OSPF_IA_H */ diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c new file mode 100644 index 0000000..a46ca6d --- /dev/null +++ b/ospfd/ospf_interface.c @@ -0,0 +1,1278 @@ +/* + * OSPF Interface functions. + * Copyright (C) 1999, 2000 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. + */ + +#include + +#include "thread.h" +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "memory.h" +#include "command.h" +#include "stream.h" +#include "log.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_spf.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_packet.h" +#include "ospfd/ospf_abr.h" +#include "ospfd/ospf_network.h" +#include "ospfd/ospf_dump.h" +#ifdef HAVE_SNMP +#include "ospfd/ospf_snmp.h" +#endif /* HAVE_SNMP */ + + +int +ospf_if_get_output_cost (struct ospf_interface *oi) +{ + /* If all else fails, use default OSPF cost */ + u_int32_t cost; + u_int32_t bw, refbw; + + bw = oi->ifp->bandwidth ? oi->ifp->bandwidth : OSPF_DEFAULT_BANDWIDTH; + refbw = oi->ospf->ref_bandwidth; + + /* A specifed ip ospf cost overrides a calculated one. */ + if (OSPF_IF_PARAM_CONFIGURED (IF_DEF_PARAMS (oi->ifp), output_cost_cmd) || + OSPF_IF_PARAM_CONFIGURED (oi->params, output_cost_cmd)) + cost = OSPF_IF_PARAM (oi, output_cost_cmd); + /* See if a cost can be calculated from the zebra processes + interface bandwidth field. */ + else + { + cost = (u_int32_t) ((double)refbw / (double)bw + (double)0.5); + if (cost < 1) + cost = 1; + else if (cost > 65535) + cost = 65535; + } + + return cost; +} + +void +ospf_if_recalculate_output_cost (struct interface *ifp) +{ + u_int32_t newcost; + struct route_node *rn; + + for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn)) + { + struct ospf_interface *oi; + + if ( (oi = rn->info) == NULL) + continue; + + newcost = ospf_if_get_output_cost (oi); + + /* Is actual output cost changed? */ + if (oi->output_cost != newcost) + { + oi->output_cost = newcost; + ospf_router_lsa_update_area (oi->area); + } + } +} + +/* Simulate down/up on the interface. This is needed, for example, when + the MTU changes. */ +void +ospf_if_reset(struct interface *ifp) +{ + struct route_node *rn; + + for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn)) + { + struct ospf_interface *oi; + + if ( (oi = rn->info) == NULL) + continue; + + ospf_if_down(oi); + ospf_if_up(oi); + } +} + +void +ospf_if_reset_variables (struct ospf_interface *oi) +{ + /* Set default values. */ + /* don't clear this flag. oi->flag = OSPF_IF_DISABLE; */ + + if (oi->vl_data) + oi->type = OSPF_IFTYPE_VIRTUALLINK; + else + /* preserve network-type */ + if (oi->type != OSPF_IFTYPE_NBMA) + oi->type = OSPF_IFTYPE_BROADCAST; + + oi->state = ISM_Down; + + oi->crypt_seqnum = 0; + + /* This must be short, (less than RxmtInterval) + - RFC 2328 Section 13.5 para 3. Set to 1 second to avoid Acks being + held back for too long - MAG */ + 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) +{ + struct prefix p; + struct route_node *rn; + struct ospf_interface *rninfo = NULL; + + p = *prefix; + p.prefixlen = IPV4_MAX_PREFIXLEN; + + /* route_node_get implicitely locks */ + if ((rn = route_node_lookup (IF_OIFS (ifp), &p))) + { + rninfo = (struct ospf_interface *) rn->info; + route_unlock_node (rn); + } + + return rninfo; +} + +static void +ospf_add_to_if (struct interface *ifp, struct ospf_interface *oi) +{ + struct route_node *rn; + struct prefix p; + + p = *oi->address; + p.prefixlen = IPV4_MAX_PREFIXLEN; + + rn = route_node_get (IF_OIFS (ifp), &p); + /* rn->info should either be NULL or equal to this oi + * as route_node_get may return an existing node + */ + assert (!rn->info || rn->info == oi); + rn->info = oi; +} + +static void +ospf_delete_from_if (struct interface *ifp, struct ospf_interface *oi) +{ + struct route_node *rn; + struct prefix p; + + p = *oi->address; + p.prefixlen = IPV4_MAX_PREFIXLEN; + + rn = route_node_lookup (IF_OIFS (oi->ifp), &p); + assert (rn); + assert (rn->info); + rn->info = NULL; + route_unlock_node (rn); + route_unlock_node (rn); +} + +struct ospf_interface * +ospf_if_new (struct ospf *ospf, struct interface *ifp, struct prefix *p) +{ + struct ospf_interface *oi; + + if ((oi = ospf_if_table_lookup (ifp, p)) == NULL) + { + oi = XCALLOC (MTYPE_OSPF_IF, sizeof (struct ospf_interface)); + memset (oi, 0, sizeof (struct ospf_interface)); + } + else + return oi; + + /* Set zebra interface pointer. */ + oi->ifp = ifp; + oi->address = p; + + ospf_add_to_if (ifp, oi); + listnode_add (ospf->oiflist, oi); + + /* Initialize neighbor list. */ + oi->nbrs = route_table_init (); + + /* Initialize static neighbor list. */ + oi->nbr_nbma = list_new (); + + /* Initialize Link State Acknowledgment list. */ + oi->ls_ack = list_new (); + oi->ls_ack_direct.ls_ack = list_new (); + + /* Set default values. */ + ospf_if_reset_variables (oi); + + /* Set pseudo neighbor to Null */ + oi->nbr_self = NULL; + + oi->ls_upd_queue = route_table_init (); + oi->t_ls_upd_event = NULL; + oi->t_ls_ack_direct = NULL; + + oi->crypt_seqnum = time (NULL); + + ospf_opaque_type9_lsa_init (oi); + + oi->ospf = ospf; + + return oi; +} + +/* Restore an interface to its pre UP state + Used from ism_interface_down only */ +void +ospf_if_cleanup (struct ospf_interface *oi) +{ + struct route_node *rn; + struct listnode *node, *nnode; + struct ospf_neighbor *nbr; + struct ospf_nbr_nbma *nbr_nbma; + struct ospf_lsa *lsa; + + /* oi->nbrs and oi->nbr_nbma should be deleted on InterfaceDown event */ + /* delete all static neighbors attached to this interface */ + for (ALL_LIST_ELEMENTS (oi->nbr_nbma, node, nnode, nbr_nbma)) + { + OSPF_POLL_TIMER_OFF (nbr_nbma->t_poll); + + if (nbr_nbma->nbr) + { + nbr_nbma->nbr->nbr_nbma = NULL; + nbr_nbma->nbr = NULL; + } + + nbr_nbma->oi = NULL; + + listnode_delete (oi->nbr_nbma, nbr_nbma); + } + + /* send Neighbor event KillNbr to all associated neighbors. */ + for (rn = route_top (oi->nbrs); rn; rn = route_next (rn)) + if ((nbr = rn->info) != NULL) + if (nbr != oi->nbr_self) + OSPF_NSM_EVENT_EXECUTE (nbr, NSM_KillNbr); + + /* Cleanup Link State Acknowlegdment list. */ + for (ALL_LIST_ELEMENTS (oi->ls_ack, node, nnode, lsa)) + ospf_lsa_unlock (&lsa); /* oi->ls_ack */ + list_delete_all_node (oi->ls_ack); + + oi->crypt_seqnum = 0; + + /* Empty link state update queue */ + ospf_ls_upd_queue_empty (oi); + + /* Reset pseudo neighbor. */ + ospf_nbr_self_reset (oi); +} + +void +ospf_if_free (struct ospf_interface *oi) +{ + ospf_if_down (oi); + + assert (oi->state == ISM_Down); + + ospf_opaque_type9_lsa_term (oi); + + /* Free Pseudo Neighbour */ + ospf_nbr_delete (oi->nbr_self); + + route_table_finish (oi->nbrs); + route_table_finish (oi->ls_upd_queue); + + /* Free any lists that should be freed */ + list_free (oi->nbr_nbma); + + list_free (oi->ls_ack); + list_free (oi->ls_ack_direct.ls_ack); + + ospf_delete_from_if (oi->ifp, oi); + + listnode_delete (oi->ospf->oiflist, oi); + listnode_delete (oi->area->oiflist, oi); + + thread_cancel_event (master, oi); + + memset (oi, 0, sizeof (*oi)); + XFREE (MTYPE_OSPF_IF, oi); +} + + +/* +* check if interface with given address is configured and +* return it if yes. special treatment for PtP networks. +*/ +struct ospf_interface * +ospf_if_is_configured (struct ospf *ospf, struct in_addr *address) +{ + struct listnode *node, *nnode; + struct ospf_interface *oi; + struct prefix_ipv4 addr; + + addr.family = AF_INET; + addr.prefix = *address; + addr.prefixlen = IPV4_MAX_PREFIXLEN; + + for (ALL_LIST_ELEMENTS (ospf->oiflist, node, nnode, oi)) + if (oi->type != OSPF_IFTYPE_VIRTUALLINK) + { + if (oi->type == OSPF_IFTYPE_POINTOPOINT) + { + /* special leniency: match if addr is anywhere on peer subnet */ + if (prefix_match(CONNECTED_PREFIX(oi->connected), + (struct prefix *)&addr)) + return oi; + } + else + { + if (IPV4_ADDR_SAME (address, &oi->address->u.prefix4)) + return oi; + } + } + return NULL; +} + +int +ospf_if_is_up (struct ospf_interface *oi) +{ + return if_is_up (oi->ifp); +} + +struct ospf_interface * +ospf_if_exists (struct ospf_interface *oic) +{ + struct listnode *node; + struct ospf *ospf; + struct ospf_interface *oi; + + if ((ospf = ospf_lookup ()) == NULL) + return NULL; + + for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi)) + if (oi == oic) + return oi; + + 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) +{ + struct listnode *node; + struct ospf_interface *oi; + + for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi)) + if (oi->type != OSPF_IFTYPE_VIRTUALLINK) + { + if (ifp && oi->ifp != ifp) + continue; + + if (IPV4_ADDR_SAME (&address, &oi->address->u.prefix4)) + return oi; + } + + return NULL; +} + +struct ospf_interface * +ospf_if_lookup_by_prefix (struct ospf *ospf, struct prefix_ipv4 *p) +{ + struct listnode *node; + struct ospf_interface *oi; + + /* Check each Interface. */ + for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi)) + { + if (oi->type != OSPF_IFTYPE_VIRTUALLINK) + { + struct prefix ptmp; + + prefix_copy (&ptmp, CONNECTED_PREFIX(oi->connected)); + apply_mask (&ptmp); + if (prefix_same (&ptmp, (struct prefix *) p)) + return oi; + } + } + return NULL; +} + +/* determine receiving interface by ifp and source address */ +struct ospf_interface * +ospf_if_lookup_recv_if (struct ospf *ospf, struct in_addr src, + struct interface *ifp) +{ + struct route_node *rn; + struct prefix_ipv4 addr; + struct ospf_interface *oi, *match; + + addr.family = AF_INET; + addr.prefix = src; + addr.prefixlen = IPV4_MAX_BITLEN; + + match = NULL; + + for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn)) + { + oi = rn->info; + + if (!oi) /* oi can be NULL for PtP aliases */ + continue; + + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) + continue; + + if (if_is_loopback (oi->ifp)) + continue; + + if (prefix_match (CONNECTED_PREFIX(oi->connected), + (struct prefix *) &addr)) + { + if ( (match == NULL) || + (match->address->prefixlen < oi->address->prefixlen) + ) + match = oi; + } + } + + return match; +} + +void +ospf_if_stream_set (struct ospf_interface *oi) +{ + /* set output fifo queue. */ + if (oi->obuf == NULL) + oi->obuf = ospf_fifo_new (); +} + +void +ospf_if_stream_unset (struct ospf_interface *oi) +{ + struct ospf *ospf = oi->ospf; + + if (oi->obuf) + { + ospf_fifo_free (oi->obuf); + oi->obuf = NULL; + + if (oi->on_write_q) + { + listnode_delete (ospf->oi_write_q, oi); + if (list_isempty(ospf->oi_write_q)) + OSPF_TIMER_OFF (ospf->t_write); + oi->on_write_q = 0; + } + } +} + + +static struct ospf_if_params * +ospf_new_if_params (void) +{ + struct ospf_if_params *oip; + + oip = XCALLOC (MTYPE_OSPF_IF_PARAMS, sizeof (struct ospf_if_params)); + + if (!oip) + return NULL; + + UNSET_IF_PARAM (oip, output_cost_cmd); + UNSET_IF_PARAM (oip, transmit_delay); + UNSET_IF_PARAM (oip, retransmit_interval); + UNSET_IF_PARAM (oip, passive_interface); + UNSET_IF_PARAM (oip, v_hello); + UNSET_IF_PARAM (oip, fast_hello); + UNSET_IF_PARAM (oip, v_wait); + UNSET_IF_PARAM (oip, priority); + UNSET_IF_PARAM (oip, type); + UNSET_IF_PARAM (oip, auth_simple); + UNSET_IF_PARAM (oip, auth_crypt); + UNSET_IF_PARAM (oip, auth_type); + + oip->auth_crypt = list_new (); + + oip->network_lsa_seqnum = htonl(OSPF_INITIAL_SEQUENCE_NUMBER); + + return oip; +} + +void +ospf_del_if_params (struct ospf_if_params *oip) +{ + list_delete (oip->auth_crypt); + XFREE (MTYPE_OSPF_IF_PARAMS, oip); +} + +void +ospf_free_if_params (struct interface *ifp, struct in_addr addr) +{ + struct ospf_if_params *oip; + struct prefix_ipv4 p; + struct route_node *rn; + + p.family = AF_INET; + p.prefixlen = IPV4_MAX_PREFIXLEN; + p.prefix = addr; + rn = route_node_lookup (IF_OIFS_PARAMS (ifp), (struct prefix*)&p); + if (!rn || !rn->info) + return; + + oip = rn->info; + route_unlock_node (rn); + + if (!OSPF_IF_PARAM_CONFIGURED (oip, output_cost_cmd) && + !OSPF_IF_PARAM_CONFIGURED (oip, transmit_delay) && + !OSPF_IF_PARAM_CONFIGURED (oip, retransmit_interval) && + !OSPF_IF_PARAM_CONFIGURED (oip, passive_interface) && + !OSPF_IF_PARAM_CONFIGURED (oip, v_hello) && + !OSPF_IF_PARAM_CONFIGURED (oip, fast_hello) && + !OSPF_IF_PARAM_CONFIGURED (oip, v_wait) && + !OSPF_IF_PARAM_CONFIGURED (oip, priority) && + !OSPF_IF_PARAM_CONFIGURED (oip, type) && + !OSPF_IF_PARAM_CONFIGURED (oip, auth_simple) && + !OSPF_IF_PARAM_CONFIGURED (oip, auth_type) && + listcount (oip->auth_crypt) == 0 && + ntohl (oip->network_lsa_seqnum) != OSPF_INITIAL_SEQUENCE_NUMBER) + { + ospf_del_if_params (oip); + rn->info = NULL; + route_unlock_node (rn); + } +} + +struct ospf_if_params * +ospf_lookup_if_params (struct interface *ifp, struct in_addr addr) +{ + struct prefix_ipv4 p; + struct route_node *rn; + + p.family = AF_INET; + p.prefixlen = IPV4_MAX_PREFIXLEN; + p.prefix = addr; + + rn = route_node_lookup (IF_OIFS_PARAMS (ifp), (struct prefix*)&p); + + if (rn) + { + route_unlock_node (rn); + return rn->info; + } + + return NULL; +} + +struct ospf_if_params * +ospf_get_if_params (struct interface *ifp, struct in_addr addr) +{ + struct prefix_ipv4 p; + struct route_node *rn; + + p.family = AF_INET; + p.prefixlen = IPV4_MAX_PREFIXLEN; + p.prefix = addr; + + rn = route_node_get (IF_OIFS_PARAMS (ifp), (struct prefix*)&p); + + if (rn->info == NULL) + rn->info = ospf_new_if_params (); + else + route_unlock_node (rn); + + return rn->info; +} + +void +ospf_if_update_params (struct interface *ifp, struct in_addr addr) +{ + struct route_node *rn; + struct ospf_interface *oi; + + for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn)) + { + if ((oi = rn->info) == NULL) + continue; + + if (IPV4_ADDR_SAME (&oi->address->u.prefix4, &addr)) + oi->params = ospf_lookup_if_params (ifp, oi->address->u.prefix4); + } +} + +int +ospf_if_new_hook (struct interface *ifp) +{ + int rc = 0; + + ifp->info = XCALLOC (MTYPE_OSPF_IF_INFO, sizeof (struct ospf_if_info)); + + IF_OIFS (ifp) = route_table_init (); + IF_OIFS_PARAMS (ifp) = route_table_init (); + + IF_DEF_PARAMS (ifp) = ospf_new_if_params (); + + SET_IF_PARAM (IF_DEF_PARAMS (ifp), transmit_delay); + IF_DEF_PARAMS (ifp)->transmit_delay = OSPF_TRANSMIT_DELAY_DEFAULT; + + SET_IF_PARAM (IF_DEF_PARAMS (ifp), retransmit_interval); + IF_DEF_PARAMS (ifp)->retransmit_interval = OSPF_RETRANSMIT_INTERVAL_DEFAULT; + + SET_IF_PARAM (IF_DEF_PARAMS (ifp), priority); + IF_DEF_PARAMS (ifp)->priority = OSPF_ROUTER_PRIORITY_DEFAULT; + + IF_DEF_PARAMS (ifp)->mtu_ignore = OSPF_MTU_IGNORE_DEFAULT; + + SET_IF_PARAM (IF_DEF_PARAMS (ifp), v_hello); + IF_DEF_PARAMS (ifp)->v_hello = OSPF_HELLO_INTERVAL_DEFAULT; + + SET_IF_PARAM (IF_DEF_PARAMS (ifp), fast_hello); + IF_DEF_PARAMS (ifp)->fast_hello = OSPF_FAST_HELLO_DEFAULT; + + SET_IF_PARAM (IF_DEF_PARAMS (ifp), v_wait); + IF_DEF_PARAMS (ifp)->v_wait = OSPF_ROUTER_DEAD_INTERVAL_DEFAULT; + + SET_IF_PARAM (IF_DEF_PARAMS (ifp), auth_simple); + memset (IF_DEF_PARAMS (ifp)->auth_simple, 0, OSPF_AUTH_SIMPLE_SIZE); + + SET_IF_PARAM (IF_DEF_PARAMS (ifp), auth_type); + IF_DEF_PARAMS (ifp)->auth_type = OSPF_AUTH_NOTSET; + + rc = ospf_opaque_new_if (ifp); + return rc; +} + +static int +ospf_if_delete_hook (struct interface *ifp) +{ + int rc = 0; + struct route_node *rn; + rc = ospf_opaque_del_if (ifp); + + route_table_finish (IF_OIFS (ifp)); + + for (rn = route_top (IF_OIFS_PARAMS (ifp)); rn; rn = route_next (rn)) + if (rn->info) + ospf_del_if_params (rn->info); + route_table_finish (IF_OIFS_PARAMS (ifp)); + + ospf_del_if_params ((struct ospf_if_params *) IF_DEF_PARAMS (ifp)); + XFREE (MTYPE_OSPF_IF_INFO, ifp->info); + ifp->info = NULL; + + return rc; +} + +int +ospf_if_is_enable (struct ospf_interface *oi) +{ + if (!if_is_loopback (oi->ifp)) + if (if_is_up (oi->ifp)) + return 1; + + return 0; +} + +void +ospf_if_set_multicast(struct ospf_interface *oi) +{ + if ((oi->state > ISM_Loopback) && + (oi->type != OSPF_IFTYPE_LOOPBACK) && + (oi->type != OSPF_IFTYPE_VIRTUALLINK) && + (OSPF_IF_PASSIVE_STATUS(oi) == OSPF_IF_ACTIVE)) + { + /* The interface should belong to the OSPF-all-routers group. */ + if (!OI_MEMBER_CHECK(oi, MEMBER_ALLROUTERS) && + (ospf_if_add_allspfrouters(oi->ospf, oi->address, + oi->ifp->ifindex) >= 0)) + /* Set the flag only if the system call to join succeeded. */ + OI_MEMBER_JOINED(oi, MEMBER_ALLROUTERS); + } + else + { + /* The interface should NOT belong to the OSPF-all-routers group. */ + if (OI_MEMBER_CHECK(oi, MEMBER_ALLROUTERS)) + { + /* Only actually drop if this is the last reference */ + if (OI_MEMBER_COUNT(oi, MEMBER_ALLROUTERS) == 1) + ospf_if_drop_allspfrouters (oi->ospf, oi->address, + oi->ifp->ifindex); + /* Unset the flag regardless of whether the system call to leave + the group succeeded, since it's much safer to assume that + we are not a member. */ + OI_MEMBER_LEFT(oi,MEMBER_ALLROUTERS); + } + } + + if (((oi->type == OSPF_IFTYPE_BROADCAST) || + (oi->type == OSPF_IFTYPE_POINTOPOINT)) && + ((oi->state == ISM_DR) || (oi->state == ISM_Backup)) && + (OSPF_IF_PASSIVE_STATUS(oi) == OSPF_IF_ACTIVE)) + { + /* The interface should belong to the OSPF-designated-routers group. */ + if (!OI_MEMBER_CHECK(oi, MEMBER_DROUTERS) && + (ospf_if_add_alldrouters(oi->ospf, oi->address, + oi->ifp->ifindex) >= 0)) + /* Set the flag only if the system call to join succeeded. */ + OI_MEMBER_JOINED(oi, MEMBER_DROUTERS); + } + else + { + /* The interface should NOT belong to the OSPF-designated-routers group */ + if (OI_MEMBER_CHECK(oi, MEMBER_DROUTERS)) + { + /* drop only if last reference */ + if (OI_MEMBER_COUNT(oi, MEMBER_DROUTERS) == 1) + ospf_if_drop_alldrouters(oi->ospf, oi->address, oi->ifp->ifindex); + + /* Unset the flag regardless of whether the system call to leave + the group succeeded, since it's much safer to assume that + we are not a member. */ + OI_MEMBER_LEFT(oi, MEMBER_DROUTERS); + } + } +} + +int +ospf_if_up (struct ospf_interface *oi) +{ + if (oi == NULL) + return 0; + + if (oi->type == OSPF_IFTYPE_LOOPBACK) + OSPF_ISM_EVENT_SCHEDULE (oi, ISM_LoopInd); + else + { + struct ospf *ospf = ospf_lookup (); + if (ospf != NULL) + ospf_adjust_sndbuflen (ospf, oi->ifp->mtu); + else + zlog_warn ("%s: ospf_lookup() returned NULL", __func__); + ospf_if_stream_set (oi); + OSPF_ISM_EVENT_SCHEDULE (oi, ISM_InterfaceUp); + } + + return 1; +} + +int +ospf_if_down (struct ospf_interface *oi) +{ + if (oi == NULL) + 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 * +ospf_vl_data_new (struct ospf_area *area, struct in_addr vl_peer) +{ + struct ospf_vl_data *vl_data; + + vl_data = XCALLOC (MTYPE_OSPF_VL_DATA, sizeof (struct ospf_vl_data)); + + vl_data->vl_peer.s_addr = vl_peer.s_addr; + vl_data->vl_area_id = area->area_id; + vl_data->format = area->format; + + return vl_data; +} + +void +ospf_vl_data_free (struct ospf_vl_data *vl_data) +{ + XFREE (MTYPE_OSPF_VL_DATA, vl_data); +} + +u_int vlink_count = 0; + +struct ospf_interface * +ospf_vl_new (struct ospf *ospf, struct ospf_vl_data *vl_data) +{ + struct ospf_interface * voi; + struct interface * vi; + char ifname[INTERFACE_NAMSIZ + 1]; + struct ospf_area *area; + struct in_addr area_id; + struct connected *co; + struct prefix_ipv4 *p; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_vl_new(): Start"); + if (vlink_count == OSPF_VL_MAX_COUNT) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_vl_new(): Alarm: " + "cannot create more than OSPF_MAX_VL_COUNT virtual links"); + return NULL; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_vl_new(): creating pseudo zebra interface"); + + 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); + + p = prefix_ipv4_new (); + p->family = AF_INET; + p->prefix.s_addr = 0; + p->prefixlen = 0; + + co->address = (struct prefix *)p; + + voi = ospf_if_new (ospf, vi, co->address); + if (voi == NULL) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_vl_new(): Alarm: OSPF int structure is not created"); + return NULL; + } + voi->connected = co; + voi->vl_data = vl_data; + voi->ifp->mtu = OSPF_VL_MTU; + voi->type = OSPF_IFTYPE_VIRTUALLINK; + + vlink_count++; + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_vl_new(): Created name: %s", ifname); + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_vl_new(): set if->name to %s", vi->name); + + area_id.s_addr = 0; + area = ospf_area_get (ospf, area_id, OSPF_AREA_ID_FORMAT_ADDRESS); + voi->area = area; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_vl_new(): set associated area to the backbone"); + + /* Add pseudo neighbor. */ + ospf_nbr_self_reset (voi); + + ospf_area_add_if (voi->area, voi); + + ospf_if_stream_set (voi); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_vl_new(): Stop"); + return voi; +} + +static void +ospf_vl_if_delete (struct ospf_vl_data *vl_data) +{ + struct interface *ifp = vl_data->vl_oi->ifp; + vl_data->vl_oi->address->u.prefix4.s_addr = 0; + vl_data->vl_oi->address->prefixlen = 0; + ospf_if_free (vl_data->vl_oi); + if_delete (ifp); + vlink_count--; +} + +/* Look up vl_data for given peer, optionally qualified to be in the + * specified area. NULL area returns first found.. + */ +struct ospf_vl_data * +ospf_vl_lookup (struct ospf *ospf, struct ospf_area *area, + struct in_addr vl_peer) +{ + struct ospf_vl_data *vl_data; + struct listnode *node; + + if (IS_DEBUG_OSPF_EVENT) + { + zlog_debug ("%s: Looking for %s", __func__, inet_ntoa (vl_peer)); + if (area) + zlog_debug ("%s: in area %s", __func__, inet_ntoa (area->area_id)); + } + + for (ALL_LIST_ELEMENTS_RO (ospf->vlinks, node, vl_data)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("%s: VL %s, peer %s", __func__, + vl_data->vl_oi->ifp->name, + inet_ntoa (vl_data->vl_peer)); + + if (area && !IPV4_ADDR_SAME (&vl_data->vl_area_id, &area->area_id)) + continue; + + if (IPV4_ADDR_SAME (&vl_data->vl_peer, &vl_peer)) + return vl_data; + } + + return NULL; +} + +static void +ospf_vl_shutdown (struct ospf_vl_data *vl_data) +{ + struct ospf_interface *oi; + + if ((oi = vl_data->vl_oi) == NULL) + return; + + oi->address->u.prefix4.s_addr = 0; + oi->address->prefixlen = 0; + + UNSET_FLAG (oi->ifp->flags, IFF_UP); + /* OSPF_ISM_EVENT_SCHEDULE (oi, ISM_InterfaceDown); */ + OSPF_ISM_EVENT_EXECUTE (oi, ISM_InterfaceDown); +} + +void +ospf_vl_add (struct ospf *ospf, struct ospf_vl_data *vl_data) +{ + listnode_add (ospf->vlinks, vl_data); +#ifdef HAVE_SNMP + ospf_snmp_vl_add (vl_data); +#endif /* HAVE_SNMP */ +} + +void +ospf_vl_delete (struct ospf *ospf, struct ospf_vl_data *vl_data) +{ + ospf_vl_shutdown (vl_data); + ospf_vl_if_delete (vl_data); + +#ifdef HAVE_SNMP + ospf_snmp_vl_delete (vl_data); +#endif /* HAVE_SNMP */ + listnode_delete (ospf->vlinks, vl_data); + + ospf_vl_data_free (vl_data); +} + +static int +ospf_vl_set_params (struct ospf_vl_data *vl_data, struct vertex *v) +{ + int changed = 0; + struct ospf_interface *voi; + struct listnode *node; + struct vertex_parent *vp = NULL; + unsigned int i; + struct router_lsa *rl; + + voi = vl_data->vl_oi; + + if (voi->output_cost != v->distance) + { + + voi->output_cost = v->distance; + changed = 1; + } + + for (ALL_LIST_ELEMENTS_RO (v->parents, node, vp)) + { + vl_data->nexthop.oi = vp->nexthop->oi; + vl_data->nexthop.router = vp->nexthop->router; + + if (!IPV4_ADDR_SAME(&voi->address->u.prefix4, + &vl_data->nexthop.oi->address->u.prefix4)) + changed = 1; + + voi->address->u.prefix4 = vl_data->nexthop.oi->address->u.prefix4; + voi->address->prefixlen = vl_data->nexthop.oi->address->prefixlen; + + break; /* We take the first interface. */ + } + + rl = (struct router_lsa *)v->lsa; + + /* use SPF determined backlink index in struct vertex + * for virtual link destination address + */ + if (vp && vp->backlink >= 0) + { + if (!IPV4_ADDR_SAME (&vl_data->peer_addr, + &rl->link[vp->backlink].link_data)) + changed = 1; + vl_data->peer_addr = rl->link[vp->backlink].link_data; + } + else + { + /* This is highly odd, there is no backlink index + * there should be due to the ospf_spf_has_link() check + * in SPF. Lets warn and try pick a link anyway. + */ + zlog_warn ("ospf_vl_set_params: No backlink for %s!", + vl_data->vl_oi->ifp->name); + for (i = 0; i < ntohs (rl->links); i++) + { + switch (rl->link[i].type) + { + case LSA_LINK_TYPE_VIRTUALLINK: + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("found back link through VL"); + case LSA_LINK_TYPE_TRANSIT: + case LSA_LINK_TYPE_POINTOPOINT: + if (!IPV4_ADDR_SAME (&vl_data->peer_addr, + &rl->link[i].link_data)) + changed = 1; + vl_data->peer_addr = rl->link[i].link_data; + } + } + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("%s: %s peer address: %s, cost: %d,%schanged", __func__, + vl_data->vl_oi->ifp->name, + inet_ntoa(vl_data->peer_addr), + voi->output_cost, + (changed ? " " : " un")); + + return changed; +} + + +void +ospf_vl_up_check (struct ospf_area *area, struct in_addr rid, + struct vertex *v) +{ + struct ospf *ospf = area->ospf; + struct listnode *node; + struct ospf_vl_data *vl_data; + struct ospf_interface *oi; + + if (IS_DEBUG_OSPF_EVENT) + { + zlog_debug ("ospf_vl_up_check(): Start"); + zlog_debug ("ospf_vl_up_check(): Router ID is %s", inet_ntoa (rid)); + zlog_debug ("ospf_vl_up_check(): Area is %s", inet_ntoa (area->area_id)); + } + + for (ALL_LIST_ELEMENTS_RO (ospf->vlinks, node, vl_data)) + { + if (IS_DEBUG_OSPF_EVENT) + { + zlog_debug ("%s: considering VL, %s in area %s", __func__, + vl_data->vl_oi->ifp->name, + inet_ntoa (vl_data->vl_area_id)); + zlog_debug ("%s: peer ID: %s", __func__, + inet_ntoa (vl_data->vl_peer)); + } + + if (IPV4_ADDR_SAME (&vl_data->vl_peer, &rid) && + IPV4_ADDR_SAME (&vl_data->vl_area_id, &area->area_id)) + { + oi = vl_data->vl_oi; + SET_FLAG (vl_data->flags, OSPF_VL_FLAG_APPROVED); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_vl_up_check(): this VL matched"); + + if (oi->state == ISM_Down) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_vl_up_check(): VL is down, waking it up"); + SET_FLAG (oi->ifp->flags, IFF_UP); + OSPF_ISM_EVENT_EXECUTE(oi,ISM_InterfaceUp); + } + + if (ospf_vl_set_params (vl_data, v)) + { + if (IS_DEBUG_OSPF (ism, ISM_EVENTS)) + zlog_debug ("ospf_vl_up_check: VL cost change," + " scheduling router lsa refresh"); + if (ospf->backbone) + ospf_router_lsa_update_area (ospf->backbone); + else if (IS_DEBUG_OSPF (ism, ISM_EVENTS)) + zlog_debug ("ospf_vl_up_check: VL cost change, no backbone!"); + } + } + } +} + +void +ospf_vl_unapprove (struct ospf *ospf) +{ + struct listnode *node; + struct ospf_vl_data *vl_data; + + for (ALL_LIST_ELEMENTS_RO (ospf->vlinks, node, vl_data)) + UNSET_FLAG (vl_data->flags, OSPF_VL_FLAG_APPROVED); +} + +void +ospf_vl_shut_unapproved (struct ospf *ospf) +{ + struct listnode *node, *nnode; + struct ospf_vl_data *vl_data; + + for (ALL_LIST_ELEMENTS (ospf->vlinks, node, nnode, vl_data)) + if (!CHECK_FLAG (vl_data->flags, OSPF_VL_FLAG_APPROVED)) + ospf_vl_shutdown (vl_data); +} + +int +ospf_full_virtual_nbrs (struct ospf_area *area) +{ + if (IS_DEBUG_OSPF_EVENT) + { + zlog_debug ("counting fully adjacent virtual neighbors in area %s", + inet_ntoa (area->area_id)); + zlog_debug ("there are %d of them", area->full_vls); + } + + return area->full_vls; +} + +int +ospf_vls_in_area (struct ospf_area *area) +{ + struct listnode *node; + struct ospf_vl_data *vl_data; + int c = 0; + + for (ALL_LIST_ELEMENTS_RO (area->ospf->vlinks, node, vl_data)) + if (IPV4_ADDR_SAME (&vl_data->vl_area_id, &area->area_id)) + c++; + + return c; +} + + +struct crypt_key * +ospf_crypt_key_new () +{ + return XCALLOC (MTYPE_OSPF_CRYPT_KEY, sizeof (struct crypt_key)); +} + +void +ospf_crypt_key_add (struct list *crypt, struct crypt_key *ck) +{ + listnode_add (crypt, ck); +} + +struct crypt_key * +ospf_crypt_key_lookup (struct list *auth_crypt, u_char key_id) +{ + struct listnode *node; + struct crypt_key *ck; + + for (ALL_LIST_ELEMENTS_RO (auth_crypt, node, ck)) + if (ck->key_id == key_id) + return ck; + + return NULL; +} + +int +ospf_crypt_key_delete (struct list *auth_crypt, u_char key_id) +{ + struct listnode *node, *nnode; + struct crypt_key *ck; + + for (ALL_LIST_ELEMENTS (auth_crypt, node, nnode, ck)) + { + if (ck->key_id == key_id) + { + listnode_delete (auth_crypt, ck); + XFREE (MTYPE_OSPF_CRYPT_KEY, ck); + return 1; + } + } + + return 0; +} + +u_char +ospf_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; +} + +void +ospf_if_init () +{ + /* Initialize Zebra interface data structure. */ + 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 new file mode 100644 index 0000000..7070353 --- /dev/null +++ b/ospfd/ospf_interface.h @@ -0,0 +1,314 @@ +/* + * OSPF Interface functions. + * Copyright (C) 1999 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 _ZEBRA_OSPF_INTERFACE_H +#define _ZEBRA_OSPF_INTERFACE_H + +#include "ospfd/ospf_packet.h" +#include "ospfd/ospf_spf.h" + +#define IF_OSPF_IF_INFO(I) ((struct ospf_if_info *)((I)->info)) +#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) + +#define DECLARE_IF_PARAM(T, P) T P; u_char P##__config:1 +#define UNSET_IF_PARAM(S, P) ((S)->P##__config) = 0 +#define SET_IF_PARAM(S, P) ((S)->P##__config) = 1 + +struct ospf_if_params +{ + DECLARE_IF_PARAM (u_int32_t, transmit_delay); /* Interface Transmisson Delay */ + DECLARE_IF_PARAM (u_int32_t, output_cost_cmd);/* Command Interface Output Cost */ + 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 + +#define OSPF_IF_PASSIVE_STATUS(O) \ + (OSPF_IF_PARAM_CONFIGURED((O)->params, passive_interface) ? \ + (O)->params->passive_interface : \ + (OSPF_IF_PARAM_CONFIGURED(IF_DEF_PARAMS((O)->ifp), passive_interface) ? \ + IF_DEF_PARAMS((O)->ifp)->passive_interface : \ + (O)->ospf->passive_interface_default)) + + DECLARE_IF_PARAM (u_int32_t, v_hello); /* Hello Interval */ + DECLARE_IF_PARAM (u_int32_t, v_wait); /* Router Dead Interval */ + + /* MTU mismatch check (see RFC2328, chap 10.6) */ + DECLARE_IF_PARAM (u_char, mtu_ignore); + + /* Fast-Hellos */ + DECLARE_IF_PARAM (u_char, fast_hello); + + /* Authentication data. */ + u_char auth_simple[OSPF_AUTH_SIMPLE_SIZE + 1]; /* Simple password. */ + u_char auth_simple__config:1; + + DECLARE_IF_PARAM (struct list *, auth_crypt); /* List of Auth cryptographic data. */ + DECLARE_IF_PARAM (int, auth_type); /* OSPF authentication type */ + + /* Other, non-configuration state */ + u_int32_t network_lsa_seqnum; /* Network LSA seqnum */ +}; + +enum +{ + MEMBER_ALLROUTERS = 0, + MEMBER_DROUTERS, + MEMBER_MAX, +}; + +struct ospf_if_info +{ + struct ospf_if_params *def_params; + struct route_table *params; + struct route_table *oifs; + unsigned int membership_counts[MEMBER_MAX]; /* multicast group refcnts */ +}; + +struct ospf_interface; + +struct ospf_vl_data +{ + struct in_addr vl_peer; /* Router-ID of the peer for VLs. */ + struct in_addr vl_area_id; /* Transit area for this VL. */ + int format; /* area ID format */ + struct ospf_interface *vl_oi; /* Interface data structure for the VL. */ + struct vertex_nexthop nexthop; /* Nexthop router and oi to use */ + struct in_addr peer_addr; /* Address used to reach the peer. */ + u_char flags; +}; + + +#define OSPF_VL_MAX_COUNT 256 +#define OSPF_VL_MTU 1500 + +#define OSPF_VL_FLAG_APPROVED 0x01 + +struct crypt_key +{ + u_char key_id; + u_char auth_key[OSPF_AUTH_MD5_SIZE + 1]; +}; + +/* OSPF interface structure. */ +struct ospf_interface +{ + /* This interface's parent ospf instance. */ + struct ospf *ospf; + + /* 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 */ + + /* Packet send buffer. */ + struct ospf_fifo *obuf; /* Output queue */ + + /* OSPF Network Type. */ + u_char type; + + /* State of Interface State Machine. */ + u_char state; + + /* To which multicast groups do we currently belong? */ + u_char multicast_memberships; +#define OI_MEMBER_FLAG(M) (1 << (M)) +#define OI_MEMBER_COUNT(O,M) (IF_OSPF_IF_INFO(oi->ifp)->membership_counts[(M)]) +#define OI_MEMBER_CHECK(O,M) \ + (CHECK_FLAG((O)->multicast_memberships, OI_MEMBER_FLAG(M))) +#define OI_MEMBER_JOINED(O,M) \ + do { \ + SET_FLAG ((O)->multicast_memberships, OI_MEMBER_FLAG(M)); \ + IF_OSPF_IF_INFO((O)->ifp)->membership_counts[(M)]++; \ + } while (0) +#define OI_MEMBER_LEFT(O,M) \ + do { \ + UNSET_FLAG ((O)->multicast_memberships, OI_MEMBER_FLAG(M)); \ + IF_OSPF_IF_INFO((O)->ifp)->membership_counts[(M)]--; \ + } while (0) + + struct prefix *address; /* Interface prefix */ + struct connected *connected; /* Pointer to connected */ + + /* Configured varables. */ + struct ospf_if_params *params; + + u_int32_t crypt_seqnum; /* Cryptographic Sequence Number */ + u_int32_t output_cost; /* Acutual Interface Output Cost */ + + /* Neighbor information. */ + struct route_table *nbrs; /* OSPF Neighbor List */ + struct ospf_neighbor *nbr_self; /* Neighbor Self */ +#define DR(I) ((I)->nbr_self->d_router) +#define BDR(I) ((I)->nbr_self->bd_router) +#define OPTIONS(I) ((I)->nbr_self->options) +#define PRIORITY(I) ((I)->nbr_self->priority) + + /* List of configured NBMA neighbor. */ + struct list *nbr_nbma; + + /* self-originated LSAs. */ + struct ospf_lsa *network_lsa_self; /* network-LSA. */ + struct list *opaque_lsa_self; /* Type-9 Opaque-LSAs */ + + struct route_table *ls_upd_queue; + + struct list *ls_ack; /* Link State Acknowledgment list. */ + + struct + { + struct list *ls_ack; + struct in_addr dst; + } ls_ack_direct; + + /* Timer values. */ + u_int32_t v_ls_ack; /* Delayed Link State Acknowledgment */ + + /* Threads. */ + struct thread *t_hello; /* timer */ + struct thread *t_wait; /* timer */ + struct thread *t_ls_ack; /* timer */ + struct thread *t_ls_ack_direct; /* event */ + struct thread *t_ls_upd_event; /* event */ + struct thread *t_opaque_lsa_self; /* Type-9 Opaque-LSAs */ + + int on_write_q; + + /* Statistics fields. */ + u_int32_t hello_in; /* Hello message input count. */ + u_int32_t hello_out; /* Hello message output count. */ + u_int32_t db_desc_in; /* database desc. message input count. */ + u_int32_t db_desc_out; /* database desc. message output count. */ + u_int32_t ls_req_in; /* LS request message input count. */ + u_int32_t ls_req_out; /* LS request message output count. */ + u_int32_t ls_upd_in; /* LS update message input count. */ + u_int32_t ls_upd_out; /* LS update message output count. */ + u_int32_t ls_ack_in; /* LS Ack message input count. */ + u_int32_t ls_ack_out; /* LS Ack message output count. */ + u_int32_t discarded; /* discarded input count by error. */ + u_int32_t state_change; /* Number of status change. */ + + u_int32_t full_nbrs; +}; + +/* Prototypes. */ +extern char *ospf_if_name (struct ospf_interface *); +extern struct ospf_interface *ospf_if_new (struct ospf *, struct interface *, + struct prefix *); +extern void ospf_if_cleanup (struct ospf_interface *); +extern void ospf_if_free (struct ospf_interface *); +extern int ospf_if_up (struct ospf_interface *); +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 + *, + struct in_addr); +extern struct ospf_interface *ospf_if_lookup_by_prefix (struct ospf *, + struct prefix_ipv4 *); +extern struct ospf_interface *ospf_if_table_lookup (struct interface *, + struct prefix *); +extern struct ospf_interface *ospf_if_addr_local (struct in_addr); +extern struct ospf_interface *ospf_if_lookup_recv_if (struct ospf *, + struct in_addr, + struct interface *); +extern struct ospf_interface *ospf_if_is_configured (struct ospf *, + struct in_addr *); + +extern struct ospf_if_params *ospf_lookup_if_params (struct interface *, + struct in_addr); +extern struct ospf_if_params *ospf_get_if_params (struct interface *, + struct in_addr); +extern void ospf_del_if_params (struct ospf_if_params *); +extern void ospf_free_if_params (struct interface *, struct in_addr); +extern void ospf_if_update_params (struct interface *, struct in_addr); + +extern int ospf_if_new_hook (struct interface *); +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 *); + +/* Simulate down/up on the interface. */ +extern void ospf_if_reset (struct interface *); + +extern struct ospf_interface *ospf_vl_new (struct ospf *, + struct ospf_vl_data *); +extern struct ospf_vl_data *ospf_vl_data_new (struct ospf_area *, + struct in_addr); +extern struct ospf_vl_data *ospf_vl_lookup (struct ospf *, struct ospf_area *, + struct in_addr); +extern void ospf_vl_data_free (struct ospf_vl_data *); +extern void ospf_vl_add (struct ospf *, struct ospf_vl_data *); +extern void ospf_vl_delete (struct ospf *, struct ospf_vl_data *); +extern void ospf_vl_up_check (struct ospf_area *, struct in_addr, + struct vertex *); +extern void ospf_vl_unapprove (struct ospf *); +extern void ospf_vl_shut_unapproved (struct ospf *); +extern int ospf_full_virtual_nbrs (struct ospf_area *); +extern int ospf_vls_in_area (struct ospf_area *); + +extern struct crypt_key *ospf_crypt_key_lookup (struct list *, u_char); +extern struct crypt_key *ospf_crypt_key_new (void); +extern void ospf_crypt_key_add (struct list *, struct crypt_key *); +extern int ospf_crypt_key_delete (struct list *, u_char); + +extern u_char ospf_default_iftype (struct interface *ifp); + +/* Set all multicast memberships appropriately based on the type and + state of the interface. */ +extern void ospf_if_set_multicast (struct ospf_interface *); + +#endif /* _ZEBRA_OSPF_INTERFACE_H */ diff --git a/ospfd/ospf_ism.c b/ospfd/ospf_ism.c new file mode 100644 index 0000000..ae6d0cd --- /dev/null +++ b/ospfd/ospf_ism.c @@ -0,0 +1,633 @@ +/* + * OSPF version 2 Interface State Machine + * From RFC2328 [OSPF Version 2] + * Copyright (C) 1999, 2000 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. + */ + +#include + +#include "thread.h" +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "log.h" + +#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_network.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_packet.h" +#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) +{ + struct listnode *node; + struct ospf_neighbor *nbr, *max = NULL; + + /* Choose highest router priority. + In case of tie, choose highest Router ID. */ + for (ALL_LIST_ELEMENTS_RO (routers, node, nbr)) + { + if (max == NULL) + max = nbr; + else + { + if (max->priority < nbr->priority) + max = nbr; + else if (max->priority == nbr->priority) + if (IPV4_ADDR_CMP (&max->router_id, &nbr->router_id) < 0) + max = nbr; + } + } + + return max; +} + +static struct ospf_neighbor * +ospf_elect_dr (struct ospf_interface *oi, struct list *el_list) +{ + struct list *dr_list; + struct listnode *node; + struct ospf_neighbor *nbr, *dr = NULL, *bdr = NULL; + + dr_list = list_new (); + + /* Add neighbors to the list. */ + for (ALL_LIST_ELEMENTS_RO (el_list, node, nbr)) + { + /* neighbor declared to be DR. */ + if (NBR_IS_DR (nbr)) + listnode_add (dr_list, nbr); + + /* Preserve neighbor BDR. */ + if (IPV4_ADDR_SAME (&BDR (oi), &nbr->address.u.prefix4)) + bdr = nbr; + } + + /* Elect Designated Router. */ + if (listcount (dr_list) > 0) + dr = ospf_dr_election_sub (dr_list); + else + dr = bdr; + + /* Set DR to interface. */ + if (dr) + DR (oi) = dr->address.u.prefix4; + else + DR (oi).s_addr = 0; + + list_delete (dr_list); + + return dr; +} + +static struct ospf_neighbor * +ospf_elect_bdr (struct ospf_interface *oi, struct list *el_list) +{ + struct list *bdr_list, *no_dr_list; + struct listnode *node; + struct ospf_neighbor *nbr, *bdr = NULL; + + bdr_list = list_new (); + no_dr_list = list_new (); + + /* Add neighbors to the list. */ + for (ALL_LIST_ELEMENTS_RO (el_list, node, nbr)) + { + /* neighbor declared to be DR. */ + if (NBR_IS_DR (nbr)) + continue; + + /* neighbor declared to be BDR. */ + if (NBR_IS_BDR (nbr)) + listnode_add (bdr_list, nbr); + + listnode_add (no_dr_list, nbr); + } + + /* Elect Backup Designated Router. */ + if (listcount (bdr_list) > 0) + bdr = ospf_dr_election_sub (bdr_list); + else + bdr = ospf_dr_election_sub (no_dr_list); + + /* Set BDR to interface. */ + if (bdr) + BDR (oi) = bdr->address.u.prefix4; + else + BDR (oi).s_addr = 0; + + list_delete (bdr_list); + list_delete (no_dr_list); + + return bdr; +} + +static int +ospf_ism_state (struct ospf_interface *oi) +{ + if (IPV4_ADDR_SAME (&DR (oi), &oi->address->u.prefix4)) + return ISM_DR; + else if (IPV4_ADDR_SAME (&BDR (oi), &oi->address->u.prefix4)) + return ISM_Backup; + else + return ISM_DROther; +} + +static void +ospf_dr_eligible_routers (struct route_table *nbrs, struct list *el_list) +{ + struct route_node *rn; + struct ospf_neighbor *nbr; + + for (rn = route_top (nbrs); rn; rn = route_next (rn)) + if ((nbr = rn->info) != NULL) + /* Ignore 0.0.0.0 node*/ + if (nbr->router_id.s_addr != 0) + /* Is neighbor eligible? */ + if (nbr->priority > 0) + /* Is neighbor upper 2-Way? */ + if (nbr->state >= NSM_TwoWay) + listnode_add (el_list, nbr); +} + +/* Generate AdjOK? NSM event. */ +static void +ospf_dr_change (struct ospf *ospf, struct route_table *nbrs) +{ + struct route_node *rn; + struct ospf_neighbor *nbr; + + for (rn = route_top (nbrs); rn; rn = route_next (rn)) + if ((nbr = rn->info) != NULL) + /* Ignore 0.0.0.0 node*/ + if (nbr->router_id.s_addr != 0) + /* Is neighbor upper 2-Way? */ + if (nbr->state >= NSM_TwoWay) + /* Ignore myself. */ + if (!IPV4_ADDR_SAME (&nbr->router_id, &ospf->router_id)) + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_AdjOK); +} + +static int +ospf_dr_election (struct ospf_interface *oi) +{ + struct in_addr old_dr, old_bdr; + int old_state, new_state; + struct list *el_list; + + /* backup current values. */ + old_dr = DR (oi); + old_bdr = BDR (oi); + old_state = oi->state; + + el_list = list_new (); + + /* List eligible routers. */ + ospf_dr_eligible_routers (oi->nbrs, el_list); + + /* First election of DR and BDR. */ + ospf_elect_bdr (oi, el_list); + ospf_elect_dr (oi, el_list); + + new_state = ospf_ism_state (oi); + + zlog_debug ("DR-Election[1st]: Backup %s", inet_ntoa (BDR (oi))); + zlog_debug ("DR-Election[1st]: DR %s", inet_ntoa (DR (oi))); + + if (new_state != old_state && + !(new_state == ISM_DROther && old_state < ISM_DROther)) + { + ospf_elect_bdr (oi, el_list); + ospf_elect_dr (oi, el_list); + + new_state = ospf_ism_state (oi); + + zlog_debug ("DR-Election[2nd]: Backup %s", inet_ntoa (BDR (oi))); + zlog_debug ("DR-Election[2nd]: DR %s", inet_ntoa (DR (oi))); + } + + list_delete (el_list); + + /* if DR or BDR changes, cause AdjOK? neighbor event. */ + if (!IPV4_ADDR_SAME (&old_dr, &DR (oi)) || + !IPV4_ADDR_SAME (&old_bdr, &BDR (oi))) + ospf_dr_change (oi->ospf, oi->nbrs); + + return new_state; +} + + +int +ospf_hello_timer (struct thread *thread) +{ + struct ospf_interface *oi; + + oi = THREAD_ARG (thread); + oi->t_hello = NULL; + + if (IS_DEBUG_OSPF (ism, ISM_TIMERS)) + zlog (NULL, LOG_DEBUG, "ISM[%s]: Timer (Hello timer expire)", + IF_NAME (oi)); + + /* Sending hello packet. */ + ospf_hello_send (oi); + + /* Hello timer set. */ + OSPF_HELLO_TIMER_ON (oi); + + return 0; +} + +static int +ospf_wait_timer (struct thread *thread) +{ + struct ospf_interface *oi; + + oi = THREAD_ARG (thread); + oi->t_wait = NULL; + + if (IS_DEBUG_OSPF (ism, ISM_TIMERS)) + zlog (NULL, LOG_DEBUG, "ISM[%s]: Timer (Wait timer expire)", + IF_NAME (oi)); + + OSPF_ISM_EVENT_SCHEDULE (oi, ISM_WaitTimer); + + return 0; +} + +/* Hook function called after ospf ISM event is occured. And vty's + network command invoke this function after making interface + structure. */ +static void +ism_timer_set (struct ospf_interface *oi) +{ + switch (oi->state) + { + case ISM_Down: + /* First entry point of ospf interface state machine. In this state + interface parameters must be set to initial values, and timers are + reset also. */ + OSPF_ISM_TIMER_OFF (oi->t_hello); + OSPF_ISM_TIMER_OFF (oi->t_wait); + OSPF_ISM_TIMER_OFF (oi->t_ls_ack); + break; + case ISM_Loopback: + /* In this state, the interface may be looped back and will be + unavailable for regular data traffic. */ + OSPF_ISM_TIMER_OFF (oi->t_hello); + OSPF_ISM_TIMER_OFF (oi->t_wait); + OSPF_ISM_TIMER_OFF (oi->t_ls_ack); + break; + case ISM_Waiting: + /* The router is trying to determine the identity of DRouter and + BDRouter. The router begin to receive and send Hello Packets. */ + /* send first hello immediately */ + OSPF_ISM_TIMER_MSEC_ON (oi->t_hello, ospf_hello_timer, 1); + OSPF_ISM_TIMER_ON (oi->t_wait, ospf_wait_timer, + OSPF_IF_PARAM (oi, v_wait)); + OSPF_ISM_TIMER_OFF (oi->t_ls_ack); + break; + case ISM_PointToPoint: + /* The interface connects to a physical Point-to-point network or + virtual link. The router attempts to form an adjacency with + neighboring router. Hello packets are also sent. */ + /* send first hello immediately */ + OSPF_ISM_TIMER_MSEC_ON (oi->t_hello, ospf_hello_timer, 1); + OSPF_ISM_TIMER_OFF (oi->t_wait); + OSPF_ISM_TIMER_ON (oi->t_ls_ack, ospf_ls_ack_timer, oi->v_ls_ack); + break; + case ISM_DROther: + /* The network type of the interface is broadcast or NBMA network, + and the router itself is neither Designated Router nor + Backup Designated Router. */ + OSPF_HELLO_TIMER_ON (oi); + OSPF_ISM_TIMER_OFF (oi->t_wait); + OSPF_ISM_TIMER_ON (oi->t_ls_ack, ospf_ls_ack_timer, oi->v_ls_ack); + break; + case ISM_Backup: + /* The network type of the interface is broadcast os NBMA network, + and the router is Backup Designated Router. */ + OSPF_HELLO_TIMER_ON (oi); + OSPF_ISM_TIMER_OFF (oi->t_wait); + OSPF_ISM_TIMER_ON (oi->t_ls_ack, ospf_ls_ack_timer, oi->v_ls_ack); + break; + case ISM_DR: + /* The network type of the interface is broadcast or NBMA network, + and the router is Designated Router. */ + OSPF_HELLO_TIMER_ON (oi); + OSPF_ISM_TIMER_OFF (oi->t_wait); + OSPF_ISM_TIMER_ON (oi->t_ls_ack, ospf_ls_ack_timer, oi->v_ls_ack); + break; + } +} + +static int +ism_interface_up (struct ospf_interface *oi) +{ + int next_state = 0; + + /* if network type is point-to-point, Point-to-MultiPoint or virtual link, + the state transitions to Point-to-Point. */ + if (oi->type == OSPF_IFTYPE_POINTOPOINT || + oi->type == OSPF_IFTYPE_POINTOMULTIPOINT || + oi->type == OSPF_IFTYPE_VIRTUALLINK) + next_state = ISM_PointToPoint; + /* Else if the router is not eligible to DR, the state transitions to + DROther. */ + else if (PRIORITY (oi) == 0) /* router is eligible? */ + next_state = ISM_DROther; + else + /* Otherwise, the state transitions to Waiting. */ + next_state = ISM_Waiting; + + if (oi->type == OSPF_IFTYPE_NBMA) + ospf_nbr_nbma_if_update (oi->ospf, oi); + + /* ospf_ism_event (t); */ + return next_state; +} + +static int +ism_loop_ind (struct ospf_interface *oi) +{ + int ret = 0; + + /* call ism_interface_down. */ + /* ret = ism_interface_down (oi); */ + + return ret; +} + +/* Interface down event handler. */ +static int +ism_interface_down (struct ospf_interface *oi) +{ + ospf_if_cleanup (oi); + return 0; +} + + +static int +ism_backup_seen (struct ospf_interface *oi) +{ + return ospf_dr_election (oi); +} + +static int +ism_wait_timer (struct ospf_interface *oi) +{ + return ospf_dr_election (oi); +} + +static int +ism_neighbor_change (struct ospf_interface *oi) +{ + return ospf_dr_election (oi); +} + +static int +ism_ignore (struct ospf_interface *oi) +{ + if (IS_DEBUG_OSPF (ism, ISM_EVENTS)) + zlog (NULL, LOG_DEBUG, "ISM[%s]: ism_ignore called", IF_NAME (oi)); + + return 0; +} + +/* Interface State Machine */ +struct { + int (*func) (struct ospf_interface *); + int next_state; +} ISM [OSPF_ISM_STATE_MAX][OSPF_ISM_EVENT_MAX] = +{ + { + /* DependUpon: dummy state. */ + { ism_ignore, ISM_DependUpon }, /* NoEvent */ + { ism_ignore, ISM_DependUpon }, /* InterfaceUp */ + { ism_ignore, ISM_DependUpon }, /* WaitTimer */ + { ism_ignore, ISM_DependUpon }, /* BackupSeen */ + { ism_ignore, ISM_DependUpon }, /* NeighborChange */ + { ism_ignore, ISM_DependUpon }, /* LoopInd */ + { ism_ignore, ISM_DependUpon }, /* UnloopInd */ + { ism_ignore, ISM_DependUpon }, /* InterfaceDown */ + }, + { + /* Down:*/ + { ism_ignore, ISM_DependUpon }, /* NoEvent */ + { ism_interface_up, ISM_DependUpon }, /* InterfaceUp */ + { ism_ignore, ISM_Down }, /* WaitTimer */ + { ism_ignore, ISM_Down }, /* BackupSeen */ + { ism_ignore, ISM_Down }, /* NeighborChange */ + { ism_loop_ind, ISM_Loopback }, /* LoopInd */ + { ism_ignore, ISM_Down }, /* UnloopInd */ + { ism_interface_down, ISM_Down }, /* InterfaceDown */ + }, + { + /* Loopback: */ + { ism_ignore, ISM_DependUpon }, /* NoEvent */ + { ism_ignore, ISM_Loopback }, /* InterfaceUp */ + { ism_ignore, ISM_Loopback }, /* WaitTimer */ + { ism_ignore, ISM_Loopback }, /* BackupSeen */ + { ism_ignore, ISM_Loopback }, /* NeighborChange */ + { ism_ignore, ISM_Loopback }, /* LoopInd */ + { ism_ignore, ISM_Down }, /* UnloopInd */ + { ism_interface_down, ISM_Down }, /* InterfaceDown */ + }, + { + /* Waiting: */ + { ism_ignore, ISM_DependUpon }, /* NoEvent */ + { ism_ignore, ISM_Waiting }, /* InterfaceUp */ + { ism_wait_timer, ISM_DependUpon }, /* WaitTimer */ + { ism_backup_seen, ISM_DependUpon }, /* BackupSeen */ + { ism_ignore, ISM_Waiting }, /* NeighborChange */ + { ism_loop_ind, ISM_Loopback }, /* LoopInd */ + { ism_ignore, ISM_Waiting }, /* UnloopInd */ + { ism_interface_down, ISM_Down }, /* InterfaceDown */ + }, + { + /* Point-to-Point: */ + { ism_ignore, ISM_DependUpon }, /* NoEvent */ + { ism_ignore, ISM_PointToPoint }, /* InterfaceUp */ + { ism_ignore, ISM_PointToPoint }, /* WaitTimer */ + { ism_ignore, ISM_PointToPoint }, /* BackupSeen */ + { ism_ignore, ISM_PointToPoint }, /* NeighborChange */ + { ism_loop_ind, ISM_Loopback }, /* LoopInd */ + { ism_ignore, ISM_PointToPoint }, /* UnloopInd */ + { ism_interface_down, ISM_Down }, /* InterfaceDown */ + }, + { + /* DROther: */ + { ism_ignore, ISM_DependUpon }, /* NoEvent */ + { ism_ignore, ISM_DROther }, /* InterfaceUp */ + { ism_ignore, ISM_DROther }, /* WaitTimer */ + { ism_ignore, ISM_DROther }, /* BackupSeen */ + { ism_neighbor_change, ISM_DependUpon }, /* NeighborChange */ + { ism_loop_ind, ISM_Loopback }, /* LoopInd */ + { ism_ignore, ISM_DROther }, /* UnloopInd */ + { ism_interface_down, ISM_Down }, /* InterfaceDown */ + }, + { + /* Backup: */ + { ism_ignore, ISM_DependUpon }, /* NoEvent */ + { ism_ignore, ISM_Backup }, /* InterfaceUp */ + { ism_ignore, ISM_Backup }, /* WaitTimer */ + { ism_ignore, ISM_Backup }, /* BackupSeen */ + { ism_neighbor_change, ISM_DependUpon }, /* NeighborChange */ + { ism_loop_ind, ISM_Loopback }, /* LoopInd */ + { ism_ignore, ISM_Backup }, /* UnloopInd */ + { ism_interface_down, ISM_Down }, /* InterfaceDown */ + }, + { + /* DR: */ + { ism_ignore, ISM_DependUpon }, /* NoEvent */ + { ism_ignore, ISM_DR }, /* InterfaceUp */ + { ism_ignore, ISM_DR }, /* WaitTimer */ + { ism_ignore, ISM_DR }, /* BackupSeen */ + { ism_neighbor_change, ISM_DependUpon }, /* NeighborChange */ + { ism_loop_ind, ISM_Loopback }, /* LoopInd */ + { ism_ignore, ISM_DR }, /* UnloopInd */ + { ism_interface_down, ISM_Down }, /* InterfaceDown */ + }, +}; + +static const char *ospf_ism_event_str[] = +{ + "NoEvent", + "InterfaceUp", + "WaitTimer", + "BackupSeen", + "NeighborChange", + "LoopInd", + "UnLoopInd", + "InterfaceDown", +}; + +static void +ism_change_state (struct ospf_interface *oi, int state) +{ + int old_state; + struct ospf_lsa *lsa; + + /* Logging change of state. */ + if (IS_DEBUG_OSPF (ism, ISM_STATUS)) + zlog (NULL, LOG_DEBUG, "ISM[%s]: State change %s -> %s", IF_NAME (oi), + LOOKUP (ospf_ism_state_msg, oi->state), + LOOKUP (ospf_ism_state_msg, state)); + + old_state = oi->state; + oi->state = state; + oi->state_change++; + +#ifdef HAVE_SNMP + /* Terminal state or regression */ + if ((state == ISM_DR) || (state == ISM_Backup) || (state == ISM_DROther) || + (state == ISM_PointToPoint) || (state < old_state)) + { + /* ospfVirtIfStateChange */ + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) + ospfTrapVirtIfStateChange (oi); + /* ospfIfStateChange */ + else + ospfTrapIfStateChange (oi); + } +#endif + + /* Set multicast memberships appropriately for new state. */ + ospf_if_set_multicast(oi); + + if (old_state == ISM_Down || state == ISM_Down) + ospf_check_abr_status (oi->ospf); + + /* Originate router-LSA. */ + if (state == ISM_Down) + { + if (oi->area->act_ints > 0) + oi->area->act_ints--; + } + else if (old_state == ISM_Down) + oi->area->act_ints++; + + /* schedule router-LSA originate. */ + ospf_router_lsa_update_area (oi->area); + + /* Originate network-LSA. */ + if (old_state != ISM_DR && state == ISM_DR) + ospf_network_lsa_update (oi); + else if (old_state == ISM_DR && state != ISM_DR) + { + /* Free self originated network LSA. */ + lsa = oi->network_lsa_self; + if (lsa) + ospf_lsa_flush_area (lsa, oi->area); + + ospf_lsa_unlock (&oi->network_lsa_self); + oi->network_lsa_self = NULL; + } + + ospf_opaque_ism_change (oi, old_state); + + /* Check area border status. */ + ospf_check_abr_status (oi->ospf); +} + +/* Execute ISM event process. */ +int +ospf_ism_event (struct thread *thread) +{ + int event; + int next_state; + struct ospf_interface *oi; + + oi = THREAD_ARG (thread); + event = THREAD_VAL (thread); + + /* Call function. */ + next_state = (*(ISM [oi->state][event].func))(oi); + + if (! next_state) + next_state = ISM [oi->state][event].next_state; + + if (IS_DEBUG_OSPF (ism, ISM_EVENTS)) + zlog (NULL, LOG_DEBUG, "ISM[%s]: %s (%s)", IF_NAME (oi), + LOOKUP (ospf_ism_state_msg, oi->state), + ospf_ism_event_str[event]); + + /* If state is changed. */ + if (next_state != oi->state) + ism_change_state (oi, next_state); + + /* Make sure timer is set. */ + ism_timer_set (oi); + + return 0; +} + diff --git a/ospfd/ospf_ism.h b/ospfd/ospf_ism.h new file mode 100644 index 0000000..f0357a4 --- /dev/null +++ b/ospfd/ospf_ism.h @@ -0,0 +1,114 @@ +/* + * OSPF version 2 Interface State Machine. + * From RFC2328 [OSPF Version 2] + * Copyright (C) 1999 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 _ZEBRA_OSPF_ISM_H +#define _ZEBRA_OSPF_ISM_H + +/* OSPF Interface State Machine Status. */ +#define ISM_DependUpon 0 +#define ISM_Down 1 +#define ISM_Loopback 2 +#define ISM_Waiting 3 +#define ISM_PointToPoint 4 +#define ISM_DROther 5 +#define ISM_Backup 6 +#define ISM_DR 7 +#define OSPF_ISM_STATE_MAX 8 + +/* Because DR/DROther values are exhanged wrt RFC */ +#define ISM_SNMP(x) (((x) == ISM_DROther) ? ISM_DR : \ + ((x) == ISM_DR) ? ISM_DROther : (x)) + +/* OSPF Interface State Machine Event. */ +#define ISM_NoEvent 0 +#define ISM_InterfaceUp 1 +#define ISM_WaitTimer 2 +#define ISM_BackupSeen 3 +#define ISM_NeighborChange 4 +#define ISM_LoopInd 5 +#define ISM_UnloopInd 6 +#define ISM_InterfaceDown 7 +#define OSPF_ISM_EVENT_MAX 8 + +#define OSPF_ISM_WRITE_ON(O) \ + do \ + { \ + if (oi->on_write_q == 0) \ + { \ + listnode_add ((O)->oi_write_q, oi); \ + oi->on_write_q = 1; \ + } \ + if ((O)->t_write == NULL) \ + (O)->t_write = \ + thread_add_write (master, ospf_write, (O), (O)->fd); \ + } while (0) + +/* Macro for OSPF ISM timer turn on. */ +#define OSPF_ISM_TIMER_ON(T,F,V) \ + do { \ + if (!(T)) \ + (T) = thread_add_timer (master, (F), oi, (V)); \ + } while (0) +#define OSPF_ISM_TIMER_MSEC_ON(T,F,V) \ + do { \ + if (!(T)) \ + (T) = thread_add_timer_msec (master, (F), oi, (V)); \ + } while (0) + +/* convenience macro to set hello timer correctly, according to + * whether fast-hello is set or not + */ +#define OSPF_HELLO_TIMER_ON(O) \ + do { \ + if (OSPF_IF_PARAM ((O), fast_hello)) \ + OSPF_ISM_TIMER_MSEC_ON ((O)->t_hello, ospf_hello_timer, \ + 1000 / OSPF_IF_PARAM ((O), fast_hello)); \ + else \ + OSPF_ISM_TIMER_ON ((O)->t_hello, ospf_hello_timer, \ + OSPF_IF_PARAM ((O), v_hello)); \ + } while (0) + +/* Macro for OSPF ISM timer turn off. */ +#define OSPF_ISM_TIMER_OFF(X) \ + do { \ + if (X) \ + { \ + thread_cancel (X); \ + (X) = NULL; \ + } \ + } while (0) + +/* Macro for OSPF schedule event. */ +#define OSPF_ISM_EVENT_SCHEDULE(I,E) \ + thread_add_event (master, ospf_ism_event, (I), (E)) + +/* Macro for OSPF execute event. */ +#define OSPF_ISM_EVENT_EXECUTE(I,E) \ + thread_execute (master, ospf_ism_event, (I), (E)) + +/* Prototypes. */ +extern int ospf_ism_event (struct thread *); +extern void ism_change_status (struct ospf_interface *, int); +extern int ospf_hello_timer (struct thread *thread); + +#endif /* _ZEBRA_OSPF_ISM_H */ diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c new file mode 100644 index 0000000..f49e263 --- /dev/null +++ b/ospfd/ospf_lsa.c @@ -0,0 +1,3780 @@ +/* + * OSPF Link State Advertisement + * Copyright (C) 1999, 2000 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. + */ + +#include + +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "memory.h" +#include "stream.h" +#include "log.h" +#include "thread.h" +#include "hash.h" +#include "sockunion.h" /* for inet_aton() */ +#include "checksum.h" + +#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_abr.h" + + +u_int32_t +get_metric (u_char *metric) +{ + u_int32_t m; + m = metric[0]; + m = (m << 8) + metric[1]; + m = (m << 8) + metric[2]; + return m; +} + + +struct timeval +tv_adjust (struct timeval a) +{ + while (a.tv_usec >= 1000000) + { + a.tv_usec -= 1000000; + a.tv_sec++; + } + + while (a.tv_usec < 0) + { + a.tv_usec += 1000000; + a.tv_sec--; + } + + return a; +} + +int +tv_ceil (struct timeval a) +{ + a = tv_adjust (a); + + return (a.tv_usec ? a.tv_sec + 1 : a.tv_sec); +} + +int +tv_floor (struct timeval a) +{ + a = tv_adjust (a); + + return a.tv_sec; +} + +struct timeval +int2tv (int a) +{ + struct timeval ret; + + ret.tv_sec = a; + ret.tv_usec = 0; + + 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) +{ + struct timeval ret; + + ret.tv_sec = a.tv_sec + b.tv_sec; + ret.tv_usec = a.tv_usec + b.tv_usec; + + return tv_adjust (ret); +} + +struct timeval +tv_sub (struct timeval a, struct timeval b) +{ + struct timeval ret; + + ret.tv_sec = a.tv_sec - b.tv_sec; + ret.tv_usec = a.tv_usec - b.tv_usec; + + return tv_adjust (ret); +} + +int +tv_cmp (struct timeval a, struct timeval b) +{ + return (a.tv_sec == b.tv_sec ? + a.tv_usec - b.tv_usec : a.tv_sec - b.tv_sec); +} + +int +ospf_lsa_refresh_delay (struct ospf_lsa *lsa) +{ + struct timeval delta, now; + int delay = 0; + + quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); + delta = tv_sub (now, lsa->tv_orig); + + if (tv_cmp (delta, msec2tv (OSPF_MIN_LS_INTERVAL)) < 0) + { + 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", + lsa->data->type, inet_ntoa (lsa->data->id), delay); + + assert (delay > 0); + } + + return delay; +} + + +int +get_age (struct ospf_lsa *lsa) +{ + int age; + + age = ntohs (lsa->data->ls_age) + + tv_floor (tv_sub (recent_relative_time (), lsa->tv_recv)); + + return age; +} + + +/* Fletcher Checksum -- Refer to RFC1008. */ + +/* All the offsets are zero-based. The offsets in the RFC1008 are + one-based. */ +u_int16_t +ospf_lsa_checksum (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; + + /* Checksum offset starts from "options" field, not the beginning of the + lsa_header struct. The offset is 14, rather than 16. */ + int checksum_offset = (u_char *) &lsa->checksum - buffer; + + 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 () +{ + struct ospf_lsa *new; + + new = XCALLOC (MTYPE_OSPF_LSA, sizeof (struct ospf_lsa)); + + new->flags = 0; + new->lock = 1; + new->retransmit_counter = 0; + new->tv_recv = recent_relative_time (); + new->tv_orig = new->tv_recv; + new->refresh_list = -1; + + return new; +} + +/* Duplicate OSPF LSA. */ +struct ospf_lsa * +ospf_lsa_dup (struct ospf_lsa *lsa) +{ + struct ospf_lsa *new; + + if (lsa == NULL) + return NULL; + + new = XCALLOC (MTYPE_OSPF_LSA, sizeof (struct ospf_lsa)); + + memcpy (new, lsa, sizeof (struct ospf_lsa)); + UNSET_FLAG (new->flags, OSPF_LSA_DISCARD); + new->lock = 1; + new->retransmit_counter = 0; + new->data = ospf_lsa_data_dup (lsa->data); + + /* kevinm: Clear the refresh_list, otherwise there are going + to be problems when we try to remove the LSA from the + queue (which it's not a member of.) + XXX: Should we add the LSA to the refresh_list queue? */ + new->refresh_list = -1; + + if (IS_DEBUG_OSPF (lsa, LSA)) + zlog_debug ("LSA: duplicated %p (new: %p)", (void *)lsa, (void *)new); + + return new; +} + +/* Free OSPF LSA. */ +void +ospf_lsa_free (struct ospf_lsa *lsa) +{ + assert (lsa->lock == 0); + + if (IS_DEBUG_OSPF (lsa, LSA)) + zlog_debug ("LSA: freed %p", (void *)lsa); + + /* Delete LSA data. */ + if (lsa->data != NULL) + ospf_lsa_data_free (lsa->data); + + assert (lsa->refresh_list < 0); + + memset (lsa, 0, sizeof (struct ospf_lsa)); + XFREE (MTYPE_OSPF_LSA, lsa); +} + +/* Lock LSA. */ +struct ospf_lsa * +ospf_lsa_lock (struct ospf_lsa *lsa) +{ + lsa->lock++; + return lsa; +} + +/* Unlock LSA. */ +void +ospf_lsa_unlock (struct ospf_lsa **lsa) +{ + /* This is sanity check. */ + if (!lsa || !*lsa) + return; + + (*lsa)->lock--; + + assert ((*lsa)->lock >= 0); + + if ((*lsa)->lock == 0) + { + assert (CHECK_FLAG ((*lsa)->flags, OSPF_LSA_DISCARD)); + ospf_lsa_free (*lsa); + *lsa = NULL; + } +} + +/* Check discard flag. */ +void +ospf_lsa_discard (struct ospf_lsa *lsa) +{ + if (!CHECK_FLAG (lsa->flags, OSPF_LSA_DISCARD)) + { + SET_FLAG (lsa->flags, OSPF_LSA_DISCARD); + ospf_lsa_unlock (&lsa); + } +} + +/* Create LSA data. */ +struct lsa_header * +ospf_lsa_data_new (size_t size) +{ + return XCALLOC (MTYPE_OSPF_LSA_DATA, size); +} + +/* Duplicate LSA data. */ +struct lsa_header * +ospf_lsa_data_dup (struct lsa_header *lsah) +{ + struct lsa_header *new; + + new = ospf_lsa_data_new (ntohs (lsah->length)); + memcpy (new, lsah, ntohs (lsah->length)); + + return new; +} + +/* Free LSA data. */ +void +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), (void *)lsah); + + XFREE (MTYPE_OSPF_LSA_DATA, lsah); +} + + +/* LSA general functions. */ + +const char * +dump_lsa_key (struct ospf_lsa *lsa) +{ + static char buf[] = { + "Type255,id(255.255.255.255),ar(255.255.255.255)" + }; + struct lsa_header *lsah; + + if (lsa != NULL && (lsah = lsa->data) != NULL) + { + char id[INET_ADDRSTRLEN], ar[INET_ADDRSTRLEN]; + strcpy (id, inet_ntoa (lsah->id)); + strcpy (ar, inet_ntoa (lsah->adv_router)); + + sprintf (buf, "Type%d,id(%s),ar(%s)", lsah->type, id, ar); + } + else + strcpy (buf, "NULL"); + + return buf; +} + +u_int32_t +lsa_seqnum_increment (struct ospf_lsa *lsa) +{ + u_int32_t seqnum; + + seqnum = ntohl (lsa->data->ls_seqnum) + 1; + + return htonl (seqnum); +} + +void +lsa_header_set (struct stream *s, u_char options, + u_char type, struct in_addr id, struct in_addr router_id) +{ + struct lsa_header *lsah; + + lsah = (struct lsa_header *) STREAM_DATA (s); + + lsah->ls_age = htons (OSPF_LSA_INITIAL_AGE); + lsah->options = options; + lsah->type = type; + lsah->id = id; + lsah->adv_router = router_id; + lsah->ls_seqnum = htonl (OSPF_INITIAL_SEQUENCE_NUMBER); + + stream_forward_endp (s, OSPF_LSA_HEADER_SIZE); +} + + +/* router-LSA related functions. */ +/* Get router-LSA flags. */ +static u_char +router_lsa_flags (struct ospf_area *area) +{ + u_char flags; + + flags = area->ospf->flags; + + /* Set virtual link flag. */ + if (ospf_full_virtual_nbrs (area)) + SET_FLAG (flags, ROUTER_LSA_VIRTUAL); + else + /* Just sanity check */ + UNSET_FLAG (flags, ROUTER_LSA_VIRTUAL); + + /* Set Shortcut ABR behabiour flag. */ + UNSET_FLAG (flags, ROUTER_LSA_SHORTCUT); + if (area->ospf->abr_type == OSPF_ABR_SHORTCUT) + if (!OSPF_IS_AREA_BACKBONE (area)) + if ((area->shortcut_configured == OSPF_SHORTCUT_DEFAULT && + area->ospf->backbone == NULL) || + area->shortcut_configured == OSPF_SHORTCUT_ENABLE) + SET_FLAG (flags, ROUTER_LSA_SHORTCUT); + + /* ASBR can't exit in stub area. */ + if (area->external_routing == OSPF_AREA_STUB) + UNSET_FLAG (flags, ROUTER_LSA_EXTERNAL); + /* If ASBR set External flag */ + else if (IS_OSPF_ASBR (area->ospf)) + SET_FLAG (flags, ROUTER_LSA_EXTERNAL); + + /* Set ABR dependent flags */ + if (IS_OSPF_ABR (area->ospf)) + { + SET_FLAG (flags, ROUTER_LSA_BORDER); + /* If Area is NSSA and we are both ABR and unconditional translator, + * set Nt bit to inform other routers. + */ + if ( (area->external_routing == OSPF_AREA_NSSA) + && (area->NSSATranslatorRole == OSPF_NSSA_ROLE_ALWAYS)) + SET_FLAG (flags, ROUTER_LSA_NT); + } + return flags; +} + +/* Lookup neighbor other than myself. + And check neighbor count, + Point-to-Point link must have only 1 neighbor. */ +struct ospf_neighbor * +ospf_nbr_lookup_ptop (struct ospf_interface *oi) +{ + struct ospf_neighbor *nbr = NULL; + struct route_node *rn; + + /* Search neighbor, there must be one of two nbrs. */ + for (rn = route_top (oi->nbrs); rn; rn = route_next (rn)) + if ((nbr = rn->info)) + if (!IPV4_ADDR_SAME (&nbr->router_id, &oi->ospf->router_id)) + if (nbr->state == NSM_Full) + { + route_unlock_node (rn); + break; + } + + /* PtoP link must have only 1 neighbor. */ + if (ospf_nbr_count (oi, 0) > 1) + zlog_warn ("Point-to-Point link has more than 1 neighobrs."); + + return nbr; +} + +/* Determine cost of link, taking RFC3137 stub-router support into + * consideration + */ +static u_int16_t +ospf_link_cost (struct ospf_interface *oi) +{ + /* RFC3137 stub router support */ + if (!CHECK_FLAG (oi->area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED)) + return oi->output_cost; + else + return OSPF_OUTPUT_COST_INFINITE; +} + +/* Set a link information. */ +static char +link_info_set (struct stream *s, struct in_addr id, + struct in_addr data, u_char type, u_char tos, u_int16_t cost) +{ + /* LSA stream is initially allocated to OSPF_MAX_LSA_SIZE, suits + * vast majority of cases. Some rare routers with lots of links need more. + * we try accomodate those here. + */ + if (STREAM_WRITEABLE(s) < OSPF_ROUTER_LSA_LINK_SIZE) + { + size_t ret = OSPF_MAX_LSA_SIZE; + + /* Can we enlarge the stream still? */ + if (STREAM_SIZE(s) == OSPF_MAX_LSA_SIZE) + { + /* we futz the size here for simplicity, really we need to account + * for just: + * IP Header - (sizeof (struct ip)) + * OSPF Header - OSPF_HEADER_SIZE + * LSA Header - OSPF_LSA_HEADER_SIZE + * MD5 auth data, if MD5 is configured - OSPF_AUTH_MD5_SIZE. + * + * Simpler just to subtract OSPF_MAX_LSA_SIZE though. + */ + ret = stream_resize (s, OSPF_MAX_PACKET_SIZE - OSPF_MAX_LSA_SIZE); + } + + if (ret == OSPF_MAX_LSA_SIZE) + { + zlog_warn ("%s: Out of space in LSA stream, left %zd, size %zd", + __func__, STREAM_REMAIN (s), STREAM_SIZE (s)); + return 0; + } + } + + /* TOS based routing is not supported. */ + stream_put_ipv4 (s, id.s_addr); /* Link ID. */ + stream_put_ipv4 (s, data.s_addr); /* Link Data. */ + stream_putc (s, type); /* Link Type. */ + stream_putc (s, tos); /* TOS = 0. */ + stream_putw (s, cost); /* Link Cost. */ + + return 1; +} + +/* Describe Point-to-Point link (Section 12.4.1.1). */ +static int +lsa_link_ptop_set (struct stream *s, struct ospf_interface *oi) +{ + int links = 0; + struct ospf_neighbor *nbr; + struct in_addr id, mask; + u_int16_t cost = ospf_link_cost (oi); + + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + zlog_debug ("LSA[Type1]: Set link Point-to-Point"); + + if ((nbr = ospf_nbr_lookup_ptop (oi))) + if (nbr->state == NSM_Full) + { + /* For unnumbered point-to-point networks, the Link Data field + should specify the interface's MIB-II ifIndex value. */ + links += link_info_set (s, nbr->router_id, oi->address->u.prefix4, + LSA_LINK_TYPE_POINTOPOINT, 0, cost); + } + + /* Regardless of the state of the neighboring router, we must + add a Type 3 link (stub network). + N.B. Options 1 & 2 share basically the same logic. */ + masklen2ip (oi->address->prefixlen, &mask); + id.s_addr = CONNECTED_PREFIX(oi->connected)->u.prefix4.s_addr & mask.s_addr; + links += link_info_set (s, id, mask, LSA_LINK_TYPE_STUB, 0, + oi->output_cost); + return links; +} + +/* Describe Broadcast Link. */ +static int +lsa_link_broadcast_set (struct stream *s, struct ospf_interface *oi) +{ + struct ospf_neighbor *dr; + struct in_addr id, mask; + u_int16_t cost = ospf_link_cost (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, + oi->output_cost); + } + + dr = ospf_nbr_lookup_by_addr (oi->nbrs, &DR (oi)); + /* Describe Type 2 link. */ + if (dr && (dr->state == NSM_Full || + 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, + oi->output_cost); + } +} + +static int +lsa_link_loopback_set (struct stream *s, struct ospf_interface *oi) +{ + struct in_addr id, mask; + + /* Describe Type 3 Link. */ + if (oi->state != ISM_Loopback) + return 0; + + 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, 0); +} + +/* Describe Virtual Link. */ +static int +lsa_link_virtuallink_set (struct stream *s, struct ospf_interface *oi) +{ + struct ospf_neighbor *nbr; + u_int16_t cost = ospf_link_cost (oi); + + if (oi->state == ISM_PointToPoint) + if ((nbr = ospf_nbr_lookup_ptop (oi))) + if (nbr->state == NSM_Full) + { + return link_info_set (s, nbr->router_id, oi->address->u.prefix4, + LSA_LINK_TYPE_VIRTUALLINK, 0, cost); + } + + return 0; +} + +#define lsa_link_nbma_set(S,O) lsa_link_broadcast_set (S, O) + +/* this function add for support point-to-multipoint ,see rfc2328 +12.4.1.4.*/ +/* from "edward rrr" + http://marc.theaimsgroup.com/?l=zebra&m=100739222210507&w=2 */ +static int +lsa_link_ptomp_set (struct stream *s, struct ospf_interface *oi) +{ + int links = 0; + struct route_node *rn; + struct ospf_neighbor *nbr = NULL; + struct in_addr id, mask; + u_int16_t cost = ospf_link_cost (oi); + + mask.s_addr = 0xffffffff; + id.s_addr = oi->address->u.prefix4.s_addr; + links += link_info_set (s, id, mask, LSA_LINK_TYPE_STUB, 0, 0); + + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + zlog_debug ("PointToMultipoint: running ptomultip_set"); + + /* Search neighbor, */ + for (rn = route_top (oi->nbrs); rn; rn = route_next (rn)) + if ((nbr = rn->info) != NULL) + /* Ignore myself. */ + if (!IPV4_ADDR_SAME (&nbr->router_id, &oi->ospf->router_id)) + if (nbr->state == NSM_Full) + + { + links += link_info_set (s, nbr->router_id, oi->address->u.prefix4, + LSA_LINK_TYPE_POINTOPOINT, 0, cost); + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + zlog_debug ("PointToMultipoint: set link to %s", + inet_ntoa(oi->address->u.prefix4)); + } + + return links; +} + +/* Set router-LSA link information. */ +static int +router_lsa_link_set (struct stream *s, struct ospf_area *area) +{ + struct listnode *node; + struct ospf_interface *oi; + int links = 0; + + for (ALL_LIST_ELEMENTS_RO (area->oiflist, node, oi)) + { + struct interface *ifp = oi->ifp; + + /* Check interface is up, OSPF is enable. */ + if (if_is_operative (ifp)) + { + if (oi->state != ISM_Down) + { + oi->lsa_pos_beg = links; + /* Describe each link. */ + switch (oi->type) + { + case OSPF_IFTYPE_POINTOPOINT: + links += lsa_link_ptop_set (s, oi); + break; + case OSPF_IFTYPE_BROADCAST: + links += lsa_link_broadcast_set (s, oi); + break; + case OSPF_IFTYPE_NBMA: + links += lsa_link_nbma_set (s, oi); + break; + case OSPF_IFTYPE_POINTOMULTIPOINT: + links += lsa_link_ptomp_set (s, oi); + break; + case OSPF_IFTYPE_VIRTUALLINK: + links += lsa_link_virtuallink_set (s, oi); + break; + case OSPF_IFTYPE_LOOPBACK: + links += lsa_link_loopback_set (s, oi); + } + oi->lsa_pos_end = links; + } + } + } + + return links; +} + +/* Set router-LSA body. */ +static void +ospf_router_lsa_body_set (struct stream *s, struct ospf_area *area) +{ + unsigned long putp; + u_int16_t cnt; + + /* Set flags. */ + stream_putc (s, router_lsa_flags (area)); + + /* Set Zero fields. */ + stream_putc (s, 0); + + /* Keep pointer to # links. */ + putp = stream_get_endp(s); + + /* Forward word */ + stream_putw(s, 0); + + /* Set all link information. */ + cnt = router_lsa_link_set (s, area); + + /* Set # of links here. */ + stream_putw_at (s, putp, cnt); +} + +static int +ospf_stub_router_timer (struct thread *t) +{ + struct ospf_area *area = THREAD_ARG (t); + + area->t_stub_router = NULL; + + SET_FLAG (area->stub_router_state, OSPF_AREA_WAS_START_STUB_ROUTED); + + /* clear stub route state and generate router-lsa refresh, don't + * clobber an administratively set stub-router state though. + */ + if (CHECK_FLAG (area->stub_router_state, OSPF_AREA_ADMIN_STUB_ROUTED)) + return 0; + + UNSET_FLAG (area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED); + + ospf_router_lsa_update_area (area); + + return 0; +} + +static void +ospf_stub_router_check (struct ospf_area *area) +{ + /* area must either be administratively configured to be stub + * or startup-time stub-router must be configured and we must in a pre-stub + * state. + */ + if (CHECK_FLAG (area->stub_router_state, OSPF_AREA_ADMIN_STUB_ROUTED)) + { + SET_FLAG (area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED); + return; + } + + /* not admin-stubbed, check whether startup stubbing is configured and + * whether it's not been done yet + */ + if (CHECK_FLAG (area->stub_router_state, OSPF_AREA_WAS_START_STUB_ROUTED)) + return; + + if (area->ospf->stub_router_startup_time == OSPF_STUB_ROUTER_UNCONFIGURED) + { + /* stub-router is hence done forever for this area, even if someone + * tries configure it (take effect next restart). + */ + SET_FLAG (area->stub_router_state, OSPF_AREA_WAS_START_STUB_ROUTED); + return; + } + + /* startup stub-router configured and not yet done */ + SET_FLAG (area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED); + + 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) +{ + struct ospf *ospf = area->ospf; + struct stream *s; + struct lsa_header *lsah; + struct ospf_lsa *new; + int length; + + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + zlog_debug ("LSA[Type1]: Create router-LSA instance"); + + /* check whether stub-router is desired, and if this is the first + * router LSA. + */ + ospf_stub_router_check (area); + + /* Create a stream for LSA. */ + s = stream_new (OSPF_MAX_LSA_SIZE); + /* Set LSA common header fields. */ + lsa_header_set (s, LSA_OPTIONS_GET (area) | LSA_OPTIONS_NSSA_GET (area), + OSPF_ROUTER_LSA, ospf->router_id, ospf->router_id); + + /* Set router-LSA body fields. */ + ospf_router_lsa_body_set (s, area); + + /* Set length. */ + length = stream_get_endp (s); + lsah = (struct lsa_header *) STREAM_DATA (s); + lsah->length = htons (length); + + /* Now, create OSPF LSA instance. */ + if ( (new = ospf_lsa_new ()) == NULL) + { + zlog_err ("%s: Unable to create new lsa", __func__); + return NULL; + } + + new->area = area; + SET_FLAG (new->flags, OSPF_LSA_SELF | OSPF_LSA_SELF_CHECKED); + + /* Copy LSA data to store, discard stream. */ + new->data = ospf_lsa_data_new (length); + memcpy (new->data, lsah, length); + stream_free (s); + + return new; +} + +/* Originate Router-LSA. */ +static struct ospf_lsa * +ospf_router_lsa_originate (struct ospf_area *area) +{ + struct ospf_lsa *new; + + /* Create new router-LSA instance. */ + if ( (new = ospf_router_lsa_new (area)) == NULL) + { + zlog_err ("%s: ospf_router_lsa_new returned NULL", __func__); + return NULL; + } + + /* Sanity check. */ + if (new->data->adv_router.s_addr == 0) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("LSA[Type1]: AdvRouter is 0, discard"); + ospf_lsa_discard (new); + return NULL; + } + + /* Install LSA to LSDB. */ + new = ospf_lsa_install (area->ospf, NULL, new); + + /* Update LSA origination count. */ + area->ospf->lsa_originate_count++; + + /* Flooding new LSA through area. */ + ospf_flood_through_area (area, NULL, new); + + 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), (void *)new); + ospf_lsa_header_dump (new->data); + } + + return new; +} + +/* Refresh router-LSA. */ +static struct ospf_lsa * +ospf_router_lsa_refresh (struct ospf_lsa *lsa) +{ + struct ospf_area *area = lsa->area; + struct ospf_lsa *new; + + /* Sanity check. */ + assert (lsa->data); + + /* Delete LSA from neighbor retransmit-list. */ + ospf_ls_retransmit_delete_nbr_area (area, lsa); + + /* Unregister LSA from refresh-list */ + ospf_refresher_unregister_lsa (area->ospf, lsa); + + /* Create new router-LSA instance. */ + if ( (new = ospf_router_lsa_new (area)) == NULL) + { + zlog_err ("%s: ospf_router_lsa_new returned NULL", __func__); + return NULL; + } + + new->data->ls_seqnum = lsa_seqnum_increment (lsa); + + ospf_lsa_install (area->ospf, NULL, new); + + /* Flood LSA through area. */ + ospf_flood_through_area (area, NULL, new); + + /* Debug logging. */ + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + { + zlog_debug ("LSA[Type%d:%s]: router-LSA refresh", + new->data->type, inet_ntoa (new->data->id)); + ospf_lsa_header_dump (new->data); + } + + return NULL; +} + +int +ospf_router_lsa_update_area (struct ospf_area *area) +{ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("[router-LSA]: (router-LSA area update)"); + + /* Now refresh router-LSA. */ + if (area->router_lsa_self) + ospf_lsa_refresh (area->ospf, area->router_lsa_self); + /* Newly originate router-LSA. */ + else + ospf_router_lsa_originate (area); + + return 0; +} + +int +ospf_router_lsa_update (struct ospf *ospf) +{ + struct listnode *node, *nnode; + struct ospf_area *area; + + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + zlog_debug ("Timer[router-LSA Update]: (timer expire)"); + + for (ALL_LIST_ELEMENTS (ospf->areas, node, nnode, area)) + { + struct ospf_lsa *lsa = area->router_lsa_self; + struct router_lsa *rl; + const char *area_str; + + /* Keep Area ID string. */ + area_str = AREA_NAME (area); + + /* If LSA not exist in this Area, originate new. */ + if (lsa == NULL) + { + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + zlog_debug("LSA[Type1]: Create router-LSA for Area %s", area_str); + + ospf_router_lsa_originate (area); + } + /* If router-ID is changed, Link ID must change. + First flush old LSA, then originate new. */ + else if (!IPV4_ADDR_SAME (&lsa->data->id, &ospf->router_id)) + { + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + zlog_debug("LSA[Type%d:%s]: Refresh router-LSA for Area %s", + lsa->data->type, inet_ntoa (lsa->data->id), area_str); + ospf_refresher_unregister_lsa (ospf, lsa); + ospf_lsa_flush_area (lsa, area); + ospf_lsa_unlock (&area->router_lsa_self); + area->router_lsa_self = NULL; + + /* Refresh router-LSA, (not install) and flood through area. */ + ospf_router_lsa_update_area (area); + } + else + { + rl = (struct router_lsa *) lsa->data; + /* Refresh router-LSA, (not install) and flood through area. */ + if (rl->flags != ospf->flags) + ospf_router_lsa_update_area (area); + } + } + + return 0; +} + + +/* network-LSA related functions. */ +/* Originate Network-LSA. */ +static void +ospf_network_lsa_body_set (struct stream *s, struct ospf_interface *oi) +{ + struct in_addr mask; + struct route_node *rn; + struct ospf_neighbor *nbr; + + masklen2ip (oi->address->prefixlen, &mask); + stream_put_ipv4 (s, mask.s_addr); + + /* The network-LSA lists those routers that are fully adjacent to + the Designated Router; each fully adjacent router is identified by + its OSPF Router ID. The Designated Router includes itself in this + list. RFC2328, Section 12.4.2 */ + + for (rn = route_top (oi->nbrs); rn; rn = route_next (rn)) + if ((nbr = rn->info) != NULL) + if (nbr->state == NSM_Full || nbr == oi->nbr_self) + stream_put_ipv4 (s, nbr->router_id.s_addr); +} + +static struct ospf_lsa * +ospf_network_lsa_new (struct ospf_interface *oi) +{ + struct stream *s; + struct ospf_lsa *new; + struct lsa_header *lsah; + struct ospf_if_params *oip; + int length; + + /* If there are no neighbours on this network (the net is stub), + the router does not originate network-LSA (see RFC 12.4.2) */ + if (oi->full_nbrs == 0) + return NULL; + + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + zlog_debug ("LSA[Type2]: Create network-LSA instance"); + + /* Create new stream for LSA. */ + s = stream_new (OSPF_MAX_LSA_SIZE); + lsah = (struct lsa_header *) STREAM_DATA (s); + + lsa_header_set (s, (OPTIONS (oi) | LSA_OPTIONS_GET (oi->area)), + OSPF_NETWORK_LSA, DR (oi), oi->ospf->router_id); + + /* Set network-LSA body fields. */ + ospf_network_lsa_body_set (s, oi); + + /* Set length. */ + length = stream_get_endp (s); + lsah->length = htons (length); + + /* Create OSPF LSA instance. */ + if ( (new = ospf_lsa_new ()) == NULL) + { + zlog_err ("%s: ospf_lsa_new returned NULL", __func__); + return NULL; + } + + new->area = oi->area; + SET_FLAG (new->flags, OSPF_LSA_SELF | OSPF_LSA_SELF_CHECKED); + + /* Copy LSA to store. */ + new->data = ospf_lsa_data_new (length); + memcpy (new->data, lsah, length); + stream_free (s); + + /* Remember prior network LSA sequence numbers, even if we stop + * originating one for this oi, to try avoid re-originating LSAs with a + * prior sequence number, and thus speed up adjency forming & convergence. + */ + if ((oip = ospf_lookup_if_params (oi->ifp, oi->address->u.prefix4))) + { + new->data->ls_seqnum = oip->network_lsa_seqnum; + new->data->ls_seqnum = lsa_seqnum_increment (new); + } + else + { + oip = ospf_get_if_params (oi->ifp, oi->address->u.prefix4); + ospf_if_update_params (oi->ifp, oi->address->u.prefix4); + } + oip->network_lsa_seqnum = new->data->ls_seqnum; + + return new; +} + +/* Originate network-LSA. */ +void +ospf_network_lsa_update (struct ospf_interface *oi) +{ + struct ospf_lsa *new; + + if (oi->network_lsa_self != NULL) + { + ospf_lsa_refresh (oi->ospf, oi->network_lsa_self); + return; + } + + /* Create new network-LSA instance. */ + new = ospf_network_lsa_new (oi); + if (new == NULL) + return; + + /* Install LSA to LSDB. */ + new = ospf_lsa_install (oi->ospf, oi, new); + + /* Update LSA origination count. */ + oi->ospf->lsa_originate_count++; + + /* Flooding new LSA through area. */ + ospf_flood_through_area (oi->area, NULL, new); + + 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), (void *)new); + ospf_lsa_header_dump (new->data); + } + + return; +} + +static struct ospf_lsa * +ospf_network_lsa_refresh (struct ospf_lsa *lsa) +{ + struct ospf_area *area = lsa->area; + struct ospf_lsa *new, *new2; + struct ospf_if_params *oip; + struct ospf_interface *oi; + + assert (lsa->data); + + /* Retrieve the oi for the network LSA */ + oi = ospf_if_lookup_by_local_addr (area->ospf, NULL, lsa->data->id); + if (oi == NULL) + { + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + { + zlog_debug ("LSA[Type%d:%s]: network-LSA refresh: " + "no oi found, ick, ignoring.", + lsa->data->type, inet_ntoa (lsa->data->id)); + ospf_lsa_header_dump (lsa->data); + } + return NULL; + } + /* Delete LSA from neighbor retransmit-list. */ + ospf_ls_retransmit_delete_nbr_area (area, lsa); + + /* Unregister LSA from refresh-list */ + ospf_refresher_unregister_lsa (area->ospf, lsa); + + /* Create new network-LSA instance. */ + new = ospf_network_lsa_new (oi); + if (new == NULL) + return NULL; + + oip = ospf_lookup_if_params (oi->ifp, oi->address->u.prefix4); + assert (oip != NULL); + oip->network_lsa_seqnum = new->data->ls_seqnum = lsa_seqnum_increment (lsa); + + new2 = ospf_lsa_install (area->ospf, oi, new); + + assert (new2 == new); + + /* Flood LSA through aera. */ + ospf_flood_through_area (area, NULL, new); + + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + { + zlog_debug ("LSA[Type%d:%s]: network-LSA refresh", + new->data->type, inet_ntoa (new->data->id)); + ospf_lsa_header_dump (new->data); + } + + return new; +} + +static void +stream_put_ospf_metric (struct stream *s, u_int32_t metric_value) +{ + u_int32_t metric; + char *mp; + + /* Put 0 metric. TOS metric is not supported. */ + metric = htonl (metric_value); + mp = (char *) &metric; + mp++; + stream_put (s, mp, 3); +} + +/* summary-LSA related functions. */ +static void +ospf_summary_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); + + /* Set # TOS. */ + stream_putc (s, (u_char) 0); + + /* Set metric. */ + stream_put_ospf_metric (s, metric); +} + +static struct ospf_lsa * +ospf_summary_lsa_new (struct ospf_area *area, struct prefix *p, + u_int32_t metric, struct in_addr id) +{ + struct stream *s; + struct ospf_lsa *new; + struct lsa_header *lsah; + int length; + + if (id.s_addr == 0xffffffff) + { + /* Maybe Link State ID not available. */ + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + zlog_debug ("LSA[Type%d]: Link ID not available, can't originate", + OSPF_SUMMARY_LSA); + return NULL; + } + + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + zlog_debug ("LSA[Type3]: Create summary-LSA instance"); + + /* Create new stream for LSA. */ + s = stream_new (OSPF_MAX_LSA_SIZE); + lsah = (struct lsa_header *) STREAM_DATA (s); + + lsa_header_set (s, LSA_OPTIONS_GET (area), OSPF_SUMMARY_LSA, + id, area->ospf->router_id); + + /* Set summary-LSA body fields. */ + ospf_summary_lsa_body_set (s, p, metric); + + /* Set length. */ + length = stream_get_endp (s); + lsah->length = htons (length); + + /* Create OSPF LSA instance. */ + new = ospf_lsa_new (); + new->area = area; + SET_FLAG (new->flags, OSPF_LSA_SELF | OSPF_LSA_SELF_CHECKED); + + /* Copy LSA to store. */ + new->data = ospf_lsa_data_new (length); + memcpy (new->data, lsah, length); + stream_free (s); + + return new; +} + +/* Originate Summary-LSA. */ +struct ospf_lsa * +ospf_summary_lsa_originate (struct prefix_ipv4 *p, u_int32_t metric, + struct ospf_area *area) +{ + struct ospf_lsa *new; + struct in_addr id; + + id = ospf_lsa_unique_id (area->ospf, area->lsdb, OSPF_SUMMARY_LSA, p); + + if (id.s_addr == 0xffffffff) + { + /* Maybe Link State ID not available. */ + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + zlog_debug ("LSA[Type%d]: Link ID not available, can't originate", + OSPF_SUMMARY_LSA); + return NULL; + } + + /* Create new summary-LSA instance. */ + if ( !(new = ospf_summary_lsa_new (area, (struct prefix *) p, metric, id))) + return NULL; + + /* Instlal LSA to LSDB. */ + new = ospf_lsa_install (area->ospf, NULL, new); + + /* Update LSA origination count. */ + area->ospf->lsa_originate_count++; + + /* Flooding new LSA through area. */ + ospf_flood_through_area (area, NULL, new); + + 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), (void *)new); + ospf_lsa_header_dump (new->data); + } + + return new; +} + +static struct ospf_lsa* +ospf_summary_lsa_refresh (struct ospf *ospf, struct ospf_lsa *lsa) +{ + struct ospf_lsa *new; + struct summary_lsa *sl; + struct prefix p; + + /* Sanity check. */ + assert (lsa->data); + + sl = (struct summary_lsa *)lsa->data; + p.prefixlen = ip_masklen (sl->mask); + new = ospf_summary_lsa_new (lsa->area, &p, GET_METRIC (sl->metric), + sl->header.id); + + if (!new) + return NULL; + + new->data->ls_seqnum = lsa_seqnum_increment (lsa); + + ospf_lsa_install (ospf, NULL, new); + + /* Flood LSA through AS. */ + ospf_flood_through_area (new->area, NULL, new); + + /* Debug logging. */ + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + { + zlog_debug ("LSA[Type%d:%s]: summary-LSA refresh", + new->data->type, inet_ntoa (new->data->id)); + ospf_lsa_header_dump (new->data); + } + + 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) +{ + /* Put Network Mask. */ + stream_put_ipv4 (s, (u_int32_t) 0); + + /* Set # TOS. */ + stream_putc (s, (u_char) 0); + + /* Set metric. */ + stream_put_ospf_metric (s, metric); +} + +static struct ospf_lsa * +ospf_summary_asbr_lsa_new (struct ospf_area *area, struct prefix *p, + u_int32_t metric, struct in_addr id) +{ + struct stream *s; + struct ospf_lsa *new; + struct lsa_header *lsah; + int length; + + if (id.s_addr == 0xffffffff) + { + /* Maybe Link State ID not available. */ + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + zlog_debug ("LSA[Type%d]: Link ID not available, can't originate", + OSPF_ASBR_SUMMARY_LSA); + return NULL; + } + + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + zlog_debug ("LSA[Type3]: Create summary-LSA instance"); + + /* Create new stream for LSA. */ + s = stream_new (OSPF_MAX_LSA_SIZE); + lsah = (struct lsa_header *) STREAM_DATA (s); + + lsa_header_set (s, LSA_OPTIONS_GET (area), OSPF_ASBR_SUMMARY_LSA, + id, area->ospf->router_id); + + /* Set summary-LSA body fields. */ + ospf_summary_asbr_lsa_body_set (s, p, metric); + + /* Set length. */ + length = stream_get_endp (s); + lsah->length = htons (length); + + /* Create OSPF LSA instance. */ + new = ospf_lsa_new (); + new->area = area; + SET_FLAG (new->flags, OSPF_LSA_SELF | OSPF_LSA_SELF_CHECKED); + + /* Copy LSA to store. */ + new->data = ospf_lsa_data_new (length); + memcpy (new->data, lsah, length); + stream_free (s); + + return new; +} + +/* Originate summary-ASBR-LSA. */ +struct ospf_lsa * +ospf_summary_asbr_lsa_originate (struct prefix_ipv4 *p, u_int32_t metric, + struct ospf_area *area) +{ + struct ospf_lsa *new; + struct in_addr id; + + id = ospf_lsa_unique_id (area->ospf, area->lsdb, OSPF_ASBR_SUMMARY_LSA, p); + + if (id.s_addr == 0xffffffff) + { + /* Maybe Link State ID not available. */ + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + zlog_debug ("LSA[Type%d]: Link ID not available, can't originate", + OSPF_ASBR_SUMMARY_LSA); + return NULL; + } + + /* Create new summary-LSA instance. */ + new = ospf_summary_asbr_lsa_new (area, (struct prefix *) p, metric, id); + if (!new) + return NULL; + + /* Install LSA to LSDB. */ + new = ospf_lsa_install (area->ospf, NULL, new); + + /* Update LSA origination count. */ + area->ospf->lsa_originate_count++; + + /* Flooding new LSA through area. */ + ospf_flood_through_area (area, NULL, new); + + 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), (void *)new); + ospf_lsa_header_dump (new->data); + } + + return new; +} + +static struct ospf_lsa* +ospf_summary_asbr_lsa_refresh (struct ospf *ospf, struct ospf_lsa *lsa) +{ + struct ospf_lsa *new; + struct summary_lsa *sl; + struct prefix p; + + /* Sanity check. */ + assert (lsa->data); + + sl = (struct summary_lsa *)lsa->data; + p.prefixlen = ip_masklen (sl->mask); + new = ospf_summary_asbr_lsa_new (lsa->area, &p, GET_METRIC (sl->metric), + sl->header.id); + if (!new) + return NULL; + + new->data->ls_seqnum = lsa_seqnum_increment (lsa); + + ospf_lsa_install (ospf, NULL, new); + + /* Flood LSA through area. */ + ospf_flood_through_area (new->area, NULL, new); + + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + { + zlog_debug ("LSA[Type%d:%s]: summary-ASBR-LSA refresh", + new->data->type, inet_ntoa (new->data->id)); + ospf_lsa_header_dump (new->data); + } + + return new; +} + +/* AS-external-LSA related functions. */ + +/* Get nexthop for AS-external-LSAs. Return nexthop if its interface + is connected, else 0*/ +static struct in_addr +ospf_external_lsa_nexthop_get (struct ospf *ospf, struct in_addr nexthop) +{ + struct in_addr fwd; + struct prefix nh; + struct listnode *node; + struct ospf_interface *oi; + + fwd.s_addr = 0; + + if (!nexthop.s_addr) + return fwd; + + /* Check whether nexthop is covered by OSPF network. */ + nh.family = AF_INET; + nh.u.prefix4 = nexthop; + nh.prefixlen = IPV4_MAX_BITLEN; + + /* XXX/SCALE: If there were a lot of oi's on an ifp, then it'd be + * better to make use of the per-ifp table of ois. + */ + for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi)) + if (if_is_operative (oi->ifp)) + if (oi->address->family == AF_INET) + if (prefix_match (oi->address, &nh)) + return nexthop; + + return fwd; +} + +/* NSSA-external-LSA related functions. */ + +/* Get 1st IP connection for Forward Addr */ + +struct in_addr +ospf_get_ip_from_ifp (struct ospf_interface *oi) +{ + struct in_addr fwd; + + fwd.s_addr = 0; + + if (if_is_operative (oi->ifp)) + return oi->address->u.prefix4; + + return fwd; +} + +/* Get 1st IP connection for Forward Addr */ +struct in_addr +ospf_get_nssa_ip (struct ospf_area *area) +{ + struct in_addr fwd; + struct in_addr best_default; + struct listnode *node; + struct ospf_interface *oi; + + fwd.s_addr = 0; + best_default.s_addr = 0; + + for (ALL_LIST_ELEMENTS_RO (area->ospf->oiflist, node, oi)) + { + if (if_is_operative (oi->ifp)) + if (oi->area->external_routing == OSPF_AREA_NSSA) + if (oi->address && oi->address->family == AF_INET) + { + if (best_default.s_addr == 0) + best_default = oi->address->u.prefix4; + if (oi->area == area) + return oi->address->u.prefix4; + } + } + if (best_default.s_addr != 0) + return best_default; + + if (best_default.s_addr != 0) + return best_default; + + return fwd; +} + +#define DEFAULT_DEFAULT_METRIC 20 +#define DEFAULT_DEFAULT_ORIGINATE_METRIC 10 +#define DEFAULT_DEFAULT_ALWAYS_METRIC 1 + +#define DEFAULT_METRIC_TYPE EXTERNAL_METRIC_TYPE_2 + +int +metric_type (struct ospf *ospf, u_char src) +{ + return (ospf->dmetric[src].type < 0 ? + DEFAULT_METRIC_TYPE : ospf->dmetric[src].type); +} + +int +metric_value (struct ospf *ospf, u_char src) +{ + if (ospf->dmetric[src].value < 0) + { + if (src == DEFAULT_ROUTE) + { + if (ospf->default_originate == DEFAULT_ORIGINATE_ZEBRA) + return DEFAULT_DEFAULT_ORIGINATE_METRIC; + else + return DEFAULT_DEFAULT_ALWAYS_METRIC; + } + else if (ospf->default_metric < 0) + return DEFAULT_DEFAULT_METRIC; + else + return ospf->default_metric; + } + + return ospf->dmetric[src].value; +} + +/* Set AS-external-LSA body. */ +static void +ospf_external_lsa_body_set (struct stream *s, struct external_info *ei, + struct ospf *ospf) +{ + struct prefix_ipv4 *p = &ei->p; + struct in_addr mask, fwd_addr; + u_int32_t mvalue; + int mtype; + int type; + + /* Put Network Mask. */ + masklen2ip (p->prefixlen, &mask); + stream_put_ipv4 (s, mask.s_addr); + + /* If prefix is default, specify DEFAULT_ROUTE. */ + type = is_prefix_default (&ei->p) ? DEFAULT_ROUTE : ei->type; + + mtype = (ROUTEMAP_METRIC_TYPE (ei) != -1) ? + ROUTEMAP_METRIC_TYPE (ei) : metric_type (ospf, type); + + mvalue = (ROUTEMAP_METRIC (ei) != -1) ? + ROUTEMAP_METRIC (ei) : metric_value (ospf, type); + + /* Put type of external metric. */ + stream_putc (s, (mtype == EXTERNAL_METRIC_TYPE_2 ? 0x80 : 0)); + + /* Put 0 metric. TOS metric is not supported. */ + stream_put_ospf_metric (s, mvalue); + + /* Get forwarding address to nexthop if on the Connection List, else 0. */ + fwd_addr = ospf_external_lsa_nexthop_get (ospf, ei->nexthop); + + /* Put forwarding address. */ + stream_put_ipv4 (s, fwd_addr.s_addr); + + /* Put route tag */ + stream_putl (s, ei->tag); +} + +/* Create new external-LSA. */ +static struct ospf_lsa * +ospf_external_lsa_new (struct ospf *ospf, + struct external_info *ei, struct in_addr *old_id) +{ + struct stream *s; + struct lsa_header *lsah; + struct ospf_lsa *new; + struct in_addr id; + int length; + + if (ei == NULL) + { + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + zlog_debug ("LSA[Type5]: External info is NULL, can't originate"); + return NULL; + } + + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + zlog_debug ("LSA[Type5]: Originate AS-external-LSA instance"); + + /* If old Link State ID is specified, refresh LSA with same ID. */ + if (old_id) + id = *old_id; + /* Get Link State with unique ID. */ + else + { + id = ospf_lsa_unique_id (ospf, ospf->lsdb, OSPF_AS_EXTERNAL_LSA, &ei->p); + if (id.s_addr == 0xffffffff) + { + /* Maybe Link State ID not available. */ + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + zlog_debug ("LSA[Type5]: Link ID not available, can't originate"); + return NULL; + } + } + + /* Create new stream for LSA. */ + s = stream_new (OSPF_MAX_LSA_SIZE); + lsah = (struct lsa_header *) STREAM_DATA (s); + + /* Set LSA common header fields. */ + lsa_header_set (s, OSPF_OPTION_E, OSPF_AS_EXTERNAL_LSA, + id, ospf->router_id); + + /* Set AS-external-LSA body fields. */ + ospf_external_lsa_body_set (s, ei, ospf); + + /* Set length. */ + length = stream_get_endp (s); + lsah->length = htons (length); + + /* Now, create OSPF LSA instance. */ + new = ospf_lsa_new (); + new->area = NULL; + SET_FLAG (new->flags, OSPF_LSA_SELF | OSPF_LSA_APPROVED | OSPF_LSA_SELF_CHECKED); + + /* Copy LSA data to store, discard stream. */ + new->data = ospf_lsa_data_new (length); + memcpy (new->data, lsah, length); + stream_free (s); + + return new; +} + +/* As Type-7 */ +static void +ospf_install_flood_nssa (struct ospf *ospf, + struct ospf_lsa *lsa, struct external_info *ei) +{ + struct ospf_lsa *new; + struct as_external_lsa *extlsa; + struct ospf_area *area; + struct listnode *node, *nnode; + + /* LSA may be a Type-5 originated via translation of a Type-7 LSA + * which originated from an NSSA area. In which case it should not be + * flooded back to NSSA areas. + */ + if (CHECK_FLAG (lsa->flags, OSPF_LSA_LOCAL_XLT)) + return; + + /* NSSA Originate or Refresh (If anyNSSA) + + LSA is self-originated. And just installed as Type-5. + Additionally, install as Type-7 LSDB for every attached NSSA. + + P-Bit controls which ABR performs translation to outside world; If + we are an ABR....do not set the P-bit, because we send the Type-5, + not as the ABR Translator, but as the ASBR owner within the AS! + + If we are NOT ABR, Flood through NSSA as Type-7 w/P-bit set. The + elected ABR Translator will see the P-bit, Translate, and re-flood. + + Later, ABR_TASK and P-bit will scan Type-7 LSDB and translate to + Type-5's to non-NSSA Areas. (it will also attempt a re-install) */ + + for (ALL_LIST_ELEMENTS (ospf->areas, node, nnode, area)) + { + /* Don't install Type-7 LSA's into nonNSSA area */ + if (area->external_routing != OSPF_AREA_NSSA) + continue; + + /* make lsa duplicate, lock=1 */ + new = ospf_lsa_dup (lsa); + new->area = area; + new->data->type = OSPF_AS_NSSA_LSA; + + /* set P-bit if not ABR */ + if (! IS_OSPF_ABR (ospf)) + { + SET_FLAG(new->data->options, OSPF_OPTION_NP); + + /* set non-zero FWD ADDR + + draft-ietf-ospf-nssa-update-09.txt + + if the network between the NSSA AS boundary router and the + adjacent AS is advertised into OSPF as an internal OSPF route, + the forwarding address should be the next op address as is cu + currently done with type-5 LSAs. If the intervening network is + not adversited into OSPF as an internal OSPF route and the + type-7 LSA's P-bit is set a forwarding address should be + selected from one of the router's active OSPF inteface addresses + which belong to the NSSA. If no such addresses exist, then + no type-7 LSA's with the P-bit set should originate from this + router. */ + + /* kevinm: not updating lsa anymore, just new */ + extlsa = (struct as_external_lsa *)(new->data); + + if (extlsa->e[0].fwd_addr.s_addr == 0) + extlsa->e[0].fwd_addr = ospf_get_nssa_ip(area); /* this NSSA area in ifp */ + + if (extlsa->e[0].fwd_addr.s_addr == 0) + { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("LSA[Type-7]: Could not build FWD-ADDR"); + ospf_lsa_discard (new); + return; + } + } + + /* install also as Type-7 */ + ospf_lsa_install (ospf, NULL, new); /* Remove Old, Lock New = 2 */ + + /* will send each copy, lock=2+n */ + ospf_flood_through_as (ospf, NULL, new); /* all attached NSSA's, no AS/STUBs */ + } +} + +static struct ospf_lsa * +ospf_lsa_translated_nssa_new (struct ospf *ospf, + struct ospf_lsa *type7) +{ + + struct ospf_lsa *new; + struct as_external_lsa *ext, *extnew; + struct external_info ei; + + ext = (struct as_external_lsa *)(type7->data); + + /* need external_info struct, fill in bare minimum */ + ei.p.family = AF_INET; + ei.p.prefix = type7->data->id; + ei.p.prefixlen = ip_masklen (ext->mask); + ei.type = ZEBRA_ROUTE_OSPF; + ei.nexthop = ext->header.adv_router; + ei.route_map_set.metric = -1; + ei.route_map_set.metric_type = -1; + ei.tag = 0; + + if ( (new = ospf_external_lsa_new (ospf, &ei, &type7->data->id)) == NULL) + { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_nssa_translate_originate(): Could not originate " + "Translated Type-5 for %s", + inet_ntoa (ei.p.prefix)); + return NULL; + } + + extnew = (struct as_external_lsa *)(new->data); + + /* copy over Type-7 data to new */ + extnew->e[0].tos = ext->e[0].tos; + extnew->e[0].route_tag = ext->e[0].route_tag; + extnew->e[0].fwd_addr.s_addr = ext->e[0].fwd_addr.s_addr; + new->data->ls_seqnum = type7->data->ls_seqnum; + + /* add translated flag, checksum and lock new lsa */ + SET_FLAG (new->flags, OSPF_LSA_LOCAL_XLT); /* Translated from 7 */ + new = ospf_lsa_lock (new); + + return new; +} + +/* Originate Translated Type-5 for supplied Type-7 NSSA LSA */ +struct ospf_lsa * +ospf_translated_nssa_originate (struct ospf *ospf, struct ospf_lsa *type7) +{ + struct ospf_lsa *new; + struct as_external_lsa *extnew; + + /* we cant use ospf_external_lsa_originate() as we need to set + * the OSPF_LSA_LOCAL_XLT flag, must originate by hand + */ + + if ( (new = ospf_lsa_translated_nssa_new (ospf, type7)) == NULL) + { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_translated_nssa_originate(): Could not translate " + "Type-7, Id %s, to Type-5", + inet_ntoa (type7->data->id)); + return NULL; + } + + extnew = (struct as_external_lsa *)new; + + if (IS_DEBUG_OSPF_NSSA) + { + zlog_debug ("ospf_translated_nssa_originate(): " + "translated Type 7, installed:"); + ospf_lsa_header_dump (new->data); + zlog_debug (" Network mask: %d",ip_masklen (extnew->mask)); + zlog_debug (" Forward addr: %s", inet_ntoa (extnew->e[0].fwd_addr)); + } + + if ( (new = ospf_lsa_install (ospf, NULL, new)) == NULL) + { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_lsa_translated_nssa_originate(): " + "Could not install LSA " + "id %s", inet_ntoa (type7->data->id)); + return NULL; + } + + ospf->lsa_originate_count++; + ospf_flood_through_as (ospf, NULL, new); + + return new; +} + +/* Refresh Translated from NSSA AS-external-LSA. */ +struct ospf_lsa * +ospf_translated_nssa_refresh (struct ospf *ospf, struct ospf_lsa *type7, + struct ospf_lsa *type5) +{ + struct ospf_lsa *new = NULL; + + /* Sanity checks. */ + assert (type7 || type5); + if (!(type7 || type5)) + return NULL; + if (type7) + assert (type7->data); + if (type5) + assert (type5->data); + assert (ospf->anyNSSA); + + /* get required data according to what has been given */ + if (type7 && type5 == NULL) + { + /* find the translated Type-5 for this Type-7 */ + struct as_external_lsa *ext = (struct as_external_lsa *)(type7->data); + struct prefix_ipv4 p = + { + .prefix = type7->data->id, + .prefixlen = ip_masklen (ext->mask), + .family = AF_INET, + }; + + type5 = ospf_external_info_find_lsa (ospf, &p); + } + else if (type5 && type7 == NULL) + { + /* find the type-7 from which supplied type-5 was translated, + * ie find first type-7 with same LSA Id. + */ + struct listnode *ln, *lnn; + struct route_node *rn; + struct ospf_lsa *lsa; + struct ospf_area *area; + + for (ALL_LIST_ELEMENTS (ospf->areas, ln, lnn, area)) + { + if (area->external_routing != OSPF_AREA_NSSA + && !type7) + continue; + + LSDB_LOOP (NSSA_LSDB(area), rn, lsa) + { + if (lsa->data->id.s_addr == type5->data->id.s_addr) + { + type7 = lsa; + break; + } + } + } + } + + /* do we have type7? */ + if (!type7) + { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_translated_nssa_refresh(): no Type-7 found for " + "Type-5 LSA Id %s", + inet_ntoa (type5->data->id)); + return NULL; + } + + /* do we have valid translated type5? */ + if (type5 == NULL || !CHECK_FLAG (type5->flags, OSPF_LSA_LOCAL_XLT) ) + { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_translated_nssa_refresh(): No translated Type-5 " + "found for Type-7 with Id %s", + inet_ntoa (type7->data->id)); + return NULL; + } + + /* Delete LSA from neighbor retransmit-list. */ + ospf_ls_retransmit_delete_nbr_as (ospf, type5); + + /* create new translated LSA */ + if ( (new = ospf_lsa_translated_nssa_new (ospf, type7)) == NULL) + { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_translated_nssa_refresh(): Could not translate " + "Type-7 for %s to Type-5", + inet_ntoa (type7->data->id)); + return NULL; + } + + if ( !(new = ospf_lsa_install (ospf, NULL, new)) ) + { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("ospf_translated_nssa_refresh(): Could not install " + "translated LSA, Id %s", + inet_ntoa (type7->data->id)); + return NULL; + } + + /* Flood LSA through area. */ + ospf_flood_through_as (ospf, NULL, new); + + return new; +} + +int +is_prefix_default (struct prefix_ipv4 *p) +{ + struct prefix_ipv4 q; + + q.family = AF_INET; + q.prefix.s_addr = 0; + q.prefixlen = 0; + + return prefix_same ((struct prefix *) p, (struct prefix *) &q); +} + +/* Originate an AS-external-LSA, install and flood. */ +struct ospf_lsa * +ospf_external_lsa_originate (struct ospf *ospf, struct external_info *ei) +{ + struct ospf_lsa *new; + + /* Added for NSSA project.... + + External LSAs are originated in ASBRs as usual, but for NSSA systems. + there is the global Type-5 LSDB and a Type-7 LSDB installed for + every area. The Type-7's are flooded to every IR and every ABR; We + install the Type-5 LSDB so that the normal "refresh" code operates + as usual, and flag them as not used during ASE calculations. The + Type-7 LSDB is used for calculations. Each Type-7 has a Forwarding + Address of non-zero. + + If an ABR is the elected NSSA translator, following SPF and during + the ABR task it will translate all the scanned Type-7's, with P-bit + ON and not-self generated, and translate to Type-5's throughout the + non-NSSA/STUB AS. + + A difference in operation depends whether this ASBR is an ABR + or not. If not an ABR, the P-bit is ON, to indicate that any + elected NSSA-ABR can perform its translation. + + If an ABR, the P-bit is OFF; No ABR will perform translation and + this ASBR will flood the Type-5 LSA as usual. + + For the case where this ASBR is not an ABR, the ASE calculations + are based on the Type-5 LSDB; The Type-7 LSDB exists just to + demonstrate to the user that there are LSA's that belong to any + attached NSSA. + + Finally, it just so happens that when the ABR is translating every + Type-7 into Type-5, it installs it into the Type-5 LSDB as an + approved Type-5 (translated from Type-7); at the end of translation + if any Translated Type-5's remain unapproved, then they must be + flushed from the AS. + + */ + + /* Check the AS-external-LSA should be originated. */ + if (!ospf_redistribute_check (ospf, ei, NULL)) + return NULL; + + /* Create new AS-external-LSA instance. */ + if ((new = ospf_external_lsa_new (ospf, ei, NULL)) == NULL) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("LSA[Type5:%s]: Could not originate AS-external-LSA", + inet_ntoa (ei->p.prefix)); + return NULL; + } + + /* Install newly created LSA into Type-5 LSDB, lock = 1. */ + ospf_lsa_install (ospf, NULL, new); + + /* Update LSA origination count. */ + ospf->lsa_originate_count++; + + /* Flooding new LSA. only to AS (non-NSSA/STUB) */ + ospf_flood_through_as (ospf, NULL, new); + + /* If there is any attached NSSA, do special handling */ + if (ospf->anyNSSA && + /* stay away from translated LSAs! */ + !(CHECK_FLAG (new->flags, OSPF_LSA_LOCAL_XLT))) + ospf_install_flood_nssa (ospf, new, ei); /* Install/Flood Type-7 to all NSSAs */ + + /* Debug logging. */ + 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), (void *)new); + ospf_lsa_header_dump (new->data); + } + + return new; +} + +/* Originate AS-external-LSA from external info with initial flag. */ +int +ospf_external_lsa_originate_timer (struct thread *thread) +{ + struct ospf *ospf = THREAD_ARG (thread); + struct route_node *rn; + struct external_info *ei; + struct route_table *rt; + int type = THREAD_VAL (thread); + + ospf->t_external_lsa = NULL; + + /* Originate As-external-LSA from all type of distribute source. */ + if ((rt = EXTERNAL_INFO (type))) + for (rn = route_top (rt); rn; rn = route_next (rn)) + if ((ei = rn->info) != NULL) + if (!is_prefix_default ((struct prefix_ipv4 *)&ei->p)) + if (!ospf_external_lsa_originate (ospf, ei)) + zlog_warn ("LSA: AS-external-LSA was not originated."); + + return 0; +} + +static struct external_info * +ospf_default_external_info (struct ospf *ospf) +{ + int type; + struct route_node *rn; + struct prefix_ipv4 p; + + p.family = AF_INET; + p.prefix.s_addr = 0; + p.prefixlen = 0; + + /* First, lookup redistributed default route. */ + for (type = 0; type <= ZEBRA_ROUTE_MAX; type++) + if (EXTERNAL_INFO (type) && type != ZEBRA_ROUTE_OSPF) + { + rn = route_node_lookup (EXTERNAL_INFO (type), (struct prefix *) &p); + if (rn != NULL) + { + route_unlock_node (rn); + assert (rn->info); + if (ospf_redistribute_check (ospf, rn->info, NULL)) + return rn->info; + } + } + + return NULL; +} + +int +ospf_default_originate_timer (struct thread *thread) +{ + struct prefix_ipv4 p; + struct in_addr nexthop; + struct external_info *ei; + struct ospf *ospf; + + ospf = THREAD_ARG (thread); + + p.family = AF_INET; + p.prefix.s_addr = 0; + p.prefixlen = 0; + + if (ospf->default_originate == DEFAULT_ORIGINATE_ALWAYS) + { + /* 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, 0); + } + + if ((ei = ospf_default_external_info (ospf))) + ospf_external_lsa_originate (ospf, ei); + + return 0; +} + +/* Flush any NSSA LSAs for given prefix */ +void +ospf_nssa_lsa_flush (struct ospf *ospf, struct prefix_ipv4 *p) +{ + struct listnode *node, *nnode; + struct ospf_lsa *lsa; + struct ospf_area *area; + + for (ALL_LIST_ELEMENTS (ospf->areas, node, nnode, area)) + { + if (area->external_routing == OSPF_AREA_NSSA) + { + if (!(lsa = ospf_lsa_lookup (area, OSPF_AS_NSSA_LSA, p->prefix, + ospf->router_id))) + { + if (IS_DEBUG_OSPF (lsa, LSA_FLOODING)) + zlog_debug ("LSA: There is no such AS-NSSA-LSA %s/%d in LSDB", + inet_ntoa (p->prefix), p->prefixlen); + continue; + } + ospf_ls_retransmit_delete_nbr_area (area, lsa); + if (!IS_LSA_MAXAGE (lsa)) + { + ospf_refresher_unregister_lsa (ospf, lsa); + ospf_lsa_flush_area (lsa, area); + } + } + } +} + +/* Flush an AS-external-LSA from LSDB and routing domain. */ +void +ospf_external_lsa_flush (struct ospf *ospf, + u_char type, struct prefix_ipv4 *p, + ifindex_t ifindex /*, struct in_addr nexthop */) +{ + struct ospf_lsa *lsa; + + if (IS_DEBUG_OSPF (lsa, LSA_FLOODING)) + zlog_debug ("LSA: Flushing AS-external-LSA %s/%d", + inet_ntoa (p->prefix), p->prefixlen); + + /* First lookup LSA from LSDB. */ + if (!(lsa = ospf_external_info_find_lsa (ospf, p))) + { + if (IS_DEBUG_OSPF (lsa, LSA_FLOODING)) + zlog_debug ("LSA: There is no such AS-external-LSA %s/%d in LSDB", + inet_ntoa (p->prefix), p->prefixlen); + return; + } + + /* If LSA is selforiginated, not a translated LSA, and there is + * NSSA area, flush Type-7 LSA's at first. + */ + if (IS_LSA_SELF(lsa) && (ospf->anyNSSA) + && !(CHECK_FLAG (lsa->flags, OSPF_LSA_LOCAL_XLT))) + ospf_nssa_lsa_flush (ospf, p); + + /* Sweep LSA from Link State Retransmit List. */ + ospf_ls_retransmit_delete_nbr_as (ospf, lsa); + + /* There must be no self-originated LSA in rtrs_external. */ +#if 0 + /* Remove External route from Zebra. */ + ospf_zebra_delete ((struct prefix_ipv4 *) p, &nexthop); +#endif + + if (!IS_LSA_MAXAGE (lsa)) + { + /* Unregister LSA from Refresh queue. */ + ospf_refresher_unregister_lsa (ospf, lsa); + + /* Flush AS-external-LSA through AS. */ + ospf_lsa_flush_as (ospf, lsa); + } + + if (IS_DEBUG_OSPF (lsa, LSA_FLOODING)) + zlog_debug ("ospf_external_lsa_flush(): stop"); +} + +void +ospf_external_lsa_refresh_default (struct ospf *ospf) +{ + struct prefix_ipv4 p; + struct external_info *ei; + struct ospf_lsa *lsa; + + p.family = AF_INET; + p.prefixlen = 0; + p.prefix.s_addr = 0; + + ei = ospf_default_external_info (ospf); + lsa = ospf_external_info_find_lsa (ospf, &p); + + if (ei) + { + if (lsa) + { + if (IS_DEBUG_OSPF_EVENT) + 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 + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("LSA[Type5:0.0.0.0]: Originate AS-external-LSA"); + ospf_external_lsa_originate (ospf, ei); + } + } + else + { + if (lsa) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("LSA[Type5:0.0.0.0]: Flush AS-external-LSA"); + ospf_refresher_unregister_lsa (ospf, lsa); + ospf_lsa_flush_as (ospf, lsa); + } + } +} + +void +ospf_external_lsa_refresh_type (struct ospf *ospf, u_char type, int force) +{ + struct route_node *rn; + struct external_info *ei; + + if (type != DEFAULT_ROUTE) + if (EXTERNAL_INFO(type)) + /* Refresh each redistributed AS-external-LSAs. */ + for (rn = route_top (EXTERNAL_INFO (type)); rn; rn = route_next (rn)) + if ((ei = rn->info)) + if (!is_prefix_default (&ei->p)) + { + struct ospf_lsa *lsa; + + if ((lsa = ospf_external_info_find_lsa (ospf, &ei->p))) + ospf_external_lsa_refresh (ospf, lsa, ei, force); + else + ospf_external_lsa_originate (ospf, ei); + } +} + +/* Refresh AS-external-LSA. */ +struct ospf_lsa * +ospf_external_lsa_refresh (struct ospf *ospf, struct ospf_lsa *lsa, + struct external_info *ei, int force) +{ + struct ospf_lsa *new; + int changed; + + /* Check the AS-external-LSA should be originated. */ + if (!ospf_redistribute_check (ospf, ei, &changed)) + { + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + zlog_debug ("LSA[Type%d:%s]: Could not be refreshed, " + "redist check fail", + lsa->data->type, inet_ntoa (lsa->data->id)); + ospf_external_lsa_flush (ospf, ei->type, &ei->p, + ei->ifindex /*, ei->nexthop */); + return NULL; + } + + if (!changed && !force) + { + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + zlog_debug ("LSA[Type%d:%s]: Not refreshed, not changed/forced", + lsa->data->type, inet_ntoa (lsa->data->id)); + return NULL; + } + + /* Delete LSA from neighbor retransmit-list. */ + ospf_ls_retransmit_delete_nbr_as (ospf, lsa); + + /* Unregister AS-external-LSA from refresh-list. */ + ospf_refresher_unregister_lsa (ospf, lsa); + + new = ospf_external_lsa_new (ospf, ei, &lsa->data->id); + + if (new == NULL) + { + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + zlog_debug ("LSA[Type%d:%s]: Could not be refreshed", lsa->data->type, + inet_ntoa (lsa->data->id)); + return NULL; + } + + new->data->ls_seqnum = lsa_seqnum_increment (lsa); + + ospf_lsa_install (ospf, NULL, new); /* As type-5. */ + + /* Flood LSA through AS. */ + ospf_flood_through_as (ospf, NULL, new); + + /* If any attached NSSA, install as Type-7, flood to all NSSA Areas */ + if (ospf->anyNSSA && !(CHECK_FLAG (new->flags, OSPF_LSA_LOCAL_XLT))) + ospf_install_flood_nssa (ospf, new, ei); /* Install/Flood per new rules */ + + /* Register self-originated LSA to refresh queue. + * Translated LSAs should not be registered, but refreshed upon + * refresh of the Type-7 + */ + if ( !CHECK_FLAG (new->flags, OSPF_LSA_LOCAL_XLT) ) + ospf_refresher_register_lsa (ospf, new); + + /* Debug logging. */ + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + { + zlog_debug ("LSA[Type%d:%s]: AS-external-LSA refresh", + new->data->type, inet_ntoa (new->data->id)); + ospf_lsa_header_dump (new->data); + } + + return new; +} + + +/* LSA installation functions. */ + +/* Install router-LSA to an area. */ +static struct ospf_lsa * +ospf_router_lsa_install (struct ospf *ospf, struct ospf_lsa *new, + int rt_recalc) +{ + struct ospf_area *area = new->area; + + /* RFC 2328 Section 13.2 Router-LSAs and network-LSAs + The entire routing table must be recalculated, starting with + the shortest path calculations for each area (not just the + area whose link-state database has changed). + */ + + if (IS_LSA_SELF (new)) + { + + /* Only install LSA if it is originated/refreshed by us. + * If LSA was received by flooding, the RECEIVED flag is set so do + * not link the LSA */ + if (CHECK_FLAG (new->flags, OSPF_LSA_RECEIVED)) + return new; /* ignore stale LSA */ + + /* Set self-originated router-LSA. */ + ospf_lsa_unlock (&area->router_lsa_self); + area->router_lsa_self = ospf_lsa_lock (new); + + ospf_refresher_register_lsa (ospf, new); + } + if (rt_recalc) + ospf_spf_calculate_schedule (ospf, SPF_FLAG_ROUTER_LSA_INSTALL); + return new; +} + +#define OSPF_INTERFACE_TIMER_ON(T,F,V) \ + if (!(T)) \ + (T) = thread_add_timer (master, (F), oi, (V)) + +/* Install network-LSA to an area. */ +static struct ospf_lsa * +ospf_network_lsa_install (struct ospf *ospf, + struct ospf_interface *oi, + struct ospf_lsa *new, + int rt_recalc) +{ + + /* RFC 2328 Section 13.2 Router-LSAs and network-LSAs + The entire routing table must be recalculated, starting with + the shortest path calculations for each area (not just the + area whose link-state database has changed). + */ + if (IS_LSA_SELF (new)) + { + /* We supposed that when LSA is originated by us, we pass the int + for which it was originated. If LSA was received by flooding, + the RECEIVED flag is set, so we do not link the LSA to the int. */ + if (CHECK_FLAG (new->flags, OSPF_LSA_RECEIVED)) + return new; /* ignore stale LSA */ + + ospf_lsa_unlock (&oi->network_lsa_self); + oi->network_lsa_self = ospf_lsa_lock (new); + ospf_refresher_register_lsa (ospf, new); + } + if (rt_recalc) + ospf_spf_calculate_schedule (ospf, SPF_FLAG_NETWORK_LSA_INSTALL); + + return new; +} + +/* Install summary-LSA to an area. */ +static struct ospf_lsa * +ospf_summary_lsa_install (struct ospf *ospf, struct ospf_lsa *new, + int rt_recalc) +{ + if (rt_recalc && !IS_LSA_SELF (new)) + { + /* RFC 2328 Section 13.2 Summary-LSAs + The best route to the destination described by the summary- + LSA must be recalculated (see Section 16.5). If this + destination is an AS boundary router, it may also be + necessary to re-examine all the AS-external-LSAs. + */ + +#if 0 + /* This doesn't exist yet... */ + ospf_summary_incremental_update(new); */ +#else /* #if 0 */ + ospf_spf_calculate_schedule (ospf, SPF_FLAG_SUMMARY_LSA_INSTALL); +#endif /* #if 0 */ + + } + + if (IS_LSA_SELF (new)) + ospf_refresher_register_lsa (ospf, new); + + return new; +} + +/* Install ASBR-summary-LSA to an area. */ +static struct ospf_lsa * +ospf_summary_asbr_lsa_install (struct ospf *ospf, struct ospf_lsa *new, + int rt_recalc) +{ + if (rt_recalc && !IS_LSA_SELF (new)) + { + /* RFC 2328 Section 13.2 Summary-LSAs + The best route to the destination described by the summary- + LSA must be recalculated (see Section 16.5). If this + destination is an AS boundary router, it may also be + necessary to re-examine all the AS-external-LSAs. + */ +#if 0 + /* These don't exist yet... */ + ospf_summary_incremental_update(new); + /* Isn't this done by the above call? + - RFC 2328 Section 16.5 implies it should be */ + /* ospf_ase_calculate_schedule(); */ +#else /* #if 0 */ + ospf_spf_calculate_schedule (ospf, SPF_FLAG_ASBR_SUMMARY_LSA_INSTALL); +#endif /* #if 0 */ + } + + /* register LSA to refresh-list. */ + if (IS_LSA_SELF (new)) + ospf_refresher_register_lsa (ospf, new); + + return new; +} + +/* Install AS-external-LSA. */ +static struct ospf_lsa * +ospf_external_lsa_install (struct ospf *ospf, struct ospf_lsa *new, + int rt_recalc) +{ + ospf_ase_register_external_lsa (new, ospf); + /* If LSA is not self-originated, calculate an external route. */ + if (rt_recalc) + { + /* RFC 2328 Section 13.2 AS-external-LSAs + The best route to the destination described by the AS- + external-LSA must be recalculated (see Section 16.6). + */ + + if (!IS_LSA_SELF (new)) + ospf_ase_incremental_update (ospf, new); + } + + if (new->data->type == OSPF_AS_NSSA_LSA) + { + /* There is no point to register selforiginate Type-7 LSA for + * refreshing. We rely on refreshing Type-5 LSA's + */ + if (IS_LSA_SELF (new)) + return new; + else + { + /* Try refresh type-5 translated LSA for this LSA, if one exists. + * New translations will be taken care of by the abr_task. + */ + ospf_translated_nssa_refresh (ospf, new, NULL); + ospf_schedule_abr_task(ospf); + } + } + + /* Register self-originated LSA to refresh queue. + * Leave Translated LSAs alone if NSSA is enabled + */ + if (IS_LSA_SELF (new) && !CHECK_FLAG (new->flags, OSPF_LSA_LOCAL_XLT ) ) + ospf_refresher_register_lsa (ospf, new); + + return new; +} + +void +ospf_discard_from_db (struct ospf *ospf, + struct ospf_lsdb *lsdb, struct ospf_lsa *lsa) +{ + struct ospf_lsa *old; + + if (!lsdb) + { + zlog_warn ("%s: Called with NULL lsdb!", __func__); + if (!lsa) + zlog_warn ("%s: and NULL LSA!", __func__); + else + zlog_warn ("LSA[Type%d:%s]: not associated with LSDB!", + lsa->data->type, inet_ntoa (lsa->data->id)); + return; + } + + old = ospf_lsdb_lookup (lsdb, lsa); + + if (!old) + return; + + if (old->refresh_list >= 0) + ospf_refresher_unregister_lsa (ospf, old); + + switch (old->data->type) + { + case OSPF_AS_EXTERNAL_LSA: + ospf_ase_unregister_external_lsa (old, ospf); + ospf_ls_retransmit_delete_nbr_as (ospf, old); + break; + case OSPF_OPAQUE_AS_LSA: + ospf_ls_retransmit_delete_nbr_as (ospf, old); + break; + case OSPF_AS_NSSA_LSA: + ospf_ls_retransmit_delete_nbr_area (old->area, old); + ospf_ase_unregister_external_lsa (old, ospf); + break; + default: + ospf_ls_retransmit_delete_nbr_area (old->area, old); + break; + } + + ospf_lsa_maxage_delete (ospf, old); + ospf_lsa_discard (old); +} + +struct ospf_lsa * +ospf_lsa_install (struct ospf *ospf, struct ospf_interface *oi, + struct ospf_lsa *lsa) +{ + struct ospf_lsa *new = NULL; + struct ospf_lsa *old = NULL; + struct ospf_lsdb *lsdb = NULL; + int rt_recalc; + + /* Set LSDB. */ + switch (lsa->data->type) + { + /* kevinm */ + case OSPF_AS_NSSA_LSA: + if (lsa->area) + lsdb = lsa->area->lsdb; + else + lsdb = ospf->lsdb; + break; + case OSPF_AS_EXTERNAL_LSA: + case OSPF_OPAQUE_AS_LSA: + lsdb = ospf->lsdb; + break; + default: + lsdb = lsa->area->lsdb; + break; + } + + assert (lsdb); + + /* RFC 2328 13.2. Installing LSAs in the database + + Installing a new LSA in the database, either as the result of + flooding or a newly self-originated LSA, may cause the OSPF + routing table structure to be recalculated. The contents of the + new LSA should be compared to the old instance, if present. If + there is no difference, there is no need to recalculate the + routing table. When comparing an LSA to its previous instance, + the following are all considered to be differences in contents: + + o The LSA's Options field has changed. + + o One of the LSA instances has LS age set to MaxAge, and + the other does not. + + o The length field in the LSA header has changed. + + o The body of the LSA (i.e., anything outside the 20-byte + LSA header) has changed. Note that this excludes changes + in LS Sequence Number and LS Checksum. + + */ + /* Look up old LSA and determine if any SPF calculation or incremental + update is needed */ + old = ospf_lsdb_lookup (lsdb, lsa); + + /* Do comparision and record if recalc needed. */ + rt_recalc = 0; + if ( old == NULL || ospf_lsa_different(old, lsa)) + rt_recalc = 1; + + /* + Sequence number check (Section 14.1 of rfc 2328) + "Premature aging is used when it is time for a self-originated + LSA's sequence number field to wrap. At this point, the current + LSA instance (having LS sequence number MaxSequenceNumber) must + be prematurely aged and flushed from the routing domain before a + new instance with sequence number equal to InitialSequenceNumber + can be originated. " + */ + + if (ntohl(lsa->data->ls_seqnum) - 1 == OSPF_MAX_SEQUENCE_NUMBER) + { + if (ospf_lsa_is_self_originated(ospf, lsa)) + { + lsa->data->ls_seqnum = htonl(OSPF_MAX_SEQUENCE_NUMBER); + + if (!IS_LSA_MAXAGE(lsa)) + lsa->flags |= OSPF_LSA_PREMATURE_AGE; + lsa->data->ls_age = htons (OSPF_LSA_MAXAGE); + + if (IS_DEBUG_OSPF (lsa, LSA_REFRESH)) + { + zlog_debug ("ospf_lsa_install() Premature Aging " + "lsa 0x%p, seqnum 0x%x", + (void *)lsa, ntohl(lsa->data->ls_seqnum)); + ospf_lsa_header_dump (lsa->data); + } + } + else + { + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + { + zlog_debug ("ospf_lsa_install() got an lsa with seq 0x80000000 " + "that was not self originated. Ignoring\n"); + ospf_lsa_header_dump (lsa->data); + } + return old; + } + } + + /* discard old LSA from LSDB */ + if (old != NULL) + ospf_discard_from_db (ospf, lsdb, lsa); + + /* Calculate Checksum if self-originated?. */ + if (IS_LSA_SELF (lsa)) + ospf_lsa_checksum (lsa->data); + + /* Insert LSA to LSDB. */ + ospf_lsdb_add (lsdb, lsa); + lsa->lsdb = lsdb; + + /* Do LSA specific installation process. */ + switch (lsa->data->type) + { + case OSPF_ROUTER_LSA: + new = ospf_router_lsa_install (ospf, lsa, rt_recalc); + break; + case OSPF_NETWORK_LSA: + assert (oi); + new = ospf_network_lsa_install (ospf, oi, lsa, rt_recalc); + break; + case OSPF_SUMMARY_LSA: + new = ospf_summary_lsa_install (ospf, lsa, rt_recalc); + break; + case OSPF_ASBR_SUMMARY_LSA: + new = ospf_summary_asbr_lsa_install (ospf, lsa, rt_recalc); + break; + case OSPF_AS_EXTERNAL_LSA: + new = ospf_external_lsa_install (ospf, lsa, rt_recalc); + break; + 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. */ + } + /* Fallthrough */ + case OSPF_OPAQUE_AREA_LSA: + case OSPF_OPAQUE_AS_LSA: + new = ospf_opaque_lsa_install (lsa, rt_recalc); + break; + case OSPF_AS_NSSA_LSA: + new = ospf_external_lsa_install (ospf, lsa, rt_recalc); + default: /* type-6,8,9....nothing special */ + break; + } + + if (new == NULL) + return new; /* Installation failed, cannot proceed further -- endo. */ + + /* Debug logs. */ + if (IS_DEBUG_OSPF (lsa, LSA_INSTALL)) + { + char area_str[INET_ADDRSTRLEN]; + + switch (lsa->data->type) + { + case OSPF_AS_EXTERNAL_LSA: + case OSPF_OPAQUE_AS_LSA: + case OSPF_AS_NSSA_LSA: + zlog_debug ("LSA[%s]: Install %s", + dump_lsa_key (new), + LOOKUP (ospf_lsa_type_msg, new->data->type)); + break; + default: + strcpy (area_str, inet_ntoa (new->area->area_id)); + zlog_debug ("LSA[%s]: Install %s to Area %s", + dump_lsa_key (new), + LOOKUP (ospf_lsa_type_msg, new->data->type), area_str); + break; + } + } + + /* + 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 (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), + (void *)lsa); + ospf_lsa_maxage (ospf, lsa); + } + + return new; +} + + +int +ospf_check_nbr_status (struct ospf *ospf) +{ + struct listnode *node, *nnode; + struct ospf_interface *oi; + + for (ALL_LIST_ELEMENTS (ospf->oiflist, node, nnode, oi)) + { + struct route_node *rn; + struct ospf_neighbor *nbr; + + if (ospf_if_is_enable (oi)) + for (rn = route_top (oi->nbrs); rn; rn = route_next (rn)) + if ((nbr = rn->info) != NULL) + if (nbr->state == NSM_Exchange || nbr->state == NSM_Loading) + { + route_unlock_node (rn); + return 0; + } + } + + return 1; +} + + + +static int +ospf_maxage_lsa_remover (struct thread *thread) +{ + struct ospf *ospf = THREAD_ARG (thread); + struct ospf_lsa *lsa; + struct route_node *rn; + int reschedule = 0; + + ospf->t_maxage = NULL; + + if (IS_DEBUG_OSPF (lsa, LSA_FLOODING)) + zlog_debug ("LSA[MaxAge]: remover Start"); + + reschedule = !ospf_check_nbr_status (ospf); + + if (!reschedule) + 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; + continue; + } + + /* 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); + route_unlock_node(rn); /* route_top/route_next */ + return 0; + } + + /* Remove LSA from the LSDB */ + if (IS_LSA_SELF (lsa)) + if (IS_DEBUG_OSPF (lsa, LSA_FLOODING)) + zlog_debug ("LSA[Type%d:%s]: LSA 0x%lx is self-originated: ", + lsa->data->type, inet_ntoa (lsa->data->id), (u_long)lsa); + + if (IS_DEBUG_OSPF (lsa, LSA_FLOODING)) + zlog_debug ("LSA[Type%d:%s]: MaxAge LSA removed from list", + lsa->data->type, inet_ntoa (lsa->data->id)); + + 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", (void *)lsa); + ospf_lsa_refresh (ospf, lsa); + } + + /* Remove from lsdb. */ + if (lsa->lsdb) + { + ospf_discard_from_db (ospf, lsa->lsdb, lsa); + ospf_lsdb_delete (lsa->lsdb, lsa); + } + else + zlog_warn ("%s: LSA[Type%d:%s]: No associated LSDB!", __func__, + lsa->data->type, inet_ntoa (lsa->data->id)); + } + + /* A MaxAge LSA must be removed immediately from the router's link + state database as soon as both a) it is no longer contained on any + neighbor Link state retransmission lists and b) none of the router's + neighbors are in states Exchange or Loading. */ + if (reschedule) + OSPF_TIMER_ON (ospf->t_maxage, ospf_maxage_lsa_remover, + ospf->maxage_delay); + + return 0; +} + +void +ospf_lsa_maxage_delete (struct ospf *ospf, struct ospf_lsa *lsa) +{ + 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 ((rn = route_node_lookup(ospf->maxage_lsa, + (struct prefix *)&lsa_prefix))) + { + 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 */ + } +} + +/* Add LSA onto the MaxAge list, and schedule for removal. + * This does *not* lead to the LSA being flooded, that must be taken + * care of elsewhere, see, e.g., ospf_lsa_flush* (which are callers of this + * function). + */ +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), (void *)lsa); + return; + } + + 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)); + + OSPF_TIMER_ON (ospf->t_maxage, ospf_maxage_lsa_remover, + ospf->maxage_delay); +} + +static int +ospf_lsa_maxage_walker_remover (struct ospf *ospf, struct ospf_lsa *lsa) +{ + /* Stay away from any Local Translated Type-7 LSAs */ + if (CHECK_FLAG (lsa->flags, OSPF_LSA_LOCAL_XLT)) + return 0; + + if (IS_LSA_MAXAGE (lsa)) + /* Self-originated LSAs should NOT time-out instead, + they're flushed and submitted to the max_age list explicitly. */ + if (!ospf_lsa_is_self_originated (ospf, lsa)) + { + if (IS_DEBUG_OSPF (lsa, LSA_FLOODING)) + zlog_debug("LSA[%s]: is MaxAge", dump_lsa_key (lsa)); + + switch (lsa->data->type) + { + case OSPF_OPAQUE_LINK_LSA: + case OSPF_OPAQUE_AREA_LSA: + case OSPF_OPAQUE_AS_LSA: + /* + * As a general rule, whenever network topology has changed + * (due to an LSA removal in this case), routing recalculation + * should be triggered. However, this is not true for opaque + * LSAs. Even if an opaque LSA instance is going to be removed + * from the routing domain, it does not mean a change in network + * topology, and thus, routing recalculation is not needed here. + */ + break; + case OSPF_AS_EXTERNAL_LSA: + case OSPF_AS_NSSA_LSA: + ospf_ase_incremental_update (ospf, lsa); + break; + default: + ospf_spf_calculate_schedule (ospf, SPF_FLAG_MAXAGE); + break; + } + ospf_lsa_maxage (ospf, lsa); + } + + if (IS_LSA_MAXAGE (lsa) && !ospf_lsa_is_self_originated (ospf, lsa)) + if (LS_AGE (lsa) > OSPF_LSA_MAXAGE + 30) + printf ("Eek! Shouldn't happen!\n"); + + return 0; +} + +/* Periodical check of MaxAge LSA. */ +int +ospf_lsa_maxage_walker (struct thread *thread) +{ + struct ospf *ospf = THREAD_ARG (thread); + struct route_node *rn; + struct ospf_lsa *lsa; + struct ospf_area *area; + struct listnode *node, *nnode; + + ospf->t_maxage_walker = NULL; + + for (ALL_LIST_ELEMENTS (ospf->areas, node, nnode, area)) + { + LSDB_LOOP (ROUTER_LSDB (area), rn, lsa) + ospf_lsa_maxage_walker_remover (ospf, lsa); + LSDB_LOOP (NETWORK_LSDB (area), rn, lsa) + ospf_lsa_maxage_walker_remover (ospf, lsa); + LSDB_LOOP (SUMMARY_LSDB (area), rn, lsa) + ospf_lsa_maxage_walker_remover (ospf, lsa); + LSDB_LOOP (ASBR_SUMMARY_LSDB (area), rn, lsa) + ospf_lsa_maxage_walker_remover (ospf, 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); + LSDB_LOOP (NSSA_LSDB (area), rn, lsa) + ospf_lsa_maxage_walker_remover (ospf, lsa); + } + + /* for AS-external-LSAs. */ + if (ospf->lsdb) + { + LSDB_LOOP (EXTERNAL_LSDB (ospf), rn, lsa) + ospf_lsa_maxage_walker_remover (ospf, lsa); + LSDB_LOOP (OPAQUE_AS_LSDB (ospf), rn, lsa) + ospf_lsa_maxage_walker_remover (ospf, lsa); + } + + OSPF_TIMER_ON (ospf->t_maxage_walker, ospf_lsa_maxage_walker, + OSPF_LSA_MAXAGE_CHECK_INTERVAL); + return 0; +} + +struct ospf_lsa * +ospf_lsa_lookup_by_prefix (struct ospf_lsdb *lsdb, u_char type, + struct prefix_ipv4 *p, struct in_addr router_id) +{ + struct ospf_lsa *lsa; + struct in_addr mask, id; + struct lsa_header_mask + { + struct lsa_header header; + struct in_addr mask; + } *hmask; + + lsa = ospf_lsdb_lookup_by_id (lsdb, type, p->prefix, router_id); + if (lsa == NULL) + return NULL; + + masklen2ip (p->prefixlen, &mask); + + hmask = (struct lsa_header_mask *) lsa->data; + + if (mask.s_addr != hmask->mask.s_addr) + { + id.s_addr = p->prefix.s_addr | (~mask.s_addr); + lsa = ospf_lsdb_lookup_by_id (lsdb, type, id, router_id); + if (!lsa) + return NULL; + } + + return lsa; +} + +struct ospf_lsa * +ospf_lsa_lookup (struct ospf_area *area, u_int32_t type, + struct in_addr id, struct in_addr adv_router) +{ + struct ospf *ospf = ospf_lookup(); + assert(ospf); + + switch (type) + { + case OSPF_ROUTER_LSA: + case OSPF_NETWORK_LSA: + case OSPF_SUMMARY_LSA: + case OSPF_ASBR_SUMMARY_LSA: + case OSPF_AS_NSSA_LSA: + case OSPF_OPAQUE_LINK_LSA: + case OSPF_OPAQUE_AREA_LSA: + return ospf_lsdb_lookup_by_id (area->lsdb, type, id, adv_router); + case OSPF_AS_EXTERNAL_LSA: + case OSPF_OPAQUE_AS_LSA: + return ospf_lsdb_lookup_by_id (ospf->lsdb, type, id, adv_router); + default: + break; + } + + return NULL; +} + +struct ospf_lsa * +ospf_lsa_lookup_by_id (struct ospf_area *area, u_int32_t type, + struct in_addr id) +{ + struct ospf_lsa *lsa; + struct route_node *rn; + + switch (type) + { + case OSPF_ROUTER_LSA: + return ospf_lsdb_lookup_by_id (area->lsdb, type, id, id); + case OSPF_NETWORK_LSA: + for (rn = route_top (NETWORK_LSDB (area)); rn; rn = route_next (rn)) + if ((lsa = rn->info)) + if (IPV4_ADDR_SAME (&lsa->data->id, &id)) + { + route_unlock_node (rn); + return lsa; + } + break; + case OSPF_SUMMARY_LSA: + case OSPF_ASBR_SUMMARY_LSA: + /* Currently not used. */ + assert (1); + return ospf_lsdb_lookup_by_id (area->lsdb, type, id, id); + case OSPF_AS_EXTERNAL_LSA: + case OSPF_AS_NSSA_LSA: + case OSPF_OPAQUE_LINK_LSA: + case OSPF_OPAQUE_AREA_LSA: + case OSPF_OPAQUE_AS_LSA: + /* Currently not used. */ + break; + default: + break; + } + + return NULL; +} + +struct ospf_lsa * +ospf_lsa_lookup_by_header (struct ospf_area *area, struct lsa_header *lsah) +{ + struct ospf_lsa *match; + + /* + * 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. + */ + + match = ospf_lsa_lookup (area, lsah->type, lsah->id, lsah->adv_router); + + if (match == NULL) + if (IS_DEBUG_OSPF (lsa, LSA) == OSPF_DEBUG_LSA) + zlog_debug ("LSA[Type%d:%s]: Lookup by header, NO MATCH", + lsah->type, inet_ntoa (lsah->id)); + + return match; +} + +/* return +n, l1 is more recent. + return -n, l2 is more recent. + return 0, l1 and l2 is identical. */ +int +ospf_lsa_more_recent (struct ospf_lsa *l1, struct ospf_lsa *l2) +{ + int r; + int x, y; + + if (l1 == NULL && l2 == NULL) + return 0; + if (l1 == NULL) + return -1; + if (l2 == NULL) + return 1; + + /* compare LS sequence number. */ + x = (int) ntohl (l1->data->ls_seqnum); + y = (int) ntohl (l2->data->ls_seqnum); + if (x > y) + return 1; + if (x < y) + return -1; + + /* compare LS checksum. */ + r = ntohs (l1->data->checksum) - ntohs (l2->data->checksum); + if (r) + return r; + + /* compare LS age. */ + if (IS_LSA_MAXAGE (l1) && !IS_LSA_MAXAGE (l2)) + return 1; + else if (!IS_LSA_MAXAGE (l1) && IS_LSA_MAXAGE (l2)) + return -1; + + /* compare LS age with MaxAgeDiff. */ + if (LS_AGE (l1) - LS_AGE (l2) > OSPF_LSA_MAXAGE_DIFF) + return -1; + else if (LS_AGE (l2) - LS_AGE (l1) > OSPF_LSA_MAXAGE_DIFF) + return 1; + + /* LSAs are identical. */ + return 0; +} + +/* If two LSAs are different, return 1, otherwise return 0. */ +int +ospf_lsa_different (struct ospf_lsa *l1, struct ospf_lsa *l2) +{ + char *p1, *p2; + assert (l1); + assert (l2); + assert (l1->data); + assert (l2->data); + + if (l1->data->options != l2->data->options) + return 1; + + if (IS_LSA_MAXAGE (l1) && !IS_LSA_MAXAGE (l2)) + return 1; + + if (IS_LSA_MAXAGE (l2) && !IS_LSA_MAXAGE (l1)) + return 1; + + if (l1->data->length != l2->data->length) + return 1; + + if (l1->data->length == 0) + return 1; + + if (CHECK_FLAG ((l1->flags ^ l2->flags), OSPF_LSA_RECEIVED)) + return 1; /* May be a stale LSA in the LSBD */ + + assert ( ntohs(l1->data->length) > OSPF_LSA_HEADER_SIZE); + + p1 = (char *) l1->data; + p2 = (char *) l2->data; + + if (memcmp (p1 + OSPF_LSA_HEADER_SIZE, p2 + OSPF_LSA_HEADER_SIZE, + ntohs( l1->data->length ) - OSPF_LSA_HEADER_SIZE) != 0) + return 1; + + return 0; +} + +#ifdef ORIGINAL_CODING +void +ospf_lsa_flush_self_originated (struct ospf_neighbor *nbr, + struct ospf_lsa *self, + struct ospf_lsa *new) +{ + u_int32_t seqnum; + + /* Adjust LS Sequence Number. */ + seqnum = ntohl (new->data->ls_seqnum) + 1; + self->data->ls_seqnum = htonl (seqnum); + + /* Recalculate LSA checksum. */ + ospf_lsa_checksum (self->data); + + /* Reflooding LSA. */ + /* RFC2328 Section 13.3 + On non-broadcast networks, separate Link State Update + packets must be sent, as unicasts, to each adjacent neighbor + (i.e., those in state Exchange or greater). The destination + IP addresses for these packets are the neighbors' IP + addresses. */ + if (nbr->oi->type == OSPF_IFTYPE_NBMA) + { + struct route_node *rn; + struct ospf_neighbor *onbr; + + for (rn = route_top (nbr->oi->nbrs); rn; rn = route_next (rn)) + if ((onbr = rn->info) != NULL) + if (onbr != nbr->oi->nbr_self && onbr->status >= NSM_Exchange) + ospf_ls_upd_send_lsa (onbr, self, OSPF_SEND_PACKET_DIRECT); + } + else + ospf_ls_upd_send_lsa (nbr, self, OSPF_SEND_PACKET_INDIRECT); + + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + zlog_debug ("LSA[Type%d:%s]: Flush self-originated LSA", + self->data->type, inet_ntoa (self->data->id)); +} +#else /* ORIGINAL_CODING */ +static int +ospf_lsa_flush_schedule (struct ospf *ospf, struct ospf_lsa *lsa) +{ + if (lsa == NULL || !IS_LSA_SELF (lsa)) + return 0; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("LSA[Type%d:%s]: Schedule self-originated LSA to FLUSH", lsa->data->type, inet_ntoa (lsa->data->id)); + + /* Force given lsa's age to MaxAge. */ + lsa->data->ls_age = htons (OSPF_LSA_MAXAGE); + + switch (lsa->data->type) + { + /* 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; + default: + ospf_refresher_unregister_lsa (ospf, lsa); + ospf_lsa_flush (ospf, lsa); + break; + } + + return 0; +} + +void +ospf_flush_self_originated_lsas_now (struct ospf *ospf) +{ + struct listnode *node, *nnode; + struct listnode *node2, *nnode2; + struct ospf_area *area; + struct ospf_interface *oi; + struct ospf_lsa *lsa; + struct route_node *rn; + int need_to_flush_ase = 0; + + for (ALL_LIST_ELEMENTS (ospf->areas, node, nnode, area)) + { + if ((lsa = area->router_lsa_self) != NULL) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("LSA[Type%d:%s]: Schedule self-originated LSA to FLUSH", + lsa->data->type, inet_ntoa (lsa->data->id)); + + ospf_refresher_unregister_lsa (ospf, lsa); + ospf_lsa_flush_area (lsa, area); + ospf_lsa_unlock (&area->router_lsa_self); + area->router_lsa_self = NULL; + } + + for (ALL_LIST_ELEMENTS (area->oiflist, node2, nnode2, oi)) + { + if ((lsa = oi->network_lsa_self) != NULL + && oi->state == ISM_DR + && oi->full_nbrs > 0) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("LSA[Type%d:%s]: Schedule self-originated LSA to FLUSH", + lsa->data->type, inet_ntoa (lsa->data->id)); + + ospf_refresher_unregister_lsa (ospf, oi->network_lsa_self); + ospf_lsa_flush_area (oi->network_lsa_self, area); + ospf_lsa_unlock (&oi->network_lsa_self); + oi->network_lsa_self = NULL; + } + + if (oi->type != OSPF_IFTYPE_VIRTUALLINK + && area->external_routing == OSPF_AREA_DEFAULT) + need_to_flush_ase = 1; + } + + LSDB_LOOP (SUMMARY_LSDB (area), rn, lsa) + ospf_lsa_flush_schedule (ospf, lsa); + LSDB_LOOP (ASBR_SUMMARY_LSDB (area), rn, lsa) + ospf_lsa_flush_schedule (ospf, 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); + } + + if (need_to_flush_ase) + { + LSDB_LOOP (EXTERNAL_LSDB (ospf), rn, lsa) + ospf_lsa_flush_schedule (ospf, lsa); + LSDB_LOOP (OPAQUE_AS_LSDB (ospf), rn, lsa) + ospf_lsa_flush_schedule (ospf, lsa); + } + + /* + * Make sure that the MaxAge LSA remover is executed immediately, + * without conflicting to other threads. + */ + if (ospf->t_maxage != NULL) + { + OSPF_TIMER_OFF (ospf->t_maxage); + thread_execute (master, ospf_maxage_lsa_remover, ospf, 0); + } + + return; +} +#endif /* ORIGINAL_CODING */ + +/* If there is self-originated LSA, then return 1, otherwise return 0. */ +/* An interface-independent version of ospf_lsa_is_self_originated */ +int +ospf_lsa_is_self_originated (struct ospf *ospf, struct ospf_lsa *lsa) +{ + struct listnode *node; + struct ospf_interface *oi; + + /* This LSA is already checked. */ + if (CHECK_FLAG (lsa->flags, OSPF_LSA_SELF_CHECKED)) + return IS_LSA_SELF (lsa); + + /* Make sure LSA is self-checked. */ + SET_FLAG (lsa->flags, OSPF_LSA_SELF_CHECKED); + + /* AdvRouter and Router ID is the same. */ + if (IPV4_ADDR_SAME (&lsa->data->adv_router, &ospf->router_id)) + SET_FLAG (lsa->flags, OSPF_LSA_SELF); + + /* LSA is router-LSA. */ + else if (lsa->data->type == OSPF_ROUTER_LSA && + IPV4_ADDR_SAME (&lsa->data->id, &ospf->router_id)) + SET_FLAG (lsa->flags, OSPF_LSA_SELF); + + /* LSA is network-LSA. Compare Link ID with all interfaces. */ + else if (lsa->data->type == OSPF_NETWORK_LSA) + for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi)) + { + /* Ignore virtual link. */ + if (oi->type != OSPF_IFTYPE_VIRTUALLINK) + if (oi->address->family == AF_INET) + if (IPV4_ADDR_SAME (&lsa->data->id, &oi->address->u.prefix4)) + { + /* to make it easier later */ + SET_FLAG (lsa->flags, OSPF_LSA_SELF); + return IS_LSA_SELF (lsa); + } + } + + return IS_LSA_SELF (lsa); +} + +/* Get unique Link State ID. */ +struct in_addr +ospf_lsa_unique_id (struct ospf *ospf, + struct ospf_lsdb *lsdb, u_char type, struct prefix_ipv4 *p) +{ + struct ospf_lsa *lsa; + struct in_addr mask, id; + + id = p->prefix; + + /* Check existence of LSA instance. */ + lsa = ospf_lsdb_lookup_by_id (lsdb, type, id, ospf->router_id); + if (lsa) + { + struct as_external_lsa *al = (struct as_external_lsa *) lsa->data; + if (ip_masklen (al->mask) == p->prefixlen) + { + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + zlog_debug ("ospf_lsa_unique_id(): " + "Can't get Link State ID for %s/%d", + inet_ntoa (p->prefix), p->prefixlen); + /* id.s_addr = 0; */ + id.s_addr = 0xffffffff; + return id; + } + /* Masklen differs, then apply wildcard mask to Link State ID. */ + else + { + masklen2ip (p->prefixlen, &mask); + + id.s_addr = p->prefix.s_addr | (~mask.s_addr); + lsa = ospf_lsdb_lookup_by_id (ospf->lsdb, type, + id, ospf->router_id); + if (lsa) + { + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + zlog_debug ("ospf_lsa_unique_id(): " + "Can't get Link State ID for %s/%d", + inet_ntoa (p->prefix), p->prefixlen); + /* id.s_addr = 0; */ + id.s_addr = 0xffffffff; + return id; + } + } + } + + return id; +} + + +#define LSA_ACTION_FLOOD_AREA 1 +#define LSA_ACTION_FLUSH_AREA 2 + +struct lsa_action +{ + u_char action; + struct ospf_area *area; + struct ospf_lsa *lsa; +}; + +static int +ospf_lsa_action (struct thread *t) +{ + struct lsa_action *data; + + data = THREAD_ARG (t); + + if (IS_DEBUG_OSPF (lsa, LSA) == OSPF_DEBUG_LSA) + zlog_debug ("LSA[Action]: Performing scheduled LSA action: %d", + data->action); + + switch (data->action) + { + case LSA_ACTION_FLOOD_AREA: + ospf_flood_through_area (data->area, NULL, data->lsa); + break; + case LSA_ACTION_FLUSH_AREA: + ospf_lsa_flush_area (data->lsa, data->area); + break; + } + + ospf_lsa_unlock (&data->lsa); /* Message */ + XFREE (MTYPE_OSPF_MESSAGE, data); + return 0; +} + +void +ospf_schedule_lsa_flood_area (struct ospf_area *area, struct ospf_lsa *lsa) +{ + struct lsa_action *data; + + data = XCALLOC (MTYPE_OSPF_MESSAGE, sizeof (struct lsa_action)); + data->action = LSA_ACTION_FLOOD_AREA; + data->area = area; + data->lsa = ospf_lsa_lock (lsa); /* Message / Flood area */ + + thread_add_event (master, ospf_lsa_action, data, 0); +} + +void +ospf_schedule_lsa_flush_area (struct ospf_area *area, struct ospf_lsa *lsa) +{ + struct lsa_action *data; + + data = XCALLOC (MTYPE_OSPF_MESSAGE, sizeof (struct lsa_action)); + data->action = LSA_ACTION_FLUSH_AREA; + data->area = area; + data->lsa = ospf_lsa_lock (lsa); /* Message / Flush area */ + + 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) +{ + struct external_info *ei; + struct ospf_lsa *new = NULL; + assert (CHECK_FLAG (lsa->flags, OSPF_LSA_SELF)); + assert (IS_LSA_SELF (lsa)); + assert (lsa->lock > 0); + + switch (lsa->data->type) + { + /* Router and Network LSAs are processed differently. */ + case OSPF_ROUTER_LSA: + new = ospf_router_lsa_refresh (lsa); + break; + case OSPF_NETWORK_LSA: + new = ospf_network_lsa_refresh (lsa); + break; + case OSPF_SUMMARY_LSA: + new = ospf_summary_lsa_refresh (ospf, lsa); + break; + case OSPF_ASBR_SUMMARY_LSA: + new = ospf_summary_asbr_lsa_refresh (ospf, lsa); + break; + case OSPF_AS_EXTERNAL_LSA: + /* Translated from NSSA Type-5s are refreshed when + * from refresh of Type-7 - do not refresh these directly. + */ + if (CHECK_FLAG (lsa->flags, OSPF_LSA_LOCAL_XLT)) + break; + ei = ospf_external_info_check (lsa); + if (ei) + new = ospf_external_lsa_refresh (ospf, lsa, ei, LSA_REFRESH_FORCE); + else + ospf_lsa_flush_as (ospf, lsa); + break; + case OSPF_OPAQUE_LINK_LSA: + case OSPF_OPAQUE_AREA_LSA: + case OSPF_OPAQUE_AS_LSA: + new = ospf_opaque_lsa_refresh (lsa); + break; + default: + break; + } + return new; +} + +void +ospf_refresher_register_lsa (struct ospf *ospf, struct ospf_lsa *lsa) +{ + u_int16_t index, current_index; + + assert (lsa->lock > 0); + assert (IS_LSA_SELF (lsa)); + + if (lsa->refresh_list < 0) + { + int delay; + + if (LS_AGE (lsa) == 0 && + ntohl (lsa->data->ls_seqnum) == OSPF_INITIAL_SEQUENCE_NUMBER) + /* Randomize first update by OSPF_LS_REFRESH_SHIFT factor */ + delay = OSPF_LS_REFRESH_SHIFT + (random () % OSPF_LS_REFRESH_TIME); + else + /* Randomize another updates by +-OSPF_LS_REFRESH_JITTER factor */ + delay = OSPF_LS_REFRESH_TIME - LS_AGE (lsa) - OSPF_LS_REFRESH_JITTER + + (random () % (2*OSPF_LS_REFRESH_JITTER)); + + if (delay < 0) + delay = 0; + + current_index = ospf->lsa_refresh_queue.index + (quagga_time (NULL) + - ospf->lsa_refresher_started)/OSPF_LSA_REFRESHER_GRANULARITY; + + index = (current_index + delay/OSPF_LSA_REFRESHER_GRANULARITY) + % (OSPF_LSA_REFRESHER_SLOTS); + + if (IS_DEBUG_OSPF (lsa, LSA_REFRESH)) + zlog_debug ("LSA[Refresh]: lsa %s with age %d added to index %d", + inet_ntoa (lsa->data->id), LS_AGE (lsa), index); + if (!ospf->lsa_refresh_queue.qs[index]) + ospf->lsa_refresh_queue.qs[index] = list_new (); + listnode_add (ospf->lsa_refresh_queue.qs[index], + ospf_lsa_lock (lsa)); /* lsa_refresh_queue */ + lsa->refresh_list = index; + 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), (void *)lsa, index); + } +} + +void +ospf_refresher_unregister_lsa (struct ospf *ospf, struct ospf_lsa *lsa) +{ + assert (lsa->lock > 0); + assert (IS_LSA_SELF (lsa)); + if (lsa->refresh_list >= 0) + { + struct list *refresh_list = ospf->lsa_refresh_queue.qs[lsa->refresh_list]; + listnode_delete (refresh_list, lsa); + if (!listcount (refresh_list)) + { + list_free (refresh_list); + ospf->lsa_refresh_queue.qs[lsa->refresh_list] = NULL; + } + ospf_lsa_unlock (&lsa); /* lsa_refresh_queue */ + lsa->refresh_list = -1; + } +} + +int +ospf_lsa_refresh_walker (struct thread *t) +{ + struct list *refresh_list; + struct listnode *node, *nnode; + struct ospf *ospf = THREAD_ARG (t); + struct ospf_lsa *lsa; + int i; + struct list *lsa_to_refresh = list_new (); + + if (IS_DEBUG_OSPF (lsa, LSA_REFRESH)) + zlog_debug ("LSA[Refresh]:ospf_lsa_refresh_walker(): start"); + + + i = ospf->lsa_refresh_queue.index; + + /* Note: if clock has jumped backwards, then time change could be negative, + so we are careful to cast the expression to unsigned before taking + modulus. */ + ospf->lsa_refresh_queue.index = + ((unsigned long)(ospf->lsa_refresh_queue.index + + (quagga_time (NULL) - ospf->lsa_refresher_started) + / OSPF_LSA_REFRESHER_GRANULARITY)) + % OSPF_LSA_REFRESHER_SLOTS; + + if (IS_DEBUG_OSPF (lsa, LSA_REFRESH)) + zlog_debug ("LSA[Refresh]: ospf_lsa_refresh_walker(): next index %d", + ospf->lsa_refresh_queue.index); + + for (;i != ospf->lsa_refresh_queue.index; + i = (i + 1) % OSPF_LSA_REFRESHER_SLOTS) + { + if (IS_DEBUG_OSPF (lsa, LSA_REFRESH)) + zlog_debug ("LSA[Refresh]: ospf_lsa_refresh_walker(): " + "refresh index %d", i); + + refresh_list = ospf->lsa_refresh_queue.qs [i]; + + assert (i >= 0); + + ospf->lsa_refresh_queue.qs [i] = NULL; + + if (refresh_list) + { + for (ALL_LIST_ELEMENTS (refresh_list, node, nnode, lsa)) + { + 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), (void *)lsa, i); + + assert (lsa->lock > 0); + list_delete_node (refresh_list, node); + lsa->refresh_list = -1; + listnode_add (lsa_to_refresh, lsa); + } + list_free (refresh_list); + } + } + + ospf->t_lsa_refresher = thread_add_timer (master, ospf_lsa_refresh_walker, + ospf, ospf->lsa_refresh_interval); + ospf->lsa_refresher_started = quagga_time (NULL); + + for (ALL_LIST_ELEMENTS (lsa_to_refresh, node, nnode, lsa)) + { + ospf_lsa_refresh (ospf, lsa); + assert (lsa->lock > 0); + ospf_lsa_unlock (&lsa); /* lsa_refresh_queue & temp for lsa_to_refresh*/ + } + + list_delete (lsa_to_refresh); + + if (IS_DEBUG_OSPF (lsa, LSA_REFRESH)) + zlog_debug ("LSA[Refresh]: ospf_lsa_refresh_walker(): end"); + + return 0; +} + diff --git a/ospfd/ospf_lsa.h b/ospfd/ospf_lsa.h new file mode 100644 index 0000000..3c87962 --- /dev/null +++ b/ospfd/ospf_lsa.h @@ -0,0 +1,340 @@ +/* + * OSPF Link State Advertisement + * Copyright (C) 1999, 2000 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 _ZEBRA_OSPF_LSA_H +#define _ZEBRA_OSPF_LSA_H + +#include "stream.h" + +/* OSPF LSA Range definition. */ +#define OSPF_MIN_LSA 1 /* begin range here */ +#define OSPF_MAX_LSA 12 + +/* OSPF LSA Type definition. */ +#define OSPF_UNKNOWN_LSA 0 +#define OSPF_ROUTER_LSA 1 +#define OSPF_NETWORK_LSA 2 +#define OSPF_SUMMARY_LSA 3 +#define OSPF_ASBR_SUMMARY_LSA 4 +#define OSPF_AS_EXTERNAL_LSA 5 +#define OSPF_GROUP_MEMBER_LSA 6 /* Not supported. */ +#define OSPF_AS_NSSA_LSA 7 +#define OSPF_EXTERNAL_ATTRIBUTES_LSA 8 /* Not supported. */ +#define OSPF_OPAQUE_LINK_LSA 9 +#define OSPF_OPAQUE_AREA_LSA 10 +#define OSPF_OPAQUE_AS_LSA 11 + +#define OSPF_LSA_HEADER_SIZE 20U +#define OSPF_ROUTER_LSA_LINK_SIZE 12U +#define OSPF_ROUTER_LSA_TOS_SIZE 4U +#define OSPF_MAX_LSA_SIZE 1500U + +/* AS-external-LSA refresh method. */ +#define LSA_REFRESH_IF_CHANGED 0 +#define LSA_REFRESH_FORCE 1 + +/* OSPF LSA header. */ +struct lsa_header +{ + u_int16_t ls_age; + u_char options; + u_char type; + struct in_addr id; + struct in_addr adv_router; + u_int32_t ls_seqnum; + u_int16_t checksum; + u_int16_t length; +}; + +/* OSPF LSA. */ +struct ospf_lsa +{ + /* LSA origination flag. */ + u_char flags; +#define OSPF_LSA_SELF 0x01 +#define OSPF_LSA_SELF_CHECKED 0x02 +#define OSPF_LSA_RECEIVED 0x04 +#define OSPF_LSA_APPROVED 0x08 +#define OSPF_LSA_DISCARD 0x10 +#define OSPF_LSA_LOCAL_XLT 0x20 +#define OSPF_LSA_PREMATURE_AGE 0x40 +#define OSPF_LSA_IN_MAXAGE 0x80 + + /* LSA data. */ + struct lsa_header *data; + + /* Received time stamp. */ + struct timeval tv_recv; + + /* Last time it was originated */ + struct timeval tv_orig; + + /* All of reference count, also lock to remove. */ + int lock; + + /* Flags for the SPF calculation. */ + int stat; + #define LSA_SPF_NOT_EXPLORED -1 + #define LSA_SPF_IN_SPFTREE -2 + /* If stat >= 0, stat is LSA position in candidates heap. */ + + /* References to this LSA in neighbor retransmission lists*/ + int retransmit_counter; + + /* Area the LSA belongs to, may be NULL if AS-external-LSA. */ + struct ospf_area *area; + + /* Parent LSDB. */ + struct ospf_lsdb *lsdb; + + /* Related Route. */ + void *route; + + /* Refreshement List or Queue */ + int refresh_list; + + /* For Type-9 Opaque-LSAs */ + struct ospf_interface *oi; +}; + +/* OSPF LSA Link Type. */ +#define LSA_LINK_TYPE_POINTOPOINT 1 +#define LSA_LINK_TYPE_TRANSIT 2 +#define LSA_LINK_TYPE_STUB 3 +#define LSA_LINK_TYPE_VIRTUALLINK 4 + +/* OSPF Router LSA Flag. */ +#define ROUTER_LSA_BORDER 0x01 /* The router is an ABR */ +#define ROUTER_LSA_EXTERNAL 0x02 /* The router is an ASBR */ +#define ROUTER_LSA_VIRTUAL 0x04 /* The router has a VL in this area */ +#define ROUTER_LSA_NT 0x10 /* The routers always translates Type-7 */ +#define ROUTER_LSA_SHORTCUT 0x20 /* Shortcut-ABR specific flag */ + +#define IS_ROUTER_LSA_VIRTUAL(x) ((x)->flags & ROUTER_LSA_VIRTUAL) +#define IS_ROUTER_LSA_EXTERNAL(x) ((x)->flags & ROUTER_LSA_EXTERNAL) +#define IS_ROUTER_LSA_BORDER(x) ((x)->flags & ROUTER_LSA_BORDER) +#define IS_ROUTER_LSA_SHORTCUT(x) ((x)->flags & ROUTER_LSA_SHORTCUT) +#define IS_ROUTER_LSA_NT(x) ((x)->flags & ROUTER_LSA_NT) + +/* OSPF Router-LSA Link information. */ +struct router_lsa_link +{ + struct in_addr link_id; + struct in_addr link_data; + struct + { + u_char type; + u_char tos_count; + u_int16_t metric; + } m[1]; +}; + +/* OSPF Router-LSAs structure. */ +#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; + u_char flags; + u_char zero; + u_int16_t links; + struct + { + struct in_addr link_id; + struct in_addr link_data; + u_char type; + u_char tos; + u_int16_t metric; + } link[1]; +}; + +/* OSPF Network-LSAs structure. */ +#define OSPF_NETWORK_LSA_MIN_SIZE 8U /* w/1 router-ID */ +struct network_lsa +{ + struct lsa_header header; + struct in_addr mask; + struct in_addr routers[1]; +}; + +/* OSPF Summary-LSAs structure. */ +#define OSPF_SUMMARY_LSA_MIN_SIZE 8U /* w/1 TOS metric block */ +struct summary_lsa +{ + struct lsa_header header; + struct in_addr mask; + u_char tos; + u_char metric[3]; +}; + +/* OSPF AS-external-LSAs structure. */ +#define OSPF_AS_EXTERNAL_LSA_MIN_SIZE 16U /* w/1 TOS forwarding block */ +struct as_external_lsa +{ + struct lsa_header header; + struct in_addr mask; + struct + { + u_char tos; + u_char metric[3]; + struct in_addr fwd_addr; + u_int32_t route_tag; + } e[1]; +}; + +#include "ospfd/ospf_opaque.h" + +/* Macros. */ +#define GET_METRIC(x) get_metric(x) +#define IS_EXTERNAL_METRIC(x) ((x) & 0x80) + +#define GET_AGE(x) (ntohs ((x)->data->ls_age) + time (NULL) - (x)->tv_recv) +#define LS_AGE(x) (OSPF_LSA_MAXAGE < get_age(x) ? \ + OSPF_LSA_MAXAGE : get_age(x)) +#define IS_LSA_SELF(L) (CHECK_FLAG ((L)->flags, OSPF_LSA_SELF)) +#define IS_LSA_MAXAGE(L) (LS_AGE ((L)) == OSPF_LSA_MAXAGE) + +#define OSPF_LSA_UPDATE_DELAY 2 + +#define OSPF_LSA_UPDATE_TIMER_ON(T,F) \ + if (!(T)) \ + (T) = thread_add_timer (master, (F), 0, 2) + +/* Prototypes. */ +/* XXX: Eek, time functions, similar are in lib/thread.c */ +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 *); +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); +extern struct ospf_lsa *ospf_lsa_dup (struct ospf_lsa *); +extern void ospf_lsa_free (struct ospf_lsa *); +extern struct ospf_lsa *ospf_lsa_lock (struct ospf_lsa *); +extern void ospf_lsa_unlock (struct ospf_lsa **); +extern void ospf_lsa_discard (struct ospf_lsa *); + +extern struct lsa_header *ospf_lsa_data_new (size_t); +extern struct lsa_header *ospf_lsa_data_dup (struct lsa_header *); +extern void ospf_lsa_data_free (struct lsa_header *); + +/* Prototype for various LSAs */ +extern int ospf_router_lsa_update (struct ospf *); +extern int ospf_router_lsa_update_area (struct ospf_area *); + +extern void ospf_network_lsa_update (struct ospf_interface *); + +extern struct ospf_lsa *ospf_summary_lsa_originate (struct prefix_ipv4 *, u_int32_t, + struct ospf_area *); +extern struct ospf_lsa *ospf_summary_asbr_lsa_originate (struct prefix_ipv4 *, + u_int32_t, + struct ospf_area *); + +extern struct ospf_lsa *ospf_lsa_install (struct ospf *, + struct ospf_interface *, struct ospf_lsa *); + +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 *, + ifindex_t /* , struct in_addr nexthop */); + +extern struct in_addr ospf_get_ip_from_ifp (struct ospf_interface *); + +extern struct ospf_lsa *ospf_external_lsa_originate (struct ospf *, struct external_info *); +extern int ospf_external_lsa_originate_timer (struct thread *); +extern int ospf_default_originate_timer (struct thread *); +extern struct ospf_lsa *ospf_lsa_lookup (struct ospf_area *, u_int32_t, + struct in_addr, struct in_addr); +extern struct ospf_lsa *ospf_lsa_lookup_by_id (struct ospf_area *, + u_int32_t, + struct in_addr); +extern struct ospf_lsa *ospf_lsa_lookup_by_header (struct ospf_area *, + struct lsa_header *); +extern int ospf_lsa_more_recent (struct ospf_lsa *, struct ospf_lsa *); +extern int ospf_lsa_different (struct ospf_lsa *, struct ospf_lsa *); +extern void ospf_flush_self_originated_lsas_now (struct ospf *); + +extern int ospf_lsa_is_self_originated (struct ospf *, struct ospf_lsa *); + +extern struct ospf_lsa *ospf_lsa_lookup_by_prefix (struct ospf_lsdb *, u_char, + struct prefix_ipv4 *, + struct in_addr); + +extern void ospf_lsa_maxage (struct ospf *, struct ospf_lsa *); +extern u_int32_t get_metric (u_char *); + +extern int ospf_lsa_maxage_walker (struct thread *); +extern struct ospf_lsa *ospf_lsa_refresh (struct ospf *, struct ospf_lsa *); + +extern void ospf_external_lsa_refresh_default (struct ospf *); + +extern void ospf_external_lsa_refresh_type (struct ospf *, u_char, int); +extern struct ospf_lsa *ospf_external_lsa_refresh (struct ospf *, + struct ospf_lsa *, + struct external_info *, + int); +extern struct in_addr ospf_lsa_unique_id (struct ospf *, struct ospf_lsdb *, u_char, + struct prefix_ipv4 *); +extern void ospf_schedule_lsa_flood_area (struct ospf_area *, struct ospf_lsa *); +extern void ospf_schedule_lsa_flush_area (struct ospf_area *, struct ospf_lsa *); + +extern void ospf_refresher_register_lsa (struct ospf *, struct ospf_lsa *); +extern void ospf_refresher_unregister_lsa (struct ospf *, struct ospf_lsa *); +extern int ospf_lsa_refresh_walker (struct thread *); + +extern void ospf_lsa_maxage_delete (struct ospf *, struct ospf_lsa *); + +extern void ospf_discard_from_db (struct ospf *, struct ospf_lsdb *, struct ospf_lsa*); +extern int is_prefix_default (struct prefix_ipv4 *); + +extern int metric_type (struct ospf *, u_char); +extern int metric_value (struct ospf *, u_char); + +extern struct in_addr ospf_get_nssa_ip (struct ospf_area *); +extern int ospf_translated_nssa_compare (struct ospf_lsa *, struct ospf_lsa *); +extern struct ospf_lsa *ospf_translated_nssa_refresh (struct ospf *, struct ospf_lsa *, + struct ospf_lsa *); +extern struct ospf_lsa *ospf_translated_nssa_originate (struct ospf *, struct ospf_lsa *); + +#endif /* _ZEBRA_OSPF_LSA_H */ diff --git a/ospfd/ospf_lsdb.c b/ospfd/ospf_lsdb.c new file mode 100644 index 0000000..b92e749 --- /dev/null +++ b/ospfd/ospf_lsdb.c @@ -0,0 +1,330 @@ +/* + * OSPF LSDB support. + * Copyright (C) 1999, 2000 Alex Zinin, 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. + */ + +#include + +#include "prefix.h" +#include "table.h" +#include "memory.h" +#include "log.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" + +struct ospf_lsdb * +ospf_lsdb_new () +{ + struct ospf_lsdb *new; + + new = XCALLOC (MTYPE_OSPF_LSDB, sizeof (struct ospf_lsdb)); + ospf_lsdb_init (new); + + return new; +} + +void +ospf_lsdb_init (struct ospf_lsdb *lsdb) +{ + int i; + + for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++) + lsdb->type[i].db = route_table_init (); +} + +void +ospf_lsdb_free (struct ospf_lsdb *lsdb) +{ + ospf_lsdb_cleanup (lsdb); + XFREE (MTYPE_OSPF_LSDB, lsdb); +} + +void +ospf_lsdb_cleanup (struct ospf_lsdb *lsdb) +{ + int i; + assert (lsdb); + assert (lsdb->total == 0); + + ospf_lsdb_delete_all (lsdb); + + for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++) + route_table_finish (lsdb->type[i].db); +} + +void +ls_prefix_set (struct prefix_ls *lp, struct ospf_lsa *lsa) +{ + 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 +ospf_lsdb_delete_entry (struct ospf_lsdb *lsdb, struct route_node *rn) +{ + struct ospf_lsa *lsa = rn->info; + + if (!lsa) + return; + + assert (rn->table == lsdb->type[lsa->data->type].db); + + if (IS_LSA_SELF (lsa)) + lsdb->type[lsa->data->type].count_self--; + lsdb->type[lsa->data->type].count--; + lsdb->type[lsa->data->type].checksum -= ntohs(lsa->data->checksum); + lsdb->total--; + rn->info = NULL; + route_unlock_node (rn); +#ifdef MONITOR_LSDB_CHANGE + if (lsdb->del_lsa_hook != NULL) + (* lsdb->del_lsa_hook)(lsa); +#endif /* MONITOR_LSDB_CHANGE */ + ospf_lsa_unlock (&lsa); /* lsdb */ + return; +} + +/* Add new LSA to lsdb. */ +void +ospf_lsdb_add (struct ospf_lsdb *lsdb, struct ospf_lsa *lsa) +{ + struct route_table *table; + struct prefix_ls lp; + struct route_node *rn; + + table = lsdb->type[lsa->data->type].db; + ls_prefix_set (&lp, lsa); + rn = route_node_get (table, (struct prefix *)&lp); + + /* nothing to do? */ + if (rn->info && rn->info == lsa) + { + route_unlock_node (rn); + return; + } + + /* purge old entry? */ + if (rn->info) + ospf_lsdb_delete_entry (lsdb, rn); + + if (IS_LSA_SELF (lsa)) + lsdb->type[lsa->data->type].count_self++; + lsdb->type[lsa->data->type].count++; + lsdb->total++; + +#ifdef MONITOR_LSDB_CHANGE + if (lsdb->new_lsa_hook != NULL) + (* lsdb->new_lsa_hook)(lsa); +#endif /* MONITOR_LSDB_CHANGE */ + lsdb->type[lsa->data->type].checksum += ntohs(lsa->data->checksum); + rn->info = ospf_lsa_lock (lsa); /* lsdb */ +} + +void +ospf_lsdb_delete (struct ospf_lsdb *lsdb, struct ospf_lsa *lsa) +{ + struct route_table *table; + struct prefix_ls lp; + struct route_node *rn; + + if (!lsdb) + { + zlog_warn ("%s: Called with NULL LSDB", __func__); + if (lsa) + zlog_warn ("LSA[Type%d:%s]: LSA %p, lsa->lsdb %p", + lsa->data->type, inet_ntoa (lsa->data->id), + (void *)lsa, (void *)lsa->lsdb); + return; + } + + if (!lsa) + { + zlog_warn ("%s: Called with NULL LSA", __func__); + return; + } + + assert (lsa->data->type < OSPF_MAX_LSA); + table = lsdb->type[lsa->data->type].db; + ls_prefix_set (&lp, lsa); + if ((rn = route_node_lookup (table, (struct prefix *) &lp))) + { + if (rn->info == lsa) + ospf_lsdb_delete_entry (lsdb, rn); + route_unlock_node (rn); /* route_node_lookup */ + } +} + +void +ospf_lsdb_delete_all (struct ospf_lsdb *lsdb) +{ + struct route_table *table; + struct route_node *rn; + int i; + + for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++) + { + table = lsdb->type[i].db; + for (rn = route_top (table); rn; rn = route_next (rn)) + if (rn->info != NULL) + ospf_lsdb_delete_entry (lsdb, rn); + } +} + +void +ospf_lsdb_clean_stat (struct ospf_lsdb *lsdb) +{ + struct route_table *table; + struct route_node *rn; + struct ospf_lsa *lsa; + int i; + + for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++) + { + table = lsdb->type[i].db; + for (rn = route_top (table); rn; rn = route_next (rn)) + if ((lsa = (rn->info)) != NULL) + lsa->stat = LSA_SPF_NOT_EXPLORED; + } +} + +struct ospf_lsa * +ospf_lsdb_lookup (struct ospf_lsdb *lsdb, struct ospf_lsa *lsa) +{ + struct route_table *table; + struct prefix_ls lp; + struct route_node *rn; + struct ospf_lsa *find; + + table = lsdb->type[lsa->data->type].db; + ls_prefix_set (&lp, lsa); + rn = route_node_lookup (table, (struct prefix *) &lp); + if (rn) + { + find = rn->info; + route_unlock_node (rn); + return find; + } + return NULL; +} + +struct ospf_lsa * +ospf_lsdb_lookup_by_id (struct ospf_lsdb *lsdb, u_char type, + struct in_addr id, struct in_addr adv_router) +{ + struct route_table *table; + struct prefix_ls lp; + struct route_node *rn; + struct ospf_lsa *find; + + table = lsdb->type[type].db; + + memset (&lp, 0, sizeof (struct prefix_ls)); + lp.family = 0; + lp.prefixlen = 64; + lp.id = id; + lp.adv_router = adv_router; + + rn = route_node_lookup (table, (struct prefix *) &lp); + if (rn) + { + find = rn->info; + route_unlock_node (rn); + return find; + } + return NULL; +} + +struct ospf_lsa * +ospf_lsdb_lookup_by_id_next (struct ospf_lsdb *lsdb, u_char type, + struct in_addr id, struct in_addr adv_router, + int first) +{ + struct route_table *table; + struct prefix_ls lp; + struct route_node *rn; + struct ospf_lsa *find; + + table = lsdb->type[type].db; + + memset (&lp, 0, sizeof (struct prefix_ls)); + lp.family = 0; + lp.prefixlen = 64; + lp.id = id; + lp.adv_router = adv_router; + + if (first) + rn = route_top (table); + else + { + if ((rn = route_node_lookup (table, (struct prefix *) &lp)) == NULL) + return NULL; + rn = route_next (rn); + } + + for (; rn; rn = route_next (rn)) + if (rn->info) + break; + + if (rn && rn->info) + { + find = rn->info; + route_unlock_node (rn); + return find; + } + return NULL; +} + +unsigned long +ospf_lsdb_count_all (struct ospf_lsdb *lsdb) +{ + return lsdb->total; +} + +unsigned long +ospf_lsdb_count (struct ospf_lsdb *lsdb, int type) +{ + return lsdb->type[type].count; +} + +unsigned long +ospf_lsdb_count_self (struct ospf_lsdb *lsdb, int type) +{ + return lsdb->type[type].count_self; +} + +unsigned int +ospf_lsdb_checksum (struct ospf_lsdb *lsdb, int type) +{ + return lsdb->type[type].checksum; +} + +unsigned long +ospf_lsdb_isempty (struct ospf_lsdb *lsdb) +{ + return (lsdb->total == 0); +} diff --git a/ospfd/ospf_lsdb.h b/ospfd/ospf_lsdb.h new file mode 100644 index 0000000..51ae45b --- /dev/null +++ b/ospfd/ospf_lsdb.h @@ -0,0 +1,87 @@ +/* + * OSPF LSDB support. + * Copyright (C) 1999, 2000 Alex Zinin, 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 _ZEBRA_OSPF_LSDB_H +#define _ZEBRA_OSPF_LSDB_H + +/* OSPF LSDB structure. */ +struct ospf_lsdb +{ + struct + { + unsigned long count; + unsigned long count_self; + unsigned int checksum; + struct route_table *db; + } type[OSPF_MAX_LSA]; + unsigned long total; +#define MONITOR_LSDB_CHANGE 1 /* XXX */ +#ifdef MONITOR_LSDB_CHANGE + /* Hooks for callback functions to catch every add/del event. */ + int (* new_lsa_hook)(struct ospf_lsa *); + int (* del_lsa_hook)(struct ospf_lsa *); +#endif /* MONITOR_LSDB_CHANGE */ +}; + +/* Macros. */ +#define LSDB_LOOP(T,N,L) \ + if ((T) != NULL) \ + for ((N) = route_top ((T)); ((N)); ((N)) = route_next ((N))) \ + if (((L) = (N)->info)) + +#define ROUTER_LSDB(A) ((A)->lsdb->type[OSPF_ROUTER_LSA].db) +#define NETWORK_LSDB(A) ((A)->lsdb->type[OSPF_NETWORK_LSA].db) +#define SUMMARY_LSDB(A) ((A)->lsdb->type[OSPF_SUMMARY_LSA].db) +#define ASBR_SUMMARY_LSDB(A) ((A)->lsdb->type[OSPF_ASBR_SUMMARY_LSA].db) +#define EXTERNAL_LSDB(O) ((O)->lsdb->type[OSPF_AS_EXTERNAL_LSA].db) +#define NSSA_LSDB(A) ((A)->lsdb->type[OSPF_AS_NSSA_LSA].db) +#define OPAQUE_LINK_LSDB(A) ((A)->lsdb->type[OSPF_OPAQUE_LINK_LSA].db) +#define OPAQUE_AREA_LSDB(A) ((A)->lsdb->type[OSPF_OPAQUE_AREA_LSA].db) +#define OPAQUE_AS_LSDB(O) ((O)->lsdb->type[OSPF_OPAQUE_AS_LSA].db) + +#define AREA_LSDB(A,T) ((A)->lsdb->type[(T)].db) +#define AS_LSDB(O,T) ((O)->lsdb->type[(T)].db) + +/* OSPF LSDB related functions. */ +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 *); +/* Set all stats to -1 (LSA_SPF_NOT_EXPLORED). */ +extern void ospf_lsdb_clean_stat (struct ospf_lsdb *lsdb); +extern struct ospf_lsa *ospf_lsdb_lookup (struct ospf_lsdb *, struct ospf_lsa *); +extern struct ospf_lsa *ospf_lsdb_lookup_by_id (struct ospf_lsdb *, u_char, + struct in_addr, struct in_addr); +extern struct ospf_lsa *ospf_lsdb_lookup_by_id_next (struct ospf_lsdb *, u_char, + struct in_addr, struct in_addr, + int); +extern unsigned long ospf_lsdb_count_all (struct ospf_lsdb *); +extern unsigned long ospf_lsdb_count (struct ospf_lsdb *, int); +extern unsigned long ospf_lsdb_count_self (struct ospf_lsdb *, int); +extern unsigned int ospf_lsdb_checksum (struct ospf_lsdb *, int); +extern unsigned long ospf_lsdb_isempty (struct ospf_lsdb *); + +#endif /* _ZEBRA_OSPF_LSDB_H */ diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c new file mode 100644 index 0000000..32b64e2 --- /dev/null +++ b/ospfd/ospf_main.c @@ -0,0 +1,344 @@ +/* + * OSPFd main routine. + * Copyright (C) 1998, 99 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. + */ + +#include + +#include +#include "getopt.h" +#include "thread.h" +#include "prefix.h" +#include "linklist.h" +#include "if.h" +#include "vector.h" +#include "vty.h" +#include "command.h" +#include "filter.h" +#include "plist.h" +#include "stream.h" +#include "log.h" +#include "memory.h" +#include "privs.h" +#include "sigevent.h" +#include "zclient.h" +#include "vrf.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_zebra.h" +#include "ospfd/ospf_vty.h" + +/* ospfd privileges */ +zebra_capabilities_t _caps_p [] = +{ + ZCAP_NET_RAW, + ZCAP_BIND, + ZCAP_NET_ADMIN, +}; + +struct zebra_privs_t ospfd_privs = +{ +#if defined(QUAGGA_USER) && defined(QUAGGA_GROUP) + .user = QUAGGA_USER, + .group = QUAGGA_GROUP, +#endif +#if defined(VTY_GROUP) + .vty_group = VTY_GROUP, +#endif + .caps_p = _caps_p, + .cap_num_p = array_size(_caps_p), + .cap_num_i = 0 +}; + +/* Configuration filename and directory. */ +char config_default[] = SYSCONFDIR OSPF_DEFAULT_CONFIG; + +/* OSPFd 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'}, + { "dryrun", no_argument, NULL, 'C'}, + { "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'}, + { "apiserver", no_argument, NULL, 'a'}, + { "version", no_argument, NULL, 'v'}, + { 0 } +}; + +/* OSPFd program name */ + +/* Master of threads. */ +struct thread_master *master; + +/* Process ID saved for use by init system */ +const char *pid_file = PATH_OSPFD_PID; + +#ifdef SUPPORT_OSPF_API +extern int ospf_apiserver_enable; +#endif /* SUPPORT_OSPF_API */ + +/* Help information display. */ +static void __attribute__ ((noreturn)) +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 OSPF.\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\ +-a. --apiserver Enable OSPF apiserver\n\ +-v, --version Print program version\n\ +-C, --dryrun Check configuration for validity and exit\n\ +-h, --help Display this help and exit\n\ +\n\ +Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); + } + exit (status); +} + +/* SIGHUP handler. */ +static void +sighup (void) +{ + zlog (NULL, LOG_INFO, "SIGHUP received"); +} + +/* SIGINT / SIGTERM handler. */ +static void +sigint (void) +{ + zlog_notice ("Terminating on signal"); + ospf_terminate (); +} + +/* SIGUSR1 handler. */ +static void +sigusr1 (void) +{ + zlog_rotate (NULL); +} + +struct quagga_signal_t ospf_signals[] = +{ + { + .signal = SIGHUP, + .handler = &sighup, + }, + { + .signal = SIGUSR1, + .handler = &sigusr1, + }, + { + .signal = SIGINT, + .handler = &sigint, + }, + { + .signal = SIGTERM, + .handler = &sigint, + }, +}; + +/* OSPFd main routine. */ +int +main (int argc, char **argv) +{ + char *p; + char *vty_addr = NULL; + int vty_port = OSPF_VTY_PORT; + int daemon_mode = 0; + char *config_file = NULL; + char *progname; + struct thread thread; + int dryrun = 0; + + /* 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]); + + while (1) + { + int opt; + + opt = getopt_long (argc, argv, "df:i:z:hA:P:u:g:avC", longopts, 0); + + if (opt == EOF) + break; + + switch (opt) + { + case 0: + break; + case 'd': + daemon_mode = 1; + break; + case 'f': + config_file = optarg; + break; + case 'A': + vty_addr = optarg; + break; + case 'i': + pid_file = optarg; + break; + case 'z': + zclient_serv_path_set (optarg); + break; + case 'P': + /* Deal with atoi() returning 0 on failure, and ospfd not + listening on ospfd port... */ + if (strcmp(optarg, "0") == 0) + { + vty_port = 0; + break; + } + vty_port = atoi (optarg); + if (vty_port <= 0 || vty_port > 0xffff) + vty_port = OSPF_VTY_PORT; + break; + case 'u': + ospfd_privs.user = optarg; + break; + case 'g': + ospfd_privs.group = optarg; + break; +#ifdef SUPPORT_OSPF_API + case 'a': + ospf_apiserver_enable = 1; + break; +#endif /* SUPPORT_OSPF_API */ + case 'v': + print_version (progname); + exit (0); + break; + case 'C': + dryrun = 1; + break; + case 'h': + usage (progname, 0); + break; + default: + usage (progname, 1); + break; + } + } + + /* Invoked by a priviledged user? -- endo. */ + if (geteuid () != 0) + { + errno = EPERM; + perror (progname); + exit (1); + } + + zlog_default = openzlog (progname, ZLOG_OSPF, + LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON); + + /* OSPF master init. */ + ospf_master_init (); + + /* Initializations. */ + master = om->master; + + /* Library inits. */ + zprivs_init (&ospfd_privs); + 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 (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 */ + ospf_opaque_init (); + + /* Get configuration file. */ + vty_read_config (config_file, config_default); + + /* Start execution only if not in dry-run mode */ + if (dryrun) + return(0); + + /* Change to the daemon program. */ + if (daemon_mode && daemon (0, 0) < 0) + { + zlog_err("OSPFd daemon failed: %s", strerror(errno)); + exit (1); + } + + /* Process id file create. */ + pid_output (pid_file); + + /* Create VTY socket */ + vty_serv_sock (vty_addr, vty_port, OSPF_VTYSH_PATH); + + /* Print banner. */ + zlog_notice ("OSPFd %s starting: vty@%d", QUAGGA_VERSION, vty_port); + + /* Fetch next active thread. */ + while (thread_fetch (master, &thread)) + thread_call (&thread); + + /* Not reached. */ + return (0); +} + diff --git a/ospfd/ospf_neighbor.c b/ospfd/ospf_neighbor.c new file mode 100644 index 0000000..06e63dd --- /dev/null +++ b/ospfd/ospf_neighbor.c @@ -0,0 +1,504 @@ +/* + * OSPF Neighbor functions. + * Copyright (C) 1999, 2000 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. + */ + +#include + +#include "linklist.h" +#include "prefix.h" +#include "memory.h" +#include "command.h" +#include "thread.h" +#include "stream.h" +#include "table.h" +#include "log.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.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_packet.h" +#include "ospfd/ospf_network.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_dump.h" + +/* 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 and PointToPoint interfaces, where + * neighbours are indexed by router-ID instead. + */ +static void +ospf_nbr_key (struct ospf_interface *oi, struct ospf_neighbor *nbr, + struct prefix *key) +{ + key->family = AF_INET; + key->prefixlen = IPV4_MAX_BITLEN; + + /* vlinks are indexed by router-id */ + if (oi->type == OSPF_IFTYPE_VIRTUALLINK || + oi->type == OSPF_IFTYPE_POINTOPOINT) + key->u.prefix4 = nbr->router_id; + else + key->u.prefix4 = nbr->src; + return; +} + +struct ospf_neighbor * +ospf_nbr_new (struct ospf_interface *oi) +{ + struct ospf_neighbor *nbr; + + /* Allcate new neighbor. */ + nbr = XCALLOC (MTYPE_OSPF_NEIGHBOR, sizeof (struct ospf_neighbor)); + + /* Relate neighbor to the interface. */ + nbr->oi = oi; + + /* Set default values. */ + nbr->state = NSM_Down; + + /* Set inheritance values. */ + nbr->v_inactivity = OSPF_IF_PARAM (oi, v_wait); + nbr->v_db_desc = OSPF_IF_PARAM (oi, retransmit_interval); + nbr->v_ls_req = OSPF_IF_PARAM (oi, retransmit_interval); + nbr->v_ls_upd = OSPF_IF_PARAM (oi, retransmit_interval); + nbr->priority = -1; + + /* DD flags. */ + nbr->dd_flags = OSPF_DD_FLAG_MS|OSPF_DD_FLAG_M|OSPF_DD_FLAG_I; + + /* Last received and sent DD. */ + nbr->last_send = NULL; + + nbr->nbr_nbma = NULL; + + ospf_lsdb_init (&nbr->db_sum); + ospf_lsdb_init (&nbr->ls_rxmt); + ospf_lsdb_init (&nbr->ls_req); + + nbr->crypt_seqnum = 0; + + return nbr; +} + +void +ospf_nbr_free (struct ospf_neighbor *nbr) +{ + /* Free DB summary list. */ + if (ospf_db_summary_count (nbr)) + ospf_db_summary_clear (nbr); + /* ospf_db_summary_delete_all (nbr); */ + + /* Free ls request list. */ + if (ospf_ls_request_count (nbr)) + ospf_ls_request_delete_all (nbr); + + /* Free retransmit list. */ + if (ospf_ls_retransmit_count (nbr)) + ospf_ls_retransmit_clear (nbr); + + /* Cleanup LSDBs. */ + ospf_lsdb_cleanup (&nbr->db_sum); + ospf_lsdb_cleanup (&nbr->ls_req); + ospf_lsdb_cleanup (&nbr->ls_rxmt); + + /* Clear last send packet. */ + if (nbr->last_send) + ospf_packet_free (nbr->last_send); + + if (nbr->nbr_nbma) + { + nbr->nbr_nbma->nbr = NULL; + nbr->nbr_nbma = NULL; + } + + /* Cancel all timers. */ + OSPF_NSM_TIMER_OFF (nbr->t_inactivity); + OSPF_NSM_TIMER_OFF (nbr->t_db_desc); + OSPF_NSM_TIMER_OFF (nbr->t_ls_req); + OSPF_NSM_TIMER_OFF (nbr->t_ls_upd); + + /* Cancel all events. *//* Thread lookup cost would be negligible. */ + thread_cancel_event (master, nbr); + + XFREE (MTYPE_OSPF_NEIGHBOR, nbr); +} + +/* Delete specified OSPF neighbor from interface. */ +void +ospf_nbr_delete (struct ospf_neighbor *nbr) +{ + struct ospf_interface *oi; + struct route_node *rn; + struct prefix p; + + oi = nbr->oi; + + /* get appropriate prefix 'key' */ + ospf_nbr_key (oi, nbr, &p); + + rn = route_node_lookup (oi->nbrs, &p); + if (rn) + { + /* If lookup for a NBR succeeds, the leaf route_node could + * only exist because there is (or was) a nbr there. + * If the nbr was deleted, the leaf route_node should have + * lost its last refcount too, and be deleted. + * Therefore a looked-up leaf route_node in nbrs table + * should never have NULL info. + */ + assert (rn->info); + + if (rn->info) + { + rn->info = NULL; + route_unlock_node (rn); + } + else + zlog_info ("Can't find neighbor %s in the interface %s", + inet_ntoa (nbr->src), IF_NAME (oi)); + + 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); +} + +/* Check myself is in the neighbor list. */ +int +ospf_nbr_bidirectional (struct in_addr *router_id, + struct in_addr *neighbors, int size) +{ + int i; + int max; + + max = size / sizeof (struct in_addr); + + for (i = 0; i < max; i ++) + if (IPV4_ADDR_SAME (router_id, &neighbors[i])) + return 1; + + 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) +{ + struct prefix p; + struct route_node *rn; + + /* Initial state */ + oi->nbr_self->address = *oi->address; + oi->nbr_self->priority = OSPF_IF_PARAM (oi, priority); + oi->nbr_self->router_id = oi->ospf->router_id; + oi->nbr_self->src = oi->address->u.prefix4; + oi->nbr_self->state = NSM_TwoWay; + + switch (oi->area->external_routing) + { + case OSPF_AREA_DEFAULT: + SET_FLAG (oi->nbr_self->options, OSPF_OPTION_E); + break; + case OSPF_AREA_STUB: + UNSET_FLAG (oi->nbr_self->options, OSPF_OPTION_E); + break; + case OSPF_AREA_NSSA: + UNSET_FLAG (oi->nbr_self->options, OSPF_OPTION_E); + SET_FLAG (oi->nbr_self->options, OSPF_OPTION_NP); + break; + } + + /* Add nbr_self to nbrs table */ + ospf_nbr_key (oi, oi->nbr_self, &p); + + rn = route_node_get (oi->nbrs, &p); + if (rn->info) + { + /* There is already pseudo neighbor. */ + assert (oi->nbr_self == rn->info); + route_unlock_node (rn); + } + else + rn->info = oi->nbr_self; +} + +/* Get neighbor count by status. + Specify status = 0, get all neighbor other than myself. */ +int +ospf_nbr_count (struct ospf_interface *oi, int state) +{ + struct ospf_neighbor *nbr; + struct route_node *rn; + int count = 0; + + for (rn = route_top (oi->nbrs); rn; rn = route_next (rn)) + if ((nbr = rn->info)) + if (!IPV4_ADDR_SAME (&nbr->router_id, &oi->ospf->router_id)) + if (state == 0 || nbr->state == state) + count++; + + return count; +} + +int +ospf_nbr_count_opaque_capable (struct ospf_interface *oi) +{ + struct ospf_neighbor *nbr; + struct route_node *rn; + int count = 0; + + for (rn = route_top (oi->nbrs); rn; rn = route_next (rn)) + if ((nbr = rn->info)) + if (!IPV4_ADDR_SAME (&nbr->router_id, &oi->ospf->router_id)) + if (nbr->state == NSM_Full) + if (CHECK_FLAG (nbr->options, OSPF_OPTION_O)) + count++; + + return count; +} + +/* lookup nbr by address - use this only if you know you must + * 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, + struct in_addr *addr) +{ + struct prefix p; + struct route_node *rn; + struct ospf_neighbor *nbr; + + p.family = AF_INET; + p.prefixlen = IPV4_MAX_BITLEN; + p.u.prefix4 = *addr; + + rn = route_node_lookup (nbrs, &p); + if (! rn) + return NULL; + + /* See comment in ospf_nbr_delete */ + assert (rn->info); + + if (rn->info == NULL) + { + route_unlock_node (rn); + return NULL; + } + + nbr = (struct ospf_neighbor *) rn->info; + route_unlock_node (rn); + + return nbr; +} + +struct ospf_neighbor * +ospf_nbr_lookup_by_routerid (struct route_table *nbrs, + struct in_addr *id) +{ + struct route_node *rn; + struct ospf_neighbor *nbr; + + for (rn = route_top (nbrs); rn; rn = route_next (rn)) + if ((nbr = rn->info) != NULL) + if (IPV4_ADDR_SAME (&nbr->router_id, id)) + { + route_unlock_node(rn); + return nbr; + } + + return NULL; +} + +void +ospf_renegotiate_optional_capabilities (struct ospf *top) +{ + struct listnode *node; + struct ospf_interface *oi; + struct route_table *nbrs; + struct route_node *rn; + struct ospf_neighbor *nbr; + + /* At first, flush self-originated LSAs from routing domain. */ + ospf_flush_self_originated_lsas_now (top); + + /* Revert all neighbor status to ExStart. */ + for (ALL_LIST_ELEMENTS_RO (top->oiflist, node, oi)) + { + if ((nbrs = oi->nbrs) == NULL) + continue; + + for (rn = route_top (nbrs); rn; rn = route_next (rn)) + { + if ((nbr = rn->info) == NULL || nbr == oi->nbr_self) + continue; + + if (nbr->state < NSM_ExStart) + continue; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Renegotiate optional capabilities with neighbor(%s)", inet_ntoa (nbr->router_id)); + + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_SeqNumberMismatch); + } + } + + return; +} + + +struct ospf_neighbor * +ospf_nbr_lookup (struct ospf_interface *oi, struct ip *iph, + struct ospf_header *ospfh) +{ + 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)); +} + +static struct ospf_neighbor * +ospf_nbr_add (struct ospf_interface *oi, struct ospf_header *ospfh, + struct prefix *p) +{ + struct ospf_neighbor *nbr; + + nbr = ospf_nbr_new (oi); + nbr->state = NSM_Down; + nbr->src = p->u.prefix4; + memcpy (&nbr->address, p, sizeof (struct prefix)); + + nbr->nbr_nbma = NULL; + if (oi->type == OSPF_IFTYPE_NBMA) + { + struct ospf_nbr_nbma *nbr_nbma; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO (oi->nbr_nbma, node, nbr_nbma)) + { + if (IPV4_ADDR_SAME(&nbr_nbma->addr, &nbr->src)) + { + nbr_nbma->nbr = nbr; + nbr->nbr_nbma = nbr_nbma; + + if (nbr_nbma->t_poll) + OSPF_POLL_TIMER_OFF (nbr_nbma->t_poll); + + nbr->state_change = nbr_nbma->state_change + 1; + } + } + } + + /* New nbr, save the crypto sequence number if necessary */ + if (ntohs (ospfh->auth_type) == OSPF_AUTH_CRYPTOGRAPHIC) + nbr->crypt_seqnum = ospfh->u.crypt.crypt_seqnum; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("NSM[%s:%s]: start", IF_NAME (nbr->oi), + inet_ntoa (nbr->router_id)); + + return nbr; +} + +struct ospf_neighbor * +ospf_nbr_get (struct ospf_interface *oi, struct ospf_header *ospfh, + struct ip *iph, struct prefix *p) +{ + struct route_node *rn; + struct prefix key; + struct ospf_neighbor *nbr; + + key.family = AF_INET; + key.prefixlen = IPV4_MAX_BITLEN; + + 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; + + rn = route_node_get (oi->nbrs, &key); + if (rn->info) + { + route_unlock_node (rn); + nbr = rn->info; + + if (oi->type == OSPF_IFTYPE_NBMA && nbr->state == NSM_Attempt) + { + nbr->src = iph->ip_src; + memcpy (&nbr->address, p, sizeof (struct prefix)); + } + } + else + { + rn->info = nbr = ospf_nbr_add (oi, ospfh, p); + } + + nbr->router_id = ospfh->router_id; + + return nbr; +} diff --git a/ospfd/ospf_neighbor.h b/ospfd/ospf_neighbor.h new file mode 100644 index 0000000..38f8cb5 --- /dev/null +++ b/ospfd/ospf_neighbor.h @@ -0,0 +1,119 @@ +/* + * OSPF Neighbor functions. + * Copyright (C) 1999, 2000 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 _ZEBRA_OSPF_NEIGHBOR_H +#define _ZEBRA_OSPF_NEIGHBOR_H + +#include + +/* Neighbor Data Structure */ +struct ospf_neighbor +{ + /* This neighbor's parent ospf interface. */ + struct ospf_interface *oi; + + /* OSPF neighbor Information */ + u_char state; /* NSM status. */ + u_char dd_flags; /* DD bit flags. */ + u_int32_t dd_seqnum; /* DD Sequence Number. */ + + /* Neighbor Information from Hello. */ + struct prefix address; /* Neighbor Interface Address. */ + + struct in_addr src; /* Src address. */ + struct in_addr router_id; /* Router ID. */ + u_char options; /* Options. */ + int priority; /* Router Priority. */ + struct in_addr d_router; /* Designated Router. */ + struct in_addr bd_router; /* Backup Designated Router. */ + + /* Last sent Database Description packet. */ + struct ospf_packet *last_send; + /* Timestemp when last Database Description packet was sent */ + struct timeval last_send_ts; + + /* Last received Databse Description packet. */ + struct + { + u_char options; + u_char flags; + u_int32_t dd_seqnum; + } last_recv; + + /* LSA data. */ + struct ospf_lsdb ls_rxmt; + struct ospf_lsdb db_sum; + struct ospf_lsdb ls_req; + struct ospf_lsa *ls_req_last; + + u_int32_t crypt_seqnum; /* Cryptographic Sequence Number. */ + + /* Timer values. */ + u_int32_t v_inactivity; + u_int32_t v_db_desc; + u_int32_t v_ls_req; + u_int32_t v_ls_upd; + + /* Threads. */ + struct thread *t_inactivity; + struct thread *t_db_desc; + struct thread *t_ls_req; + struct thread *t_ls_upd; + struct thread *t_hello_reply; + + /* NBMA configured neighbour */ + struct ospf_nbr_nbma *nbr_nbma; + + /* Statistics */ + struct timeval ts_last_progress; /* last advance of NSM */ + struct timeval ts_last_regress; /* last regressive NSM change */ + const char *last_regress_str; /* Event which last regressed NSM */ + u_int32_t state_change; /* NSM state change counter */ +}; + +/* Macros. */ +#define NBR_IS_DR(n) IPV4_ADDR_SAME (&n->address.u.prefix4, &n->d_router) +#define NBR_IS_BDR(n) IPV4_ADDR_SAME (&n->address.u.prefix4, &n->bd_router) + +/* Prototypes. */ +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); +extern int ospf_nbr_count_opaque_capable (struct ospf_interface *); +extern struct ospf_neighbor *ospf_nbr_get (struct ospf_interface *, + struct ospf_header *, + struct ip *, struct prefix *); +extern struct ospf_neighbor *ospf_nbr_lookup (struct ospf_interface *, + struct ip *, + struct ospf_header *); +extern struct ospf_neighbor *ospf_nbr_lookup_by_addr (struct route_table *, + struct in_addr *); +extern struct ospf_neighbor *ospf_nbr_lookup_by_routerid (struct route_table + *, + struct in_addr *); +extern void ospf_renegotiate_optional_capabilities (struct ospf *top); + +#endif /* _ZEBRA_OSPF_NEIGHBOR_H */ diff --git a/ospfd/ospf_network.c b/ospfd/ospf_network.c new file mode 100644 index 0000000..02ddf92 --- /dev/null +++ b/ospfd/ospf_network.c @@ -0,0 +1,260 @@ +/* + * OSPF network related functions + * Copyright (C) 1999 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. + */ + +#include + +#include "thread.h" +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "sockunion.h" +#include "log.h" +#include "sockopt.h" +#include "privs.h" + +extern struct zebra_privs_t ospfd_privs; + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_network.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_packet.h" +#include "ospfd/ospf_dump.h" + + + +/* Join to the OSPF ALL SPF ROUTERS multicast group. */ +int +ospf_if_add_allspfrouters (struct ospf *top, struct prefix *p, + ifindex_t ifindex) +{ + int ret; + + ret = setsockopt_ipv4_multicast (top->fd, IP_ADD_MEMBERSHIP, + htonl (OSPF_ALLSPFROUTERS), + ifindex); + if (ret < 0) + zlog_warn ("can't setsockopt IP_ADD_MEMBERSHIP (fd %d, addr %s, " + "ifindex %u, AllSPFRouters): %s; perhaps a kernel limit " + "on # of multicast group memberships has been exceeded?", + top->fd, inet_ntoa(p->u.prefix4), ifindex, safe_strerror(errno)); + else + zlog_debug ("interface %s [%u] join AllSPFRouters Multicast group.", + inet_ntoa (p->u.prefix4), ifindex); + + return ret; +} + +int +ospf_if_drop_allspfrouters (struct ospf *top, struct prefix *p, + ifindex_t ifindex) +{ + int ret; + + ret = setsockopt_ipv4_multicast (top->fd, IP_DROP_MEMBERSHIP, + htonl (OSPF_ALLSPFROUTERS), + ifindex); + if (ret < 0) + zlog_warn ("can't setsockopt IP_DROP_MEMBERSHIP (fd %d, addr %s, " + "ifindex %u, AllSPFRouters): %s", + top->fd, inet_ntoa(p->u.prefix4), ifindex, safe_strerror(errno)); + else + zlog_debug ("interface %s [%u] leave AllSPFRouters Multicast group.", + inet_ntoa (p->u.prefix4), ifindex); + + return ret; +} + +/* Join to the OSPF ALL Designated ROUTERS multicast group. */ +int +ospf_if_add_alldrouters (struct ospf *top, struct prefix *p, ifindex_t ifindex) +{ + int ret; + + ret = setsockopt_ipv4_multicast (top->fd, IP_ADD_MEMBERSHIP, + htonl (OSPF_ALLDROUTERS), + ifindex); + if (ret < 0) + zlog_warn ("can't setsockopt IP_ADD_MEMBERSHIP (fd %d, addr %s, " + "ifindex %u, AllDRouters): %s; perhaps a kernel limit " + "on # of multicast group memberships has been exceeded?", + top->fd, inet_ntoa(p->u.prefix4), ifindex, safe_strerror(errno)); + else + zlog_debug ("interface %s [%u] join AllDRouters Multicast group.", + inet_ntoa (p->u.prefix4), ifindex); + + return ret; +} + +int +ospf_if_drop_alldrouters (struct ospf *top, struct prefix *p, ifindex_t ifindex) +{ + int ret; + + ret = setsockopt_ipv4_multicast (top->fd, IP_DROP_MEMBERSHIP, + htonl (OSPF_ALLDROUTERS), + ifindex); + if (ret < 0) + zlog_warn ("can't setsockopt IP_DROP_MEMBERSHIP (fd %d, addr %s, " + "ifindex %u, AllDRouters): %s", + top->fd, inet_ntoa(p->u.prefix4), ifindex, safe_strerror(errno)); + else + zlog_debug ("interface %s [%u] leave AllDRouters Multicast group.", + inet_ntoa (p->u.prefix4), ifindex); + + return ret; +} + +int +ospf_if_ipmulticast (struct ospf *top, struct prefix *p, ifindex_t ifindex) +{ + u_char val; + int ret, len; + + val = 0; + len = sizeof (val); + + /* Prevent receiving self-origined multicast packets. */ + ret = setsockopt (top->fd, IPPROTO_IP, IP_MULTICAST_LOOP, (void *)&val, len); + if (ret < 0) + zlog_warn ("can't setsockopt IP_MULTICAST_LOOP(0) for fd %d: %s", + top->fd, safe_strerror(errno)); + + /* Explicitly set multicast ttl to 1 -- endo. */ + val = 1; + ret = setsockopt (top->fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&val, len); + if (ret < 0) + zlog_warn ("can't setsockopt IP_MULTICAST_TTL(1) for fd %d: %s", + top->fd, safe_strerror (errno)); + + ret = setsockopt_ipv4_multicast_if (top->fd, ifindex); + if (ret < 0) + zlog_warn("can't setsockopt IP_MULTICAST_IF(fd %d, addr %s, " + "ifindex %u): %s", + top->fd, inet_ntoa(p->u.prefix4), ifindex, safe_strerror(errno)); + + return ret; +} + +int +ospf_sock_init (void) +{ + int ospf_sock; + int ret, hincl = 1; + + if ( ospfd_privs.change (ZPRIVS_RAISE) ) + zlog_err ("ospf_sock_init: could not raise privs, %s", + safe_strerror (errno) ); + + ospf_sock = socket (AF_INET, SOCK_RAW, IPPROTO_OSPFIGP); + if (ospf_sock < 0) + { + int save_errno = errno; + if ( ospfd_privs.change (ZPRIVS_LOWER) ) + zlog_err ("ospf_sock_init: could not lower privs, %s", + safe_strerror (errno) ); + zlog_err ("ospf_read_sock_init: socket: %s", safe_strerror (save_errno)); + exit(1); + } + +#ifdef IP_HDRINCL + /* we will include IP header with packet */ + ret = setsockopt (ospf_sock, IPPROTO_IP, IP_HDRINCL, &hincl, sizeof (hincl)); + if (ret < 0) + { + int save_errno = errno; + if ( ospfd_privs.change (ZPRIVS_LOWER) ) + zlog_err ("ospf_sock_init: could not lower privs, %s", + safe_strerror (errno) ); + zlog_warn ("Can't set IP_HDRINCL option for fd %d: %s", + ospf_sock, safe_strerror(save_errno)); + } +#elif defined (IPTOS_PREC_INTERNETCONTROL) +#warning "IP_HDRINCL not available on this system" +#warning "using IPTOS_PREC_INTERNETCONTROL" + ret = setsockopt_ipv4_tos(ospf_sock, IPTOS_PREC_INTERNETCONTROL); + if (ret < 0) + { + int save_errno = errno; + if ( ospfd_privs.change (ZPRIVS_LOWER) ) + zlog_err ("ospf_sock_init: could not lower privs, %s", + safe_strerror (errno) ); + zlog_warn ("can't set sockopt IP_TOS %d to socket %d: %s", + tos, ospf_sock, safe_strerror(save_errno)); + close (ospf_sock); /* Prevent sd leak. */ + return ret; + } +#else /* !IPTOS_PREC_INTERNETCONTROL */ +#warning "IP_HDRINCL not available, nor is IPTOS_PREC_INTERNETCONTROL" + zlog_warn ("IP_HDRINCL option not available"); +#endif /* IP_HDRINCL */ + + ret = setsockopt_ifindex (AF_INET, ospf_sock, 1); + + if (ret < 0) + zlog_warn ("Can't set pktinfo option for fd %d", ospf_sock); + + if (ospfd_privs.change (ZPRIVS_LOWER)) + { + zlog_err ("ospf_sock_init: could not lower privs, %s", + safe_strerror (errno) ); + } + + return ospf_sock; +} + +void +ospf_adjust_sndbuflen (struct ospf * ospf, unsigned int buflen) +{ + int ret, newbuflen; + /* Check if any work has to be done at all. */ + if (ospf->maxsndbuflen >= buflen) + return; + if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE)) + zlog_debug ("%s: adjusting OSPF send buffer size to %d", + __func__, buflen); + if (ospfd_privs.change (ZPRIVS_RAISE)) + zlog_err ("%s: could not raise privs, %s", __func__, + safe_strerror (errno)); + /* Now we try to set SO_SNDBUF to what our caller has requested + * (the MTU of a newly added interface). However, if the OS has + * truncated the actual buffer size to somewhat less size, try + * to detect it and update our records appropriately. The OS + * may allocate more buffer space, than requested, this isn't + * a error. + */ + ret = setsockopt_so_sendbuf (ospf->fd, buflen); + newbuflen = getsockopt_so_sendbuf (ospf->fd); + 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 = (unsigned int)newbuflen; + else + zlog_warn ("%s: failed to get SO_SNDBUF", __func__); + if (ospfd_privs.change (ZPRIVS_LOWER)) + zlog_err ("%s: could not lower privs, %s", __func__, + safe_strerror (errno)); +} diff --git a/ospfd/ospf_network.h b/ospfd/ospf_network.h new file mode 100644 index 0000000..8257adb --- /dev/null +++ b/ospfd/ospf_network.h @@ -0,0 +1,39 @@ +/* + * OSPF network related functions. + * Copyright (C) 1999 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 _ZEBRA_OSPF_NETWORK_H +#define _ZEBRA_OSPF_NETWORK_H + +/* Prototypes. */ +extern int ospf_if_add_allspfrouters (struct ospf *, struct prefix *, + ifindex_t); +extern int ospf_if_drop_allspfrouters (struct ospf *, struct prefix *, + ifindex_t); +extern int ospf_if_add_alldrouters (struct ospf *, struct prefix *, + ifindex_t); +extern int ospf_if_drop_alldrouters (struct ospf *, struct prefix *, + 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 *, unsigned int); + +#endif /* _ZEBRA_OSPF_NETWORK_H */ diff --git a/ospfd/ospf_nsm.c b/ospfd/ospf_nsm.c new file mode 100644 index 0000000..5c01a58 --- /dev/null +++ b/ospfd/ospf_nsm.c @@ -0,0 +1,867 @@ +/* + * OSPF version 2 Neighbor State Machine + * From RFC2328 [OSPF Version 2] + * Copyright (C) 1999, 2000 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. + */ + +#include + +#include "thread.h" +#include "memory.h" +#include "hash.h" +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "stream.h" +#include "table.h" +#include "log.h" + +#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_network.h" +#include "ospfd/ospf_packet.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_abr.h" +#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) +{ + struct ospf_neighbor *nbr; + + nbr = THREAD_ARG (thread); + nbr->t_inactivity = NULL; + + if (IS_DEBUG_OSPF (nsm, NSM_TIMERS)) + zlog (NULL, LOG_DEBUG, "NSM[%s:%s]: Timer (Inactivity timer expire)", + IF_NAME (nbr->oi), inet_ntoa (nbr->router_id)); + + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_InactivityTimer); + + return 0; +} + +static int +ospf_db_desc_timer (struct thread *thread) +{ + struct ospf_neighbor *nbr; + + nbr = THREAD_ARG (thread); + nbr->t_db_desc = NULL; + + 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)); + + /* resent last send DD packet. */ + assert (nbr->last_send); + ospf_db_desc_resend (nbr); + + /* DD Retransmit timer set. */ + OSPF_NSM_TIMER_ON (nbr->t_db_desc, ospf_db_desc_timer, nbr->v_db_desc); + + return 0; +} + +/* Hook function called after ospf NSM event is occured. + * + * Set/clear any timers whose condition is implicit to the neighbour + * state. There may be other timers which are set/unset according to other + * state. + * + * We rely on this function to properly clear timers in lower states, + * particularly before deleting a neighbour. + */ +static void +nsm_timer_set (struct ospf_neighbor *nbr) +{ + switch (nbr->state) + { + case NSM_Deleted: + case NSM_Down: + OSPF_NSM_TIMER_OFF (nbr->t_inactivity); + OSPF_NSM_TIMER_OFF (nbr->t_hello_reply); + case NSM_Attempt: + case NSM_Init: + case NSM_TwoWay: + OSPF_NSM_TIMER_OFF (nbr->t_db_desc); + OSPF_NSM_TIMER_OFF (nbr->t_ls_upd); + OSPF_NSM_TIMER_OFF (nbr->t_ls_req); + break; + case NSM_ExStart: + OSPF_NSM_TIMER_ON (nbr->t_db_desc, ospf_db_desc_timer, nbr->v_db_desc); + OSPF_NSM_TIMER_OFF (nbr->t_ls_upd); + OSPF_NSM_TIMER_OFF (nbr->t_ls_req); + break; + case NSM_Exchange: + OSPF_NSM_TIMER_ON (nbr->t_ls_upd, ospf_ls_upd_timer, nbr->v_ls_upd); + if (!IS_SET_DD_MS (nbr->dd_flags)) + OSPF_NSM_TIMER_OFF (nbr->t_db_desc); + break; + case NSM_Loading: + case NSM_Full: + default: + OSPF_NSM_TIMER_OFF (nbr->t_db_desc); + break; + } +} + +/* 10.4 of RFC2328, indicate whether an adjacency is appropriate with + * the given neighbour + */ +static int +nsm_should_adj (struct ospf_neighbor *nbr) +{ + struct ospf_interface *oi = nbr->oi; + + /* These network types must always form adjacencies. */ + if (oi->type == OSPF_IFTYPE_POINTOPOINT + || oi->type == OSPF_IFTYPE_POINTOMULTIPOINT + || oi->type == OSPF_IFTYPE_VIRTUALLINK + /* Router itself is the DRouter or the BDRouter. */ + || IPV4_ADDR_SAME (&oi->address->u.prefix4, &DR (oi)) + || IPV4_ADDR_SAME (&oi->address->u.prefix4, &BDR (oi)) + /* Neighboring Router is the DRouter or the BDRouter. */ + || IPV4_ADDR_SAME (&nbr->address.u.prefix4, &DR (oi)) + || IPV4_ADDR_SAME (&nbr->address.u.prefix4, &BDR (oi))) + return 1; + + return 0; +} + +/* OSPF NSM functions. */ +static int +nsm_packet_received (struct ospf_neighbor *nbr) +{ + /* Start or Restart Inactivity Timer. */ + OSPF_NSM_TIMER_OFF (nbr->t_inactivity); + + OSPF_NSM_TIMER_ON (nbr->t_inactivity, ospf_inactivity_timer, + nbr->v_inactivity); + + if (nbr->oi->type == OSPF_IFTYPE_NBMA && nbr->nbr_nbma) + OSPF_POLL_TIMER_OFF (nbr->nbr_nbma->t_poll); + + return 0; +} + +static int +nsm_start (struct ospf_neighbor *nbr) +{ + if (nbr->nbr_nbma) + OSPF_POLL_TIMER_OFF (nbr->nbr_nbma->t_poll); + + OSPF_NSM_TIMER_OFF (nbr->t_inactivity); + + OSPF_NSM_TIMER_ON (nbr->t_inactivity, ospf_inactivity_timer, + nbr->v_inactivity); + + return 0; +} + +static int +nsm_twoway_received (struct ospf_neighbor *nbr) +{ + return (nsm_should_adj (nbr) ? NSM_ExStart : NSM_TwoWay); +} + +int +ospf_db_summary_count (struct ospf_neighbor *nbr) +{ + return ospf_lsdb_count_all (&nbr->db_sum); +} + +int +ospf_db_summary_isempty (struct ospf_neighbor *nbr) +{ + return ospf_lsdb_isempty (&nbr->db_sum); +} + +static int +ospf_db_summary_add (struct ospf_neighbor *nbr, struct ospf_lsa *lsa) +{ + switch (lsa->data->type) + { + case OSPF_OPAQUE_LINK_LSA: + /* Exclude type-9 LSAs that does not have the same "oi" with "nbr". */ + if (nbr->oi && ospf_if_exists (lsa->oi) != nbr->oi) + return 0; + break; + case OSPF_OPAQUE_AREA_LSA: + /* + * It is assured by the caller function "nsm_negotiation_done()" + * that every given LSA belongs to the same area with "nbr". + */ + break; + case OSPF_OPAQUE_AS_LSA: + default: + break; + } + + /* Stay away from any Local Translated Type-7 LSAs */ + if (CHECK_FLAG (lsa->flags, OSPF_LSA_LOCAL_XLT)) + return 0; + + if (IS_LSA_MAXAGE (lsa)) + ospf_ls_retransmit_add (nbr, lsa); + else + ospf_lsdb_add (&nbr->db_sum, lsa); + + return 0; +} + +void +ospf_db_summary_clear (struct ospf_neighbor *nbr) +{ + struct ospf_lsdb *lsdb; + int i; + + lsdb = &nbr->db_sum; + for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++) + { + struct route_table *table = lsdb->type[i].db; + struct route_node *rn; + + for (rn = route_top (table); rn; rn = route_next (rn)) + if (rn->info) + ospf_lsdb_delete (&nbr->db_sum, rn->info); + } +} + + + +/* The area link state database consists of the router-LSAs, + network-LSAs and summary-LSAs contained in the area structure, + along with the AS-external-LSAs contained in the global structure. + AS-external-LSAs are omitted from a virtual neighbor's Database + summary list. AS-external-LSAs are omitted from the Database + summary list if the area has been configured as a stub. */ +static int +nsm_negotiation_done (struct ospf_neighbor *nbr) +{ + struct ospf_area *area = nbr->oi->area; + struct ospf_lsa *lsa; + struct route_node *rn; + + LSDB_LOOP (ROUTER_LSDB (area), rn, lsa) + ospf_db_summary_add (nbr, lsa); + LSDB_LOOP (NETWORK_LSDB (area), rn, lsa) + ospf_db_summary_add (nbr, lsa); + LSDB_LOOP (SUMMARY_LSDB (area), rn, lsa) + ospf_db_summary_add (nbr, lsa); + LSDB_LOOP (ASBR_SUMMARY_LSDB (area), rn, lsa) + ospf_db_summary_add (nbr, lsa); + + /* Process only if the neighbor is opaque capable. */ + if (CHECK_FLAG (nbr->options, OSPF_OPTION_O)) + { + LSDB_LOOP (OPAQUE_LINK_LSDB (area), rn, lsa) + ospf_db_summary_add (nbr, lsa); + LSDB_LOOP (OPAQUE_AREA_LSDB (area), rn, lsa) + ospf_db_summary_add (nbr, lsa); + } + + if (CHECK_FLAG (nbr->options, OSPF_OPTION_NP)) + { + LSDB_LOOP (NSSA_LSDB (area), rn, lsa) + ospf_db_summary_add (nbr, lsa); + } + + if (nbr->oi->type != OSPF_IFTYPE_VIRTUALLINK + && area->external_routing == OSPF_AREA_DEFAULT) + LSDB_LOOP (EXTERNAL_LSDB (nbr->oi->ospf), rn, lsa) + ospf_db_summary_add (nbr, 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); + + /* Send Link State Request. */ + if (nbr->t_ls_req == NULL) + ospf_ls_req_send (nbr); + + return 0; +} + +static int +nsm_exchange_done (struct ospf_neighbor *nbr) +{ + if (ospf_ls_request_isempty (nbr)) + return NSM_Full; + + if (nbr->t_ls_req == NULL) + ospf_ls_req_send (nbr); + + return NSM_Loading; +} + +static int +nsm_adj_ok (struct ospf_neighbor *nbr) +{ + int next_state = nbr->state; + int adj = nsm_should_adj (nbr); + + if (nbr->state == NSM_TwoWay && adj == 1) + next_state = NSM_ExStart; + else if (nbr->state >= NSM_ExStart && adj == 0) + next_state = NSM_TwoWay; + + return next_state; +} + +/* Clear adjacency related state for a neighbour, intended where nbr + * transitions from > ExStart (i.e. a Full or forming adjacency) + * to <= ExStart. + */ +static void +nsm_clear_adj (struct ospf_neighbor *nbr) +{ + /* Clear Database Summary list. */ + if (!ospf_db_summary_isempty (nbr)) + ospf_db_summary_clear (nbr); + + /* Clear Link State Request list. */ + if (!ospf_ls_request_isempty (nbr)) + ospf_ls_request_delete_all (nbr); + + /* Clear Link State Retransmission list. */ + if (!ospf_ls_retransmit_isempty (nbr)) + ospf_ls_retransmit_clear (nbr); + + if (CHECK_FLAG (nbr->options, OSPF_OPTION_O)) + UNSET_FLAG (nbr->options, OSPF_OPTION_O); +} + +static int +nsm_kill_nbr (struct ospf_neighbor *nbr) +{ + /* killing nbr_self is invalid */ + if (nbr == nbr->oi->nbr_self) + { + assert (nbr != nbr->oi->nbr_self); + return 0; + } + + if (nbr->oi->type == OSPF_IFTYPE_NBMA && nbr->nbr_nbma != NULL) + { + struct ospf_nbr_nbma *nbr_nbma = nbr->nbr_nbma; + + nbr_nbma->nbr = NULL; + nbr_nbma->state_change = nbr->state_change; + + nbr->nbr_nbma = NULL; + + OSPF_POLL_TIMER_ON (nbr_nbma->t_poll, ospf_poll_timer, + nbr_nbma->v_poll); + + if (IS_DEBUG_OSPF (nsm, NSM_EVENTS)) + zlog_debug ("NSM[%s:%s]: Down (PollIntervalTimer scheduled)", + IF_NAME (nbr->oi), inet_ntoa (nbr->address.u.prefix4)); + } + + return 0; +} + +/* Neighbor State Machine */ +struct { + int (*func) (struct ospf_neighbor *); + int next_state; +} NSM [OSPF_NSM_STATE_MAX][OSPF_NSM_EVENT_MAX] = +{ + { + /* DependUpon: dummy state. */ + { NULL, NSM_DependUpon }, /* NoEvent */ + { NULL, NSM_DependUpon }, /* PacketReceived */ + { NULL, NSM_DependUpon }, /* Start */ + { NULL, NSM_DependUpon }, /* 2-WayReceived */ + { NULL, NSM_DependUpon }, /* NegotiationDone */ + { NULL, NSM_DependUpon }, /* ExchangeDone */ + { NULL, NSM_DependUpon }, /* BadLSReq */ + { NULL, NSM_DependUpon }, /* LoadingDone */ + { NULL, NSM_DependUpon }, /* AdjOK? */ + { NULL, NSM_DependUpon }, /* SeqNumberMismatch */ + { NULL, NSM_DependUpon }, /* 1-WayReceived */ + { NULL, NSM_DependUpon }, /* KillNbr */ + { NULL, NSM_DependUpon }, /* InactivityTimer */ + { NULL, NSM_DependUpon }, /* LLDown */ + }, + { + /* Deleted: dummy state. */ + { NULL, NSM_Deleted }, /* NoEvent */ + { NULL, NSM_Deleted }, /* PacketReceived */ + { NULL, NSM_Deleted }, /* Start */ + { NULL, NSM_Deleted }, /* 2-WayReceived */ + { NULL, NSM_Deleted }, /* NegotiationDone */ + { NULL, NSM_Deleted }, /* ExchangeDone */ + { NULL, NSM_Deleted }, /* BadLSReq */ + { NULL, NSM_Deleted }, /* LoadingDone */ + { NULL, NSM_Deleted }, /* AdjOK? */ + { NULL, NSM_Deleted }, /* SeqNumberMismatch */ + { NULL, NSM_Deleted }, /* 1-WayReceived */ + { NULL, NSM_Deleted }, /* KillNbr */ + { NULL, NSM_Deleted }, /* InactivityTimer */ + { NULL, NSM_Deleted }, /* LLDown */ + }, + { + /* Down: */ + { NULL, NSM_DependUpon }, /* NoEvent */ + { nsm_packet_received, NSM_Init }, /* PacketReceived */ + { nsm_start, NSM_Attempt }, /* Start */ + { NULL, NSM_Down }, /* 2-WayReceived */ + { NULL, NSM_Down }, /* NegotiationDone */ + { NULL, NSM_Down }, /* ExchangeDone */ + { NULL, NSM_Down }, /* BadLSReq */ + { NULL, NSM_Down }, /* LoadingDone */ + { NULL, NSM_Down }, /* AdjOK? */ + { NULL, NSM_Down }, /* SeqNumberMismatch */ + { NULL, NSM_Down }, /* 1-WayReceived */ + { nsm_kill_nbr, NSM_Deleted }, /* KillNbr */ + { nsm_kill_nbr, NSM_Deleted }, /* InactivityTimer */ + { nsm_kill_nbr, NSM_Deleted }, /* LLDown */ + }, + { + /* Attempt: */ + { NULL, NSM_DependUpon }, /* NoEvent */ + { nsm_packet_received, NSM_Init }, /* PacketReceived */ + { NULL, NSM_Attempt }, /* Start */ + { NULL, NSM_Attempt }, /* 2-WayReceived */ + { NULL, NSM_Attempt }, /* NegotiationDone */ + { NULL, NSM_Attempt }, /* ExchangeDone */ + { NULL, NSM_Attempt }, /* BadLSReq */ + { NULL, NSM_Attempt }, /* LoadingDone */ + { NULL, NSM_Attempt }, /* AdjOK? */ + { NULL, NSM_Attempt }, /* SeqNumberMismatch */ + { NULL, NSM_Attempt }, /* 1-WayReceived */ + { nsm_kill_nbr, NSM_Deleted }, /* KillNbr */ + { nsm_kill_nbr, NSM_Deleted }, /* InactivityTimer */ + { nsm_kill_nbr, NSM_Deleted }, /* LLDown */ + }, + { + /* Init: */ + { NULL, NSM_DependUpon }, /* NoEvent */ + { nsm_packet_received, NSM_Init }, /* PacketReceived */ + { NULL, NSM_Init }, /* Start */ + { nsm_twoway_received, NSM_DependUpon }, /* 2-WayReceived */ + { NULL, NSM_Init }, /* NegotiationDone */ + { NULL, NSM_Init }, /* ExchangeDone */ + { NULL, NSM_Init }, /* BadLSReq */ + { NULL, NSM_Init }, /* LoadingDone */ + { NULL, NSM_Init }, /* AdjOK? */ + { NULL, NSM_Init }, /* SeqNumberMismatch */ + { NULL, NSM_Init }, /* 1-WayReceived */ + { nsm_kill_nbr, NSM_Deleted }, /* KillNbr */ + { nsm_kill_nbr, NSM_Deleted }, /* InactivityTimer */ + { nsm_kill_nbr, NSM_Deleted }, /* LLDown */ + }, + { + /* 2-Way: */ + { NULL, NSM_DependUpon }, /* NoEvent */ + { nsm_packet_received, NSM_TwoWay }, /* HelloReceived */ + { NULL, NSM_TwoWay }, /* Start */ + { NULL, NSM_TwoWay }, /* 2-WayReceived */ + { NULL, NSM_TwoWay }, /* NegotiationDone */ + { NULL, NSM_TwoWay }, /* ExchangeDone */ + { NULL, NSM_TwoWay }, /* BadLSReq */ + { NULL, NSM_TwoWay }, /* LoadingDone */ + { nsm_adj_ok, NSM_DependUpon }, /* AdjOK? */ + { NULL, NSM_TwoWay }, /* SeqNumberMismatch */ + { NULL, NSM_Init }, /* 1-WayReceived */ + { nsm_kill_nbr, NSM_Deleted }, /* KillNbr */ + { nsm_kill_nbr, NSM_Deleted }, /* InactivityTimer */ + { nsm_kill_nbr, NSM_Deleted }, /* LLDown */ + }, + { + /* ExStart: */ + { NULL, NSM_DependUpon }, /* NoEvent */ + { nsm_packet_received, NSM_ExStart }, /* PacaketReceived */ + { NULL, NSM_ExStart }, /* Start */ + { NULL, NSM_ExStart }, /* 2-WayReceived */ + { nsm_negotiation_done, NSM_Exchange }, /* NegotiationDone */ + { NULL, NSM_ExStart }, /* ExchangeDone */ + { NULL, NSM_ExStart }, /* BadLSReq */ + { NULL, NSM_ExStart }, /* LoadingDone */ + { nsm_adj_ok, NSM_DependUpon }, /* AdjOK? */ + { NULL, NSM_ExStart }, /* SeqNumberMismatch */ + { NULL, NSM_Init }, /* 1-WayReceived */ + { nsm_kill_nbr, NSM_Deleted }, /* KillNbr */ + { nsm_kill_nbr, NSM_Deleted }, /* InactivityTimer */ + { nsm_kill_nbr, NSM_Deleted }, /* LLDown */ + }, + { + /* Exchange: */ + { NULL, NSM_DependUpon }, /* NoEvent */ + { nsm_packet_received, NSM_Exchange }, /* PacketReceived */ + { NULL, NSM_Exchange }, /* Start */ + { NULL, NSM_Exchange }, /* 2-WayReceived */ + { NULL, NSM_Exchange }, /* NegotiationDone */ + { nsm_exchange_done, NSM_DependUpon }, /* ExchangeDone */ + { NULL, NSM_ExStart }, /* BadLSReq */ + { NULL, NSM_Exchange }, /* LoadingDone */ + { nsm_adj_ok, NSM_DependUpon }, /* AdjOK? */ + { NULL, NSM_ExStart }, /* SeqNumberMismatch */ + { NULL, NSM_Init }, /* 1-WayReceived */ + { nsm_kill_nbr, NSM_Deleted }, /* KillNbr */ + { nsm_kill_nbr, NSM_Deleted }, /* InactivityTimer */ + { nsm_kill_nbr, NSM_Deleted }, /* LLDown */ + }, + { + /* Loading: */ + { NULL, NSM_DependUpon }, /* NoEvent */ + { nsm_packet_received, NSM_Loading }, /* PacketReceived */ + { NULL, NSM_Loading }, /* Start */ + { NULL, NSM_Loading }, /* 2-WayReceived */ + { NULL, NSM_Loading }, /* NegotiationDone */ + { NULL, NSM_Loading }, /* ExchangeDone */ + { NULL, NSM_ExStart }, /* BadLSReq */ + { NULL, NSM_Full }, /* LoadingDone */ + { nsm_adj_ok, NSM_DependUpon }, /* AdjOK? */ + { NULL, NSM_ExStart }, /* SeqNumberMismatch */ + { NULL, NSM_Init }, /* 1-WayReceived */ + { nsm_kill_nbr, NSM_Deleted }, /* KillNbr */ + { nsm_kill_nbr, NSM_Deleted }, /* InactivityTimer */ + { nsm_kill_nbr, NSM_Deleted }, /* LLDown */ + }, + { /* Full: */ + { NULL, NSM_DependUpon }, /* NoEvent */ + { nsm_packet_received, NSM_Full }, /* PacketReceived */ + { NULL, NSM_Full }, /* Start */ + { NULL, NSM_Full }, /* 2-WayReceived */ + { NULL, NSM_Full }, /* NegotiationDone */ + { NULL, NSM_Full }, /* ExchangeDone */ + { NULL, NSM_ExStart }, /* BadLSReq */ + { NULL, NSM_Full }, /* LoadingDone */ + { nsm_adj_ok, NSM_DependUpon }, /* AdjOK? */ + { NULL, NSM_ExStart }, /* SeqNumberMismatch */ + { NULL, NSM_Init }, /* 1-WayReceived */ + { nsm_kill_nbr, NSM_Deleted }, /* KillNbr */ + { nsm_kill_nbr, NSM_Deleted }, /* InactivityTimer */ + { nsm_kill_nbr, NSM_Deleted }, /* LLDown */ + }, +}; + +static const char *ospf_nsm_event_str[] = +{ + "NoEvent", + "PacketReceived", + "Start", + "2-WayReceived", + "NegotiationDone", + "ExchangeDone", + "BadLSReq", + "LoadingDone", + "AdjOK?", + "SeqNumberMismatch", + "1-WayReceived", + "KillNbr", + "InactivityTimer", + "LLDown", +}; + +static void +nsm_notice_state_change (struct ospf_neighbor *nbr, int next_state, int event) +{ + /* Logging change of status. */ + if (IS_DEBUG_OSPF (nsm, NSM_STATUS)) + zlog_debug ("NSM[%s:%s]: State change %s -> %s (%s)", + IF_NAME (nbr->oi), inet_ntoa (nbr->router_id), + LOOKUP (ospf_nsm_state_msg, nbr->state), + LOOKUP (ospf_nsm_state_msg, next_state), + ospf_nsm_event_str [event]); + + /* Optionally notify about adjacency changes */ + if (CHECK_FLAG(nbr->oi->ospf->config, OSPF_LOG_ADJACENCY_CHANGES) && + (CHECK_FLAG(nbr->oi->ospf->config, OSPF_LOG_ADJACENCY_DETAIL) || + (next_state == NSM_Full) || (next_state < nbr->state))) + zlog_notice("AdjChg: Nbr %s on %s: %s -> %s (%s)", + inet_ntoa (nbr->router_id), IF_NAME (nbr->oi), + LOOKUP (ospf_nsm_state_msg, nbr->state), + LOOKUP (ospf_nsm_state_msg, next_state), + ospf_nsm_event_str [event]); + + /* Advance in NSM */ + if (next_state > nbr->state) + nbr->ts_last_progress = recent_relative_time (); + else /* regression in NSM */ + { + nbr->ts_last_regress = recent_relative_time (); + nbr->last_regress_str = ospf_nsm_event_str [event]; + } + +} + +static void +nsm_change_state (struct ospf_neighbor *nbr, int state) +{ + struct ospf_interface *oi = nbr->oi; + struct ospf_area *vl_area = NULL; + u_char old_state; + int x; + int force = 1; + + /* Preserve old status. */ + old_state = nbr->state; + + /* Change to new status. */ + nbr->state = state; + + /* Statistics. */ + nbr->state_change++; + + 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)) + { + if (state == NSM_Full) + { + oi->full_nbrs++; + oi->area->full_nbrs++; + + ospf_check_abr_status (oi->ospf); + + if (oi->type == OSPF_IFTYPE_VIRTUALLINK && vl_area) + if (++vl_area->full_vls == 1) + ospf_schedule_abr_task (oi->ospf); + + /* kevinm: refresh any redistributions */ + for (x = ZEBRA_ROUTE_SYSTEM; x < ZEBRA_ROUTE_MAX; x++) + { + if (x == ZEBRA_ROUTE_OSPF || x == ZEBRA_ROUTE_OSPF6) + continue; + ospf_external_lsa_refresh_type (oi->ospf, x, force); + } + /* XXX: Clearly some thing is wrong with refresh of external LSAs + * this added to hack around defaults not refreshing after a timer + * jump. + */ + ospf_external_lsa_refresh_default (oi->ospf); + } + else + { + oi->full_nbrs--; + oi->area->full_nbrs--; + + ospf_check_abr_status (oi->ospf); + + if (oi->type == OSPF_IFTYPE_VIRTUALLINK && vl_area) + if (vl_area->full_vls > 0) + if (--vl_area->full_vls == 0) + ospf_schedule_abr_task (oi->ospf); + } + + zlog_info ("nsm_change_state(%s, %s -> %s): " + "scheduling new router-LSA origination", + inet_ntoa (nbr->router_id), + LOOKUP(ospf_nsm_state_msg, old_state), + LOOKUP(ospf_nsm_state_msg, state)); + + ospf_router_lsa_update_area (oi->area); + + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) + { + struct ospf_area *vl_area = + ospf_area_lookup_by_area_id (oi->ospf, oi->vl_data->vl_area_id); + + if (vl_area) + ospf_router_lsa_update_area (vl_area); + } + + /* Originate network-LSA. */ + if (oi->state == ISM_DR) + { + if (oi->network_lsa_self && oi->full_nbrs == 0) + { + ospf_lsa_flush_area (oi->network_lsa_self, oi->area); + ospf_lsa_unlock (&oi->network_lsa_self); + oi->network_lsa_self = NULL; + } + else + ospf_network_lsa_update (oi); + } + } + + ospf_opaque_nsm_change (nbr, old_state); + + /* State changes from > ExStart to <= ExStart should clear any Exchange + * or Full/LSA Update related lists and state. + * Potential causal events: BadLSReq, SeqNumberMismatch, AdjOK? + */ + if ((old_state > NSM_ExStart) && (state <= NSM_ExStart)) + nsm_clear_adj (nbr); + + /* Start DD exchange protocol */ + if (state == NSM_ExStart) + { + if (nbr->dd_seqnum == 0) + nbr->dd_seqnum = quagga_time (NULL); + else + nbr->dd_seqnum++; + + nbr->dd_flags = OSPF_DD_FLAG_I|OSPF_DD_FLAG_M|OSPF_DD_FLAG_MS; + ospf_db_desc_send (nbr); + } + + /* clear cryptographic sequence number */ + if (state == NSM_Down) + nbr->crypt_seqnum = 0; + + /* Preserve old status? */ +} + +/* Execute NSM event process. */ +int +ospf_nsm_event (struct thread *thread) +{ + int event; + int next_state; + struct ospf_neighbor *nbr; + + nbr = THREAD_ARG (thread); + event = THREAD_VAL (thread); + + if (IS_DEBUG_OSPF (nsm, NSM_EVENTS)) + zlog_debug ("NSM[%s:%s]: %s (%s)", IF_NAME (nbr->oi), + inet_ntoa (nbr->router_id), + LOOKUP (ospf_nsm_state_msg, nbr->state), + ospf_nsm_event_str [event]); + + next_state = NSM [nbr->state][event].next_state; + + /* Call function. */ + if (NSM [nbr->state][event].func != NULL) + { + int func_state = (*(NSM [nbr->state][event].func))(nbr); + + if (NSM [nbr->state][event].next_state == NSM_DependUpon) + next_state = func_state; + else if (func_state) + { + /* There's a mismatch between the FSM tables and what an FSM + * action/state-change function returned. State changes which + * do not have conditional/DependUpon next-states should not + * try set next_state. + */ + zlog_warn ("NSM[%s:%s]: %s (%s): " + "Warning: action tried to change next_state to %s", + IF_NAME (nbr->oi), inet_ntoa (nbr->router_id), + LOOKUP (ospf_nsm_state_msg, nbr->state), + ospf_nsm_event_str [event], + LOOKUP (ospf_nsm_state_msg, func_state)); + } + } + + assert (next_state != NSM_DependUpon); + + /* If state is changed. */ + 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. */ + nsm_timer_set (nbr); + + /* When event is NSM_KillNbr, InactivityTimer or LLDown, the neighbor + * is deleted. + * + * Rather than encode knowledge here of which events lead to NBR + * delete, we take our cue from the NSM table, via the dummy + * 'Deleted' neighbour state. + */ + if (nbr->state == NSM_Deleted) + ospf_nbr_delete (nbr); + + return 0; +} + +/* Check loading state. */ +void +ospf_check_nbr_loading (struct ospf_neighbor *nbr) +{ + if (nbr->state == NSM_Loading) + { + if (ospf_ls_request_isempty (nbr)) + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_LoadingDone); + else if (nbr->ls_req_last == NULL) + ospf_ls_req_event (nbr); + } +} diff --git a/ospfd/ospf_nsm.h b/ospfd/ospf_nsm.h new file mode 100644 index 0000000..9b7e14a --- /dev/null +++ b/ospfd/ospf_nsm.h @@ -0,0 +1,90 @@ +/* + * OSPF version 2 Neighbor State Machine + * From RFC2328 [OSPF Version 2] + * Copyright (C) 1999 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 _ZEBRA_OSPF_NSM_H +#define _ZEBRA_OSPF_NSM_H + +/* OSPF Neighbor State Machine State. */ +#define NSM_DependUpon 0 +#define NSM_Deleted 1 +#define NSM_Down 2 +#define NSM_Attempt 3 +#define NSM_Init 4 +#define NSM_TwoWay 5 +#define NSM_ExStart 6 +#define NSM_Exchange 7 +#define NSM_Loading 8 +#define NSM_Full 9 +#define OSPF_NSM_STATE_MAX 10 + +/* OSPF Neighbor State Machine Event. */ +#define NSM_NoEvent 0 +#define NSM_PacketReceived 1 /* HelloReceived in the protocol */ +#define NSM_Start 2 +#define NSM_TwoWayReceived 3 +#define NSM_NegotiationDone 4 +#define NSM_ExchangeDone 5 +#define NSM_BadLSReq 6 +#define NSM_LoadingDone 7 +#define NSM_AdjOK 8 +#define NSM_SeqNumberMismatch 9 +#define NSM_OneWayReceived 10 +#define NSM_KillNbr 11 +#define NSM_InactivityTimer 12 +#define NSM_LLDown 13 +#define OSPF_NSM_EVENT_MAX 14 + +/* Macro for OSPF NSM timer turn on. */ +#define OSPF_NSM_TIMER_ON(T,F,V) \ + do { \ + if (!(T)) \ + (T) = thread_add_timer (master, (F), nbr, (V)); \ + } while (0) + +/* Macro for OSPF NSM timer turn off. */ +#define OSPF_NSM_TIMER_OFF(X) \ + do { \ + if (X) \ + { \ + thread_cancel (X); \ + (X) = NULL; \ + } \ + } while (0) + +/* Macro for OSPF NSM schedule event. */ +#define OSPF_NSM_EVENT_SCHEDULE(N,E) \ + thread_add_event (master, ospf_nsm_event, (N), (E)) + +/* Macro for OSPF NSM execute event. */ +#define OSPF_NSM_EVENT_EXECUTE(N,E) \ + thread_execute (master, ospf_nsm_event, (N), (E)) + +/* Prototypes. */ +extern int ospf_nsm_event (struct thread *); +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 *); +extern void ospf_db_summary_clear (struct ospf_neighbor *); + +#endif /* _ZEBRA_OSPF_NSM_H */ + diff --git a/ospfd/ospf_opaque.c b/ospfd/ospf_opaque.c new file mode 100644 index 0000000..39465c1 --- /dev/null +++ b/ospfd/ospf_opaque.c @@ -0,0 +1,2173 @@ +/* + * This is an implementation of rfc2370. + * Copyright (C) 2001 KDD R&D Laboratories, Inc. + * http://www.kddlabs.co.jp/ + * + * 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. + */ + +/***** MTYPE definitions are not reflected to "memory.h" yet. *****/ +#define MTYPE_OSPF_OPAQUE_FUNCTAB MTYPE_TMP +#define MTYPE_OPAQUE_INFO_PER_TYPE MTYPE_TMP +#define MTYPE_OPAQUE_INFO_PER_ID MTYPE_TMP + +#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" + +/*------------------------------------------------------------------------* + * Followings are initialize/terminate functions for Opaque-LSAs handling. + *------------------------------------------------------------------------*/ + +#include "ospfd/ospf_te.h" +#include "ospfd/ospf_ri.h" + +#ifdef SUPPORT_OSPF_API +int ospf_apiserver_init (void); +void ospf_apiserver_term (void); +/* Init apiserver? It's disabled by default. */ +int ospf_apiserver_enable; +#endif /* SUPPORT_OSPF_API */ + +static void ospf_opaque_register_vty (void); +static void ospf_opaque_funclist_init (void); +static void ospf_opaque_funclist_term (void); +static void free_opaque_info_per_type (void *val); +static void free_opaque_info_per_id (void *val); +static int ospf_opaque_lsa_install_hook (struct ospf_lsa *lsa); +static int ospf_opaque_lsa_delete_hook (struct ospf_lsa *lsa); + +void +ospf_opaque_init (void) +{ + ospf_opaque_register_vty (); + ospf_opaque_funclist_init (); + + if (ospf_mpls_te_init () != 0) + exit (1); + + if (ospf_router_info_init () != 0) + exit (1); + +#ifdef SUPPORT_OSPF_API + if ((ospf_apiserver_enable) && (ospf_apiserver_init () != 0)) + exit (1); +#endif /* SUPPORT_OSPF_API */ + + return; +} + +void +ospf_opaque_term (void) +{ + ospf_mpls_te_term (); + + ospf_router_info_term (); + +#ifdef SUPPORT_OSPF_API + ospf_apiserver_term (); +#endif /* SUPPORT_OSPF_API */ + + ospf_opaque_funclist_term (); + return; +} + +int +ospf_opaque_type9_lsa_init (struct ospf_interface *oi) +{ + if (oi->opaque_lsa_self != NULL) + list_delete (oi->opaque_lsa_self); + + oi->opaque_lsa_self = list_new (); + oi->opaque_lsa_self->del = free_opaque_info_per_type; + oi->t_opaque_lsa_self = NULL; + return 0; +} + +void +ospf_opaque_type9_lsa_term (struct ospf_interface *oi) +{ + OSPF_TIMER_OFF (oi->t_opaque_lsa_self); + if (oi->opaque_lsa_self != NULL) + list_delete (oi->opaque_lsa_self); + oi->opaque_lsa_self = NULL; + return; +} + +int +ospf_opaque_type10_lsa_init (struct ospf_area *area) +{ + if (area->opaque_lsa_self != NULL) + list_delete (area->opaque_lsa_self); + + area->opaque_lsa_self = list_new (); + area->opaque_lsa_self->del = free_opaque_info_per_type; + area->t_opaque_lsa_self = NULL; + +#ifdef MONITOR_LSDB_CHANGE + area->lsdb->new_lsa_hook = ospf_opaque_lsa_install_hook; + area->lsdb->del_lsa_hook = ospf_opaque_lsa_delete_hook; +#endif /* MONITOR_LSDB_CHANGE */ + return 0; +} + +void +ospf_opaque_type10_lsa_term (struct ospf_area *area) +{ +#ifdef MONITOR_LSDB_CHANGE + area->lsdb->new_lsa_hook = + area->lsdb->del_lsa_hook = NULL; +#endif /* MONITOR_LSDB_CHANGE */ + + OSPF_TIMER_OFF (area->t_opaque_lsa_self); + if (area->opaque_lsa_self != NULL) + list_delete (area->opaque_lsa_self); + area->opaque_lsa_self = NULL; + return; +} + +int +ospf_opaque_type11_lsa_init (struct ospf *top) +{ + if (top->opaque_lsa_self != NULL) + list_delete (top->opaque_lsa_self); + + top->opaque_lsa_self = list_new (); + top->opaque_lsa_self->del = free_opaque_info_per_type; + top->t_opaque_lsa_self = NULL; + +#ifdef MONITOR_LSDB_CHANGE + top->lsdb->new_lsa_hook = ospf_opaque_lsa_install_hook; + top->lsdb->del_lsa_hook = ospf_opaque_lsa_delete_hook; +#endif /* MONITOR_LSDB_CHANGE */ + return 0; +} + +void +ospf_opaque_type11_lsa_term (struct ospf *top) +{ +#ifdef MONITOR_LSDB_CHANGE + top->lsdb->new_lsa_hook = + top->lsdb->del_lsa_hook = NULL; +#endif /* MONITOR_LSDB_CHANGE */ + + OSPF_TIMER_OFF (top->t_opaque_lsa_self); + if (top->opaque_lsa_self != NULL) + list_delete (top->opaque_lsa_self); + top->opaque_lsa_self = NULL; + return; +} + +static const char * +ospf_opaque_type_name (u_char opaque_type) +{ + const char *name = "Unknown"; + + switch (opaque_type) + { + case OPAQUE_TYPE_WILDCARD: /* This is a special assignment! */ + name = "Wildcard"; + break; + case OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA: + name = "Traffic Engineering LSA"; + break; + case OPAQUE_TYPE_SYCAMORE_OPTICAL_TOPOLOGY_DESC: + name = "Sycamore optical topology description"; + break; + 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"; + 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; +} + +/*------------------------------------------------------------------------* + * Followings are management functions to store user specified callbacks. + *------------------------------------------------------------------------*/ + +struct opaque_info_per_type; /* Forward declaration. */ + +struct ospf_opaque_functab +{ + u_char opaque_type; + struct opaque_info_per_type *oipt; + + int (* new_if_hook)(struct interface *ifp); + int (* del_if_hook)(struct interface *ifp); + void (* ism_change_hook)(struct ospf_interface *oi, int old_status); + void (* nsm_change_hook)(struct ospf_neighbor *nbr, int old_status); + void (* config_write_router)(struct vty *vty); + void (* config_write_if )(struct vty *vty, struct interface *ifp); + void (* config_write_debug )(struct vty *vty); + void (* show_opaque_info )(struct vty *vty, struct ospf_lsa *lsa); + int (* lsa_originator)(void *arg); + struct ospf_lsa *(* lsa_refresher )(struct ospf_lsa *lsa); + int (* new_lsa_hook)(struct ospf_lsa *lsa); + int (* del_lsa_hook)(struct ospf_lsa *lsa); +}; + +/* Handle LSA-9/10/11 altogether. */ +static struct list *ospf_opaque_wildcard_funclist; +static struct list *ospf_opaque_type9_funclist; +static struct list *ospf_opaque_type10_funclist; +static struct list *ospf_opaque_type11_funclist; + +static void +ospf_opaque_del_functab (void *val) +{ + XFREE (MTYPE_OSPF_OPAQUE_FUNCTAB, val); + return; +} + +static void +ospf_opaque_funclist_init (void) +{ + struct list *funclist; + + funclist = ospf_opaque_wildcard_funclist = list_new (); + funclist->del = ospf_opaque_del_functab; + + funclist = ospf_opaque_type9_funclist = list_new (); + funclist->del = ospf_opaque_del_functab; + + funclist = ospf_opaque_type10_funclist = list_new (); + funclist->del = ospf_opaque_del_functab; + + funclist = ospf_opaque_type11_funclist = list_new (); + funclist->del = ospf_opaque_del_functab; + return; +} + +static void +ospf_opaque_funclist_term (void) +{ + struct list *funclist; + + funclist = ospf_opaque_wildcard_funclist; + list_delete (funclist); + + funclist = ospf_opaque_type9_funclist; + list_delete (funclist); + + funclist = ospf_opaque_type10_funclist; + list_delete (funclist); + + funclist = ospf_opaque_type11_funclist; + list_delete (funclist); + return; +} + +static struct list * +ospf_get_opaque_funclist (u_char lsa_type) +{ + struct list *funclist = NULL; + + switch (lsa_type) + { + case OPAQUE_TYPE_WILDCARD: + /* XXX + * This is an ugly trick to handle type-9/10/11 LSA altogether. + * Yes, "OPAQUE_TYPE_WILDCARD (value 0)" is not an LSA-type, nor + * an officially assigned opaque-type. + * Though it is possible that the value might be officially used + * in the future, we use it internally as a special label, for now. + */ + funclist = ospf_opaque_wildcard_funclist; + break; + case OSPF_OPAQUE_LINK_LSA: + funclist = ospf_opaque_type9_funclist; + break; + case OSPF_OPAQUE_AREA_LSA: + funclist = ospf_opaque_type10_funclist; + break; + case OSPF_OPAQUE_AS_LSA: + funclist = ospf_opaque_type11_funclist; + break; + default: + zlog_warn ("ospf_get_opaque_funclist: Unexpected LSA-type(%u)", lsa_type); + break; + } + return funclist; +} + +/* XXX: such a huge argument list can /not/ be healthy... */ +int +ospf_register_opaque_functab ( + u_char lsa_type, + u_char opaque_type, + int (* new_if_hook)(struct interface *ifp), + int (* del_if_hook)(struct interface *ifp), + void (* ism_change_hook)(struct ospf_interface *oi, int old_status), + void (* nsm_change_hook)(struct ospf_neighbor *nbr, int old_status), + void (* config_write_router)(struct vty *vty), + void (* config_write_if )(struct vty *vty, struct interface *ifp), + void (* config_write_debug )(struct vty *vty), + void (* show_opaque_info )(struct vty *vty, struct ospf_lsa *lsa), + int (* lsa_originator)(void *arg), + struct ospf_lsa *(* lsa_refresher )(struct ospf_lsa *lsa), + int (* new_lsa_hook)(struct ospf_lsa *lsa), + int (* del_lsa_hook)(struct ospf_lsa *lsa)) +{ + struct list *funclist; + struct ospf_opaque_functab *new; + int rc = -1; + + if ((funclist = ospf_get_opaque_funclist (lsa_type)) == NULL) + { + zlog_warn ("ospf_register_opaque_functab: Cannot get funclist" + " for Type-%u LSAs?", + lsa_type); + goto out; + } + else + { + struct listnode *node, *nnode; + struct ospf_opaque_functab *functab; + + for (ALL_LIST_ELEMENTS (funclist, node, nnode, functab)) + if (functab->opaque_type == opaque_type) + { + zlog_warn ("ospf_register_opaque_functab: Duplicated entry?:" + " lsa_type(%u), opaque_type(%u)", + lsa_type, opaque_type); + goto out; + } + } + + if ((new = XCALLOC (MTYPE_OSPF_OPAQUE_FUNCTAB, + sizeof (struct ospf_opaque_functab))) == NULL) + { + zlog_warn ("ospf_register_opaque_functab: XMALLOC: %s", + safe_strerror (errno)); + goto out; + } + + new->opaque_type = opaque_type; + new->oipt = NULL; + new->new_if_hook = new_if_hook; + new->del_if_hook = del_if_hook; + new->ism_change_hook = ism_change_hook; + new->nsm_change_hook = nsm_change_hook; + new->config_write_router = config_write_router; + new->config_write_if = config_write_if; + new->config_write_debug = config_write_debug; + new->show_opaque_info = show_opaque_info; + new->lsa_originator = lsa_originator; + new->lsa_refresher = lsa_refresher; + new->new_lsa_hook = new_lsa_hook; + new->del_lsa_hook = del_lsa_hook; + + listnode_add (funclist, new); + rc = 0; + +out: + return rc; +} + +void +ospf_delete_opaque_functab (u_char lsa_type, u_char opaque_type) +{ + struct list *funclist; + struct listnode *node, *nnode; + struct ospf_opaque_functab *functab; + + if ((funclist = ospf_get_opaque_funclist (lsa_type)) != NULL) + for (ALL_LIST_ELEMENTS (funclist, node, nnode, functab)) + { + if (functab->opaque_type == opaque_type) + { + /* Cleanup internal control information, if it still remains. */ + if (functab->oipt != NULL) + free_opaque_info_per_type (functab->oipt); + + /* Dequeue listnode entry from the list. */ + listnode_delete (funclist, functab); + + /* Avoid misjudgement in the next lookup. */ + if (listcount (funclist) == 0) + funclist->head = funclist->tail = NULL; + + XFREE (MTYPE_OSPF_OPAQUE_FUNCTAB, functab); + break; + } + } + + return; +} + +static struct ospf_opaque_functab * +ospf_opaque_functab_lookup (struct ospf_lsa *lsa) +{ + struct list *funclist; + struct listnode *node; + struct ospf_opaque_functab *functab; + u_char key = GET_OPAQUE_TYPE (ntohl (lsa->data->id.s_addr)); + + if ((funclist = ospf_get_opaque_funclist (lsa->data->type)) != NULL) + for (ALL_LIST_ELEMENTS_RO (funclist, node, functab)) + if (functab->opaque_type == key) + return functab; + + return NULL; +} + +/*------------------------------------------------------------------------* + * Followings are management functions for self-originated LSA entries. + *------------------------------------------------------------------------*/ + +/* + * Opaque-LSA control information per opaque-type. + * Single Opaque-Type may have multiple instances; each of them will be + * identified by their opaque-id. + */ +struct opaque_info_per_type +{ + u_char lsa_type; + u_char opaque_type; + + enum { PROC_NORMAL, PROC_SUSPEND } status; + + /* + * Thread for (re-)origination scheduling for this opaque-type. + * + * Initial origination of Opaque-LSAs is controlled by generic + * Opaque-LSA handling module so that same opaque-type entries are + * called all at once when certain conditions are met. + * However, there might be cases that some Opaque-LSA clients need + * to (re-)originate their own Opaque-LSAs out-of-sync with others. + * This thread is prepared for that specific purpose. + */ + struct thread *t_opaque_lsa_self; + + /* + * Backpointer to an "owner" which is LSA-type dependent. + * type-9: struct ospf_interface + * type-10: struct ospf_area + * type-11: struct ospf + */ + void *owner; + + /* Collection of callback functions for this opaque-type. */ + struct ospf_opaque_functab *functab; + + /* List of Opaque-LSA control informations per opaque-id. */ + struct list *id_list; +}; + +/* Opaque-LSA control information per opaque-id. */ +struct opaque_info_per_id +{ + u_int32_t opaque_id; + + /* Thread for refresh/flush scheduling for this opaque-type/id. */ + struct thread *t_opaque_lsa_self; + + /* Backpointer to Opaque-LSA control information per opaque-type. */ + struct opaque_info_per_type *opqctl_type; + + /* Here comes an actual Opaque-LSA entry for this opaque-type/id. */ + struct ospf_lsa *lsa; +}; + +static struct opaque_info_per_type *register_opaque_info_per_type (struct ospf_opaque_functab *functab, struct ospf_lsa *new); +static struct opaque_info_per_type *lookup_opaque_info_by_type (struct ospf_lsa *lsa); +static struct opaque_info_per_id *register_opaque_info_per_id (struct opaque_info_per_type *oipt, struct ospf_lsa *new); +static struct opaque_info_per_id *lookup_opaque_info_by_id (struct opaque_info_per_type *oipt, struct ospf_lsa *lsa); +static struct opaque_info_per_id *register_opaque_lsa (struct ospf_lsa *new); + + +static struct opaque_info_per_type * +register_opaque_info_per_type (struct ospf_opaque_functab *functab, + struct ospf_lsa *new) +{ + struct ospf *top; + struct opaque_info_per_type *oipt; + + if ((oipt = XCALLOC (MTYPE_OPAQUE_INFO_PER_TYPE, + sizeof (struct opaque_info_per_type))) == NULL) + { + zlog_warn ("register_opaque_info_per_type: XMALLOC: %s", safe_strerror (errno)); + goto out; + } + + switch (new->data->type) + { + case OSPF_OPAQUE_LINK_LSA: + oipt->owner = new->oi; + listnode_add (new->oi->opaque_lsa_self, oipt); + break; + case OSPF_OPAQUE_AREA_LSA: + oipt->owner = new->area; + listnode_add (new->area->opaque_lsa_self, oipt); + break; + case OSPF_OPAQUE_AS_LSA: + top = ospf_lookup (); + if (new->area != NULL && (top = new->area->ospf) == NULL) + { + free_opaque_info_per_type ((void *) oipt); + oipt = NULL; + goto out; /* This case may not exist. */ + } + oipt->owner = top; + listnode_add (top->opaque_lsa_self, oipt); + break; + default: + zlog_warn ("register_opaque_info_per_type: Unexpected LSA-type(%u)", new->data->type); + free_opaque_info_per_type ((void *) oipt); + oipt = NULL; + goto out; /* This case may not exist. */ + } + + oipt->lsa_type = new->data->type; + oipt->opaque_type = GET_OPAQUE_TYPE (ntohl (new->data->id.s_addr)); + oipt->status = PROC_NORMAL; + oipt->t_opaque_lsa_self = NULL; + oipt->functab = functab; + functab->oipt = oipt; + oipt->id_list = list_new (); + oipt->id_list->del = free_opaque_info_per_id; + +out: + return oipt; +} + +static void +free_opaque_info_per_type (void *val) +{ + struct opaque_info_per_type *oipt = (struct opaque_info_per_type *) val; + struct opaque_info_per_id *oipi; + struct ospf_lsa *lsa; + struct listnode *node, *nnode; + + /* Control information per opaque-id may still exist. */ + for (ALL_LIST_ELEMENTS (oipt->id_list, node, nnode, oipi)) + { + if ((lsa = oipi->lsa) == NULL) + continue; + if (IS_LSA_MAXAGE (lsa)) + continue; + ospf_opaque_lsa_flush_schedule (lsa); + } + + /* Remove "oipt" from its owner's self-originated LSA list. */ + switch (oipt->lsa_type) + { + case OSPF_OPAQUE_LINK_LSA: + { + struct ospf_interface *oi = (struct ospf_interface *)(oipt->owner); + listnode_delete (oi->opaque_lsa_self, oipt); + break; + } + case OSPF_OPAQUE_AREA_LSA: + { + struct ospf_area *area = (struct ospf_area *)(oipt->owner); + listnode_delete (area->opaque_lsa_self, oipt); + break; + } + case OSPF_OPAQUE_AS_LSA: + { + struct ospf *top = (struct ospf *)(oipt->owner); + listnode_delete (top->opaque_lsa_self, oipt); + break; + } + default: + zlog_warn ("free_opaque_info_per_type: Unexpected LSA-type(%u)", oipt->lsa_type); + break; /* This case may not exist. */ + } + + OSPF_TIMER_OFF (oipt->t_opaque_lsa_self); + list_delete (oipt->id_list); + XFREE (MTYPE_OPAQUE_INFO_PER_TYPE, oipt); + return; +} + +static struct opaque_info_per_type * +lookup_opaque_info_by_type (struct ospf_lsa *lsa) +{ + struct ospf *top; + struct ospf_area *area; + struct ospf_interface *oi; + struct list *listtop = NULL; + struct listnode *node, *nnode; + struct opaque_info_per_type *oipt = NULL; + u_char key = GET_OPAQUE_TYPE (ntohl (lsa->data->id.s_addr)); + + switch (lsa->data->type) + { + case OSPF_OPAQUE_LINK_LSA: + if ((oi = lsa->oi) != NULL) + listtop = oi->opaque_lsa_self; + else + zlog_warn ("Type-9 Opaque-LSA: Reference to OI is missing?"); + break; + case OSPF_OPAQUE_AREA_LSA: + if ((area = lsa->area) != NULL) + listtop = area->opaque_lsa_self; + else + zlog_warn ("Type-10 Opaque-LSA: Reference to AREA is missing?"); + break; + case OSPF_OPAQUE_AS_LSA: + top = ospf_lookup (); + if ((area = lsa->area) != NULL && (top = area->ospf) == NULL) + { + zlog_warn ("Type-11 Opaque-LSA: Reference to OSPF is missing?"); + break; /* Unlikely to happen. */ + } + listtop = top->opaque_lsa_self; + break; + default: + zlog_warn ("lookup_opaque_info_by_type: Unexpected LSA-type(%u)", lsa->data->type); + break; + } + + if (listtop != NULL) + for (ALL_LIST_ELEMENTS (listtop, node, nnode, oipt)) + if (oipt->opaque_type == key) + return oipt; + + return NULL; +} + +static struct opaque_info_per_id * +register_opaque_info_per_id (struct opaque_info_per_type *oipt, + struct ospf_lsa *new) +{ + struct opaque_info_per_id *oipi; + + if ((oipi = XCALLOC (MTYPE_OPAQUE_INFO_PER_ID, + sizeof (struct opaque_info_per_id))) == NULL) + { + zlog_warn ("register_opaque_info_per_id: XMALLOC: %s", safe_strerror (errno)); + goto out; + } + oipi->opaque_id = GET_OPAQUE_ID (ntohl (new->data->id.s_addr)); + oipi->t_opaque_lsa_self = NULL; + oipi->opqctl_type = oipt; + oipi->lsa = ospf_lsa_lock (new); + + listnode_add (oipt->id_list, oipi); + +out: + return oipi; +} + +static void +free_opaque_info_per_id (void *val) +{ + struct opaque_info_per_id *oipi = (struct opaque_info_per_id *) val; + + OSPF_TIMER_OFF (oipi->t_opaque_lsa_self); + if (oipi->lsa != NULL) + ospf_lsa_unlock (&oipi->lsa); + XFREE (MTYPE_OPAQUE_INFO_PER_ID, oipi); + return; +} + +static struct opaque_info_per_id * +lookup_opaque_info_by_id (struct opaque_info_per_type *oipt, + struct ospf_lsa *lsa) +{ + struct listnode *node, *nnode; + struct opaque_info_per_id *oipi; + u_int32_t key = GET_OPAQUE_ID (ntohl (lsa->data->id.s_addr)); + + for (ALL_LIST_ELEMENTS (oipt->id_list, node, nnode, oipi)) + if (oipi->opaque_id == key) + return oipi; + + return NULL; +} + +static struct opaque_info_per_id * +register_opaque_lsa (struct ospf_lsa *new) +{ + struct ospf_opaque_functab *functab; + struct opaque_info_per_type *oipt; + struct opaque_info_per_id *oipi = NULL; + + if ((functab = ospf_opaque_functab_lookup (new)) == NULL) + goto out; + + if ((oipt = lookup_opaque_info_by_type (new)) == NULL + && (oipt = register_opaque_info_per_type (functab, new)) == NULL) + goto out; + + if ((oipi = register_opaque_info_per_id (oipt, new)) == NULL) + goto out; + +out: + return oipi; +} + +/*------------------------------------------------------------------------* + * Followings are (vty) configuration functions for Opaque-LSAs handling. + *------------------------------------------------------------------------*/ + +DEFUN (capability_opaque, + capability_opaque_cmd, + "capability opaque", + "Enable specific OSPF feature\n" + "Opaque LSA\n") +{ + struct ospf *ospf = (struct ospf *) vty->index; + + /* Turn on the "master switch" of opaque-lsa capability. */ + if (!CHECK_FLAG (ospf->config, OSPF_OPAQUE_CAPABLE)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Opaque capability: OFF -> ON"); + + SET_FLAG (ospf->config, OSPF_OPAQUE_CAPABLE); + ospf_renegotiate_optional_capabilities (ospf); + } + return CMD_SUCCESS; +} + +ALIAS (capability_opaque, + ospf_opaque_capable_cmd, + "ospf opaque-lsa", + "OSPF specific commands\n" + "Enable the Opaque-LSA capability (rfc2370)\n") + +DEFUN (no_capability_opaque, + no_capability_opaque_cmd, + "no capability opaque", + NO_STR + "Enable specific OSPF feature\n" + "Opaque LSA\n") +{ + struct ospf *ospf = (struct ospf *) vty->index; + + /* Turn off the "master switch" of opaque-lsa capability. */ + if (CHECK_FLAG (ospf->config, OSPF_OPAQUE_CAPABLE)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Opaque capability: ON -> OFF"); + + UNSET_FLAG (ospf->config, OSPF_OPAQUE_CAPABLE); + ospf_renegotiate_optional_capabilities (ospf); + } + return CMD_SUCCESS; +} + +ALIAS (no_capability_opaque, + no_ospf_opaque_capable_cmd, + "no ospf opaque-lsa", + NO_STR + "OSPF specific commands\n" + "Disable the Opaque-LSA capability (rfc2370)\n") + +static void +ospf_opaque_register_vty (void) +{ + install_element (OSPF_NODE, &capability_opaque_cmd); + install_element (OSPF_NODE, &no_capability_opaque_cmd); + install_element (OSPF_NODE, &ospf_opaque_capable_cmd); + install_element (OSPF_NODE, &no_ospf_opaque_capable_cmd); + return; +} + +/*------------------------------------------------------------------------* + * Followings are collection of user-registered function callers. + *------------------------------------------------------------------------*/ + +static int +opaque_lsa_new_if_callback (struct list *funclist, struct interface *ifp) +{ + struct listnode *node, *nnode; + struct ospf_opaque_functab *functab; + int rc = -1; + + for (ALL_LIST_ELEMENTS (funclist, node, nnode, functab)) + if (functab->new_if_hook != NULL) + if ((* functab->new_if_hook)(ifp) != 0) + goto out; + rc = 0; +out: + return rc; +} + +static int +opaque_lsa_del_if_callback (struct list *funclist, struct interface *ifp) +{ + struct listnode *node, *nnode; + struct ospf_opaque_functab *functab; + int rc = -1; + + for (ALL_LIST_ELEMENTS (funclist, node, nnode, functab)) + if (functab->del_if_hook != NULL) + if ((* functab->del_if_hook)(ifp) != 0) + goto out; + rc = 0; +out: + return rc; +} + +static void +opaque_lsa_ism_change_callback (struct list *funclist, + struct ospf_interface *oi, int old_status) +{ + struct listnode *node, *nnode; + struct ospf_opaque_functab *functab; + + for (ALL_LIST_ELEMENTS (funclist, node, nnode, functab)) + if (functab->ism_change_hook != NULL) + (* functab->ism_change_hook)(oi, old_status); + + return; +} + +static void +opaque_lsa_nsm_change_callback (struct list *funclist, + struct ospf_neighbor *nbr, int old_status) +{ + struct listnode *node, *nnode; + struct ospf_opaque_functab *functab; + + for (ALL_LIST_ELEMENTS (funclist, node, nnode, functab)) + if (functab->nsm_change_hook != NULL) + (* functab->nsm_change_hook)(nbr, old_status); + return; +} + +static void +opaque_lsa_config_write_router_callback (struct list *funclist, + struct vty *vty) +{ + struct listnode *node, *nnode; + struct ospf_opaque_functab *functab; + + for (ALL_LIST_ELEMENTS (funclist, node, nnode, functab)) + if (functab->config_write_router != NULL) + (* functab->config_write_router)(vty); + return; +} + +static void +opaque_lsa_config_write_if_callback (struct list *funclist, + struct vty *vty, struct interface *ifp) +{ + struct listnode *node, *nnode; + struct ospf_opaque_functab *functab; + + for (ALL_LIST_ELEMENTS (funclist, node, nnode, functab)) + if (functab->config_write_if != NULL) + (* functab->config_write_if)(vty, ifp); + return; +} + +static void +opaque_lsa_config_write_debug_callback (struct list *funclist, struct vty *vty) +{ + struct listnode *node, *nnode; + struct ospf_opaque_functab *functab; + + for (ALL_LIST_ELEMENTS (funclist, node, nnode, functab)) + if (functab->config_write_debug != NULL) + (* functab->config_write_debug)(vty); + return; +} + +static int +opaque_lsa_originate_callback (struct list *funclist, void *lsa_type_dependent) +{ + struct listnode *node, *nnode; + struct ospf_opaque_functab *functab; + int rc = -1; + + for (ALL_LIST_ELEMENTS (funclist, node, nnode, functab)) + if (functab->lsa_originator != NULL) + if ((* functab->lsa_originator)(lsa_type_dependent) != 0) + goto out; + rc = 0; +out: + return rc; +} + +static int +new_lsa_callback (struct list *funclist, struct ospf_lsa *lsa) +{ + struct listnode *node, *nnode; + struct ospf_opaque_functab *functab; + int rc = -1; + + /* This function handles ALL types of LSAs, not only opaque ones. */ + for (ALL_LIST_ELEMENTS (funclist, node, nnode, functab)) + if (functab->new_lsa_hook != NULL) + if ((* functab->new_lsa_hook)(lsa) != 0) + goto out; + rc = 0; +out: + return rc; +} + +static int +del_lsa_callback (struct list *funclist, struct ospf_lsa *lsa) +{ + struct listnode *node, *nnode; + struct ospf_opaque_functab *functab; + int rc = -1; + + /* This function handles ALL types of LSAs, not only opaque ones. */ + for (ALL_LIST_ELEMENTS (funclist, node, nnode, functab)) + if (functab->del_lsa_hook != NULL) + if ((* functab->del_lsa_hook)(lsa) != 0) + goto out; + rc = 0; +out: + return rc; +} + +/*------------------------------------------------------------------------* + * Followings are glue functions to call Opaque-LSA specific processing. + *------------------------------------------------------------------------*/ + +int +ospf_opaque_new_if (struct interface *ifp) +{ + struct list *funclist; + int rc = -1; + + funclist = ospf_opaque_wildcard_funclist; + if (opaque_lsa_new_if_callback (funclist, ifp) != 0) + goto out; + + funclist = ospf_opaque_type9_funclist; + if (opaque_lsa_new_if_callback (funclist, ifp) != 0) + goto out; + + funclist = ospf_opaque_type10_funclist; + if (opaque_lsa_new_if_callback (funclist, ifp) != 0) + goto out; + + funclist = ospf_opaque_type11_funclist; + if (opaque_lsa_new_if_callback (funclist, ifp) != 0) + goto out; + + rc = 0; +out: + return rc; +} + +int +ospf_opaque_del_if (struct interface *ifp) +{ + struct list *funclist; + int rc = -1; + + funclist = ospf_opaque_wildcard_funclist; + if (opaque_lsa_del_if_callback (funclist, ifp) != 0) + goto out; + + funclist = ospf_opaque_type9_funclist; + if (opaque_lsa_del_if_callback (funclist, ifp) != 0) + goto out; + + funclist = ospf_opaque_type10_funclist; + if (opaque_lsa_del_if_callback (funclist, ifp) != 0) + goto out; + + funclist = ospf_opaque_type11_funclist; + if (opaque_lsa_del_if_callback (funclist, ifp) != 0) + goto out; + + rc = 0; +out: + return rc; +} + +void +ospf_opaque_ism_change (struct ospf_interface *oi, int old_status) +{ + struct list *funclist; + + funclist = ospf_opaque_wildcard_funclist; + opaque_lsa_ism_change_callback (funclist, oi, old_status); + + funclist = ospf_opaque_type9_funclist; + opaque_lsa_ism_change_callback (funclist, oi, old_status); + + funclist = ospf_opaque_type10_funclist; + opaque_lsa_ism_change_callback (funclist, oi, old_status); + + funclist = ospf_opaque_type11_funclist; + opaque_lsa_ism_change_callback (funclist, oi, old_status); + + return; +} + +void +ospf_opaque_nsm_change (struct ospf_neighbor *nbr, int old_state) +{ + struct ospf *top; + struct list *funclist; + + if ((top = oi_to_top (nbr->oi)) == NULL) + goto out; + + if (old_state != NSM_Full && nbr->state == NSM_Full) + { + if (CHECK_FLAG (nbr->options, OSPF_OPTION_O)) + { + if (! CHECK_FLAG (top->opaque, OPAQUE_OPERATION_READY_BIT)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Opaque-LSA: Now get operational!"); + + SET_FLAG (top->opaque, OPAQUE_OPERATION_READY_BIT); + } + + ospf_opaque_lsa_originate_schedule (nbr->oi, NULL); + } + } + else + if (old_state == NSM_Full && nbr->state != NSM_Full) + { +#ifdef NOTYET + /* + * If no more opaque-capable full-state neighbor remains in the + * flooding scope which corresponds to Opaque-LSA type, periodic + * LS flooding should be stopped. + */ +#endif /* NOTYET */ + ; + } + + funclist = ospf_opaque_wildcard_funclist; + opaque_lsa_nsm_change_callback (funclist, nbr, old_state); + + funclist = ospf_opaque_type9_funclist; + opaque_lsa_nsm_change_callback (funclist, nbr, old_state); + + funclist = ospf_opaque_type10_funclist; + opaque_lsa_nsm_change_callback (funclist, nbr, old_state); + + funclist = ospf_opaque_type11_funclist; + opaque_lsa_nsm_change_callback (funclist, nbr, old_state); + +out: + return; +} + +void +ospf_opaque_config_write_router (struct vty *vty, struct ospf *ospf) +{ + struct list *funclist; + + if (CHECK_FLAG (ospf->config, OSPF_OPAQUE_CAPABLE)) + vty_out (vty, " capability opaque%s", VTY_NEWLINE); + + funclist = ospf_opaque_wildcard_funclist; + opaque_lsa_config_write_router_callback (funclist, vty); + + funclist = ospf_opaque_type9_funclist; + opaque_lsa_config_write_router_callback (funclist, vty); + + funclist = ospf_opaque_type10_funclist; + opaque_lsa_config_write_router_callback (funclist, vty); + + funclist = ospf_opaque_type11_funclist; + opaque_lsa_config_write_router_callback (funclist, vty); + + return; +} + +void +ospf_opaque_config_write_if (struct vty *vty, struct interface *ifp) +{ + struct list *funclist; + + funclist = ospf_opaque_wildcard_funclist; + opaque_lsa_config_write_if_callback (funclist, vty, ifp); + + funclist = ospf_opaque_type9_funclist; + opaque_lsa_config_write_if_callback (funclist, vty, ifp); + + funclist = ospf_opaque_type10_funclist; + opaque_lsa_config_write_if_callback (funclist, vty, ifp); + + funclist = ospf_opaque_type11_funclist; + opaque_lsa_config_write_if_callback (funclist, vty, ifp); + + return; +} + +void +ospf_opaque_config_write_debug (struct vty *vty) +{ + struct list *funclist; + + funclist = ospf_opaque_wildcard_funclist; + opaque_lsa_config_write_debug_callback (funclist, vty); + + funclist = ospf_opaque_type9_funclist; + opaque_lsa_config_write_debug_callback (funclist, vty); + + funclist = ospf_opaque_type10_funclist; + opaque_lsa_config_write_debug_callback (funclist, vty); + + funclist = ospf_opaque_type11_funclist; + opaque_lsa_config_write_debug_callback (funclist, vty); + + return; +} + +void +show_opaque_info_detail (struct vty *vty, struct ospf_lsa *lsa) +{ + struct lsa_header *lsah = (struct lsa_header *) lsa->data; + u_int32_t lsid = ntohl (lsah->id.s_addr); + u_char opaque_type = GET_OPAQUE_TYPE (lsid); + u_int32_t opaque_id = GET_OPAQUE_ID (lsid); + struct ospf_opaque_functab *functab; + + /* Switch output functionality by vty address. */ + if (vty != NULL) + { + vty_out (vty, " Opaque-Type %u (%s)%s", opaque_type, + ospf_opaque_type_name (opaque_type), VTY_NEWLINE); + vty_out (vty, " Opaque-ID 0x%x%s", opaque_id, VTY_NEWLINE); + + vty_out (vty, " Opaque-Info: %u octets of data%s%s", + ntohs (lsah->length) - OSPF_LSA_HEADER_SIZE, + VALID_OPAQUE_INFO_LEN(lsah) ? "" : "(Invalid length?)", + VTY_NEWLINE); + } + else + { + zlog_debug (" Opaque-Type %u (%s)", opaque_type, + ospf_opaque_type_name (opaque_type)); + zlog_debug (" Opaque-ID 0x%x", opaque_id); + + zlog_debug (" Opaque-Info: %u octets of data%s", + ntohs (lsah->length) - OSPF_LSA_HEADER_SIZE, + VALID_OPAQUE_INFO_LEN(lsah) ? "" : "(Invalid length?)"); + } + + /* Call individual output functions. */ + if ((functab = ospf_opaque_functab_lookup (lsa)) != NULL) + if (functab->show_opaque_info != NULL) + (* functab->show_opaque_info)(vty, lsa); + + return; +} + +void +ospf_opaque_lsa_dump (struct stream *s, u_int16_t length) +{ + struct ospf_lsa lsa; + + lsa.data = (struct lsa_header *) STREAM_PNT (s); + show_opaque_info_detail (NULL, &lsa); + return; +} + +static int +ospf_opaque_lsa_install_hook (struct ospf_lsa *lsa) +{ + struct list *funclist; + int rc = -1; + + /* + * Some Opaque-LSA user may want to monitor every LSA installation + * into the LSDB, regardless with target LSA type. + */ + funclist = ospf_opaque_wildcard_funclist; + if (new_lsa_callback (funclist, lsa) != 0) + goto out; + + funclist = ospf_opaque_type9_funclist; + if (new_lsa_callback (funclist, lsa) != 0) + goto out; + + funclist = ospf_opaque_type10_funclist; + if (new_lsa_callback (funclist, lsa) != 0) + goto out; + + funclist = ospf_opaque_type11_funclist; + if (new_lsa_callback (funclist, lsa) != 0) + goto out; + + rc = 0; +out: + return rc; +} + +static int +ospf_opaque_lsa_delete_hook (struct ospf_lsa *lsa) +{ + struct list *funclist; + int rc = -1; + + /* + * Some Opaque-LSA user may want to monitor every LSA deletion + * from the LSDB, regardless with target LSA type. + */ + funclist = ospf_opaque_wildcard_funclist; + if (del_lsa_callback (funclist, lsa) != 0) + goto out; + + funclist = ospf_opaque_type9_funclist; + if (del_lsa_callback (funclist, lsa) != 0) + goto out; + + funclist = ospf_opaque_type10_funclist; + if (del_lsa_callback (funclist, lsa) != 0) + goto out; + + funclist = ospf_opaque_type11_funclist; + if (del_lsa_callback (funclist, lsa) != 0) + goto out; + + rc = 0; +out: + return rc; +} + +/*------------------------------------------------------------------------* + * Followings are Opaque-LSA origination/refresh management functions. + *------------------------------------------------------------------------*/ + +static int ospf_opaque_type9_lsa_originate (struct thread *t); +static int ospf_opaque_type10_lsa_originate (struct thread *t); +static int ospf_opaque_type11_lsa_originate (struct thread *t); +static void ospf_opaque_lsa_reoriginate_resume (struct list *listtop, void *arg); + +void +ospf_opaque_lsa_originate_schedule (struct ospf_interface *oi, int *delay0) +{ + struct ospf *top; + struct ospf_area *area; + struct listnode *node, *nnode; + struct opaque_info_per_type *oipt; + int delay = 0; + + if ((top = oi_to_top (oi)) == NULL || (area = oi->area) == NULL) + { + zlog_warn ("ospf_opaque_lsa_originate_schedule: Invalid argument?"); + goto out; + } + + /* It may not a right time to schedule origination now. */ + if (! CHECK_FLAG (top->opaque, OPAQUE_OPERATION_READY_BIT)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_opaque_lsa_originate_schedule: Not operational."); + goto out; /* This is not an error. */ + } + + if (delay0 != NULL) + delay = *delay0; + + /* + * There might be some entries that have been waiting for triggering + * of per opaque-type re-origination get resumed. + */ + ospf_opaque_lsa_reoriginate_resume ( oi->opaque_lsa_self, (void *) oi); + ospf_opaque_lsa_reoriginate_resume (area->opaque_lsa_self, (void *) area); + ospf_opaque_lsa_reoriginate_resume ( top->opaque_lsa_self, (void *) top); + + /* + * Now, schedule origination of all Opaque-LSAs per opaque-type. + */ + if (! list_isempty (ospf_opaque_type9_funclist) + && list_isempty (oi->opaque_lsa_self) + && oi->t_opaque_lsa_self == NULL) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Schedule Type-9 Opaque-LSA origination in %d ms later.", delay); + oi->t_opaque_lsa_self = + thread_add_timer_msec (master, ospf_opaque_type9_lsa_originate, oi, delay); + delay += top->min_ls_interval; + } + + if (! list_isempty (ospf_opaque_type10_funclist) + && list_isempty (area->opaque_lsa_self) + && area->t_opaque_lsa_self == NULL) + { + /* + * One AREA may contain multiple OIs, but above 2nd and 3rd + * conditions prevent from scheduling the originate function + * again and again. + */ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Schedule Type-10 Opaque-LSA origination in %d ms later.", delay); + area->t_opaque_lsa_self = + thread_add_timer_msec (master, ospf_opaque_type10_lsa_originate, + area, delay); + delay += top->min_ls_interval; + } + + if (! list_isempty (ospf_opaque_type11_funclist) + && list_isempty (top->opaque_lsa_self) + && top->t_opaque_lsa_self == NULL) + { + /* + * One OSPF may contain multiple AREAs, but above 2nd and 3rd + * conditions prevent from scheduling the originate function + * again and again. + */ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Schedule Type-11 Opaque-LSA origination in %d ms later.", delay); + top->t_opaque_lsa_self = + thread_add_timer_msec (master, ospf_opaque_type11_lsa_originate, + top, delay); + delay += top->min_ls_interval; + } + + /* + * Following section treats a special situation that this node's + * opaque capability has changed as "ON -> OFF -> ON". + */ + if (! list_isempty (ospf_opaque_type9_funclist) + && ! list_isempty (oi->opaque_lsa_self)) + { + for (ALL_LIST_ELEMENTS (oi->opaque_lsa_self, node, nnode, oipt)) + { + /* + * removed the test for + * (! list_isempty (oipt->id_list)) * Handler is already active. * + * because opaque cababilities ON -> OFF -> ON result in list_isempty (oipt->id_list) + * not being empty. + */ + if (oipt->t_opaque_lsa_self != NULL /* Waiting for a thread call. */ + || oipt->status == PROC_SUSPEND) /* Cannot originate now. */ + continue; + + ospf_opaque_lsa_reoriginate_schedule ((void *) oi, + OSPF_OPAQUE_LINK_LSA, oipt->opaque_type); + } + } + + if (! list_isempty (ospf_opaque_type10_funclist) + && ! list_isempty (area->opaque_lsa_self)) + { + for (ALL_LIST_ELEMENTS (area->opaque_lsa_self, node, nnode, oipt)) + { + /* + * removed the test for + * (! list_isempty (oipt->id_list)) * Handler is already active. * + * because opaque cababilities ON -> OFF -> ON result in list_isempty (oipt->id_list) + * not being empty. + */ + if (oipt->t_opaque_lsa_self != NULL /* Waiting for a thread call. */ + || oipt->status == PROC_SUSPEND) /* Cannot originate now. */ + continue; + + ospf_opaque_lsa_reoriginate_schedule ((void *) area, + OSPF_OPAQUE_AREA_LSA, oipt->opaque_type); + } + } + + if (! list_isempty (ospf_opaque_type11_funclist) + && ! list_isempty (top->opaque_lsa_self)) + { + for (ALL_LIST_ELEMENTS (top->opaque_lsa_self, node, nnode, oipt)) + { + /* + * removed the test for + * (! list_isempty (oipt->id_list)) * Handler is already active. * + * because opaque cababilities ON -> OFF -> ON result in list_isempty (oipt->id_list) + * not being empty. + */ + if (oipt->t_opaque_lsa_self != NULL /* Waiting for a thread call. */ + || oipt->status == PROC_SUSPEND) /* Cannot originate now. */ + continue; + + ospf_opaque_lsa_reoriginate_schedule ((void *) top, + OSPF_OPAQUE_AS_LSA, oipt->opaque_type); + } + } + + if (delay0 != NULL) + *delay0 = delay; + +out: + return; +} + +static int +ospf_opaque_type9_lsa_originate (struct thread *t) +{ + struct ospf_interface *oi; + int rc; + + oi = THREAD_ARG (t); + oi->t_opaque_lsa_self = NULL; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Timer[Type9-LSA]: Originate Opaque-LSAs for OI %s", + IF_NAME (oi)); + + rc = opaque_lsa_originate_callback (ospf_opaque_type9_funclist, oi); + + return rc; +} + +static int +ospf_opaque_type10_lsa_originate (struct thread *t) +{ + struct ospf_area *area; + int rc; + + area = THREAD_ARG (t); + area->t_opaque_lsa_self = NULL; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Timer[Type10-LSA]: Originate Opaque-LSAs for Area %s", + inet_ntoa (area->area_id)); + + rc = opaque_lsa_originate_callback (ospf_opaque_type10_funclist, area); + + return rc; +} + +static int +ospf_opaque_type11_lsa_originate (struct thread *t) +{ + struct ospf *top; + int rc; + + top = THREAD_ARG (t); + top->t_opaque_lsa_self = NULL; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Timer[Type11-LSA]: Originate AS-External Opaque-LSAs"); + + rc = opaque_lsa_originate_callback (ospf_opaque_type11_funclist, top); + + return rc; +} + +static void +ospf_opaque_lsa_reoriginate_resume (struct list *listtop, void *arg) +{ + struct listnode *node, *nnode; + struct opaque_info_per_type *oipt; + struct ospf_opaque_functab *functab; + + if (listtop == NULL) + goto out; + + /* + * Pickup oipt entries those which in SUSPEND status, and give + * them a chance to start re-origination now. + */ + for (ALL_LIST_ELEMENTS (listtop, node, nnode, oipt)) + { + if (oipt->status != PROC_SUSPEND) + continue; + + oipt->status = PROC_NORMAL; + + if ((functab = oipt->functab) == NULL + || functab->lsa_originator == NULL) + continue; + + if ((* functab->lsa_originator)(arg) != 0) + { + zlog_warn ("ospf_opaque_lsa_reoriginate_resume: Failed (opaque-type=%u)", oipt->opaque_type); + continue; + } + } + +out: + return; +} + +struct ospf_lsa * +ospf_opaque_lsa_install (struct ospf_lsa *lsa, int rt_recalc) +{ + struct ospf_lsa *new = NULL; + struct opaque_info_per_type *oipt; + struct opaque_info_per_id *oipi; + struct ospf *top; + + /* Don't take "rt_recalc" into consideration for now. *//* XXX */ + + if (! IS_LSA_SELF (lsa)) + { + new = lsa; /* Don't touch this LSA. */ + goto out; + } + + if (IS_DEBUG_OSPF (lsa, LSA_INSTALL)) + zlog_debug ("Install Type-%u Opaque-LSA: [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))); + + /* Replace the existing lsa with the new one. */ + if ((oipt = lookup_opaque_info_by_type (lsa)) != NULL + && (oipi = lookup_opaque_info_by_id (oipt, lsa)) != NULL) + { + ospf_lsa_unlock (&oipi->lsa); + oipi->lsa = ospf_lsa_lock (lsa); + } + /* Register the new lsa entry and get its control info. */ + else + if ((oipi = register_opaque_lsa (lsa)) == NULL) + { + zlog_warn ("ospf_opaque_lsa_install: register_opaque_lsa() ?"); + goto out; + } + + /* + * Make use of a common mechanism (ospf_lsa_refresh_walker) + * for periodic refresh of self-originated Opaque-LSAs. + */ + switch (lsa->data->type) + { + case OSPF_OPAQUE_LINK_LSA: + if ((top = oi_to_top (lsa->oi)) == NULL) + { + /* Above conditions must have passed. */ + zlog_warn ("ospf_opaque_lsa_install: Sonmething wrong?"); + goto out; + } + break; + case OSPF_OPAQUE_AREA_LSA: + if (lsa->area == NULL || (top = lsa->area->ospf) == NULL) + { + /* Above conditions must have passed. */ + zlog_warn ("ospf_opaque_lsa_install: Sonmething wrong?"); + goto out; + } + break; + case OSPF_OPAQUE_AS_LSA: + top = ospf_lookup (); + if (lsa->area != NULL && (top = lsa->area->ospf) == NULL) + { + /* Above conditions must have passed. */ + zlog_warn ("ospf_opaque_lsa_install: Sonmething wrong?"); + goto out; + } + break; + default: + zlog_warn ("ospf_opaque_lsa_install: Unexpected LSA-type(%u)", lsa->data->type); + goto out; + } + + ospf_refresher_register_lsa (top, lsa); + new = lsa; + +out: + return new; +} + +struct ospf_lsa * +ospf_opaque_lsa_refresh (struct ospf_lsa *lsa) +{ + struct ospf *ospf; + struct ospf_opaque_functab *functab; + struct ospf_lsa *new = NULL; + + ospf = ospf_lookup (); + + if ((functab = ospf_opaque_functab_lookup (lsa)) == NULL + || functab->lsa_refresher == NULL) + { + /* + * Though this LSA seems to have originated on this node, the + * handling module for this "lsa-type and opaque-type" was + * already deleted sometime ago. + * Anyway, this node still has a responsibility to flush this + * LSA from the routing domain. + */ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("LSA[Type%d:%s]: Flush stray Opaque-LSA", lsa->data->type, inet_ntoa (lsa->data->id)); + + lsa->data->ls_age = htons (OSPF_LSA_MAXAGE); + ospf_lsa_flush (ospf, lsa); + } + else + new = (* functab->lsa_refresher)(lsa); + + return new; +} + +/*------------------------------------------------------------------------* + * Followings are re-origination/refresh/flush operations of Opaque-LSAs, + * triggered by external interventions (vty session, signaling, etc). + *------------------------------------------------------------------------*/ + +#define OSPF_OPAQUE_TIMER_ON(T,F,L,V) \ + if (!(T)) \ + (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); +static int ospf_opaque_type10_lsa_reoriginate_timer (struct thread *t); +static int ospf_opaque_type11_lsa_reoriginate_timer (struct thread *t); +static int ospf_opaque_lsa_refresh_timer (struct thread *t); + +void +ospf_opaque_lsa_reoriginate_schedule (void *lsa_type_dependent, + u_char lsa_type, u_char opaque_type) +{ + struct ospf *top; + struct ospf_area dummy, *area = NULL; + struct ospf_interface *oi = NULL; + + struct ospf_lsa *lsa; + struct opaque_info_per_type *oipt; + int (*func) (struct thread * t) = NULL; + int delay; + + switch (lsa_type) + { + case OSPF_OPAQUE_LINK_LSA: + if ((oi = (struct ospf_interface *) lsa_type_dependent) == NULL) + { + zlog_warn ("ospf_opaque_lsa_reoriginate_schedule:" + " Type-9 Opaque-LSA: Invalid parameter?"); + goto out; + } + if ((top = oi_to_top (oi)) == NULL) + { + zlog_warn ("ospf_opaque_lsa_reoriginate_schedule: OI(%s) -> TOP?", + IF_NAME (oi)); + goto out; + } + if (!list_isempty (ospf_opaque_type9_funclist) + && list_isempty (oi->opaque_lsa_self) + && oi->t_opaque_lsa_self != NULL) + { + zlog_warn ("Type-9 Opaque-LSA (opaque_type=%u):" + " Common origination for OI(%s) has already started", + opaque_type, IF_NAME (oi)); + goto out; + } + func = ospf_opaque_type9_lsa_reoriginate_timer; + break; + case OSPF_OPAQUE_AREA_LSA: + if ((area = (struct ospf_area *) lsa_type_dependent) == NULL) + { + zlog_warn ("ospf_opaque_lsa_reoriginate_schedule:" + " Type-10 Opaque-LSA: Invalid parameter?"); + goto out; + } + if ((top = area->ospf) == NULL) + { + zlog_warn ("ospf_opaque_lsa_reoriginate_schedule:" + " AREA(%s) -> TOP?", inet_ntoa (area->area_id)); + goto out; + } + if (!list_isempty (ospf_opaque_type10_funclist) + && list_isempty (area->opaque_lsa_self) + && area->t_opaque_lsa_self != NULL) + { + zlog_warn ("Type-10 Opaque-LSA (opaque_type=%u):" + " Common origination for AREA(%s) has already started", + opaque_type, inet_ntoa (area->area_id)); + goto out; + } + func = ospf_opaque_type10_lsa_reoriginate_timer; + break; + case OSPF_OPAQUE_AS_LSA: + if ((top = (struct ospf *) lsa_type_dependent) == NULL) + { + zlog_warn ("ospf_opaque_lsa_reoriginate_schedule:" + " Type-11 Opaque-LSA: Invalid parameter?"); + goto out; + } + if (!list_isempty (ospf_opaque_type11_funclist) + && list_isempty (top->opaque_lsa_self) + && top->t_opaque_lsa_self != NULL) + { + zlog_warn ("Type-11 Opaque-LSA (opaque_type=%u):" + " Common origination has already started", opaque_type); + goto out; + } + + /* Fake "area" to pass "ospf" to a lookup function later. */ + dummy.ospf = top; + area = &dummy; + + func = ospf_opaque_type11_lsa_reoriginate_timer; + break; + default: + zlog_warn ("ospf_opaque_lsa_reoriginate_schedule:" + " Unexpected LSA-type(%u)", + lsa_type); + goto out; + } + + /* It may not a right time to schedule reorigination now. */ + if (!CHECK_FLAG (top->opaque, OPAQUE_OPERATION_READY_BIT)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_opaque_lsa_reoriginate_schedule: Not operational."); + goto out; /* This is not an error. */ + } + + /* Generate a dummy lsa to be passed for a lookup function. */ + lsa = pseudo_lsa (oi, area, lsa_type, opaque_type); + + if ((oipt = lookup_opaque_info_by_type (lsa)) == NULL) + { + struct ospf_opaque_functab *functab; + if ((functab = ospf_opaque_functab_lookup (lsa)) == NULL) + { + zlog_warn ("ospf_opaque_lsa_reoriginate_schedule:" + " No associated function?: lsa_type(%u)," + " opaque_type(%u)", + lsa_type, opaque_type); + goto out; + } + if ((oipt = register_opaque_info_per_type (functab, lsa)) == NULL) + { + zlog_warn ("ospf_opaque_lsa_reoriginate_schedule:" + " Cannot get a control info?: lsa_type(%u)," + " opaque_type(%u)", + lsa_type, opaque_type); + goto out; + } + } + + if (oipt->t_opaque_lsa_self != NULL) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Type-%u Opaque-LSA has already scheduled to" + " RE-ORIGINATE: [opaque-type=%u]", + lsa_type, GET_OPAQUE_TYPE (ntohl (lsa->data->id.s_addr))); + goto out; + } + + /* + * Different from initial origination time, in which various conditions + * (opaque capability, neighbor status etc) are assured by caller of + * the originating function "ospf_opaque_lsa_originate_schedule ()", + * it is highly possible that these conditions might not be satisfied + * at the time of re-origination function is to be called. + */ + delay = top->min_ls_interval; /* XXX */ + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Schedule Type-%u Opaque-LSA to RE-ORIGINATE in %d" + " ms later: [opaque-type=%u]", + lsa_type, delay, + GET_OPAQUE_TYPE (ntohl (lsa->data->id.s_addr))); + + OSPF_OPAQUE_TIMER_ON (oipt->t_opaque_lsa_self, func, oipt, delay); + +out: + return; +} + +static struct ospf_lsa * +pseudo_lsa (struct ospf_interface *oi, struct ospf_area *area, + u_char lsa_type, u_char opaque_type) +{ + static struct ospf_lsa lsa = { 0 }; + static struct lsa_header lsah = { 0 }; + u_int32_t tmp; + + lsa.oi = oi; + lsa.area = area; + lsa.data = &lsah; + + lsah.type = lsa_type; + tmp = SET_OPAQUE_LSID (opaque_type, 0); /* Opaque-ID is unused here. */ + lsah.id.s_addr = htonl (tmp); + + return &lsa; +} + +static int +ospf_opaque_type9_lsa_reoriginate_timer (struct thread *t) +{ + struct opaque_info_per_type *oipt; + struct ospf_opaque_functab *functab; + struct ospf *top; + struct ospf_interface *oi; + int rc = -1; + + oipt = THREAD_ARG (t); + oipt->t_opaque_lsa_self = NULL; + + if ((functab = oipt->functab) == NULL + || functab->lsa_originator == NULL) + { + zlog_warn ("ospf_opaque_type9_lsa_reoriginate_timer: No associated function?"); + goto out; + } + + oi = (struct ospf_interface *) oipt->owner; + if ((top = oi_to_top (oi)) == NULL) + { + zlog_warn ("ospf_opaque_type9_lsa_reoriginate_timer: Something wrong?"); + goto out; + } + + if (! CHECK_FLAG (top->config, OSPF_OPAQUE_CAPABLE) + || ! ospf_if_is_enable (oi) + || ospf_nbr_count_opaque_capable (oi) == 0) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Suspend re-origination of Type-9 Opaque-LSAs (opaque-type=%u) for a while...", oipt->opaque_type); + + oipt->status = PROC_SUSPEND; + rc = 0; + goto out; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Timer[Type9-LSA]: Re-originate Opaque-LSAs (opaque-type=%u) for OI (%s)", oipt->opaque_type, IF_NAME (oi)); + + rc = (* functab->lsa_originator)(oi); +out: + return rc; +} + +static int +ospf_opaque_type10_lsa_reoriginate_timer (struct thread *t) +{ + struct opaque_info_per_type *oipt; + struct ospf_opaque_functab *functab; + struct listnode *node, *nnode; + struct ospf *top; + struct ospf_area *area; + struct ospf_interface *oi; + int n, rc = -1; + + oipt = THREAD_ARG (t); + oipt->t_opaque_lsa_self = NULL; + + if ((functab = oipt->functab) == NULL + || functab->lsa_originator == NULL) + { + zlog_warn ("ospf_opaque_type10_lsa_reoriginate_timer: No associated function?"); + goto out; + } + + area = (struct ospf_area *) oipt->owner; + if (area == NULL || (top = area->ospf) == NULL) + { + zlog_warn ("ospf_opaque_type10_lsa_reoriginate_timer: Something wrong?"); + goto out; + } + + /* There must be at least one "opaque-capable, full-state" neighbor. */ + n = 0; + for (ALL_LIST_ELEMENTS (area->oiflist, node, nnode, oi)) + { + if ((n = ospf_nbr_count_opaque_capable (oi)) > 0) + break; + } + + if (n == 0 || ! CHECK_FLAG (top->config, OSPF_OPAQUE_CAPABLE)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Suspend re-origination of Type-10 Opaque-LSAs" + " (opaque-type=%u) for a while...", + oipt->opaque_type); + + oipt->status = PROC_SUSPEND; + rc = 0; + goto out; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Timer[Type10-LSA]: Re-originate Opaque-LSAs" + " (opaque-type=%u) for Area %s", + oipt->opaque_type, inet_ntoa (area->area_id)); + + rc = (* functab->lsa_originator)(area); +out: + return rc; +} + +static int +ospf_opaque_type11_lsa_reoriginate_timer (struct thread *t) +{ + struct opaque_info_per_type *oipt; + struct ospf_opaque_functab *functab; + struct ospf *top; + int rc = -1; + + oipt = THREAD_ARG (t); + oipt->t_opaque_lsa_self = NULL; + + if ((functab = oipt->functab) == NULL + || functab->lsa_originator == NULL) + { + zlog_warn ("ospf_opaque_type11_lsa_reoriginate_timer:" + " No associated function?"); + goto out; + } + + if ((top = (struct ospf *) oipt->owner) == NULL) + { + zlog_warn ("ospf_opaque_type11_lsa_reoriginate_timer: Something wrong?"); + goto out; + } + + if (! CHECK_FLAG (top->config, OSPF_OPAQUE_CAPABLE)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Suspend re-origination of Type-11 Opaque-LSAs (opaque-type=%u) for a while...", oipt->opaque_type); + + oipt->status = PROC_SUSPEND; + rc = 0; + goto out; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Timer[Type11-LSA]: Re-originate Opaque-LSAs (opaque-type=%u).", oipt->opaque_type); + + rc = (* functab->lsa_originator)(top); +out: + return rc; +} + +void +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 + || (oipi = lookup_opaque_info_by_id (oipt, lsa0)) == NULL) + { + zlog_warn ("ospf_opaque_lsa_refresh_schedule: Invalid parameter?"); + goto out; + } + + /* Given "lsa0" and current "oipi->lsa" may different, but harmless. */ + if ((lsa = oipi->lsa) == NULL) + { + zlog_warn ("ospf_opaque_lsa_refresh_schedule: Something wrong?"); + goto out; + } + + if (oipi->t_opaque_lsa_self != NULL) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Type-%u Opaque-LSA has already scheduled to REFRESH: [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))); + goto out; + } + + /* Delete this lsa from neighbor retransmit-list. */ + switch (lsa->data->type) + { + case OSPF_OPAQUE_LINK_LSA: + case OSPF_OPAQUE_AREA_LSA: + ospf_ls_retransmit_delete_nbr_area (lsa->area, lsa); + break; + case OSPF_OPAQUE_AS_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); + goto out; + } + + delay = ospf_lsa_refresh_delay (lsa); + + if (IS_DEBUG_OSPF_EVENT) + 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 * 1000); +out: + return; +} + +static int +ospf_opaque_lsa_refresh_timer (struct thread *t) +{ + struct opaque_info_per_id *oipi; + struct ospf_opaque_functab *functab; + struct ospf_lsa *lsa; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Timer[Opaque-LSA]: (Opaque-LSA Refresh expire)"); + + oipi = THREAD_ARG (t); + oipi->t_opaque_lsa_self = NULL; + + if ((lsa = oipi->lsa) != NULL) + if ((functab = oipi->opqctl_type->functab) != NULL) + if (functab->lsa_refresher != NULL) + (* functab->lsa_refresher)(lsa); + + return 0; +} + +void +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) + { + zlog_warn ("ospf_opaque_lsa_flush_schedule: Invalid parameter?"); + goto out; + } + + /* Given "lsa0" and current "oipi->lsa" may different, but harmless. */ + if ((lsa = oipi->lsa) == NULL) + { + zlog_warn ("ospf_opaque_lsa_flush_schedule: Something wrong?"); + goto out; + } + + /* Delete this lsa from neighbor retransmit-list. */ + switch (lsa->data->type) + { + case OSPF_OPAQUE_LINK_LSA: + case OSPF_OPAQUE_AREA_LSA: + ospf_ls_retransmit_delete_nbr_area (lsa->area, lsa); + break; + case OSPF_OPAQUE_AS_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); + goto out; + } + + /* Dequeue listnode entry from the list. */ + listnode_delete (oipt->id_list, oipi); + + /* Avoid misjudgement in the next lookup. */ + if (listcount (oipt->id_list) == 0) + oipt->id_list->head = oipt->id_list->tail = NULL; + + /* Disassociate internal control information with the given lsa. */ + free_opaque_info_per_id ((void *) oipi); + + /* Force given lsa's age to MaxAge. */ + lsa->data->ls_age = htons (OSPF_LSA_MAXAGE); + + if (IS_DEBUG_OSPF_EVENT) + 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 (top, lsa); + +out: + return; +} + +void +ospf_opaque_self_originated_lsa_received (struct ospf_neighbor *nbr, + struct ospf_lsa *lsa) +{ + struct ospf *top; + + if ((top = oi_to_top (nbr->oi)) == NULL) + return; + + /* + * Since these LSA entries are not yet installed into corresponding + * LSDB, just flush them without calling ospf_ls_maxage() afterward. + */ + lsa->data->ls_age = htons (OSPF_LSA_MAXAGE); + switch (lsa->data->type) + { + case OSPF_OPAQUE_LINK_LSA: + ospf_flood_through_area (nbr->oi->area, NULL/*inbr*/, lsa); + break; + case OSPF_OPAQUE_AREA_LSA: + ospf_flood_through_area (nbr->oi->area, NULL/*inbr*/, lsa); + break; + case OSPF_OPAQUE_AS_LSA: + 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. */ +} + +/*------------------------------------------------------------------------* + * Followings are util functions; probably be used by Opaque-LSAs only... + *------------------------------------------------------------------------*/ + +struct ospf * +oi_to_top (struct ospf_interface *oi) +{ + struct ospf *top = NULL; + struct ospf_area *area; + + if (oi == NULL || (area = oi->area) == NULL || (top = area->ospf) == NULL) + zlog_warn ("Broken relationship for \"OI -> AREA -> OSPF\"?"); + + return top; +} + diff --git a/ospfd/ospf_opaque.h b/ospfd/ospf_opaque.h new file mode 100644 index 0000000..2ac9b41 --- /dev/null +++ b/ospfd/ospf_opaque.h @@ -0,0 +1,146 @@ +/* + * This is an implementation of rfc2370. + * Copyright (C) 2001 KDD R&D Laboratories, Inc. + * http://www.kddlabs.co.jp/ + * + * 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_OPAQUE_H +#define _ZEBRA_OSPF_OPAQUE_H + +#include "vty.h" + +#define IS_OPAQUE_LSA(type) \ + ((type) == OSPF_OPAQUE_LINK_LSA || \ + (type) == OSPF_OPAQUE_AREA_LSA || \ + (type) == OSPF_OPAQUE_AS_LSA) + +/* + * Opaque LSA's link state ID is redefined as follows. + * + * 24 16 8 0 + * +--------+--------+--------+--------+ + * |tttttttt|........|........|........| + * +--------+--------+--------+--------+ + * |<-Type->|<------- Opaque ID ------>| + */ +#define LSID_OPAQUE_TYPE_MASK 0xff000000 /* 8 bits */ +#define LSID_OPAQUE_ID_MASK 0x00ffffff /* 24 bits */ + +#define GET_OPAQUE_TYPE(lsid) \ + (((u_int32_t)(lsid) & LSID_OPAQUE_TYPE_MASK) >> 24) + +#define GET_OPAQUE_ID(lsid) \ + ((u_int32_t)(lsid) & LSID_OPAQUE_ID_MASK) + +#define SET_OPAQUE_LSID(type, id) \ + ((((type) << 24) & LSID_OPAQUE_TYPE_MASK) \ + | ((id) & LSID_OPAQUE_ID_MASK)) + +/* + * Opaque LSA types will be assigned by IANA. + * + */ +#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 +#define OPAQUE_TYPE_SECONDARY_NEIGHBOR_DISCOVERY 224 +#define OPAQUE_TYPE_FLOODGATE 225 + +/* Ugly hack to make use of an unallocated value for wildcard matching! */ +#define OPAQUE_TYPE_WILDCARD 0 + +#define OPAQUE_TYPE_RANGE_UNASSIGNED(type) \ + ( OPAQUE_TYPE_MAX <= (type) && (type) <= 127) + +#define OPAQUE_TYPE_RANGE_RESERVED(type) \ + (127 < (type) && (type) <= 255) + +#define VALID_OPAQUE_INFO_LEN(lsahdr) \ + ((ntohs((lsahdr)->length) >= sizeof (struct lsa_header)) && \ + ((ntohs((lsahdr)->length) % sizeof (u_int32_t)) == 0)) + +/* Prototypes. */ + +extern void ospf_opaque_init (void); +extern void ospf_opaque_term (void); +extern int ospf_opaque_type9_lsa_init (struct ospf_interface *oi); +extern void ospf_opaque_type9_lsa_term (struct ospf_interface *oi); +extern int ospf_opaque_type10_lsa_init (struct ospf_area *area); +extern void ospf_opaque_type10_lsa_term (struct ospf_area *area); +extern int ospf_opaque_type11_lsa_init (struct ospf *ospf); +extern void ospf_opaque_type11_lsa_term (struct ospf *ospf); + +extern int +ospf_register_opaque_functab ( + u_char lsa_type, + u_char opaque_type, + int (* new_if_hook)(struct interface *ifp), + int (* del_if_hook)(struct interface *ifp), + void (* ism_change_hook)(struct ospf_interface *oi, int old_status), + void (* nsm_change_hook)(struct ospf_neighbor *nbr, int old_status), + void (* config_write_router)(struct vty *vty), + void (* config_write_if )(struct vty *vty, struct interface *ifp), + void (* config_write_debug )(struct vty *vty), + void (* show_opaque_info )(struct vty *vty, struct ospf_lsa *lsa), + int (* lsa_originator)(void *arg), + struct ospf_lsa *(* lsa_refresher )(struct ospf_lsa *lsa), + int (* new_lsa_hook)(struct ospf_lsa *lsa), + int (* del_lsa_hook)(struct ospf_lsa *lsa) +); +extern void ospf_delete_opaque_functab (u_char lsa_type, u_char opaque_type); + +extern int ospf_opaque_new_if (struct interface *ifp); +extern int ospf_opaque_del_if (struct interface *ifp); +extern void ospf_opaque_ism_change (struct ospf_interface *oi, + int old_status); +extern void ospf_opaque_nsm_change (struct ospf_neighbor *nbr, + int old_status); +extern void ospf_opaque_config_write_router (struct vty *vty, struct ospf *); +extern void ospf_opaque_config_write_if (struct vty *vty, + struct interface *ifp); +extern void ospf_opaque_config_write_debug (struct vty *vty); +extern void show_opaque_info_detail (struct vty *vty, struct ospf_lsa *lsa); +extern void ospf_opaque_lsa_dump (struct stream *s, u_int16_t length); + +extern void ospf_opaque_lsa_originate_schedule (struct ospf_interface *oi, + int *init_delay); +extern struct ospf_lsa *ospf_opaque_lsa_install (struct ospf_lsa *, + int rt_recalc); +extern struct ospf_lsa *ospf_opaque_lsa_refresh (struct ospf_lsa *lsa); + +extern void ospf_opaque_lsa_reoriginate_schedule (void *lsa_type_dependent, + u_char lsa_type, + u_char opaque_type); +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_self_originated_lsa_received (struct ospf_neighbor + *nbr, + struct ospf_lsa *lsa); +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 new file mode 100644 index 0000000..facba89 --- /dev/null +++ b/ospfd/ospf_packet.c @@ -0,0 +1,3912 @@ +/* + * OSPF Sending and Receiving OSPF Packets. + * Copyright (C) 1999, 2000 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. + */ + +#include + +#include "thread.h" +#include "memory.h" +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "sockunion.h" +#include "stream.h" +#include "log.h" +#include "sockopt.h" +#include "checksum.h" +#include "md5.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_network.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_packet.h" +#include "ospfd/ospf_spf.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_dump.h" + +/* Packet Type String. */ +const struct message ospf_packet_type_str[] = +{ + { OSPF_MSG_HELLO, "Hello" }, + { OSPF_MSG_DB_DESC, "Database Description" }, + { OSPF_MSG_LS_REQ, "Link State Request" }, + { OSPF_MSG_LS_UPD, "Link State Update" }, + { OSPF_MSG_LS_ACK, "Link State Acknowledgment" }, +}; +const size_t ospf_packet_type_str_max = sizeof (ospf_packet_type_str) / + sizeof (ospf_packet_type_str[0]); + +/* Minimum (besides OSPF_HEADER_SIZE) lengths for OSPF packets of + particular types, offset is the "type" field of a packet. */ +static const u_int16_t ospf_packet_minlen[] = +{ + 0, + OSPF_HELLO_MIN_SIZE, + OSPF_DB_DESC_MIN_SIZE, + OSPF_LS_REQ_MIN_SIZE, + OSPF_LS_UPD_MIN_SIZE, + OSPF_LS_ACK_MIN_SIZE, +}; + +/* Minimum (besides OSPF_LSA_HEADER_SIZE) lengths for LSAs of particular + types, offset is the "LSA type" field. */ +static const u_int16_t ospf_lsa_minlen[] = +{ + 0, + OSPF_ROUTER_LSA_MIN_SIZE, + OSPF_NETWORK_LSA_MIN_SIZE, + OSPF_SUMMARY_LSA_MIN_SIZE, + OSPF_SUMMARY_LSA_MIN_SIZE, + OSPF_AS_EXTERNAL_LSA_MIN_SIZE, + 0, + OSPF_AS_EXTERNAL_LSA_MIN_SIZE, + 0, + 0, + 0, + 0, +}; + +/* for ospf_check_auth() */ +static int ospf_check_sum (struct ospf_header *); + +/* OSPF authentication checking function */ +static int +ospf_auth_type (struct ospf_interface *oi) +{ + int auth_type; + + if (OSPF_IF_PARAM (oi, auth_type) == OSPF_AUTH_NOTSET) + auth_type = oi->area->auth_type; + else + auth_type = OSPF_IF_PARAM (oi, auth_type); + + /* Handle case where MD5 key list is not configured aka Cisco */ + if (auth_type == OSPF_AUTH_CRYPTOGRAPHIC && + list_isempty (OSPF_IF_PARAM (oi, auth_crypt))) + return OSPF_AUTH_NULL; + + return auth_type; + +} + +struct ospf_packet * +ospf_packet_new (size_t size) +{ + struct ospf_packet *new; + + new = XCALLOC (MTYPE_OSPF_PACKET, sizeof (struct ospf_packet)); + new->s = stream_new (size); + + return new; +} + +void +ospf_packet_free (struct ospf_packet *op) +{ + if (op->s) + stream_free (op->s); + + XFREE (MTYPE_OSPF_PACKET, op); + + op = NULL; +} + +struct ospf_fifo * +ospf_fifo_new () +{ + struct ospf_fifo *new; + + new = XCALLOC (MTYPE_OSPF_FIFO, sizeof (struct ospf_fifo)); + return new; +} + +/* Add new packet to fifo. */ +void +ospf_fifo_push (struct ospf_fifo *fifo, struct ospf_packet *op) +{ + if (fifo->tail) + fifo->tail->next = op; + else + fifo->head = op; + + fifo->tail = op; + + fifo->count++; +} + +/* Add new packet to head of fifo. */ +static void +ospf_fifo_push_head (struct ospf_fifo *fifo, struct ospf_packet *op) +{ + op->next = fifo->head; + + if (fifo->tail == NULL) + fifo->tail = op; + + fifo->head = op; + + fifo->count++; +} + +/* Delete first packet from fifo. */ +struct ospf_packet * +ospf_fifo_pop (struct ospf_fifo *fifo) +{ + struct ospf_packet *op; + + op = fifo->head; + + if (op) + { + fifo->head = op->next; + + if (fifo->head == NULL) + fifo->tail = NULL; + + fifo->count--; + } + + return op; +} + +/* Return first fifo entry. */ +struct ospf_packet * +ospf_fifo_head (struct ospf_fifo *fifo) +{ + return fifo->head; +} + +/* Flush ospf packet fifo. */ +void +ospf_fifo_flush (struct ospf_fifo *fifo) +{ + struct ospf_packet *op; + struct ospf_packet *next; + + for (op = fifo->head; op; op = next) + { + next = op->next; + ospf_packet_free (op); + } + fifo->head = fifo->tail = NULL; + fifo->count = 0; +} + +/* Free ospf packet fifo. */ +void +ospf_fifo_free (struct ospf_fifo *fifo) +{ + ospf_fifo_flush (fifo); + + XFREE (MTYPE_OSPF_FIFO, fifo); +} + +void +ospf_packet_add (struct ospf_interface *oi, struct ospf_packet *op) +{ + if (!oi->obuf) + { + zlog_err("ospf_packet_add(interface %s in state %d [%s], packet type %s, " + "destination %s) called with NULL obuf, ignoring " + "(please report this bug)!\n", + IF_NAME(oi), oi->state, LOOKUP (ospf_ism_state_msg, oi->state), + LOOKUP (ospf_packet_type_str, stream_getc_from(op->s, 1)), + inet_ntoa (op->dst)); + return; + } + + /* Add packet to end of queue. */ + ospf_fifo_push (oi->obuf, op); + + /* Debug of packet fifo*/ + /* ospf_fifo_debug (oi->obuf); */ +} + +static void +ospf_packet_add_top (struct ospf_interface *oi, struct ospf_packet *op) +{ + if (!oi->obuf) + { + zlog_err("ospf_packet_add(interface %s in state %d [%s], packet type %s, " + "destination %s) called with NULL obuf, ignoring " + "(please report this bug)!\n", + IF_NAME(oi), oi->state, LOOKUP (ospf_ism_state_msg, oi->state), + LOOKUP (ospf_packet_type_str, stream_getc_from(op->s, 1)), + inet_ntoa (op->dst)); + return; + } + + /* Add packet to head of queue. */ + ospf_fifo_push_head (oi->obuf, op); + + /* Debug of packet fifo*/ + /* ospf_fifo_debug (oi->obuf); */ +} + +void +ospf_packet_delete (struct ospf_interface *oi) +{ + struct ospf_packet *op; + + op = ospf_fifo_pop (oi->obuf); + + if (op) + ospf_packet_free (op); +} + +struct ospf_packet * +ospf_packet_dup (struct ospf_packet *op) +{ + struct ospf_packet *new; + + if (stream_get_endp(op->s) != op->length) + /* XXX size_t */ + zlog_warn ("ospf_packet_dup stream %lu ospf_packet %u size mismatch", + (u_long)STREAM_SIZE(op->s), op->length); + + /* Reserve space for MD5 authentication that may be added later. */ + new = ospf_packet_new (stream_get_endp(op->s) + OSPF_AUTH_MD5_SIZE); + stream_copy (new->s, op->s); + + new->dst = op->dst; + new->length = op->length; + + return new; +} + +/* XXX inline */ +static unsigned int +ospf_packet_authspace (struct ospf_interface *oi) +{ + int auth = 0; + + if ( ospf_auth_type (oi) == OSPF_AUTH_CRYPTOGRAPHIC) + auth = OSPF_AUTH_MD5_SIZE; + + return auth; +} + +static unsigned int +ospf_packet_max (struct ospf_interface *oi) +{ + int max; + + max = oi->ifp->mtu - ospf_packet_authspace(oi); + + max -= (OSPF_HEADER_SIZE + sizeof (struct ip)); + + return max; +} + + +static int +ospf_check_md5_digest (struct ospf_interface *oi, struct ospf_header *ospfh) +{ + MD5_CTX ctx; + unsigned char digest[OSPF_AUTH_MD5_SIZE]; + struct crypt_key *ck; + struct ospf_neighbor *nbr; + u_int16_t length = ntohs (ospfh->length); + + /* Get secret key. */ + ck = ospf_crypt_key_lookup (OSPF_IF_PARAM (oi, auth_crypt), + ospfh->u.crypt.key_id); + if (ck == NULL) + { + zlog_warn ("interface %s: ospf_check_md5 no key %d", + IF_NAME (oi), ospfh->u.crypt.key_id); + return 0; + } + + /* check crypto seqnum. */ + nbr = ospf_nbr_lookup_by_routerid (oi->nbrs, &ospfh->router_id); + + if (nbr && ntohl(nbr->crypt_seqnum) > ntohl(ospfh->u.crypt.crypt_seqnum)) + { + zlog_warn ("interface %s: ospf_check_md5 bad sequence %d (expect %d)", + IF_NAME (oi), + ntohl(ospfh->u.crypt.crypt_seqnum), + ntohl(nbr->crypt_seqnum)); + return 0; + } + + /* Generate a digest for the ospf packet - their digest + our digest. */ + memset(&ctx, 0, sizeof(ctx)); + MD5Init(&ctx); + MD5Update(&ctx, ospfh, length); + MD5Update(&ctx, ck->auth_key, OSPF_AUTH_MD5_SIZE); + MD5Final(digest, &ctx); + + /* compare the two */ + if (memcmp ((caddr_t)ospfh + length, digest, OSPF_AUTH_MD5_SIZE)) + { + zlog_warn ("interface %s: ospf_check_md5 checksum mismatch", + IF_NAME (oi)); + return 0; + } + + /* save neighbor's crypt_seqnum */ + if (nbr) + nbr->crypt_seqnum = ospfh->u.crypt.crypt_seqnum; + return 1; +} + +/* This function is called from ospf_write(), it will detect the + authentication scheme and if it is MD5, it will change the sequence + and update the MD5 digest. */ +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] = {0}; + MD5_CTX ctx; + void *ibuf; + u_int32_t t; + struct crypt_key *ck; + const u_int8_t *auth_key; + + ibuf = STREAM_DATA (op->s); + ospfh = (struct ospf_header *) ibuf; + + if (ntohs (ospfh->auth_type) != OSPF_AUTH_CRYPTOGRAPHIC) + return 0; + + /* We do this here so when we dup a packet, we don't have to + waste CPU rewriting other headers. + + Note that quagga_time /deliberately/ is not used here */ + t = (time(NULL) & 0xFFFFFFFF); + if (t > oi->crypt_seqnum) + oi->crypt_seqnum = t; + else + oi->crypt_seqnum++; + + ospfh->u.crypt.crypt_seqnum = htonl (oi->crypt_seqnum); + + /* Get MD5 Authentication key from auth_key list. */ + if (list_isempty (OSPF_IF_PARAM (oi, auth_crypt))) + auth_key = (const u_int8_t *) digest; + else + { + ck = listgetdata (listtail(OSPF_IF_PARAM (oi, auth_crypt))); + auth_key = ck->auth_key; + } + + /* Generate a digest for the entire packet + our secret key. */ + memset(&ctx, 0, sizeof(ctx)); + MD5Init(&ctx); + MD5Update(&ctx, ibuf, ntohs (ospfh->length)); + MD5Update(&ctx, auth_key, OSPF_AUTH_MD5_SIZE); + MD5Final(digest, &ctx); + + /* Append md5 digest to the end of the stream. */ + stream_put (op->s, digest, OSPF_AUTH_MD5_SIZE); + + /* We do *NOT* increment the OSPF header length. */ + op->length = ntohs (ospfh->length) + OSPF_AUTH_MD5_SIZE; + + if (stream_get_endp(op->s) != op->length) + /* XXX size_t */ + zlog_warn("ospf_make_md5_digest: length mismatch stream %lu ospf_packet %u", + (u_long)stream_get_endp(op->s), op->length); + + return OSPF_AUTH_MD5_SIZE; +} + + +static int +ospf_ls_req_timer (struct thread *thread) +{ + struct ospf_neighbor *nbr; + + nbr = THREAD_ARG (thread); + nbr->t_ls_req = NULL; + + /* Send Link State Request. */ + if (ospf_ls_request_count (nbr)) + ospf_ls_req_send (nbr); + + /* Set Link State Request retransmission timer. */ + OSPF_NSM_TIMER_ON (nbr->t_ls_req, ospf_ls_req_timer, nbr->v_ls_req); + + return 0; +} + +void +ospf_ls_req_event (struct ospf_neighbor *nbr) +{ + if (nbr->t_ls_req) + { + thread_cancel (nbr->t_ls_req); + nbr->t_ls_req = NULL; + } + nbr->t_ls_req = thread_add_event (master, ospf_ls_req_timer, nbr, 0); +} + +/* Cyclic timer function. Fist registered in ospf_nbr_new () in + ospf_neighbor.c */ +int +ospf_ls_upd_timer (struct thread *thread) +{ + struct ospf_neighbor *nbr; + + nbr = THREAD_ARG (thread); + nbr->t_ls_upd = NULL; + + /* Send Link State Update. */ + if (ospf_ls_retransmit_count (nbr) > 0) + { + struct list *update; + struct ospf_lsdb *lsdb; + int i; + int retransmit_interval; + + retransmit_interval = OSPF_IF_PARAM (nbr->oi, retransmit_interval); + + lsdb = &nbr->ls_rxmt; + update = list_new (); + + for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++) + { + struct route_table *table = lsdb->type[i].db; + struct route_node *rn; + + for (rn = route_top (table); rn; rn = route_next (rn)) + { + struct ospf_lsa *lsa; + + if ((lsa = rn->info) != NULL) + /* Don't retransmit an LSA if we received it within + the last RxmtInterval seconds - this is to allow the + neighbour a chance to acknowledge the LSA as it may + have ben just received before the retransmit timer + fired. This is a small tweak to what is in the RFC, + but it will cut out out a lot of retransmit traffic + - MAG */ + if (tv_cmp (tv_sub (recent_relative_time (), lsa->tv_recv), + int2tv (retransmit_interval)) >= 0) + listnode_add (update, rn->info); + } + } + + if (listcount (update) > 0) + ospf_ls_upd_send (nbr, update, OSPF_SEND_PACKET_DIRECT); + list_delete (update); + } + + /* Set LS Update retransmission timer. */ + OSPF_NSM_TIMER_ON (nbr->t_ls_upd, ospf_ls_upd_timer, nbr->v_ls_upd); + + return 0; +} + +int +ospf_ls_ack_timer (struct thread *thread) +{ + struct ospf_interface *oi; + + oi = THREAD_ARG (thread); + oi->t_ls_ack = NULL; + + /* Send Link State Acknowledgment. */ + if (listcount (oi->ls_ack) > 0) + ospf_ls_ack_send_delayed (oi); + + /* Set LS Ack timer. */ + OSPF_ISM_TIMER_ON (oi->t_ls_ack, ospf_ls_ack_timer, oi->v_ls_ack); + + return 0; +} + +#ifdef WANT_OSPF_WRITE_FRAGMENT +static void +ospf_write_frags (int fd, struct ospf_packet *op, struct ip *iph, + struct msghdr *msg, unsigned int maxdatasize, + unsigned int mtu, int flags, u_char type) +{ +#define OSPF_WRITE_FRAG_SHIFT 3 + u_int16_t offset; + struct iovec *iovp; + int ret; + + assert ( op->length == stream_get_endp(op->s) ); + assert (msg->msg_iovlen == 2); + + /* we can but try. + * + * SunOS, BSD and BSD derived kernels likely will clear ip_id, as + * well as the IP_MF flag, making this all quite pointless. + * + * However, for a system on which IP_MF is left alone, and ip_id left + * alone or else which sets same ip_id for each fragment this might + * work, eg linux. + * + * XXX-TODO: It would be much nicer to have the kernel's use their + * existing fragmentation support to do this for us. Bugs/RFEs need to + * be raised against the various kernels. + */ + + /* set More Frag */ + iph->ip_off |= IP_MF; + + /* ip frag offset is expressed in units of 8byte words */ + offset = maxdatasize >> OSPF_WRITE_FRAG_SHIFT; + + iovp = &msg->msg_iov[1]; + + while ( (stream_get_endp(op->s) - stream_get_getp (op->s)) + > maxdatasize ) + { + /* data length of this frag is to next offset value */ + iovp->iov_len = offset << OSPF_WRITE_FRAG_SHIFT; + iph->ip_len = iovp->iov_len + sizeof (struct ip); + assert (iph->ip_len <= mtu); + + sockopt_iphdrincl_swab_htosys (iph); + + ret = sendmsg (fd, msg, flags); + + sockopt_iphdrincl_swab_systoh (iph); + + if (ret < 0) + zlog_warn ("*** ospf_write_frags: sendmsg failed to %s," + " id %d, off %d, len %d, mtu %u failed with %s", + inet_ntoa (iph->ip_dst), + iph->ip_id, + iph->ip_off, + iph->ip_len, + mtu, + safe_strerror (errno)); + + if (IS_DEBUG_OSPF_PACKET (type - 1, SEND)) + { + zlog_debug ("ospf_write_frags: sent id %d, off %d, len %d to %s\n", + iph->ip_id, iph->ip_off, iph->ip_len, + inet_ntoa (iph->ip_dst)); + if (IS_DEBUG_OSPF_PACKET (type - 1, DETAIL)) + { + zlog_debug ("-----------------IP Header Dump----------------------"); + ospf_ip_header_dump (iph); + zlog_debug ("-----------------------------------------------------"); + } + } + + iph->ip_off += offset; + stream_forward_getp (op->s, iovp->iov_len); + iovp->iov_base = STREAM_PNT (op->s); + } + + /* setup for final fragment */ + iovp->iov_len = stream_get_endp(op->s) - stream_get_getp (op->s); + iph->ip_len = iovp->iov_len + sizeof (struct ip); + iph->ip_off &= (~IP_MF); +} +#endif /* WANT_OSPF_WRITE_FRAGMENT */ + +static int +ospf_write (struct thread *thread) +{ + struct ospf *ospf = THREAD_ARG (thread); + struct ospf_interface *oi; + struct ospf_packet *op; + struct sockaddr_in sa_dst; + struct ip iph; + struct msghdr msg; + struct iovec iov[2]; + u_char type; + int ret; + int flags = 0; + struct listnode *node; +#ifdef WANT_OSPF_WRITE_FRAGMENT + static u_int16_t ipid = 0; + u_int16_t maxdatasize; +#endif /* WANT_OSPF_WRITE_FRAGMENT */ +#define OSPF_WRITE_IPHL_SHIFT 2 + + ospf->t_write = NULL; + + node = listhead (ospf->oi_write_q); + assert (node); + oi = listgetdata (node); + assert (oi); + +#ifdef WANT_OSPF_WRITE_FRAGMENT + /* seed ipid static with low order bits of time */ + if (ipid == 0) + ipid = (time(NULL) & 0xffff); + + /* convenience - max OSPF data per packet, + * and reliability - not more data, than our + * socket can accept + */ + 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); + assert (op); + assert (op->length >= OSPF_HEADER_SIZE); + + if (op->dst.s_addr == htonl (OSPF_ALLSPFROUTERS) + || op->dst.s_addr == htonl (OSPF_ALLDROUTERS)) + ospf_if_ipmulticast (ospf, oi->address, oi->ifp->ifindex); + + /* Rewrite the md5 signature & update the seq */ + ospf_make_md5_digest (oi, op); + + /* Retrieve OSPF packet type. */ + stream_set_getp (op->s, 1); + type = stream_getc (op->s); + + /* reset get pointer */ + stream_set_getp (op->s, 0); + + memset (&iph, 0, sizeof (struct ip)); + memset (&sa_dst, 0, sizeof (sa_dst)); + + sa_dst.sin_family = AF_INET; +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + sa_dst.sin_len = sizeof(sa_dst); +#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ + sa_dst.sin_addr = op->dst; + sa_dst.sin_port = htons (0); + + /* Set DONTROUTE flag if dst is unicast. */ + if (oi->type != OSPF_IFTYPE_VIRTUALLINK) + if (!IN_MULTICAST (htonl (op->dst.s_addr))) + flags = MSG_DONTROUTE; + + iph.ip_hl = sizeof (struct ip) >> OSPF_WRITE_IPHL_SHIFT; + /* it'd be very strange for header to not be 4byte-word aligned but.. */ + if ( sizeof (struct ip) + > (unsigned int)(iph.ip_hl << OSPF_WRITE_IPHL_SHIFT) ) + iph.ip_hl++; /* we presume sizeof struct ip cant overflow ip_hl.. */ + + iph.ip_v = IPVERSION; + iph.ip_tos = IPTOS_PREC_INTERNETCONTROL; + iph.ip_len = (iph.ip_hl << OSPF_WRITE_IPHL_SHIFT) + op->length; + +#if defined(__DragonFly__) + /* + * DragonFly's raw socket expects ip_len/ip_off in network byte order. + */ + iph.ip_len = htons(iph.ip_len); +#endif + +#ifdef WANT_OSPF_WRITE_FRAGMENT + /* XXX-MT: not thread-safe at all.. + * XXX: this presumes this is only programme sending OSPF packets + * otherwise, no guarantee ipid will be unique + */ + iph.ip_id = ++ipid; +#endif /* WANT_OSPF_WRITE_FRAGMENT */ + + iph.ip_off = 0; + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) + iph.ip_ttl = OSPF_VL_IP_TTL; + else + iph.ip_ttl = OSPF_IP_TTL; + iph.ip_p = IPPROTO_OSPFIGP; + iph.ip_sum = 0; + iph.ip_src.s_addr = oi->address->u.prefix4.s_addr; + iph.ip_dst.s_addr = op->dst.s_addr; + + memset (&msg, 0, sizeof (msg)); + msg.msg_name = (caddr_t) &sa_dst; + msg.msg_namelen = sizeof (sa_dst); + msg.msg_iov = iov; + msg.msg_iovlen = 2; + iov[0].iov_base = (char*)&iph; + iov[0].iov_len = iph.ip_hl << OSPF_WRITE_IPHL_SHIFT; + iov[1].iov_base = STREAM_PNT (op->s); + iov[1].iov_len = op->length; + + /* Sadly we can not rely on kernels to fragment packets because of either + * IP_HDRINCL and/or multicast destination being set. + */ +#ifdef WANT_OSPF_WRITE_FRAGMENT + if ( op->length > maxdatasize ) + ospf_write_frags (ospf->fd, op, &iph, &msg, maxdatasize, + oi->ifp->mtu, flags, type); +#endif /* WANT_OSPF_WRITE_FRAGMENT */ + + /* send final fragment (could be first) */ + sockopt_iphdrincl_swab_htosys (&iph); + ret = sendmsg (ospf->fd, &msg, flags); + sockopt_iphdrincl_swab_systoh (&iph); + + if (ret < 0) + zlog_warn ("*** sendmsg in ospf_write failed to %s, " + "id %d, off %d, len %d, interface %s, mtu %u: %s", + inet_ntoa (iph.ip_dst), iph.ip_id, iph.ip_off, iph.ip_len, + oi->ifp->name, oi->ifp->mtu, safe_strerror (errno)); + + /* Show debug sending packet. */ + if (IS_DEBUG_OSPF_PACKET (type - 1, SEND)) + { + if (IS_DEBUG_OSPF_PACKET (type - 1, DETAIL)) + { + zlog_debug ("-----------------------------------------------------"); + ospf_ip_header_dump (&iph); + stream_set_getp (op->s, 0); + ospf_packet_dump (op->s); + } + + zlog_debug ("%s sent to [%s] via [%s].", + LOOKUP (ospf_packet_type_str, type), inet_ntoa (op->dst), + IF_NAME (oi)); + + if (IS_DEBUG_OSPF_PACKET (type - 1, DETAIL)) + zlog_debug ("-----------------------------------------------------"); + } + + /* 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; + list_delete_node (ospf->oi_write_q, node); + } + + /* If packets still remain in queue, call write thread. */ + if (!list_isempty (ospf->oi_write_q)) + ospf->t_write = + thread_add_write (master, ospf_write, ospf, ospf->fd); + + return 0; +} + +/* OSPF Hello message read -- RFC2328 Section 10.5. */ +static void +ospf_hello (struct ip *iph, struct ospf_header *ospfh, + struct stream * s, struct ospf_interface *oi, int size) +{ + struct ospf_hello *hello; + struct ospf_neighbor *nbr; + int old_state; + struct prefix p; + + /* increment statistics. */ + oi->hello_in++; + + hello = (struct ospf_hello *) STREAM_PNT (s); + + /* If Hello is myself, silently discard. */ + if (IPV4_ADDR_SAME (&ospfh->router_id, &oi->ospf->router_id)) + { + if (IS_DEBUG_OSPF_PACKET (ospfh->type - 1, RECV)) + { + zlog_debug ("ospf_header[%s/%s]: selforiginated, " + "dropping.", + LOOKUP (ospf_packet_type_str, ospfh->type), + inet_ntoa (iph->ip_src)); + } + return; + } + + /* get neighbor prefix. */ + p.family = AF_INET; + p.prefixlen = ip_masklen (hello->network_mask); + p.u.prefix4 = iph->ip_src; + + /* Compare network mask. */ + /* Checking is ignored for Point-to-Point and Virtual link. */ + if (oi->type != OSPF_IFTYPE_POINTOPOINT + && oi->type != OSPF_IFTYPE_VIRTUALLINK) + if (oi->address->prefixlen != p.prefixlen) + { + zlog_warn ("Packet %s [Hello:RECV]: NetworkMask mismatch on %s (configured prefix length is %d, but hello packet indicates %d).", + inet_ntoa(ospfh->router_id), IF_NAME(oi), + (int)oi->address->prefixlen, (int)p.prefixlen); + return; + } + + /* Compare Router Dead Interval. */ + if (OSPF_IF_PARAM (oi, v_wait) != ntohl (hello->dead_interval)) + { + zlog_warn ("Packet %s [Hello:RECV]: RouterDeadInterval mismatch " + "(expected %u, but received %u).", + inet_ntoa(ospfh->router_id), + OSPF_IF_PARAM(oi, v_wait), ntohl(hello->dead_interval)); + return; + } + + /* Compare Hello Interval - ignored if fast-hellos are set. */ + if (OSPF_IF_PARAM (oi, fast_hello) == 0) + { + if (OSPF_IF_PARAM (oi, v_hello) != ntohs (hello->hello_interval)) + { + zlog_warn ("Packet %s [Hello:RECV]: HelloInterval mismatch " + "(expected %u, but received %u).", + inet_ntoa(ospfh->router_id), + OSPF_IF_PARAM(oi, v_hello), ntohs(hello->hello_interval)); + return; + } + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Packet %s [Hello:RECV]: Options %s", + inet_ntoa (ospfh->router_id), + ospf_options_dump (hello->options)); + + /* Compare options. */ +#define REJECT_IF_TBIT_ON 1 /* XXX */ +#ifdef REJECT_IF_TBIT_ON + if (CHECK_FLAG (hello->options, OSPF_OPTION_MT)) + { + /* + * This router does not support non-zero TOS. + * Drop this Hello packet not to establish neighbor relationship. + */ + zlog_warn ("Packet %s [Hello:RECV]: T-bit on, drop it.", + inet_ntoa (ospfh->router_id)); + return; + } +#endif /* REJECT_IF_TBIT_ON */ + + if (CHECK_FLAG (oi->ospf->config, OSPF_OPAQUE_CAPABLE) + && CHECK_FLAG (hello->options, OSPF_OPTION_O)) + { + /* + * This router does know the correct usage of O-bit + * the bit should be set in DD packet only. + */ + zlog_warn ("Packet %s [Hello:RECV]: O-bit abuse?", + inet_ntoa (ospfh->router_id)); +#ifdef STRICT_OBIT_USAGE_CHECK + return; /* Reject this packet. */ +#else /* STRICT_OBIT_USAGE_CHECK */ + UNSET_FLAG (hello->options, OSPF_OPTION_O); /* Ignore O-bit. */ +#endif /* STRICT_OBIT_USAGE_CHECK */ + } + + /* new for NSSA is to ensure that NP is on and E is off */ + + if (oi->area->external_routing == OSPF_AREA_NSSA) + { + if (! (CHECK_FLAG (OPTIONS (oi), OSPF_OPTION_NP) + && CHECK_FLAG (hello->options, OSPF_OPTION_NP) + && ! CHECK_FLAG (OPTIONS (oi), OSPF_OPTION_E) + && ! CHECK_FLAG (hello->options, OSPF_OPTION_E))) + { + zlog_warn ("NSSA-Packet-%s[Hello:RECV]: my options: %x, his options %x", inet_ntoa (ospfh->router_id), OPTIONS (oi), hello->options); + return; + } + if (IS_DEBUG_OSPF_NSSA) + zlog_debug ("NSSA-Hello:RECV:Packet from %s:", inet_ntoa(ospfh->router_id)); + } + else + /* The setting of the E-bit found in the Hello Packet's Options + field must match this area's ExternalRoutingCapability A + mismatch causes processing to stop and the packet to be + dropped. The setting of the rest of the bits in the Hello + Packet's Options field should be ignored. */ + if (CHECK_FLAG (OPTIONS (oi), OSPF_OPTION_E) != + CHECK_FLAG (hello->options, OSPF_OPTION_E)) + { + zlog_warn ("Packet %s [Hello:RECV]: my options: %x, his options %x", + inet_ntoa(ospfh->router_id), OPTIONS (oi), hello->options); + return; + } + + /* get neighbour struct */ + nbr = ospf_nbr_get (oi, ospfh, iph, &p); + + /* neighbour must be valid, ospf_nbr_get creates if none existed */ + assert (nbr); + + old_state = nbr->state; + + /* Add event to thread. */ + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_PacketReceived); + + /* RFC2328 Section 9.5.1 + If the router is not eligible to become Designated Router, + (snip) It must also send an Hello Packet in reply to an + Hello Packet received from any eligible neighbor (other than + the current Designated Router and Backup Designated Router). */ + if (oi->type == OSPF_IFTYPE_NBMA) + if (PRIORITY(oi) == 0 && hello->priority > 0 + && IPV4_ADDR_CMP(&DR(oi), &iph->ip_src) + && IPV4_ADDR_CMP(&BDR(oi), &iph->ip_src)) + OSPF_NSM_TIMER_ON (nbr->t_hello_reply, ospf_hello_reply_timer, + OSPF_HELLO_REPLY_DELAY); + + /* on NBMA network type, it happens to receive bidirectional Hello packet + without advance 1-Way Received event. + To avoid incorrect DR-seletion, raise 1-Way Received event.*/ + if (oi->type == OSPF_IFTYPE_NBMA && + (old_state == NSM_Down || old_state == NSM_Attempt)) + { + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_OneWayReceived); + nbr->priority = hello->priority; + nbr->d_router = hello->d_router; + nbr->bd_router = hello->bd_router; + return; + } + + if (ospf_nbr_bidirectional (&oi->ospf->router_id, hello->neighbors, + size - OSPF_HELLO_MIN_SIZE)) + { + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_TwoWayReceived); + nbr->options |= hello->options; + } + else + { + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_OneWayReceived); + /* Set neighbor information. */ + nbr->priority = hello->priority; + nbr->d_router = hello->d_router; + nbr->bd_router = hello->bd_router; + return; + } + + /* If neighbor itself declares DR and no BDR exists, + cause event BackupSeen */ + if (IPV4_ADDR_SAME (&nbr->address.u.prefix4, &hello->d_router)) + if (hello->bd_router.s_addr == 0 && oi->state == ISM_Waiting) + OSPF_ISM_EVENT_SCHEDULE (oi, ISM_BackupSeen); + + /* neighbor itself declares BDR. */ + if (oi->state == ISM_Waiting && + IPV4_ADDR_SAME (&nbr->address.u.prefix4, &hello->bd_router)) + OSPF_ISM_EVENT_SCHEDULE (oi, ISM_BackupSeen); + + /* had not previously. */ + if ((IPV4_ADDR_SAME (&nbr->address.u.prefix4, &hello->d_router) && + IPV4_ADDR_CMP (&nbr->address.u.prefix4, &nbr->d_router)) || + (IPV4_ADDR_CMP (&nbr->address.u.prefix4, &hello->d_router) && + IPV4_ADDR_SAME (&nbr->address.u.prefix4, &nbr->d_router))) + OSPF_ISM_EVENT_SCHEDULE (oi, ISM_NeighborChange); + + /* had not previously. */ + if ((IPV4_ADDR_SAME (&nbr->address.u.prefix4, &hello->bd_router) && + IPV4_ADDR_CMP (&nbr->address.u.prefix4, &nbr->bd_router)) || + (IPV4_ADDR_CMP (&nbr->address.u.prefix4, &hello->bd_router) && + IPV4_ADDR_SAME (&nbr->address.u.prefix4, &nbr->bd_router))) + OSPF_ISM_EVENT_SCHEDULE (oi, ISM_NeighborChange); + + /* Neighbor priority check. */ + if (nbr->priority >= 0 && nbr->priority != hello->priority) + OSPF_ISM_EVENT_SCHEDULE (oi, ISM_NeighborChange); + + /* Set neighbor information. */ + nbr->priority = hello->priority; + nbr->d_router = hello->d_router; + nbr->bd_router = hello->bd_router; +} + +/* Save DD flags/options/Seqnum received. */ +static void +ospf_db_desc_save_current (struct ospf_neighbor *nbr, + struct ospf_db_desc *dd) +{ + nbr->last_recv.flags = dd->flags; + nbr->last_recv.options = dd->options; + nbr->last_recv.dd_seqnum = ntohl (dd->dd_seqnum); +} + +/* Process rest of DD packet. */ +static void +ospf_db_desc_proc (struct stream *s, struct ospf_interface *oi, + struct ospf_neighbor *nbr, struct ospf_db_desc *dd, + u_int16_t size) +{ + struct ospf_lsa *new, *find; + struct lsa_header *lsah; + + stream_forward_getp (s, OSPF_DB_DESC_MIN_SIZE); + for (size -= OSPF_DB_DESC_MIN_SIZE; + size >= OSPF_LSA_HEADER_SIZE; size -= OSPF_LSA_HEADER_SIZE) + { + lsah = (struct lsa_header *) STREAM_PNT (s); + stream_forward_getp (s, OSPF_LSA_HEADER_SIZE); + + /* Unknown LS type. */ + if (lsah->type < OSPF_MIN_LSA || lsah->type >= OSPF_MAX_LSA) + { + zlog_warn ("Packet [DD:RECV]: Unknown LS type %d.", lsah->type); + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_SeqNumberMismatch); + return; + } + + if (IS_OPAQUE_LSA (lsah->type) + && ! CHECK_FLAG (nbr->options, OSPF_OPTION_O)) + { + zlog_warn ("LSA[Type%d:%s]: Opaque capability mismatch?", lsah->type, inet_ntoa (lsah->id)); + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_SeqNumberMismatch); + return; + } + + switch (lsah->type) + { + case OSPF_AS_EXTERNAL_LSA: + case OSPF_OPAQUE_AS_LSA: + /* Check for stub area. Reject if AS-External from stub but + allow if from NSSA. */ + if (oi->area->external_routing == OSPF_AREA_STUB) + { + zlog_warn ("Packet [DD:RECV]: LSA[Type%d:%s] from %s area.", + lsah->type, inet_ntoa (lsah->id), + (oi->area->external_routing == OSPF_AREA_STUB) ?\ + "STUB" : "NSSA"); + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_SeqNumberMismatch); + return; + } + break; + default: + break; + } + + /* Create LS-request object. */ + new = ospf_ls_request_new (lsah); + + /* Lookup received LSA, then add LS request list. */ + find = ospf_lsa_lookup_by_header (oi->area, lsah); + + /* ospf_lsa_more_recent is fine with NULL pointers */ + switch (ospf_lsa_more_recent (find, new)) + { + case -1: + /* Neighbour has a more recent LSA, we must request it */ + ospf_ls_request_add (nbr, new); + case 0: + /* If we have a copy of this LSA, it's either less recent + * and we're requesting it from neighbour (the case above), or + * it's as recent and we both have same copy (this case). + * + * In neither of these two cases is there any point in + * describing our copy of the LSA to the neighbour in a + * DB-Summary packet, if we're still intending to do so. + * + * See: draft-ogier-ospf-dbex-opt-00.txt, describing the + * backward compatible optimisation to OSPF DB Exchange / + * DB Description process implemented here. + */ + if (find) + ospf_lsdb_delete (&nbr->db_sum, find); + ospf_lsa_discard (new); + break; + default: + /* We have the more recent copy, nothing specific to do: + * - no need to request neighbours stale copy + * - must leave DB summary list copy alone + */ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Packet [DD:RECV]: LSA received Type %d, " + "ID %s is not recent.", lsah->type, inet_ntoa (lsah->id)); + ospf_lsa_discard (new); + } + } + + /* Master */ + if (IS_SET_DD_MS (nbr->dd_flags)) + { + nbr->dd_seqnum++; + + /* Both sides have no More, then we're done with Exchange */ + if (!IS_SET_DD_M (dd->flags) && !IS_SET_DD_M (nbr->dd_flags)) + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_ExchangeDone); + else + ospf_db_desc_send (nbr); + } + /* Slave */ + else + { + nbr->dd_seqnum = ntohl (dd->dd_seqnum); + + /* Send DD packet in reply. + * + * Must be done to acknowledge the Master's DD, regardless of + * whether we have more LSAs ourselves to describe. + * + * This function will clear the 'More' bit, if after this DD + * we have no more LSAs to describe to the master.. + */ + ospf_db_desc_send (nbr); + + /* Slave can raise ExchangeDone now, if master is also done */ + if (!IS_SET_DD_M (dd->flags) && !IS_SET_DD_M (nbr->dd_flags)) + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_ExchangeDone); + } + + /* Save received neighbor values from DD. */ + ospf_db_desc_save_current (nbr, dd); +} + +static int +ospf_db_desc_is_dup (struct ospf_db_desc *dd, struct ospf_neighbor *nbr) +{ + /* Is DD duplicated? */ + if (dd->options == nbr->last_recv.options && + dd->flags == nbr->last_recv.flags && + dd->dd_seqnum == htonl (nbr->last_recv.dd_seqnum)) + return 1; + + return 0; +} + +/* OSPF Database Description message read -- RFC2328 Section 10.6. */ +static void +ospf_db_desc (struct ip *iph, struct ospf_header *ospfh, + struct stream *s, struct ospf_interface *oi, u_int16_t size) +{ + struct ospf_db_desc *dd; + struct ospf_neighbor *nbr; + + /* Increment statistics. */ + oi->db_desc_in++; + + dd = (struct ospf_db_desc *) STREAM_PNT (s); + + nbr = ospf_nbr_lookup (oi, iph, ospfh); + if (nbr == NULL) + { + zlog_warn ("Packet[DD]: Unknown Neighbor %s", + inet_ntoa (ospfh->router_id)); + return; + } + + /* Check MTU. */ + if ((OSPF_IF_PARAM (oi, mtu_ignore) == 0) && + (ntohs (dd->mtu) > oi->ifp->mtu)) + { + zlog_warn ("Packet[DD]: Neighbor %s MTU %u is larger than [%s]'s MTU %u", + inet_ntoa (nbr->router_id), ntohs (dd->mtu), + IF_NAME (oi), oi->ifp->mtu); + return; + } + + /* + * XXX HACK by Hasso Tepper. Setting N/P bit in NSSA area DD packets is not + * required. In fact at least JunOS sends DD packets with P bit clear. + * Until proper solution is developped, this hack should help. + * + * Update: According to the RFCs, N bit is specified /only/ for Hello + * options, unfortunately its use in DD options is not specified. Hence some + * implementations follow E-bit semantics and set it in DD options, and some + * treat it as unspecified and hence follow the directive "default for + * options is clear", ie unset. + * + * Reset the flag, as ospfd follows E-bit semantics. + */ + if ( (oi->area->external_routing == OSPF_AREA_NSSA) + && (CHECK_FLAG (nbr->options, OSPF_OPTION_NP)) + && (!CHECK_FLAG (dd->options, OSPF_OPTION_NP)) ) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Packet[DD]: Neighbour %s: Has NSSA capability, sends with N bit clear in DD options", + inet_ntoa (nbr->router_id) ); + SET_FLAG (dd->options, OSPF_OPTION_NP); + } + +#ifdef REJECT_IF_TBIT_ON + if (CHECK_FLAG (dd->options, OSPF_OPTION_MT)) + { + /* + * In Hello protocol, optional capability must have checked + * to prevent this T-bit enabled router be my neighbor. + */ + zlog_warn ("Packet[DD]: Neighbor %s: T-bit on?", inet_ntoa (nbr->router_id)); + return; + } +#endif /* REJECT_IF_TBIT_ON */ + + if (CHECK_FLAG (dd->options, OSPF_OPTION_O) + && !CHECK_FLAG (oi->ospf->config, OSPF_OPAQUE_CAPABLE)) + { + /* + * This node is not configured to handle O-bit, for now. + * Clear it to ignore unsupported capability proposed by neighbor. + */ + UNSET_FLAG (dd->options, OSPF_OPTION_O); + } + + /* Add event to thread. */ + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_PacketReceived); + + /* Process DD packet by neighbor status. */ + switch (nbr->state) + { + case NSM_Down: + case NSM_Attempt: + case NSM_TwoWay: + zlog_warn ("Packet[DD]: Neighbor %s state is %s, packet discarded.", + inet_ntoa(nbr->router_id), + LOOKUP (ospf_nsm_state_msg, nbr->state)); + break; + case NSM_Init: + OSPF_NSM_EVENT_EXECUTE (nbr, NSM_TwoWayReceived); + /* If the new state is ExStart, the processing of the current + packet should then continue in this new state by falling + through to case ExStart below. */ + if (nbr->state != NSM_ExStart) + break; + case NSM_ExStart: + /* Initial DBD */ + if ((IS_SET_DD_ALL (dd->flags) == OSPF_DD_FLAG_ALL) && + (size == OSPF_DB_DESC_MIN_SIZE)) + { + if (IPV4_ADDR_CMP (&nbr->router_id, &oi->ospf->router_id) > 0) + { + /* We're Slave---obey */ + zlog_info ("Packet[DD]: Neighbor %s Negotiation done (Slave).", + inet_ntoa(nbr->router_id)); + nbr->dd_seqnum = ntohl (dd->dd_seqnum); + + /* Reset I/MS */ + UNSET_FLAG (nbr->dd_flags, (OSPF_DD_FLAG_MS|OSPF_DD_FLAG_I)); + } + else + { + /* We're Master, ignore the initial DBD from Slave */ + zlog_info ("Packet[DD]: Neighbor %s: Initial DBD from Slave, " + "ignoring.", inet_ntoa(nbr->router_id)); + break; + } + } + /* Ack from the Slave */ + else if (!IS_SET_DD_MS (dd->flags) && !IS_SET_DD_I (dd->flags) && + ntohl (dd->dd_seqnum) == nbr->dd_seqnum && + IPV4_ADDR_CMP (&nbr->router_id, &oi->ospf->router_id) < 0) + { + zlog_info ("Packet[DD]: Neighbor %s Negotiation done (Master).", + inet_ntoa(nbr->router_id)); + /* Reset I, leaving MS */ + UNSET_FLAG (nbr->dd_flags, OSPF_DD_FLAG_I); + } + else + { + zlog_warn ("Packet[DD]: Neighbor %s Negotiation fails.", + inet_ntoa(nbr->router_id)); + break; + } + + /* This is where the real Options are saved */ + nbr->options = dd->options; + + if (CHECK_FLAG (oi->ospf->config, OSPF_OPAQUE_CAPABLE)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Neighbor[%s] is %sOpaque-capable.", + inet_ntoa (nbr->router_id), + CHECK_FLAG (nbr->options, OSPF_OPTION_O) ? "" : "NOT "); + + if (! CHECK_FLAG (nbr->options, OSPF_OPTION_O) + && IPV4_ADDR_SAME (&DR (oi), &nbr->address.u.prefix4)) + { + zlog_warn ("DR-neighbor[%s] is NOT opaque-capable; " + "Opaque-LSAs cannot be reliably advertised " + "in this network.", + inet_ntoa (nbr->router_id)); + /* This situation is undesirable, but not a real error. */ + } + } + + OSPF_NSM_EVENT_EXECUTE (nbr, NSM_NegotiationDone); + + /* continue processing rest of packet. */ + ospf_db_desc_proc (s, oi, nbr, dd, size); + break; + case NSM_Exchange: + if (ospf_db_desc_is_dup (dd, nbr)) + { + if (IS_SET_DD_MS (nbr->dd_flags)) + /* Master: discard duplicated DD packet. */ + zlog_info ("Packet[DD] (Master): Neighbor %s packet duplicated.", + inet_ntoa (nbr->router_id)); + else + /* Slave: cause to retransmit the last Database Description. */ + { + zlog_info ("Packet[DD] [Slave]: Neighbor %s packet duplicated.", + inet_ntoa (nbr->router_id)); + ospf_db_desc_resend (nbr); + } + break; + } + + /* Otherwise DD packet should be checked. */ + /* Check Master/Slave bit mismatch */ + if (IS_SET_DD_MS (dd->flags) != IS_SET_DD_MS (nbr->last_recv.flags)) + { + zlog_warn ("Packet[DD]: Neighbor %s MS-bit mismatch.", + inet_ntoa(nbr->router_id)); + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_SeqNumberMismatch); + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Packet[DD]: dd->flags=%d, nbr->dd_flags=%d", + dd->flags, nbr->dd_flags); + break; + } + + /* Check initialize bit is set. */ + if (IS_SET_DD_I (dd->flags)) + { + zlog_info ("Packet[DD]: Neighbor %s I-bit set.", + inet_ntoa(nbr->router_id)); + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_SeqNumberMismatch); + break; + } + + /* Check DD Options. */ + if (dd->options != nbr->options) + { +#ifdef ORIGINAL_CODING + /* Save the new options for debugging */ + nbr->options = dd->options; +#endif /* ORIGINAL_CODING */ + zlog_warn ("Packet[DD]: Neighbor %s options mismatch.", + inet_ntoa(nbr->router_id)); + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_SeqNumberMismatch); + break; + } + + /* Check DD sequence number. */ + if ((IS_SET_DD_MS (nbr->dd_flags) && + ntohl (dd->dd_seqnum) != nbr->dd_seqnum) || + (!IS_SET_DD_MS (nbr->dd_flags) && + ntohl (dd->dd_seqnum) != nbr->dd_seqnum + 1)) + { + zlog_warn ("Packet[DD]: Neighbor %s sequence number mismatch.", + inet_ntoa(nbr->router_id)); + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_SeqNumberMismatch); + break; + } + + /* Continue processing rest of packet. */ + ospf_db_desc_proc (s, oi, nbr, dd, size); + break; + case NSM_Loading: + case NSM_Full: + if (ospf_db_desc_is_dup (dd, nbr)) + { + if (IS_SET_DD_MS (nbr->dd_flags)) + { + /* Master should discard duplicate DD packet. */ + zlog_info ("Packet[DD]: Neighbor %s duplicated, " + "packet discarded.", + inet_ntoa(nbr->router_id)); + break; + } + else + { + struct timeval t, now; + quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); + t = tv_sub (now, nbr->last_send_ts); + if (tv_cmp (t, int2tv (nbr->v_inactivity)) < 0) + { + /* In states Loading and Full the slave must resend + its last Database Description packet in response to + duplicate Database Description packets received + from the master. For this reason the slave must + wait RouterDeadInterval seconds before freeing the + last Database Description packet. Reception of a + Database Description packet from the master after + this interval will generate a SeqNumberMismatch + neighbor event. RFC2328 Section 10.8 */ + ospf_db_desc_resend (nbr); + break; + } + } + } + + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_SeqNumberMismatch); + break; + default: + zlog_warn ("Packet[DD]: Neighbor %s NSM illegal status %u.", + inet_ntoa(nbr->router_id), nbr->state); + break; + } +} + +#define OSPF_LSA_KEY_SIZE 12 /* type(4) + id(4) + ar(4) */ + +/* OSPF Link State Request Read -- RFC2328 Section 10.7. */ +static void +ospf_ls_req (struct ip *iph, struct ospf_header *ospfh, + struct stream *s, struct ospf_interface *oi, u_int16_t size) +{ + struct ospf_neighbor *nbr; + u_int32_t ls_type; + struct in_addr ls_id; + struct in_addr adv_router; + struct ospf_lsa *find; + struct list *ls_upd; + unsigned int length; + + /* Increment statistics. */ + oi->ls_req_in++; + + nbr = ospf_nbr_lookup (oi, iph, ospfh); + if (nbr == NULL) + { + zlog_warn ("Link State Request: Unknown Neighbor %s.", + inet_ntoa (ospfh->router_id)); + return; + } + + /* Add event to thread. */ + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_PacketReceived); + + /* Neighbor State should be Exchange or later. */ + if (nbr->state != NSM_Exchange && + nbr->state != NSM_Loading && + nbr->state != NSM_Full) + { + zlog_warn ("Link State Request received from %s: " + "Neighbor state is %s, packet discarded.", + inet_ntoa (ospfh->router_id), + LOOKUP (ospf_nsm_state_msg, nbr->state)); + return; + } + + /* Send Link State Update for ALL requested LSAs. */ + ls_upd = list_new (); + length = OSPF_HEADER_SIZE + OSPF_LS_UPD_MIN_SIZE; + + while (size >= OSPF_LSA_KEY_SIZE) + { + /* Get one slice of Link State Request. */ + ls_type = stream_getl (s); + ls_id.s_addr = stream_get_ipv4 (s); + adv_router.s_addr = stream_get_ipv4 (s); + + /* Verify LSA type. */ + if (ls_type < OSPF_MIN_LSA || ls_type >= OSPF_MAX_LSA) + { + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_BadLSReq); + list_delete (ls_upd); + return; + } + + /* Search proper LSA in LSDB. */ + find = ospf_lsa_lookup (oi->area, ls_type, ls_id, adv_router); + if (find == NULL) + { + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_BadLSReq); + list_delete (ls_upd); + return; + } + + /* Packet overflows MTU size, send immediately. */ + if (length + ntohs (find->data->length) > ospf_packet_max (oi)) + { + if (oi->type == OSPF_IFTYPE_NBMA) + ospf_ls_upd_send (nbr, ls_upd, OSPF_SEND_PACKET_DIRECT); + else + ospf_ls_upd_send (nbr, ls_upd, OSPF_SEND_PACKET_INDIRECT); + + /* Only remove list contents. Keep ls_upd. */ + list_delete_all_node (ls_upd); + + length = OSPF_HEADER_SIZE + OSPF_LS_UPD_MIN_SIZE; + } + + /* Append LSA to update list. */ + listnode_add (ls_upd, find); + length += ntohs (find->data->length); + + size -= OSPF_LSA_KEY_SIZE; + } + + /* Send rest of Link State Update. */ + if (listcount (ls_upd) > 0) + { + if (oi->type == OSPF_IFTYPE_NBMA) + ospf_ls_upd_send (nbr, ls_upd, OSPF_SEND_PACKET_DIRECT); + else + ospf_ls_upd_send (nbr, ls_upd, OSPF_SEND_PACKET_INDIRECT); + + list_delete (ls_upd); + } + else + list_free (ls_upd); +} + +/* Get the list of LSAs from Link State Update packet. + And process some validation -- RFC2328 Section 13. (1)-(2). */ +static struct list * +ospf_ls_upd_list_lsa (struct ospf_neighbor *nbr, struct stream *s, + struct ospf_interface *oi, size_t size) +{ + u_int16_t count, sum; + u_int32_t length; + struct lsa_header *lsah; + struct ospf_lsa *lsa; + struct list *lsas; + + lsas = list_new (); + + count = stream_getl (s); + size -= OSPF_LS_UPD_MIN_SIZE; /* # LSAs */ + + for (; size >= OSPF_LSA_HEADER_SIZE && count > 0; + size -= length, stream_forward_getp (s, length), count--) + { + lsah = (struct lsa_header *) STREAM_PNT (s); + length = ntohs (lsah->length); + + if (length > size) + { + zlog_warn ("Link State Update: LSA length exceeds packet size."); + break; + } + + /* Validate the LSA's LS checksum. */ + sum = lsah->checksum; + 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 + * chance to compress repeated messages in syslog on the other */ + zlog_warn ("Link State Update: LSA checksum error %x/%x, ID=%s from: nbr %s, router ID %s, adv router %s", + sum, lsah->checksum, inet_ntoa (lsah->id), + inet_ntoa (nbr->src), inet_ntoa (nbr->router_id), + inet_ntoa (lsah->adv_router)); + continue; + } + + /* Examine the LSA's LS type. */ + if (lsah->type < OSPF_MIN_LSA || lsah->type >= OSPF_MAX_LSA) + { + zlog_warn ("Link State Update: Unknown LS type %d", lsah->type); + continue; + } + + /* + * What if the received LSA's age is greater than MaxAge? + * Treat it as a MaxAge case -- endo. + */ + if (ntohs (lsah->ls_age) > OSPF_LSA_MAXAGE) + lsah->ls_age = htons (OSPF_LSA_MAXAGE); + + if (CHECK_FLAG (nbr->options, OSPF_OPTION_O)) + { +#ifdef STRICT_OBIT_USAGE_CHECK + if ((IS_OPAQUE_LSA(lsah->type) && + ! CHECK_FLAG (lsah->options, OSPF_OPTION_O)) + || (! IS_OPAQUE_LSA(lsah->type) && + CHECK_FLAG (lsah->options, OSPF_OPTION_O))) + { + /* + * This neighbor must know the exact usage of O-bit; + * the bit will be set in Type-9,10,11 LSAs only. + */ + zlog_warn ("LSA[Type%d:%s]: O-bit abuse?", lsah->type, inet_ntoa (lsah->id)); + continue; + } +#endif /* STRICT_OBIT_USAGE_CHECK */ + + /* Do not take in AS External Opaque-LSAs if we are a stub. */ + if (lsah->type == OSPF_OPAQUE_AS_LSA + && nbr->oi->area->external_routing != OSPF_AREA_DEFAULT) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("LSA[Type%d:%s]: We are a stub, don't take this LSA.", lsah->type, inet_ntoa (lsah->id)); + continue; + } + } + else if (IS_OPAQUE_LSA(lsah->type)) + { + zlog_warn ("LSA[Type%d:%s]: Opaque capability mismatch?", lsah->type, inet_ntoa (lsah->id)); + continue; + } + + /* Create OSPF LSA instance. */ + lsa = ospf_lsa_new (); + + /* We may wish to put some error checking if type NSSA comes in + and area not in NSSA mode */ + switch (lsah->type) + { + case OSPF_AS_EXTERNAL_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 */ + default: + lsa->area = oi->area; + break; + } + + lsa->data = ospf_lsa_data_new (length); + memcpy (lsa->data, lsah, length); + + 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), (void *)lsa); + listnode_add (lsas, lsa); + } + + return lsas; +} + +/* Cleanup Update list. */ +static void +ospf_upd_list_clean (struct list *lsas) +{ + struct listnode *node, *nnode; + struct ospf_lsa *lsa; + + for (ALL_LIST_ELEMENTS (lsas, node, nnode, lsa)) + ospf_lsa_discard (lsa); + + list_delete (lsas); +} + +/* OSPF Link State Update message read -- RFC2328 Section 13. */ +static void +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; + struct list *lsas; + struct listnode *node, *nnode; + struct ospf_lsa *lsa = NULL; + /* unsigned long ls_req_found = 0; */ + + /* Dis-assemble the stream, update each entry, re-encapsulate for flooding */ + + /* Increment statistics. */ + oi->ls_upd_in++; + + /* Check neighbor. */ + nbr = ospf_nbr_lookup (oi, iph, ospfh); + if (nbr == NULL) + { + zlog_warn ("Link State Update: Unknown Neighbor %s on int: %s", + inet_ntoa (ospfh->router_id), IF_NAME (oi)); + return; + } + + /* Add event to thread. */ + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_PacketReceived); + + /* Check neighbor state. */ + if (nbr->state < NSM_Exchange) + { + zlog_warn ("Link State Update: " + "Neighbor[%s] state %s is less than Exchange", + inet_ntoa (ospfh->router_id), + LOOKUP(ospf_nsm_state_msg, nbr->state)); + return; + } + + /* Get list of LSAs from Link State Update packet. - Also perorms Stages + * 1 (validate LSA checksum) and 2 (check for LSA consistent type) + * of section 13. + */ + lsas = ospf_ls_upd_list_lsa (nbr, s, oi, size); + +#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, (void *)lsa, (int) lsa->data->type); \ + ospf_lsa_discard (L); \ + continue; } + + /* 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; + int ret = 1; + + if (IS_DEBUG_OSPF_NSSA) + { + char buf1[INET_ADDRSTRLEN]; + char buf2[INET_ADDRSTRLEN]; + char buf3[INET_ADDRSTRLEN]; + + zlog_debug("LSA Type-%d from %s, ID: %s, ADV: %s", + lsa->data->type, + 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)); + } + + listnode_delete (lsas, lsa); /* We don't need it in list anymore */ + + /* (1) Validate Checksum - Done above by ospf_ls_upd_list_lsa() */ + + /* (2) LSA Type - Done above by ospf_ls_upd_list_lsa() */ + + /* (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 */ + + /* Do take in Type-7's if we are an NSSA */ + + /* If we are also an ABR, later translate them to a Type-5 packet */ + + /* Later, an NSSA Re-fresh can Re-fresh Type-7's and an ABR will + translate them to a separate Type-5 packet. */ + + if (lsa->data->type == OSPF_AS_EXTERNAL_LSA) + /* Reject from STUB or NSSA */ + if (nbr->oi->area->external_routing != OSPF_AREA_DEFAULT) + { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("Incoming External LSA Discarded: We are NSSA/STUB Area"); + DISCARD_LSA (lsa, 1); + } + + if (lsa->data->type == OSPF_AS_NSSA_LSA) + if (nbr->oi->area->external_routing != OSPF_AREA_NSSA) + { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("Incoming NSSA LSA Discarded: Not NSSA Area"); + 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); + + /* (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: */ + + if (IS_LSA_MAXAGE (lsa) && !current && + ospf_check_nbr_status(oi->ospf)) + { + /* (4a) Response Link State Acknowledgment. */ + ospf_ls_ack_send (nbr, 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); + } + + if (IS_OPAQUE_LSA (lsa->data->type) + && IPV4_ADDR_SAME (&lsa->data->adv_router, &oi->ospf->router_id)) + { + /* + * Even if initial flushing seems to be completed, there might + * be a case that self-originated LSA with MaxAge still remain + * in the routing domain. + * Just send an LSAck message to cease retransmission. + */ + if (IS_LSA_MAXAGE (lsa)) + { + zlog_warn ("LSA[%s]: Boomerang effect?", dump_lsa_key (lsa)); + ospf_ls_ack_send (nbr, lsa); + ospf_lsa_discard (lsa); + + if (current != NULL && ! IS_LSA_MAXAGE (current)) + ospf_opaque_lsa_refresh_schedule (current); + continue; + } + + /* + * If an instance of self-originated Opaque-LSA is not found + * in the LSDB, there are some possible cases here. + * + * 1) This node lost opaque-capability after restart. + * 2) Else, a part of opaque-type is no more supported. + * 3) Else, a part of opaque-id is no more supported. + * + * Anyway, it is still this node's responsibility to flush it. + * Otherwise, the LSA instance remains in the routing domain + * until its age reaches to MaxAge. + */ + /* XXX: We should deal with this for *ALL* LSAs, not just opaque */ + if (current == NULL) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("LSA[%s]: Previously originated Opaque-LSA," + "not found in the LSDB.", dump_lsa_key (lsa)); + + SET_FLAG (lsa->flags, OSPF_LSA_SELF); + + ospf_opaque_self_originated_lsa_received (nbr, lsa); + ospf_ls_ack_send (nbr, lsa); + + continue; + } + } + + /* It might be happen that received LSA is self-originated network LSA, but + * 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. + */ + + if(lsa->data->type == OSPF_NETWORK_LSA) + { + struct listnode *oinode, *oinnode; + struct ospf_interface *out_if; + int Flag = 0; + + for (ALL_LIST_ELEMENTS (oi->ospf->oiflist, oinode, oinnode, out_if)) + { + if(out_if == NULL) + break; + + if((IPV4_ADDR_SAME(&out_if->address->u.prefix4, &lsa->data->id)) && + (!(IPV4_ADDR_SAME(&oi->ospf->router_id, &lsa->data->adv_router)))) + { + if(out_if->network_lsa_self) + { + 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", + (void *)lsa, (int) lsa->data->type); + ospf_lsa_discard (lsa); + Flag = 1; + } + break; + } + } + if(Flag) + continue; + } + + /* (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 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) + { + /* Actual flooding procedure. */ + if (ospf_flood (oi->ospf, nbr, current, lsa) < 0) /* Trap NSSA later. */ + DISCARD_LSA (lsa, 4); + continue; + } + + /* (6) Else, If there is an instance of the LSA on the sending + neighbor's Link state request list, an error has occurred in + the Database Exchange process. In this case, restart the + Database Exchange process by generating the neighbor event + BadLSReq for the sending neighbor and stop processing the + Link State Update packet. */ + + if (ospf_ls_request_lookup (nbr, lsa)) + { + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_BadLSReq); + zlog_warn("LSA[%s] instance exists on Link state request list", + dump_lsa_key(lsa)); + + /* Clean list of LSAs. */ + ospf_upd_list_clean (lsas); + /* this lsa is not on lsas list already. */ + ospf_lsa_discard (lsa); + return; + } + + /* If the received LSA is the same instance as the database copy + (i.e., neither one is more recent) the following two steps + should be performed: */ + + if (ret == 0) + { + /* If the LSA is listed in the Link state retransmission list + for the receiving adjacency, the router itself is expecting + an acknowledgment for this LSA. The router should treat the + received LSA as an acknowledgment by removing the LSA from + the Link state retransmission list. This is termed an + "implied acknowledgment". */ + + ls_ret = ospf_ls_retransmit_lookup (nbr, lsa); + + if (ls_ret != NULL) + { + ospf_ls_retransmit_delete (nbr, ls_ret); + + /* Delayed acknowledgment sent if advertisement received + from Designated Router, otherwise do nothing. */ + if (oi->state == ISM_Backup) + if (NBR_IS_DR (nbr)) + listnode_add (oi->ls_ack, ospf_lsa_lock (lsa)); + + DISCARD_LSA (lsa, 5); + } + else + /* Acknowledge the receipt of the LSA by sending a + Link State Acknowledgment packet back out the receiving + interface. */ + { + ospf_ls_ack_send (nbr, lsa); + DISCARD_LSA (lsa, 6); + } + } + + /* The database copy is more recent. If the database copy + has LS age equal to MaxAge and LS sequence number equal to + MaxSequenceNumber, simply discard the received LSA without + acknowledging it. (In this case, the LSA's LS sequence number is + wrapping, and the MaxSequenceNumber LSA must be completely + flushed before any new LSA instance can be introduced). */ + + else if (ret > 0) /* Database copy is more recent */ + { + if (IS_LSA_MAXAGE (current) && + current->data->ls_seqnum == htonl (OSPF_MAX_SEQUENCE_NUMBER)) + { + DISCARD_LSA (lsa, 7); + } + /* Otherwise, as long as the database copy has not been sent in a + Link State Update within the last MinLSArrival seconds, send the + database copy back to the sending neighbor, encapsulated within + a Link State Update Packet. The Link State Update Packet should + be sent directly to the neighbor. In so doing, do not put the + database copy of the LSA on the neighbor's link state + retransmission list, and do not acknowledge the received (less + recent) LSA instance. */ + else + { + struct timeval now; + + quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); + + if (tv_cmp (tv_sub (now, current->tv_orig), + 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); + } + } + } +#undef DISCARD_LSA + + assert (listcount (lsas) == 0); + list_delete (lsas); +} + +/* OSPF Link State Acknowledgment message read -- RFC2328 Section 13.7. */ +static void +ospf_ls_ack (struct ip *iph, struct ospf_header *ospfh, + struct stream *s, struct ospf_interface *oi, u_int16_t size) +{ + struct ospf_neighbor *nbr; + + /* increment statistics. */ + oi->ls_ack_in++; + + nbr = ospf_nbr_lookup (oi, iph, ospfh); + if (nbr == NULL) + { + zlog_warn ("Link State Acknowledgment: Unknown Neighbor %s.", + inet_ntoa (ospfh->router_id)); + return; + } + + /* Add event to thread. */ + OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_PacketReceived); + + if (nbr->state < NSM_Exchange) + { + zlog_warn ("Link State Acknowledgment: " + "Neighbor[%s] state %s is less than Exchange", + inet_ntoa (ospfh->router_id), + LOOKUP(ospf_nsm_state_msg, nbr->state)); + return; + } + + while (size >= OSPF_LSA_HEADER_SIZE) + { + struct ospf_lsa *lsa, *lsr; + + lsa = ospf_lsa_new (); + lsa->data = (struct lsa_header *) STREAM_PNT (s); + + /* lsah = (struct lsa_header *) STREAM_PNT (s); */ + size -= OSPF_LSA_HEADER_SIZE; + stream_forward_getp (s, OSPF_LSA_HEADER_SIZE); + + if (lsa->data->type < OSPF_MIN_LSA || lsa->data->type >= OSPF_MAX_LSA) + { + lsa->data = NULL; + ospf_lsa_discard (lsa); + continue; + } + + lsr = ospf_ls_retransmit_lookup (nbr, lsa); + + if (lsr != NULL && ospf_lsa_more_recent (lsr, lsa) == 0) + ospf_ls_retransmit_delete (nbr, lsr); + + lsa->data = NULL; + ospf_lsa_discard (lsa); + } + + 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; + ifindex_t ifindex = 0; + struct iovec iov; + /* Header and data both require alignment. */ + char buff [CMSG_SPACE(SOPT_SIZE_CMSG_IFINDEX_IPV4())]; + struct msghdr msgh; + + memset (&msgh, 0, sizeof (struct msghdr)); + msgh.msg_iov = &iov; + msgh.msg_iovlen = 1; + msgh.msg_control = (caddr_t) buff; + msgh.msg_controllen = sizeof (buff); + + ret = stream_recvmsg (ibuf, fd, &msgh, 0, OSPF_MAX_PACKET_SIZE+1); + if (ret < 0) + { + zlog_warn("stream_recvmsg failed: %s", safe_strerror(errno)); + return NULL; + } + if ((unsigned int)ret < sizeof(iph)) /* ret must be > 0 now */ + { + zlog_warn("ospf_recv_packet: discarding runt packet of length %d " + "(ip header size is %u)", + ret, (u_int)sizeof(iph)); + return NULL; + } + + /* Note that there should not be alignment problems with this assignment + because this is at the beginning of the stream data buffer. */ + iph = (struct ip *) STREAM_DATA(ibuf); + sockopt_iphdrincl_swab_systoh (iph); + + ip_len = iph->ip_len; + +#if !defined(GNU_LINUX) && (OpenBSD < 200311) && (__FreeBSD_version < 1000000) + /* + * Kernel network code touches incoming IP header parameters, + * before protocol specific processing. + * + * 1) Convert byteorder to host representation. + * --> ip_len, ip_id, ip_off + * + * 2) Adjust ip_len to strip IP header size! + * --> If user process receives entire IP packet via RAW + * socket, it must consider adding IP header size to + * the "ip_len" field of "ip" structure. + * + * For more details, see . + */ + ip_len = ip_len + (iph->ip_hl << 2); +#endif + +#if defined(__DragonFly__) + /* + * in DragonFly's raw socket, ip_len/ip_off are read + * in network byte order. + * As OpenBSD < 200311 adjust ip_len to strip IP header size! + */ + ip_len = ntohs(iph->ip_len) + (iph->ip_hl << 2); +#endif + + ifindex = getsockopt_ifindex (AF_INET, &msgh); + + *ifp = if_lookup_by_index (ifindex); + + if (ret != ip_len) + { + zlog_warn ("ospf_recv_packet read length mismatch: ip_len is %d, " + "but recvmsg returned %d", ip_len, ret); + return NULL; + } + + return ibuf; +} + +static struct ospf_interface * +ospf_associate_packet_vl (struct ospf *ospf, struct interface *ifp, + struct ip *iph, struct ospf_header *ospfh) +{ + struct ospf_interface *rcv_oi; + struct ospf_vl_data *vl_data; + struct ospf_area *vl_area; + struct listnode *node; + + if (IN_MULTICAST (ntohl (iph->ip_dst.s_addr)) || + !OSPF_IS_AREA_BACKBONE (ospfh)) + return NULL; + + /* look for local OSPF interface matching the destination + * to determine Area ID. We presume therefore the destination address + * is unique, or at least (for "unnumbered" links), not used in other + * areas + */ + if ((rcv_oi = ospf_if_lookup_by_local_addr (ospf, NULL, + iph->ip_dst)) == NULL) + return NULL; + + for (ALL_LIST_ELEMENTS_RO (ospf->vlinks, node, vl_data)) + { + vl_area = ospf_area_lookup_by_area_id (ospf, vl_data->vl_area_id); + if (!vl_area) + continue; + + if (OSPF_AREA_SAME (&vl_area, &rcv_oi->area) && + IPV4_ADDR_SAME (&vl_data->vl_peer, &ospfh->router_id)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("associating packet with %s", + IF_NAME (vl_data->vl_oi)); + if (! CHECK_FLAG (vl_data->vl_oi->ifp->flags, IFF_UP)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("This VL is not up yet, sorry"); + return NULL; + } + + return vl_data->vl_oi; + } + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("couldn't find any VL to associate the packet with"); + + return NULL; +} + +static int +ospf_check_area_id (struct ospf_interface *oi, struct ospf_header *ospfh) +{ + /* Check match the Area ID of the receiving interface. */ + if (OSPF_AREA_SAME (&oi->area, &ospfh)) + return 1; + + return 0; +} + +/* Unbound socket will accept any Raw IP packets if proto is matched. + To prevent it, compare src IP address and i/f address with masking + i/f network mask. */ +static int +ospf_check_network_mask (struct ospf_interface *oi, struct in_addr ip_src) +{ + struct in_addr mask, me, him; + + if (oi->type == OSPF_IFTYPE_POINTOPOINT || + oi->type == OSPF_IFTYPE_VIRTUALLINK) + return 1; + + masklen2ip (oi->address->prefixlen, &mask); + + me.s_addr = oi->address->u.prefix4.s_addr & mask.s_addr; + him.s_addr = ip_src.s_addr & mask.s_addr; + + if (IPV4_ADDR_SAME (&me, &him)) + return 1; + + return 0; +} + +/* Return 1, if the packet is properly authenticated and checksummed, + 0 otherwise. In particular, check that AuType header field is valid and + matches the locally configured AuType, and that D.5 requirements are met. */ +static int +ospf_check_auth (struct ospf_interface *oi, struct ospf_header *ospfh) +{ + struct crypt_key *ck; + u_int16_t iface_auth_type; + u_int16_t pkt_auth_type = ntohs (ospfh->auth_type); + + switch (pkt_auth_type) + { + case OSPF_AUTH_NULL: /* RFC2328 D.5.1 */ + if (OSPF_AUTH_NULL != (iface_auth_type = ospf_auth_type (oi))) + { + if (IS_DEBUG_OSPF_PACKET (ospfh->type - 1, RECV)) + zlog_warn ("interface %s: auth-type mismatch, local %s, rcvd Null", + IF_NAME (oi), LOOKUP (ospf_auth_type_str, iface_auth_type)); + return 0; + } + if (! ospf_check_sum (ospfh)) + { + if (IS_DEBUG_OSPF_PACKET (ospfh->type - 1, RECV)) + zlog_warn ("interface %s: Null auth OK, but checksum error, Router-ID %s", + IF_NAME (oi), inet_ntoa (ospfh->router_id)); + return 0; + } + return 1; + case OSPF_AUTH_SIMPLE: /* RFC2328 D.5.2 */ + if (OSPF_AUTH_SIMPLE != (iface_auth_type = ospf_auth_type (oi))) + { + if (IS_DEBUG_OSPF_PACKET (ospfh->type - 1, RECV)) + zlog_warn ("interface %s: auth-type mismatch, local %s, rcvd Simple", + IF_NAME (oi), LOOKUP (ospf_auth_type_str, iface_auth_type)); + return 0; + } + if (memcmp (OSPF_IF_PARAM (oi, auth_simple), ospfh->u.auth_data, OSPF_AUTH_SIMPLE_SIZE)) + { + if (IS_DEBUG_OSPF_PACKET (ospfh->type - 1, RECV)) + zlog_warn ("interface %s: Simple auth failed", IF_NAME (oi)); + return 0; + } + if (! ospf_check_sum (ospfh)) + { + if (IS_DEBUG_OSPF_PACKET (ospfh->type - 1, RECV)) + zlog_warn ("interface %s: Simple auth OK, checksum error, Router-ID %s", + IF_NAME (oi), inet_ntoa (ospfh->router_id)); + return 0; + } + return 1; + case OSPF_AUTH_CRYPTOGRAPHIC: /* RFC2328 D.5.3 */ + if (OSPF_AUTH_CRYPTOGRAPHIC != (iface_auth_type = ospf_auth_type (oi))) + { + if (IS_DEBUG_OSPF_PACKET (ospfh->type - 1, RECV)) + zlog_warn ("interface %s: auth-type mismatch, local %s, rcvd Cryptographic", + IF_NAME (oi), LOOKUP (ospf_auth_type_str, iface_auth_type)); + return 0; + } + if (ospfh->checksum) + { + if (IS_DEBUG_OSPF_PACKET (ospfh->type - 1, RECV)) + zlog_warn ("interface %s: OSPF header checksum is not 0", IF_NAME (oi)); + return 0; + } + /* only MD5 crypto method can pass ospf_packet_examin() */ + if + ( + NULL == (ck = listgetdata (listtail(OSPF_IF_PARAM (oi,auth_crypt)))) || + ospfh->u.crypt.key_id != ck->key_id || + /* Condition above uses the last key ID on the list, which is + different from what ospf_crypt_key_lookup() does. A bug? */ + ! ospf_check_md5_digest (oi, ospfh) + ) + { + if (IS_DEBUG_OSPF_PACKET (ospfh->type - 1, RECV)) + zlog_warn ("interface %s: MD5 auth failed", IF_NAME (oi)); + return 0; + } + return 1; + default: + if (IS_DEBUG_OSPF_PACKET (ospfh->type - 1, RECV)) + zlog_warn ("interface %s: invalid packet auth-type (%02x)", + IF_NAME (oi), pkt_auth_type); + return 0; + } +} + +static int +ospf_check_sum (struct ospf_header *ospfh) +{ + u_int32_t ret; + u_int16_t sum; + + /* clear auth_data for checksum. */ + memset (ospfh->u.auth_data, 0, OSPF_AUTH_SIMPLE_SIZE); + + /* keep checksum and clear. */ + sum = ospfh->checksum; + memset (&ospfh->checksum, 0, sizeof (u_int16_t)); + + /* calculate checksum. */ + ret = in_cksum (ospfh, ntohs (ospfh->length)); + + if (ret != sum) + { + zlog_info ("ospf_check_sum(): checksum mismatch, my %X, his %X", + ret, sum); + return 0; + } + + return 1; +} + +/* Verify, that given link/TOS records are properly sized/aligned and match + Router-LSA "# links" and "# TOS" fields as specified in RFC2328 A.4.2. */ +static unsigned +ospf_router_lsa_links_examin +( + struct router_lsa_link * link, + u_int16_t linkbytes, + const u_int16_t num_links +) +{ + unsigned counted_links = 0, thislinklen; + + while (linkbytes) + { + thislinklen = OSPF_ROUTER_LSA_LINK_SIZE + 4 * link->m[0].tos_count; + if (thislinklen > linkbytes) + { + if (IS_DEBUG_OSPF_PACKET (0, RECV)) + zlog_debug ("%s: length error in link block #%u", __func__, counted_links); + return MSG_NG; + } + link = (struct router_lsa_link *)((caddr_t) link + thislinklen); + linkbytes -= thislinklen; + counted_links++; + } + if (counted_links != num_links) + { + if (IS_DEBUG_OSPF_PACKET (0, RECV)) + zlog_debug ("%s: %u link blocks declared, %u present", + __func__, num_links, counted_links); + return MSG_NG; + } + return MSG_OK; +} + +/* Verify, that the given LSA is properly sized/aligned (including type-specific + minimum length constraint). */ +static unsigned +ospf_lsa_examin (struct lsa_header * lsah, const u_int16_t lsalen, const u_char headeronly) +{ + unsigned ret; + struct router_lsa * rlsa; + if + ( + lsah->type < OSPF_MAX_LSA && + ospf_lsa_minlen[lsah->type] && + lsalen < OSPF_LSA_HEADER_SIZE + ospf_lsa_minlen[lsah->type] + ) + { + if (IS_DEBUG_OSPF_PACKET (0, RECV)) + zlog_debug ("%s: undersized (%u B) %s", + __func__, lsalen, LOOKUP (ospf_lsa_type_msg, lsah->type)); + return MSG_NG; + } + switch (lsah->type) + { + case OSPF_ROUTER_LSA: + /* RFC2328 A.4.2, LSA header + 4 bytes followed by N>=1 (12+)-byte link blocks */ + if (headeronly) + { + ret = (lsalen - OSPF_LSA_HEADER_SIZE - OSPF_ROUTER_LSA_MIN_SIZE) % 4 ? MSG_NG : MSG_OK; + break; + } + rlsa = (struct router_lsa *) lsah; + ret = ospf_router_lsa_links_examin + ( + (struct router_lsa_link *) rlsa->link, + lsalen - OSPF_LSA_HEADER_SIZE - 4, /* skip: basic header, "flags", 0, "# links" */ + ntohs (rlsa->links) /* 16 bits */ + ); + break; + case OSPF_AS_EXTERNAL_LSA: + /* RFC2328 A.4.5, LSA header + 4 bytes followed by N>=1 12-bytes long blocks */ + case OSPF_AS_NSSA_LSA: + /* RFC3101 C, idem */ + ret = (lsalen - OSPF_LSA_HEADER_SIZE - OSPF_AS_EXTERNAL_LSA_MIN_SIZE) % 12 ? MSG_NG : MSG_OK; + break; + /* Following LSA types are considered OK length-wise as soon as their minimum + * length constraint is met and length of the whole LSA is a multiple of 4 + * (basic LSA header size is already a multiple of 4). */ + case OSPF_NETWORK_LSA: + /* RFC2328 A.4.3, LSA header + 4 bytes followed by N>=1 router-IDs */ + 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 */ + case OSPF_OPAQUE_LINK_LSA: + case OSPF_OPAQUE_AREA_LSA: + case OSPF_OPAQUE_AS_LSA: + /* RFC5250 A.2, "some number of octets (of application-specific + * 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. */ + ret = lsalen % 4 ? MSG_NG : MSG_OK; + break; + default: + if (IS_DEBUG_OSPF_PACKET (0, RECV)) + zlog_debug ("%s: unsupported LSA type 0x%02x", __func__, lsah->type); + return MSG_NG; + } + if (ret != MSG_OK && IS_DEBUG_OSPF_PACKET (0, RECV)) + zlog_debug ("%s: alignment error in %s", + __func__, LOOKUP (ospf_lsa_type_msg, lsah->type)); + return ret; +} + +/* Verify if the provided input buffer is a valid sequence of LSAs. This + includes verification of LSA blocks length/alignment and dispatching + of deeper-level checks. */ +static unsigned +ospf_lsaseq_examin +( + struct lsa_header *lsah, /* start of buffered data */ + size_t length, + const u_char headeronly, + /* When declared_num_lsas is not 0, compare it to the real number of LSAs + and treat the difference as an error. */ + const u_int32_t declared_num_lsas +) +{ + u_int32_t counted_lsas = 0; + + while (length) + { + u_int16_t lsalen; + if (length < OSPF_LSA_HEADER_SIZE) + { + if (IS_DEBUG_OSPF_PACKET (0, RECV)) + zlog_debug ("%s: undersized (%zu B) trailing (#%u) LSA header", + __func__, length, counted_lsas); + return MSG_NG; + } + /* save on ntohs() calls here and in the LSA validator */ + lsalen = ntohs (lsah->length); + if (lsalen < OSPF_LSA_HEADER_SIZE) + { + if (IS_DEBUG_OSPF_PACKET (0, RECV)) + zlog_debug ("%s: malformed LSA header #%u, declared length is %u B", + __func__, counted_lsas, lsalen); + return MSG_NG; + } + if (headeronly) + { + /* less checks here and in ospf_lsa_examin() */ + if (MSG_OK != ospf_lsa_examin (lsah, lsalen, 1)) + { + if (IS_DEBUG_OSPF_PACKET (0, RECV)) + zlog_debug ("%s: malformed header-only LSA #%u", __func__, counted_lsas); + return MSG_NG; + } + lsah = (struct lsa_header *) ((caddr_t) lsah + OSPF_LSA_HEADER_SIZE); + length -= OSPF_LSA_HEADER_SIZE; + } + else + { + /* make sure the input buffer is deep enough before further checks */ + if (lsalen > length) + { + if (IS_DEBUG_OSPF_PACKET (0, RECV)) + zlog_debug ("%s: anomaly in LSA #%u: declared length is %u B, buffered length is %zu B", + __func__, counted_lsas, lsalen, length); + return MSG_NG; + } + if (MSG_OK != ospf_lsa_examin (lsah, lsalen, 0)) + { + if (IS_DEBUG_OSPF_PACKET (0, RECV)) + zlog_debug ("%s: malformed LSA #%u", __func__, counted_lsas); + return MSG_NG; + } + lsah = (struct lsa_header *) ((caddr_t) lsah + lsalen); + length -= lsalen; + } + counted_lsas++; + } + + if (declared_num_lsas && counted_lsas != declared_num_lsas) + { + if (IS_DEBUG_OSPF_PACKET (0, RECV)) + zlog_debug ("%s: #LSAs declared (%u) does not match actual (%u)", + __func__, declared_num_lsas, counted_lsas); + return MSG_NG; + } + return MSG_OK; +} + +/* Verify a complete OSPF packet for proper sizing/alignment. */ +static unsigned +ospf_packet_examin (struct ospf_header * oh, const unsigned bytesonwire) +{ + u_int16_t bytesdeclared, bytesauth; + unsigned ret; + struct ospf_ls_update * lsupd; + + /* Length, 1st approximation. */ + if (bytesonwire < OSPF_HEADER_SIZE) + { + if (IS_DEBUG_OSPF_PACKET (0, RECV)) + zlog_debug ("%s: undersized (%u B) packet", __func__, bytesonwire); + return MSG_NG; + } + /* Now it is safe to access header fields. Performing length check, allow + * for possible extra bytes of crypto auth/padding, which are not counted + * in the OSPF header "length" field. */ + if (oh->version != OSPF_VERSION) + { + if (IS_DEBUG_OSPF_PACKET (0, RECV)) + zlog_debug ("%s: invalid (%u) protocol version", __func__, oh->version); + return MSG_NG; + } + bytesdeclared = ntohs (oh->length); + if (ntohs (oh->auth_type) != OSPF_AUTH_CRYPTOGRAPHIC) + bytesauth = 0; + else + { + if (oh->u.crypt.auth_data_len != OSPF_AUTH_MD5_SIZE) + { + if (IS_DEBUG_OSPF_PACKET (0, RECV)) + zlog_debug ("%s: unsupported crypto auth length (%u B)", + __func__, oh->u.crypt.auth_data_len); + return MSG_NG; + } + bytesauth = OSPF_AUTH_MD5_SIZE; + } + if (bytesdeclared + bytesauth > bytesonwire) + { + if (IS_DEBUG_OSPF_PACKET (0, RECV)) + zlog_debug ("%s: packet length error (%u real, %u+%u declared)", + __func__, bytesonwire, bytesdeclared, bytesauth); + return MSG_NG; + } + /* Length, 2nd approximation. The type-specific constraint is checked + against declared length, not amount of bytes on wire. */ + if + ( + oh->type >= OSPF_MSG_HELLO && + oh->type <= OSPF_MSG_LS_ACK && + bytesdeclared < OSPF_HEADER_SIZE + ospf_packet_minlen[oh->type] + ) + { + if (IS_DEBUG_OSPF_PACKET (0, RECV)) + zlog_debug ("%s: undersized (%u B) %s packet", __func__, + bytesdeclared, LOOKUP (ospf_packet_type_str, oh->type)); + return MSG_NG; + } + switch (oh->type) + { + case OSPF_MSG_HELLO: + /* RFC2328 A.3.2, packet header + OSPF_HELLO_MIN_SIZE bytes followed + by N>=0 router-IDs. */ + ret = (bytesdeclared - OSPF_HEADER_SIZE - OSPF_HELLO_MIN_SIZE) % 4 ? MSG_NG : MSG_OK; + break; + case OSPF_MSG_DB_DESC: + /* RFC2328 A.3.3, packet header + OSPF_DB_DESC_MIN_SIZE bytes followed + by N>=0 header-only LSAs. */ + ret = ospf_lsaseq_examin + ( + (struct lsa_header *) ((caddr_t) oh + OSPF_HEADER_SIZE + OSPF_DB_DESC_MIN_SIZE), + bytesdeclared - OSPF_HEADER_SIZE - OSPF_DB_DESC_MIN_SIZE, + 1, /* header-only LSAs */ + 0 + ); + break; + case OSPF_MSG_LS_REQ: + /* RFC2328 A.3.4, packet header followed by N>=0 12-bytes request blocks. */ + ret = (bytesdeclared - OSPF_HEADER_SIZE - OSPF_LS_REQ_MIN_SIZE) % + OSPF_LSA_KEY_SIZE ? MSG_NG : MSG_OK; + break; + case OSPF_MSG_LS_UPD: + /* RFC2328 A.3.5, packet header + OSPF_LS_UPD_MIN_SIZE bytes followed + by N>=0 full LSAs (with N declared beforehand). */ + lsupd = (struct ospf_ls_update *) ((caddr_t) oh + OSPF_HEADER_SIZE); + ret = ospf_lsaseq_examin + ( + (struct lsa_header *) ((caddr_t) lsupd + OSPF_LS_UPD_MIN_SIZE), + bytesdeclared - OSPF_HEADER_SIZE - OSPF_LS_UPD_MIN_SIZE, + 0, /* full LSAs */ + ntohl (lsupd->num_lsas) /* 32 bits */ + ); + break; + case OSPF_MSG_LS_ACK: + /* RFC2328 A.3.6, packet header followed by N>=0 header-only LSAs. */ + ret = ospf_lsaseq_examin + ( + (struct lsa_header *) ((caddr_t) oh + OSPF_HEADER_SIZE + OSPF_LS_ACK_MIN_SIZE), + bytesdeclared - OSPF_HEADER_SIZE - OSPF_LS_ACK_MIN_SIZE, + 1, /* header-only LSAs */ + 0 + ); + break; + default: + if (IS_DEBUG_OSPF_PACKET (0, RECV)) + zlog_debug ("%s: invalid packet type 0x%02x", __func__, oh->type); + return MSG_NG; + } + if (ret != MSG_OK && IS_DEBUG_OSPF_PACKET (0, RECV)) + zlog_debug ("%s: malformed %s packet", __func__, LOOKUP (ospf_packet_type_str, oh->type)); + return ret; +} + +/* OSPF Header verification. */ +static int +ospf_verify_header (struct stream *ibuf, struct ospf_interface *oi, + struct ip *iph, struct ospf_header *ospfh) +{ + /* Check Area ID. */ + if (!ospf_check_area_id (oi, ospfh)) + { + zlog_warn ("interface %s: ospf_read invalid Area ID %s.", + IF_NAME (oi), inet_ntoa (ospfh->area_id)); + return -1; + } + + /* Check network mask, Silently discarded. */ + if (! ospf_check_network_mask (oi, iph->ip_src)) + { + zlog_warn ("interface %s: ospf_read network address is not same [%s]", + IF_NAME (oi), inet_ntoa (iph->ip_src)); + return -1; + } + + /* Check authentication. The function handles logging actions, where required. */ + if (! ospf_check_auth (oi, ospfh)) + return -1; + + return 0; +} + +/* Starting point of packet process function. */ +int +ospf_read (struct thread *thread) +{ + int ret; + struct stream *ibuf; + struct ospf *ospf; + struct ospf_interface *oi; + struct ip *iph; + struct ospf_header *ospfh; + u_int16_t length; + struct interface *ifp; + + /* first of all get interface pointer. */ + ospf = THREAD_ARG (thread); + + /* prepare for next packet. */ + ospf->t_read = thread_add_read (master, ospf_read, ospf, ospf->fd); + + stream_reset(ospf->ibuf); + if (!(ibuf = ospf_recv_packet (ospf->fd, &ifp, ospf->ibuf))) + return -1; + /* This raw packet is known to be at least as big as its IP header. */ + + /* Note that there should not be alignment problems with this assignment + because this is at the beginning of the stream data buffer. */ + iph = (struct ip *) STREAM_DATA (ibuf); + /* Note that sockopt_iphdrincl_swab_systoh was called in ospf_recv_packet. */ + + if (ifp == NULL) + /* Handle cases where the platform does not support retrieving the ifindex, + and also platforms (such as Solaris 8) that claim to support ifindex + retrieval but do not. */ + ifp = if_lookup_address (iph->ip_src); + + if (ifp == NULL) + return 0; + + /* IP Header dump. */ + if (IS_DEBUG_OSPF_PACKET(0, RECV)) + ospf_ip_header_dump (iph); + + /* Self-originated packet should be discarded silently. */ + if (ospf_if_lookup_by_local_addr (ospf, NULL, iph->ip_src)) + { + if (IS_DEBUG_OSPF_PACKET (0, RECV)) + { + zlog_debug ("ospf_read[%s]: Dropping self-originated packet", + inet_ntoa (iph->ip_src)); + } + return 0; + } + + /* Advance from IP header to OSPF header (iph->ip_hl has been verified + by ospf_recv_packet() to be correct). */ + stream_forward_getp (ibuf, iph->ip_hl * 4); + + ospfh = (struct ospf_header *) STREAM_PNT (ibuf); + if (MSG_OK != ospf_packet_examin (ospfh, stream_get_endp (ibuf) - stream_get_getp (ibuf))) + return -1; + /* Now it is safe to access all fields of OSPF packet header. */ + + /* associate packet with ospf interface */ + oi = ospf_if_lookup_recv_if (ospf, iph->ip_src, ifp); + + /* ospf_verify_header() relies on a valid "oi" and thus can be called only + after the passive/backbone/other checks below are passed. These checks + in turn access the fields of unverified "ospfh" structure for their own + purposes and must remain very accurate in doing this. */ + + /* If incoming interface is passive one, ignore it. */ + if (oi && OSPF_IF_PASSIVE_STATUS (oi) == OSPF_IF_PASSIVE) + { + char buf[3][INET_ADDRSTRLEN]; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ignoring packet from router %s sent to %s, " + "received on a passive interface, %s", + inet_ntop(AF_INET, &ospfh->router_id, buf[0], sizeof(buf[0])), + inet_ntop(AF_INET, &iph->ip_dst, buf[1], sizeof(buf[1])), + inet_ntop(AF_INET, &oi->address->u.prefix4, + buf[2], sizeof(buf[2]))); + + if (iph->ip_dst.s_addr == htonl(OSPF_ALLSPFROUTERS)) + { + /* Try to fix multicast membership. + * Some OS:es may have problems in this area, + * make sure it is removed. + */ + OI_MEMBER_JOINED(oi, MEMBER_ALLROUTERS); + ospf_if_set_multicast(oi); + } + return 0; + } + + + /* if no local ospf_interface, + * or header area is backbone but ospf_interface is not + * check for VLINK interface + */ + if ( (oi == NULL) || + (OSPF_IS_AREA_ID_BACKBONE(ospfh->area_id) + && !OSPF_IS_AREA_ID_BACKBONE(oi->area->area_id)) + ) + { + if ((oi = ospf_associate_packet_vl (ospf, ifp, iph, ospfh)) == NULL) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Packet from [%s] received on link %s" + " but no ospf_interface", + inet_ntoa (iph->ip_src), ifp->name); + return 0; + } + } + + /* else it must be a local ospf interface, check it was received on + * correct link + */ + else if (oi->ifp != ifp) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_warn ("Packet from [%s] received on wrong link %s", + inet_ntoa (iph->ip_src), ifp->name); + return 0; + } + else if (oi->state == ISM_Down) + { + char buf[2][INET_ADDRSTRLEN]; + zlog_warn ("Ignoring packet from %s to %s received on interface that is " + "down [%s]; interface flags are %s", + inet_ntop(AF_INET, &iph->ip_src, buf[0], sizeof(buf[0])), + inet_ntop(AF_INET, &iph->ip_dst, buf[1], sizeof(buf[1])), + ifp->name, if_flag_dump(ifp->flags)); + /* Fix multicast memberships? */ + if (iph->ip_dst.s_addr == htonl(OSPF_ALLSPFROUTERS)) + OI_MEMBER_JOINED(oi, MEMBER_ALLROUTERS); + else if (iph->ip_dst.s_addr == htonl(OSPF_ALLDROUTERS)) + OI_MEMBER_JOINED(oi, MEMBER_DROUTERS); + if (oi->multicast_memberships) + ospf_if_set_multicast(oi); + return 0; + } + + /* + * If the received packet is destined for AllDRouters, the packet + * should be accepted only if the received ospf interface state is + * either DR or Backup -- endo. + */ + if (iph->ip_dst.s_addr == htonl (OSPF_ALLDROUTERS) + && (oi->state != ISM_DR && oi->state != ISM_Backup)) + { + zlog_warn ("Dropping packet for AllDRouters from [%s] via [%s] (ISM: %s)", + inet_ntoa (iph->ip_src), IF_NAME (oi), + LOOKUP (ospf_ism_state_msg, oi->state)); + /* Try to fix multicast membership. */ + SET_FLAG(oi->multicast_memberships, MEMBER_DROUTERS); + ospf_if_set_multicast(oi); + return 0; + } + + /* Verify more OSPF header fields. */ + ret = ospf_verify_header (ibuf, oi, iph, ospfh); + if (ret < 0) + { + if (IS_DEBUG_OSPF_PACKET (0, RECV)) + zlog_debug ("ospf_read[%s]: Header check failed, " + "dropping.", + inet_ntoa (iph->ip_src)); + return ret; + } + + /* Show debug receiving packet. */ + if (IS_DEBUG_OSPF_PACKET (ospfh->type - 1, RECV)) + { + if (IS_DEBUG_OSPF_PACKET (ospfh->type - 1, DETAIL)) + { + zlog_debug ("-----------------------------------------------------"); + ospf_packet_dump (ibuf); + } + + zlog_debug ("%s received from [%s] via [%s]", + LOOKUP (ospf_packet_type_str, ospfh->type), + inet_ntoa (ospfh->router_id), IF_NAME (oi)); + zlog_debug (" src [%s],", inet_ntoa (iph->ip_src)); + zlog_debug (" dst [%s]", inet_ntoa (iph->ip_dst)); + + if (IS_DEBUG_OSPF_PACKET (ospfh->type - 1, DETAIL)) + zlog_debug ("-----------------------------------------------------"); + } + + stream_forward_getp (ibuf, OSPF_HEADER_SIZE); + + /* Adjust size to message length. */ + length = ntohs (ospfh->length) - OSPF_HEADER_SIZE; + + /* Read rest of the packet and call each sort of packet routine. */ + switch (ospfh->type) + { + case OSPF_MSG_HELLO: + ospf_hello (iph, ospfh, ibuf, oi, length); + break; + case OSPF_MSG_DB_DESC: + ospf_db_desc (iph, ospfh, ibuf, oi, length); + break; + case OSPF_MSG_LS_REQ: + ospf_ls_req (iph, ospfh, ibuf, oi, length); + break; + case OSPF_MSG_LS_UPD: + ospf_ls_upd (ospf, iph, ospfh, ibuf, oi, length); + break; + case OSPF_MSG_LS_ACK: + ospf_ls_ack (iph, ospfh, ibuf, oi, length); + break; + default: + zlog (NULL, LOG_WARNING, + "interface %s: OSPF packet header type %d is illegal", + IF_NAME (oi), ospfh->type); + break; + } + + return 0; +} + +/* Make OSPF header. */ +static void +ospf_make_header (int type, struct ospf_interface *oi, struct stream *s) +{ + struct ospf_header *ospfh; + + ospfh = (struct ospf_header *) STREAM_DATA (s); + + ospfh->version = (u_char) OSPF_VERSION; + ospfh->type = (u_char) type; + + ospfh->router_id = oi->ospf->router_id; + + ospfh->checksum = 0; + ospfh->area_id = oi->area->area_id; + ospfh->auth_type = htons (ospf_auth_type (oi)); + + memset (ospfh->u.auth_data, 0, OSPF_AUTH_SIMPLE_SIZE); + + stream_forward_endp (s, OSPF_HEADER_SIZE); +} + +/* Make Authentication Data. */ +static int +ospf_make_auth (struct ospf_interface *oi, struct ospf_header *ospfh) +{ + struct crypt_key *ck; + + switch (ospf_auth_type (oi)) + { + case OSPF_AUTH_NULL: + /* memset (ospfh->u.auth_data, 0, sizeof (ospfh->u.auth_data)); */ + break; + case OSPF_AUTH_SIMPLE: + memcpy (ospfh->u.auth_data, OSPF_IF_PARAM (oi, auth_simple), + OSPF_AUTH_SIMPLE_SIZE); + break; + case OSPF_AUTH_CRYPTOGRAPHIC: + /* If key is not set, then set 0. */ + if (list_isempty (OSPF_IF_PARAM (oi, auth_crypt))) + { + ospfh->u.crypt.zero = 0; + ospfh->u.crypt.key_id = 0; + ospfh->u.crypt.auth_data_len = OSPF_AUTH_MD5_SIZE; + } + else + { + ck = listgetdata (listtail(OSPF_IF_PARAM (oi, auth_crypt))); + ospfh->u.crypt.zero = 0; + ospfh->u.crypt.key_id = ck->key_id; + ospfh->u.crypt.auth_data_len = OSPF_AUTH_MD5_SIZE; + } + /* note: the seq is done in ospf_make_md5_digest() */ + break; + default: + /* memset (ospfh->u.auth_data, 0, sizeof (ospfh->u.auth_data)); */ + break; + } + + return 0; +} + +/* Fill rest of OSPF header. */ +static void +ospf_fill_header (struct ospf_interface *oi, + struct stream *s, u_int16_t length) +{ + struct ospf_header *ospfh; + + ospfh = (struct ospf_header *) STREAM_DATA (s); + + /* Fill length. */ + ospfh->length = htons (length); + + /* Calculate checksum. */ + if (ntohs (ospfh->auth_type) != OSPF_AUTH_CRYPTOGRAPHIC) + ospfh->checksum = in_cksum (ospfh, length); + else + ospfh->checksum = 0; + + /* Add Authentication Data. */ + ospf_make_auth (oi, ospfh); +} + +static int +ospf_make_hello (struct ospf_interface *oi, struct stream *s) +{ + struct ospf_neighbor *nbr; + struct route_node *rn; + u_int16_t length = OSPF_HELLO_MIN_SIZE; + struct in_addr mask; + unsigned long p; + int flag = 0; + + /* Set netmask of interface. */ + 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 + memset ((char *) &mask, 0, sizeof (struct in_addr)); + stream_put_ipv4 (s, mask.s_addr); + + /* Set Hello Interval. */ + if (OSPF_IF_PARAM (oi, fast_hello) == 0) + stream_putw (s, OSPF_IF_PARAM (oi, v_hello)); + else + stream_putw (s, 0); /* hello-interval of 0 for fast-hellos */ + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("make_hello: options: %x, int: %s", + OPTIONS(oi), IF_NAME (oi)); + + /* Set Options. */ + stream_putc (s, OPTIONS (oi)); + + /* Set Router Priority. */ + stream_putc (s, PRIORITY (oi)); + + /* Set Router Dead Interval. */ + stream_putl (s, OSPF_IF_PARAM (oi, v_wait)); + + /* Set Designated Router. */ + stream_put_ipv4 (s, DR (oi).s_addr); + + p = stream_get_endp (s); + + /* Set Backup Designated Router. */ + stream_put_ipv4 (s, BDR (oi).s_addr); + + /* Add neighbor seen. */ + for (rn = route_top (oi->nbrs); rn; rn = route_next (rn)) + if ((nbr = rn->info)) + if (nbr->router_id.s_addr != 0) /* Ignore 0.0.0.0 node. */ + if (nbr->state != NSM_Attempt) /* Ignore Down neighbor. */ + if (nbr->state != NSM_Down) /* This is myself for DR election. */ + if (!IPV4_ADDR_SAME (&nbr->router_id, &oi->ospf->router_id)) + { + /* Check neighbor is sane? */ + if (nbr->d_router.s_addr != 0 + && IPV4_ADDR_SAME (&nbr->d_router, &oi->address->u.prefix4) + && IPV4_ADDR_SAME (&nbr->bd_router, &oi->address->u.prefix4)) + flag = 1; + + stream_put_ipv4 (s, nbr->router_id.s_addr); + length += 4; + } + + /* Let neighbor generate BackupSeen. */ + if (flag == 1) + stream_putl_at (s, p, 0); /* ipv4 address, normally */ + + return length; +} + +static int +ospf_make_db_desc (struct ospf_interface *oi, struct ospf_neighbor *nbr, + struct stream *s) +{ + struct ospf_lsa *lsa; + u_int16_t length = OSPF_DB_DESC_MIN_SIZE; + u_char options; + unsigned long pp; + int i; + struct ospf_lsdb *lsdb; + + /* Set Interface MTU. */ + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) + stream_putw (s, 0); + else + stream_putw (s, oi->ifp->mtu); + + /* Set Options. */ + options = OPTIONS (oi); + if (CHECK_FLAG (oi->ospf->config, OSPF_OPAQUE_CAPABLE)) + SET_FLAG (options, OSPF_OPTION_O); + stream_putc (s, options); + + /* DD flags */ + pp = stream_get_endp (s); + stream_putc (s, nbr->dd_flags); + + /* Set DD Sequence Number. */ + stream_putl (s, nbr->dd_seqnum); + + /* shortcut unneeded walk of (empty) summary LSDBs */ + if (ospf_db_summary_isempty (nbr)) + goto empty; + + /* Describe LSA Header from Database Summary List. */ + lsdb = &nbr->db_sum; + + for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++) + { + struct route_table *table = lsdb->type[i].db; + struct route_node *rn; + + for (rn = route_top (table); rn; rn = route_next (rn)) + if ((lsa = rn->info) != NULL) + { + if (IS_OPAQUE_LSA (lsa->data->type) + && (! CHECK_FLAG (options, OSPF_OPTION_O))) + { + /* Suppress advertising opaque-informations. */ + /* Remove LSA from DB summary list. */ + ospf_lsdb_delete (lsdb, lsa); + continue; + } + + if (!CHECK_FLAG (lsa->flags, OSPF_LSA_DISCARD)) + { + struct lsa_header *lsah; + u_int16_t ls_age; + + /* DD packet overflows interface MTU. */ + if (length + OSPF_LSA_HEADER_SIZE > ospf_packet_max (oi)) + break; + + /* Keep pointer to LS age. */ + lsah = (struct lsa_header *) (STREAM_DATA (s) + + stream_get_endp (s)); + + /* Proceed stream pointer. */ + stream_put (s, lsa->data, OSPF_LSA_HEADER_SIZE); + length += OSPF_LSA_HEADER_SIZE; + + /* Set LS age. */ + ls_age = LS_AGE (lsa); + lsah->ls_age = htons (ls_age); + + } + + /* Remove LSA from DB summary list. */ + ospf_lsdb_delete (lsdb, lsa); + } + } + + /* Update 'More' bit */ + if (ospf_db_summary_isempty (nbr)) + { +empty: + if (nbr->state >= NSM_Exchange) + { + UNSET_FLAG (nbr->dd_flags, OSPF_DD_FLAG_M); + /* Rewrite DD flags */ + stream_putc_at (s, pp, nbr->dd_flags); + } + else + { + assert (IS_SET_DD_M(nbr->dd_flags)); + } + } + return length; +} + +static int +ospf_make_ls_req_func (struct stream *s, u_int16_t *length, + unsigned long delta, struct ospf_neighbor *nbr, + struct ospf_lsa *lsa) +{ + struct ospf_interface *oi; + + oi = nbr->oi; + + /* LS Request packet overflows interface MTU. */ + if (*length + delta > ospf_packet_max(oi)) + return 0; + + stream_putl (s, lsa->data->type); + stream_put_ipv4 (s, lsa->data->id.s_addr); + stream_put_ipv4 (s, lsa->data->adv_router.s_addr); + + ospf_lsa_unlock (&nbr->ls_req_last); + nbr->ls_req_last = ospf_lsa_lock (lsa); + + *length += 12; + return 1; +} + +static int +ospf_make_ls_req (struct ospf_neighbor *nbr, struct stream *s) +{ + struct ospf_lsa *lsa; + u_int16_t length = OSPF_LS_REQ_MIN_SIZE; + unsigned long delta = stream_get_endp(s)+12; + struct route_table *table; + struct route_node *rn; + int i; + struct ospf_lsdb *lsdb; + + lsdb = &nbr->ls_req; + + for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++) + { + table = lsdb->type[i].db; + for (rn = route_top (table); rn; rn = route_next (rn)) + if ((lsa = (rn->info)) != NULL) + if (ospf_make_ls_req_func (s, &length, delta, nbr, lsa) == 0) + { + route_unlock_node (rn); + break; + } + } + return length; +} + +static int +ls_age_increment (struct ospf_lsa *lsa, int delay) +{ + int age; + + age = IS_LSA_MAXAGE (lsa) ? OSPF_LSA_MAXAGE : LS_AGE (lsa) + delay; + + return (age > OSPF_LSA_MAXAGE ? OSPF_LSA_MAXAGE : age); +} + +static int +ospf_make_ls_upd (struct ospf_interface *oi, struct list *update, struct stream *s) +{ + struct ospf_lsa *lsa; + struct listnode *node; + u_int16_t length = 0; + unsigned int size_noauth; + unsigned long delta = stream_get_endp (s); + unsigned long pp; + int count = 0; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_make_ls_upd: Start"); + + pp = stream_get_endp (s); + stream_forward_endp (s, OSPF_LS_UPD_MIN_SIZE); + length += OSPF_LS_UPD_MIN_SIZE; + + /* Calculate amount of packet usable for data. */ + size_noauth = stream_get_size(s) - ospf_packet_authspace(oi); + + while ((node = listhead (update)) != NULL) + { + struct lsa_header *lsah; + u_int16_t ls_age; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_make_ls_upd: List Iteration %d", count); + + lsa = listgetdata (node); + + assert (lsa->data); + + /* Will it fit? */ + if (length + delta + ntohs (lsa->data->length) > size_noauth) + break; + + /* Keep pointer to LS age. */ + lsah = (struct lsa_header *) (STREAM_DATA (s) + stream_get_endp (s)); + + /* Put LSA to Link State Request. */ + stream_put (s, lsa->data, ntohs (lsa->data->length)); + + /* Set LS age. */ + /* each hop must increment an lsa_age by transmit_delay + of OSPF interface */ + ls_age = ls_age_increment (lsa, OSPF_IF_PARAM (oi, transmit_delay)); + lsah->ls_age = htons (ls_age); + + length += ntohs (lsa->data->length); + count++; + + list_delete_node (update, node); + ospf_lsa_unlock (&lsa); /* oi->ls_upd_queue */ + } + + /* Now set #LSAs. */ + stream_putl_at (s, pp, count); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_make_ls_upd: Stop"); + return length; +} + +static int +ospf_make_ls_ack (struct ospf_interface *oi, struct list *ack, struct stream *s) +{ + struct listnode *node, *nnode; + u_int16_t length = OSPF_LS_ACK_MIN_SIZE; + unsigned long delta = stream_get_endp(s) + 24; + struct ospf_lsa *lsa; + + for (ALL_LIST_ELEMENTS (ack, node, nnode, lsa)) + { + assert (lsa); + + if (length + delta > ospf_packet_max (oi)) + break; + + stream_put (s, lsa->data, OSPF_LSA_HEADER_SIZE); + length += OSPF_LSA_HEADER_SIZE; + + listnode_delete (ack, lsa); + ospf_lsa_unlock (&lsa); /* oi->ls_ack_direct.ls_ack */ + } + + return length; +} + +static void +ospf_hello_send_sub (struct ospf_interface *oi, in_addr_t addr) +{ + struct ospf_packet *op; + u_int16_t length = OSPF_HEADER_SIZE; + + op = ospf_packet_new (oi->ifp->mtu); + + /* Prepare OSPF common header. */ + ospf_make_header (OSPF_MSG_HELLO, oi, op->s); + + /* Prepare OSPF Hello body. */ + length += ospf_make_hello (oi, op->s); + + /* Fill OSPF header. */ + ospf_fill_header (oi, op->s, length); + + /* Set packet length. */ + op->length = length; + + op->dst.s_addr = addr; + + /* Add packet to the top of the interface output queue, so that they + * can't get delayed by things like long queues of LS Update packets + */ + ospf_packet_add_top (oi, op); + + /* Hook thread to write packet. */ + OSPF_ISM_WRITE_ON (oi->ospf); +} + +static void +ospf_poll_send (struct ospf_nbr_nbma *nbr_nbma) +{ + struct ospf_interface *oi; + + oi = nbr_nbma->oi; + assert(oi); + + /* If this is passive interface, do not send OSPF Hello. */ + if (OSPF_IF_PASSIVE_STATUS (oi) == OSPF_IF_PASSIVE) + return; + + if (oi->type != OSPF_IFTYPE_NBMA) + return; + + if (nbr_nbma->nbr != NULL && nbr_nbma->nbr->state != NSM_Down) + return; + + if (PRIORITY(oi) == 0) + return; + + if (nbr_nbma->priority == 0 + && oi->state != ISM_DR && oi->state != ISM_Backup) + return; + + ospf_hello_send_sub (oi, nbr_nbma->addr.s_addr); +} + +int +ospf_poll_timer (struct thread *thread) +{ + struct ospf_nbr_nbma *nbr_nbma; + + nbr_nbma = THREAD_ARG (thread); + nbr_nbma->t_poll = NULL; + + if (IS_DEBUG_OSPF (nsm, NSM_TIMERS)) + zlog (NULL, LOG_DEBUG, "NSM[%s:%s]: Timer (Poll timer expire)", + IF_NAME (nbr_nbma->oi), inet_ntoa (nbr_nbma->addr)); + + ospf_poll_send (nbr_nbma); + + if (nbr_nbma->v_poll > 0) + OSPF_POLL_TIMER_ON (nbr_nbma->t_poll, ospf_poll_timer, + nbr_nbma->v_poll); + + return 0; +} + + +int +ospf_hello_reply_timer (struct thread *thread) +{ + struct ospf_neighbor *nbr; + + nbr = THREAD_ARG (thread); + nbr->t_hello_reply = NULL; + + assert (nbr->oi); + + if (IS_DEBUG_OSPF (nsm, NSM_TIMERS)) + zlog (NULL, LOG_DEBUG, "NSM[%s:%s]: Timer (hello-reply timer expire)", + IF_NAME (nbr->oi), inet_ntoa (nbr->router_id)); + + ospf_hello_send_sub (nbr->oi, nbr->address.u.prefix4.s_addr); + + return 0; +} + +/* Send OSPF Hello. */ +void +ospf_hello_send (struct ospf_interface *oi) +{ + /* If this is passive interface, do not send OSPF Hello. */ + if (OSPF_IF_PASSIVE_STATUS (oi) == OSPF_IF_PASSIVE) + return; + + if (oi->type == OSPF_IFTYPE_NBMA) + { + struct ospf_neighbor *nbr; + struct route_node *rn; + + for (rn = route_top (oi->nbrs); rn; rn = route_next (rn)) + if ((nbr = rn->info)) + if (nbr != oi->nbr_self) + if (nbr->state != NSM_Down) + { + /* RFC 2328 Section 9.5.1 + If the router is not eligible to become Designated Router, + it must periodically send Hello Packets to both the + Designated Router and the Backup Designated Router (if they + exist). */ + if (PRIORITY(oi) == 0 && + IPV4_ADDR_CMP(&DR(oi), &nbr->address.u.prefix4) && + IPV4_ADDR_CMP(&BDR(oi), &nbr->address.u.prefix4)) + continue; + + /* If the router is eligible to become Designated Router, it + must periodically send Hello Packets to all neighbors that + are also eligible. In addition, if the router is itself the + Designated Router or Backup Designated Router, it must also + send periodic Hello Packets to all other neighbors. */ + + if (nbr->priority == 0 && oi->state == ISM_DROther) + continue; + /* if oi->state == Waiting, send hello to all neighbors */ + ospf_hello_send_sub (oi, nbr->address.u.prefix4.s_addr); + } + } + else + { + /* Decide destination address. */ + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) + ospf_hello_send_sub (oi, oi->vl_data->peer_addr.s_addr); + else + ospf_hello_send_sub (oi, htonl (OSPF_ALLSPFROUTERS)); + } +} + +/* Send OSPF Database Description. */ +void +ospf_db_desc_send (struct ospf_neighbor *nbr) +{ + struct ospf_interface *oi; + struct ospf_packet *op; + u_int16_t length = OSPF_HEADER_SIZE; + + oi = nbr->oi; + op = ospf_packet_new (oi->ifp->mtu); + + /* Prepare OSPF common header. */ + ospf_make_header (OSPF_MSG_DB_DESC, oi, op->s); + + /* Prepare OSPF Database Description body. */ + length += ospf_make_db_desc (oi, nbr, op->s); + + /* Fill OSPF header. */ + ospf_fill_header (oi, op->s, length); + + /* Set packet length. */ + op->length = length; + + /* Decide destination address. */ + if (oi->type == OSPF_IFTYPE_POINTOPOINT) + op->dst.s_addr = htonl (OSPF_ALLSPFROUTERS); + else + op->dst = nbr->address.u.prefix4; + + /* Add packet to the interface output queue. */ + ospf_packet_add (oi, op); + + /* Hook thread to write packet. */ + OSPF_ISM_WRITE_ON (oi->ospf); + + /* Remove old DD packet, then copy new one and keep in neighbor structure. */ + if (nbr->last_send) + ospf_packet_free (nbr->last_send); + nbr->last_send = ospf_packet_dup (op); + quagga_gettime (QUAGGA_CLK_MONOTONIC, &nbr->last_send_ts); +} + +/* Re-send Database Description. */ +void +ospf_db_desc_resend (struct ospf_neighbor *nbr) +{ + struct ospf_interface *oi; + + oi = nbr->oi; + + /* Add packet to the interface output queue. */ + ospf_packet_add (oi, ospf_packet_dup (nbr->last_send)); + + /* Hook thread to write packet. */ + OSPF_ISM_WRITE_ON (oi->ospf); +} + +/* Send Link State Request. */ +void +ospf_ls_req_send (struct ospf_neighbor *nbr) +{ + struct ospf_interface *oi; + struct ospf_packet *op; + u_int16_t length = OSPF_HEADER_SIZE; + + oi = nbr->oi; + op = ospf_packet_new (oi->ifp->mtu); + + /* Prepare OSPF common header. */ + ospf_make_header (OSPF_MSG_LS_REQ, oi, op->s); + + /* Prepare OSPF Link State Request body. */ + length += ospf_make_ls_req (nbr, op->s); + if (length == OSPF_HEADER_SIZE) + { + ospf_packet_free (op); + return; + } + + /* Fill OSPF header. */ + ospf_fill_header (oi, op->s, length); + + /* Set packet length. */ + op->length = length; + + /* Decide destination address. */ + if (oi->type == OSPF_IFTYPE_POINTOPOINT) + op->dst.s_addr = htonl (OSPF_ALLSPFROUTERS); + else + op->dst = nbr->address.u.prefix4; + + /* Add packet to the interface output queue. */ + ospf_packet_add (oi, op); + + /* Hook thread to write packet. */ + OSPF_ISM_WRITE_ON (oi->ospf); + + /* Add Link State Request Retransmission Timer. */ + OSPF_NSM_TIMER_ON (nbr->t_ls_req, ospf_ls_req_timer, nbr->v_ls_req); +} + +/* Send Link State Update with an LSA. */ +void +ospf_ls_upd_send_lsa (struct ospf_neighbor *nbr, struct ospf_lsa *lsa, + int flag) +{ + struct list *update; + + update = list_new (); + + listnode_add (update, lsa); + ospf_ls_upd_send (nbr, update, flag); + + list_delete (update); +} + +/* Determine size for packet. Must be at least big enough to accomodate next + * LSA on list, which may be bigger than MTU size. + * + * Return pointer to new ospf_packet + * NULL if we can not allocate, eg because LSA is bigger than imposed limit + * on packet sizes (in which case offending LSA is deleted from update list) + */ +static struct ospf_packet * +ospf_ls_upd_packet_new (struct list *update, struct ospf_interface *oi) +{ + struct ospf_lsa *lsa; + struct listnode *ln; + size_t size; + static char warned = 0; + + lsa = listgetdata((ln = listhead (update))); + assert (lsa->data); + + if ((OSPF_LS_UPD_MIN_SIZE + ntohs (lsa->data->length)) + > ospf_packet_max (oi)) + { + if (!warned) + { + zlog_warn ("ospf_ls_upd_packet_new: oversized LSA encountered!" + "will need to fragment. Not optimal. Try divide up" + " your network with areas. Use 'debug ospf packet send'" + " to see details, or look at 'show ip ospf database ..'"); + warned = 1; + } + + if (IS_DEBUG_OSPF_PACKET (0, SEND)) + zlog_debug ("ospf_ls_upd_packet_new: oversized LSA id:%s," + " %d bytes originated by %s, will be fragmented!", + inet_ntoa (lsa->data->id), + ntohs (lsa->data->length), + inet_ntoa (lsa->data->adv_router)); + + /* + * Allocate just enough to fit this LSA only, to avoid including other + * LSAs in fragmented LSA Updates. + */ + size = ntohs (lsa->data->length) + (oi->ifp->mtu - ospf_packet_max (oi)) + + OSPF_LS_UPD_MIN_SIZE; + } + else + size = oi->ifp->mtu; + + if (size > OSPF_MAX_PACKET_SIZE) + { + zlog_warn ("ospf_ls_upd_packet_new: oversized LSA id:%s too big," + " %d bytes, packet size %ld, dropping it completely." + " OSPF routing is broken!", + inet_ntoa (lsa->data->id), ntohs (lsa->data->length), + (long int) size); + list_delete_node (update, ln); + return NULL; + } + + /* IP header is built up separately by ospf_write(). This means, that we must + * reduce the "affordable" size just calculated by length of an IP header. + * This makes sure, that even if we manage to fill the payload with LSA data + * completely, the final packet (our data plus IP header) still fits into + * outgoing interface MTU. This correction isn't really meaningful for an + * oversized LSA, but for consistency the correction is done for both cases. + * + * P.S. OSPF_MAX_PACKET_SIZE above already includes IP header size + */ + return ospf_packet_new (size - sizeof (struct ip)); +} + +static void +ospf_ls_upd_queue_send (struct ospf_interface *oi, struct list *update, + struct in_addr addr) +{ + struct ospf_packet *op; + u_int16_t length = OSPF_HEADER_SIZE; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("listcount = %d, [%s]dst %s", listcount (update), IF_NAME(oi), + inet_ntoa(addr)); + + op = ospf_ls_upd_packet_new (update, oi); + + /* Prepare OSPF common header. */ + ospf_make_header (OSPF_MSG_LS_UPD, oi, op->s); + + /* Prepare OSPF Link State Update body. + * Includes Type-7 translation. + */ + length += ospf_make_ls_upd (oi, update, op->s); + + /* Fill OSPF header. */ + ospf_fill_header (oi, op->s, length); + + /* Set packet length. */ + op->length = length; + + /* Decide destination address. */ + if (oi->type == OSPF_IFTYPE_POINTOPOINT) + op->dst.s_addr = htonl (OSPF_ALLSPFROUTERS); + else + op->dst.s_addr = addr.s_addr; + + /* Add packet to the interface output queue. */ + ospf_packet_add (oi, op); + + /* Hook thread to write packet. */ + OSPF_ISM_WRITE_ON (oi->ospf); +} + +static int +ospf_ls_upd_send_queue_event (struct thread *thread) +{ + struct ospf_interface *oi = THREAD_ARG(thread); + struct route_node *rn; + struct route_node *rnext; + struct list *update; + char again = 0; + + oi->t_ls_upd_event = NULL; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_ls_upd_send_queue start"); + + for (rn = route_top (oi->ls_upd_queue); rn; rn = rnext) + { + rnext = route_next (rn); + + if (rn->info == NULL) + continue; + + update = (struct list *)rn->info; + + ospf_ls_upd_queue_send (oi, update, rn->p.u.prefix4); + + /* list might not be empty. */ + if (listcount(update) == 0) + { + list_delete (rn->info); + rn->info = NULL; + route_unlock_node (rn); + } + else + again = 1; + } + + if (again != 0) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_ls_upd_send_queue: update lists not cleared," + " %d nodes to try again, raising new event", again); + oi->t_ls_upd_event = + thread_add_event (master, ospf_ls_upd_send_queue_event, oi, 0); + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_ls_upd_send_queue stop"); + + return 0; +} + +void +ospf_ls_upd_send (struct ospf_neighbor *nbr, struct list *update, int flag) +{ + struct ospf_interface *oi; + struct ospf_lsa *lsa; + struct prefix_ipv4 p; + struct route_node *rn; + struct listnode *node; + + oi = nbr->oi; + + p.family = AF_INET; + p.prefixlen = IPV4_MAX_BITLEN; + + /* Decide destination address. */ + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) + p.prefix = oi->vl_data->peer_addr; + else if (oi->type == OSPF_IFTYPE_POINTOPOINT) + p.prefix.s_addr = htonl (OSPF_ALLSPFROUTERS); + else if (flag == OSPF_SEND_PACKET_DIRECT) + p.prefix = nbr->address.u.prefix4; + else if (oi->state == ISM_DR || oi->state == ISM_Backup) + p.prefix.s_addr = htonl (OSPF_ALLSPFROUTERS); + else if (oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) + p.prefix.s_addr = htonl (OSPF_ALLSPFROUTERS); + else + p.prefix.s_addr = htonl (OSPF_ALLDROUTERS); + + if (oi->type == OSPF_IFTYPE_NBMA) + { + if (flag == OSPF_SEND_PACKET_INDIRECT) + zlog_warn ("* LS-Update is directly sent on NBMA network."); + if (IPV4_ADDR_SAME(&oi->address->u.prefix4, &p.prefix.s_addr)) + zlog_warn ("* LS-Update is sent to myself."); + } + + rn = route_node_get (oi->ls_upd_queue, (struct prefix *) &p); + + 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 */ + + if (oi->t_ls_upd_event == NULL) + oi->t_ls_upd_event = + thread_add_event (master, ospf_ls_upd_send_queue_event, oi, 0); +} + +static void +ospf_ls_ack_send_list (struct ospf_interface *oi, struct list *ack, + struct in_addr dst) +{ + struct ospf_packet *op; + u_int16_t length = OSPF_HEADER_SIZE; + + op = ospf_packet_new (oi->ifp->mtu); + + /* Prepare OSPF common header. */ + ospf_make_header (OSPF_MSG_LS_ACK, oi, op->s); + + /* Prepare OSPF Link State Acknowledgment body. */ + length += ospf_make_ls_ack (oi, ack, op->s); + + /* Fill OSPF header. */ + ospf_fill_header (oi, op->s, length); + + /* Set packet length. */ + op->length = length; + + /* 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); + + /* Hook thread to write packet. */ + OSPF_ISM_WRITE_ON (oi->ospf); +} + +static int +ospf_ls_ack_send_event (struct thread *thread) +{ + struct ospf_interface *oi = THREAD_ARG (thread); + + oi->t_ls_ack_direct = NULL; + + while (listcount (oi->ls_ack_direct.ls_ack)) + ospf_ls_ack_send_list (oi, oi->ls_ack_direct.ls_ack, + oi->ls_ack_direct.dst); + + return 0; +} + +void +ospf_ls_ack_send (struct ospf_neighbor *nbr, struct ospf_lsa *lsa) +{ + struct ospf_interface *oi = nbr->oi; + + if (listcount (oi->ls_ack_direct.ls_ack) == 0) + oi->ls_ack_direct.dst = nbr->address.u.prefix4; + + listnode_add (oi->ls_ack_direct.ls_ack, ospf_lsa_lock (lsa)); + + if (oi->t_ls_ack_direct == NULL) + oi->t_ls_ack_direct = + thread_add_event (master, ospf_ls_ack_send_event, oi, 0); +} + +/* Send Link State Acknowledgment delayed. */ +void +ospf_ls_ack_send_delayed (struct ospf_interface *oi) +{ + struct in_addr dst; + + /* Decide destination address. */ + /* RFC2328 Section 13.5 On non-broadcast + networks, delayed Link State Acknowledgment packets must be + unicast separately over each adjacency (i.e., neighbor whose + state is >= Exchange). */ + if (oi->type == OSPF_IFTYPE_NBMA) + { + struct ospf_neighbor *nbr; + struct route_node *rn; + + for (rn = route_top (oi->nbrs); rn; rn = route_next (rn)) + if ((nbr = rn->info) != NULL) + if (nbr != oi->nbr_self && nbr->state >= NSM_Exchange) + while (listcount (oi->ls_ack)) + ospf_ls_ack_send_list (oi, oi->ls_ack, nbr->address.u.prefix4); + return; + } + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) + dst.s_addr = oi->vl_data->peer_addr.s_addr; + else if (oi->state == ISM_DR || oi->state == ISM_Backup) + dst.s_addr = htonl (OSPF_ALLSPFROUTERS); + else if (oi->type == OSPF_IFTYPE_POINTOPOINT) + dst.s_addr = htonl (OSPF_ALLSPFROUTERS); + else if (oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) + dst.s_addr = htonl (OSPF_ALLSPFROUTERS); + else + dst.s_addr = htonl (OSPF_ALLDROUTERS); + + while (listcount (oi->ls_ack)) + ospf_ls_ack_send_list (oi, oi->ls_ack, dst); +} diff --git a/ospfd/ospf_packet.h b/ospfd/ospf_packet.h new file mode 100644 index 0000000..337686a --- /dev/null +++ b/ospfd/ospf_packet.h @@ -0,0 +1,177 @@ +/* + * OSPF Sending and Receiving OSPF Packets. + * Copyright (C) 1999 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 _ZEBRA_OSPF_PACKET_H +#define _ZEBRA_OSPF_PACKET_H + +#define OSPF_HEADER_SIZE 24U +#define OSPF_AUTH_SIMPLE_SIZE 8U +#define OSPF_AUTH_MD5_SIZE 16U + +#define OSPF_MAX_PACKET_SIZE 65535U /* includes IP Header size. */ +#define OSPF_HELLO_MIN_SIZE 20U /* not including neighbors */ +#define OSPF_DB_DESC_MIN_SIZE 8U +#define OSPF_LS_REQ_MIN_SIZE 0U +#define OSPF_LS_UPD_MIN_SIZE 4U +#define OSPF_LS_ACK_MIN_SIZE 0U + +#define OSPF_MSG_HELLO 1 /* OSPF Hello Message. */ +#define OSPF_MSG_DB_DESC 2 /* OSPF Database Descriptoin Message. */ +#define OSPF_MSG_LS_REQ 3 /* OSPF Link State Request Message. */ +#define OSPF_MSG_LS_UPD 4 /* OSPF Link State Update Message. */ +#define OSPF_MSG_LS_ACK 5 /* OSPF Link State Acknoledgement Message. */ + +#define OSPF_SEND_PACKET_DIRECT 1 +#define OSPF_SEND_PACKET_INDIRECT 2 +#define OSPF_SEND_PACKET_LOOP 3 + +#define OSPF_HELLO_REPLY_DELAY 1 + +/* Return values of functions involved in packet verification, see ospf6d. */ +#define MSG_OK 0 +#define MSG_NG 1 + +struct ospf_packet +{ + struct ospf_packet *next; + + /* Pointer to data stream. */ + struct stream *s; + + /* IP destination address. */ + struct in_addr dst; + + /* OSPF packet length. */ + u_int16_t length; +}; + +/* OSPF packet queue structure. */ +struct ospf_fifo +{ + unsigned long count; + + struct ospf_packet *head; + struct ospf_packet *tail; +}; + +/* OSPF packet header structure. */ +struct ospf_header +{ + u_char version; /* OSPF Version. */ + u_char type; /* Packet Type. */ + u_int16_t length; /* Packet Length. */ + struct in_addr router_id; /* Router ID. */ + struct in_addr area_id; /* Area ID. */ + u_int16_t checksum; /* Check Sum. */ + u_int16_t auth_type; /* Authentication Type. */ + /* Authentication Data. */ + union + { + /* Simple Authentication. */ + u_char auth_data [OSPF_AUTH_SIMPLE_SIZE]; + /* Cryptographic Authentication. */ + struct + { + u_int16_t zero; /* Should be 0. */ + u_char key_id; /* Key ID. */ + u_char auth_data_len; /* Auth Data Length. */ + u_int32_t crypt_seqnum; /* Cryptographic Sequence Number. */ + } crypt; + } u; +}; + +/* OSPF Hello body format. */ +struct ospf_hello +{ + struct in_addr network_mask; + u_int16_t hello_interval; + u_char options; + u_char priority; + u_int32_t dead_interval; + struct in_addr d_router; + struct in_addr bd_router; + struct in_addr neighbors[1]; +}; + +/* OSPF Database Description body format. */ +struct ospf_db_desc +{ + u_int16_t mtu; + u_char options; + u_char flags; + u_int32_t dd_seqnum; +}; + +struct ospf_ls_update +{ + u_int32_t num_lsas; +}; + +/* Macros. */ +/* XXX Perhaps obsolete; function in ospf_packet.c */ +#define OSPF_PACKET_MAX(oi) ospf_packet_max (oi) + +#define OSPF_OUTPUT_PNT(S) ((S)->data + (S)->putp) +#define OSPF_OUTPUT_LENGTH(S) ((S)->endp) + +#define IS_SET_DD_MS(X) ((X) & OSPF_DD_FLAG_MS) +#define IS_SET_DD_M(X) ((X) & OSPF_DD_FLAG_M) +#define IS_SET_DD_I(X) ((X) & OSPF_DD_FLAG_I) +#define IS_SET_DD_ALL(X) ((X) & OSPF_DD_FLAG_ALL) + +/* Prototypes. */ +extern void ospf_output_forward (struct stream *, int); +extern struct ospf_packet *ospf_packet_new (size_t); +extern void ospf_packet_free (struct ospf_packet *); +extern struct ospf_fifo *ospf_fifo_new (void); +extern void ospf_fifo_push (struct ospf_fifo *, struct ospf_packet *); +extern struct ospf_packet *ospf_fifo_pop (struct ospf_fifo *); +extern struct ospf_packet *ospf_fifo_head (struct ospf_fifo *); +extern void ospf_fifo_flush (struct ospf_fifo *); +extern void ospf_fifo_free (struct ospf_fifo *); +extern void ospf_packet_add (struct ospf_interface *, struct ospf_packet *); +extern void ospf_packet_delete (struct ospf_interface *); +extern struct stream *ospf_stream_dup (struct stream *); +extern struct ospf_packet *ospf_packet_dup (struct ospf_packet *); + +extern int ospf_read (struct thread *); +extern void ospf_hello_send (struct ospf_interface *); +extern void ospf_db_desc_send (struct ospf_neighbor *); +extern void ospf_db_desc_resend (struct ospf_neighbor *); +extern void ospf_ls_req_send (struct ospf_neighbor *); +extern void ospf_ls_upd_send_lsa (struct ospf_neighbor *, struct ospf_lsa *, + int); +extern void ospf_ls_upd_send (struct ospf_neighbor *, struct list *, int); +extern void ospf_ls_ack_send (struct ospf_neighbor *, struct ospf_lsa *); +extern void ospf_ls_ack_send_delayed (struct ospf_interface *); +extern void ospf_ls_retransmit (struct ospf_interface *, struct ospf_lsa *); +extern void ospf_ls_req_event (struct ospf_neighbor *); + +extern int ospf_ls_upd_timer (struct thread *); +extern int ospf_ls_ack_timer (struct thread *); +extern int ospf_poll_timer (struct thread *); +extern int ospf_hello_reply_timer (struct thread *); + +extern const struct message ospf_packet_type_str[]; +extern const size_t ospf_packet_type_str_max; + +#endif /* _ZEBRA_OSPF_PACKET_H */ diff --git a/ospfd/ospf_ri.c b/ospfd/ospf_ri.c new file mode 100644 index 0000000..f093208 --- /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 0000000..c507434 --- /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 new file mode 100644 index 0000000..eb7829a --- /dev/null +++ b/ospfd/ospf_route.c @@ -0,0 +1,1049 @@ +/* + * OSPF routing table. + * Copyright (C) 1999, 2000 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. + */ + +#include + +#include "prefix.h" +#include "table.h" +#include "memory.h" +#include "linklist.h" +#include "log.h" +#include "if.h" +#include "command.h" +#include "sockunion.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_spf.h" +#include "ospfd/ospf_zebra.h" +#include "ospfd/ospf_dump.h" + +struct ospf_route * +ospf_route_new () +{ + struct ospf_route *new; + + new = XCALLOC (MTYPE_OSPF_ROUTE, sizeof (struct ospf_route)); + + new->ctime = quagga_time (NULL); + new->mtime = new->ctime; + new->paths = list_new (); + new->paths->del = (void (*) (void *))ospf_path_free; + + return new; +} + +void +ospf_route_free (struct ospf_route *or) +{ + if (or->paths) + list_delete (or->paths); + + XFREE (MTYPE_OSPF_ROUTE, or); +} + +struct ospf_path * +ospf_path_new () +{ + struct ospf_path *new; + + new = XCALLOC (MTYPE_OSPF_PATH, sizeof (struct ospf_path)); + + return new; +} + +static struct ospf_path * +ospf_path_dup (struct ospf_path *path) +{ + struct ospf_path *new; + + new = ospf_path_new (); + memcpy (new, path, sizeof (struct ospf_path)); + + return new; +} + +void +ospf_path_free (struct ospf_path *op) +{ + XFREE (MTYPE_OSPF_PATH, op); +} + +void +ospf_route_delete (struct route_table *rt) +{ + struct route_node *rn; + struct ospf_route *or; + + for (rn = route_top (rt); rn; rn = route_next (rn)) + if ((or = rn->info) != NULL) + { + if (or->type == OSPF_DESTINATION_NETWORK) + ospf_zebra_delete ((struct prefix_ipv4 *) &rn->p, + or); + else if (or->type == OSPF_DESTINATION_DISCARD) + ospf_zebra_delete_discard ((struct prefix_ipv4 *) &rn->p); + } +} + +void +ospf_route_table_free (struct route_table *rt) +{ + struct route_node *rn; + struct ospf_route *or; + + for (rn = route_top (rt); rn; rn = route_next (rn)) + if ((or = rn->info) != NULL) + { + ospf_route_free (or); + + rn->info = NULL; + route_unlock_node (rn); + } + + 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 +ospf_route_match_same (struct route_table *rt, struct prefix_ipv4 *prefix, + struct ospf_route *newor) +{ + struct route_node *rn; + struct ospf_route *or; + struct ospf_path *op; + struct ospf_path *newop; + struct listnode *n1; + struct listnode *n2; + + if (! rt || ! prefix) + return 0; + + rn = route_node_lookup (rt, (struct prefix *) prefix); + if (! rn || ! rn->info) + return 0; + + route_unlock_node (rn); + + or = rn->info; + if (or->type == newor->type && or->cost == newor->cost) + { + if (or->type == OSPF_DESTINATION_NETWORK) + { + if (or->paths->count != newor->paths->count) + return 0; + + /* Check each path. */ + for (n1 = listhead (or->paths), n2 = listhead (newor->paths); + n1 && n2; n1 = listnextnode (n1), n2 = listnextnode (n2)) + { + op = listgetdata (n1); + newop = listgetdata (n2); + + if (! IPV4_ADDR_SAME (&op->nexthop, &newop->nexthop)) + return 0; + if (op->ifindex != newop->ifindex) + return 0; + } + return 1; + } + else if (prefix_same (&rn->p, (struct prefix *) prefix)) + return 1; + } + return 0; +} + +/* delete routes generated from AS-External routes if there is a inter/intra + * area route + */ +static void +ospf_route_delete_same_ext(struct route_table *external_routes, + struct route_table *routes) +{ + struct route_node *rn, + *ext_rn; + + if ( (external_routes == NULL) || (routes == NULL) ) + return; + + /* Remove deleted routes */ + for ( rn = route_top (routes); rn; rn = route_next (rn) ) + { + if (rn && rn->info) + { + struct prefix_ipv4 *p = (struct prefix_ipv4 *)(&rn->p); + if ( (ext_rn = route_node_lookup (external_routes, (struct prefix *)p)) ) + { + if (ext_rn->info) + { + ospf_zebra_delete (p, ext_rn->info); + ospf_route_free( ext_rn->info); + ext_rn->info = NULL; + } + route_unlock_node (ext_rn); + } + } + } +} + +/* rt: Old, cmprt: New */ +static void +ospf_route_delete_uniq (struct route_table *rt, struct route_table *cmprt) +{ + struct route_node *rn; + struct ospf_route *or; + + for (rn = route_top (rt); rn; rn = route_next (rn)) + if ((or = rn->info) != NULL) + if (or->path_type == OSPF_PATH_INTRA_AREA || + or->path_type == OSPF_PATH_INTER_AREA) + { + if (or->type == OSPF_DESTINATION_NETWORK) + { + 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_exist_new_table (cmprt, + (struct prefix_ipv4 *) &rn->p)) + ospf_zebra_delete_discard ((struct prefix_ipv4 *) &rn->p); + } +} + +/* Install routes to table. */ +void +ospf_route_install (struct ospf *ospf, struct route_table *rt) +{ + struct route_node *rn; + struct ospf_route *or; + + /* rt contains new routing table, new_table contains an old one. + updating pointers */ + if (ospf->old_table) + ospf_route_table_free (ospf->old_table); + + ospf->old_table = ospf->new_table; + ospf->new_table = rt; + + /* Delete old routes. */ + if (ospf->old_table) + ospf_route_delete_uniq (ospf->old_table, rt); + if (ospf->old_external_route) + ospf_route_delete_same_ext (ospf->old_external_route, rt); + + /* Install new routes. */ + for (rn = route_top (rt); rn; rn = route_next (rn)) + if ((or = rn->info) != NULL) + { + if (or->type == OSPF_DESTINATION_NETWORK) + { + if (! ospf_route_match_same (ospf->old_table, + (struct prefix_ipv4 *)&rn->p, or)) + ospf_zebra_add ((struct prefix_ipv4 *) &rn->p, or); + } + else if (or->type == OSPF_DESTINATION_DISCARD) + if (! ospf_route_match_same (ospf->old_table, + (struct prefix_ipv4 *) &rn->p, or)) + ospf_zebra_add_discard ((struct prefix_ipv4 *) &rn->p); + } +} + +/* RFC2328 16.1. (4). For "router". */ +void +ospf_intra_add_router (struct route_table *rt, struct vertex *v, + struct ospf_area *area) +{ + struct route_node *rn; + struct ospf_route *or; + struct prefix_ipv4 p; + struct router_lsa *lsa; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_intra_add_router: Start"); + + lsa = (struct router_lsa *) v->lsa; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_intra_add_router: LS ID: %s", + inet_ntoa (lsa->header.id)); + + if (!OSPF_IS_AREA_BACKBONE(area)) + ospf_vl_up_check (area, lsa->header.id, v); + + if (!CHECK_FLAG (lsa->flags, ROUTER_LSA_SHORTCUT)) + area->shortcut_capability = 0; + + /* If the newly added vertex is an area border router or AS boundary + router, a routing table entry is added whose destination type is + "router". */ + if (! IS_ROUTER_LSA_BORDER (lsa) && ! IS_ROUTER_LSA_EXTERNAL (lsa)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_intra_add_router: " + "this router is neither ASBR nor ABR, skipping it"); + return; + } + + /* Update ABR and ASBR count in this area. */ + if (IS_ROUTER_LSA_BORDER (lsa)) + area->abr_count++; + if (IS_ROUTER_LSA_EXTERNAL (lsa)) + area->asbr_count++; + + /* The Options field found in the associated router-LSA is copied + into the routing table entry's Optional capabilities field. Call + the newly added vertex Router X. */ + or = ospf_route_new (); + + or->id = v->id; + or->u.std.area_id = area->area_id; + or->u.std.external_routing = area->external_routing; + or->path_type = OSPF_PATH_INTRA_AREA; + or->cost = v->distance; + or->type = OSPF_DESTINATION_ROUTER; + or->u.std.origin = (struct lsa_header *) lsa; + or->u.std.options = lsa->header.options; + or->u.std.flags = lsa->flags; + + /* If Router X is the endpoint of one of the calculating router's + virtual links, and the virtual link uses Area A as Transit area: + the virtual link is declared up, the IP address of the virtual + interface is set to the IP address of the outgoing interface + calculated above for Router X, and the virtual neighbor's IP + address is set to Router X's interface address (contained in + Router X's router-LSA) that points back to the root of the + shortest- path tree; equivalently, this is the interface that + points back to Router X's parent vertex on the shortest-path tree + (similar to the calculation in Section 16.1.1). */ + + p.family = AF_INET; + p.prefix = v->id; + p.prefixlen = IPV4_MAX_BITLEN; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_intra_add_router: talking about %s/%d", + inet_ntoa (p.prefix), p.prefixlen); + + rn = route_node_get (rt, (struct prefix *) &p); + + /* Note that we keep all routes to ABRs and ASBRs, not only the best */ + if (rn->info == NULL) + rn->info = list_new (); + else + route_unlock_node (rn); + + ospf_route_copy_nexthops_from_vertex (or, v); + + listnode_add (rn->info, or); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_intra_add_router: Stop"); +} + +/* RFC2328 16.1. (4). For transit network. */ +void +ospf_intra_add_transit (struct route_table *rt, struct vertex *v, + struct ospf_area *area) +{ + struct route_node *rn; + struct ospf_route *or; + struct prefix_ipv4 p; + struct network_lsa *lsa; + + lsa = (struct network_lsa*) v->lsa; + + /* If the newly added vertex is a transit network, the routing table + entry for the network is located. The entry's Destination ID is + the IP network number, which can be obtained by masking the + Vertex ID (Link State ID) with its associated subnet mask (found + in the body of the associated network-LSA). */ + p.family = AF_INET; + p.prefix = v->id; + p.prefixlen = ip_masklen (lsa->mask); + apply_mask_ipv4 (&p); + + rn = route_node_get (rt, (struct prefix *) &p); + + /* If the routing table entry already exists (i.e., there is already + an intra-area route to the destination installed in the routing + table), multiple vertices have mapped to the same IP network. + For example, this can occur when a new Designated Router is being + established. In this case, the current routing table entry + should be overwritten if and only if the newly found path is just + as short and the current routing table entry's Link State Origin + has a smaller Link State ID than the newly added vertex' LSA. */ + if (rn->info) + { + struct ospf_route *cur_or; + + route_unlock_node (rn); + cur_or = rn->info; + + if (v->distance > cur_or->cost || + IPV4_ADDR_CMP (&cur_or->u.std.origin->id, &lsa->header.id) > 0) + return; + + ospf_route_free (rn->info); + } + + or = ospf_route_new (); + + or->id = v->id; + or->u.std.area_id = area->area_id; + or->u.std.external_routing = area->external_routing; + or->path_type = OSPF_PATH_INTRA_AREA; + or->cost = v->distance; + or->type = OSPF_DESTINATION_NETWORK; + or->u.std.origin = (struct lsa_header *) lsa; + + ospf_route_copy_nexthops_from_vertex (or, v); + + rn->info = or; +} + +/* RFC2328 16.1. second stage. */ +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 lsa_pos) +{ + u_int32_t cost; + struct route_node *rn; + struct ospf_route *or; + struct prefix_ipv4 p; + struct router_lsa *lsa; + struct ospf_interface *oi; + struct ospf_path *path; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_intra_add_stub(): Start"); + + lsa = (struct router_lsa *) v->lsa; + + p.family = AF_INET; + p.prefix = link->link_id; + p.prefixlen = ip_masklen (link->link_data); + apply_mask_ipv4 (&p); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_intra_add_stub(): processing route to %s/%d", + inet_ntoa (p.prefix), p.prefixlen); + + /* (1) Calculate the distance D of stub network from the root. D is + equal to the distance from the root to the router vertex + (calculated in stage 1), plus the stub network link's advertised + cost. */ + cost = v->distance + ntohs (link->m[0].metric); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_intra_add_stub(): calculated cost is %d + %d = %d", + v->distance, ntohs(link->m[0].metric), cost); + + /* PtP links with /32 masks adds host routes to remote, directly + * connected hosts, see RFC 2328, 12.4.1.1, Option 1. + * Such routes can just be ignored for the sake of tidyness. + */ + if (parent_is_root && link->link_data.s_addr == 0xffffffff && + ospf_if_lookup_by_local_addr (area->ospf, NULL, link->link_id)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("%s: ignoring host route %s/32 to self.", + __func__, inet_ntoa (link->link_id)); + return; + } + + rn = route_node_get (rt, (struct prefix *) &p); + + /* Lookup current routing table. */ + if (rn->info) + { + struct ospf_route *cur_or; + + route_unlock_node (rn); + + cur_or = rn->info; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_intra_add_stub(): " + "another route to the same prefix found with cost %u", + cur_or->cost); + + /* Compare this distance to the current best cost to the stub + network. This is done by looking up the stub network's + current routing table entry. If the calculated distance D is + larger, go on to examine the next stub network link in the + LSA. */ + if (cost > cur_or->cost) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_intra_add_stub(): old route is better, exit"); + return; + } + + /* (2) If this step is reached, the stub network's routing table + entry must be updated. Calculate the set of next hops that + would result from using the stub network link. This + calculation is shown in Section 16.1.1; input to this + calculation is the destination (the stub network) and the + parent vertex (the router vertex). If the distance D is the + same as the current routing table cost, simply add this set + of next hops to the routing table entry's list of next hops. + In this case, the routing table already has a Link State + Origin. If this Link State Origin is a router-LSA whose Link + State ID is smaller than V's Router ID, reset the Link State + Origin to V's router-LSA. */ + + if (cost == cur_or->cost) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_intra_add_stub(): routes are equal, merge"); + + ospf_route_copy_nexthops_from_vertex (cur_or, v); + + if (IPV4_ADDR_CMP (&cur_or->u.std.origin->id, &lsa->header.id) < 0) + cur_or->u.std.origin = (struct lsa_header *) lsa; + return; + } + + /* Otherwise D is smaller than the routing table cost. + Overwrite the current routing table entry by setting the + routing table entry's cost to D, and by setting the entry's + list of next hops to the newly calculated set. Set the + routing table entry's Link State Origin to V's router-LSA. + Then go on to examine the next stub network link. */ + + if (cost < cur_or->cost) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_intra_add_stub(): new route is better, set it"); + + cur_or->cost = cost; + + list_delete_all_node (cur_or->paths); + + ospf_route_copy_nexthops_from_vertex (cur_or, v); + + cur_or->u.std.origin = (struct lsa_header *) lsa; + return; + } + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_intra_add_stub(): installing new route"); + + or = ospf_route_new (); + + or->id = v->id; + or->u.std.area_id = area->area_id; + or->u.std.external_routing = area->external_routing; + or->path_type = OSPF_PATH_INTRA_AREA; + or->cost = cost; + or->type = OSPF_DESTINATION_NETWORK; + or->u.std.origin = (struct lsa_header *) lsa; + + /* Nexthop is depend on connection type. */ + if (v != area->spf) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_intra_add_stub(): this network is on remote router"); + ospf_route_copy_nexthops_from_vertex (or, v); + } + else + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_intra_add_stub(): this network is on this router"); + + 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", + IF_NAME (oi)); + + path = ospf_path_new (); + path->nexthop.s_addr = 0; + path->ifindex = oi->ifp->ifindex; + listnode_add (or->paths, path); + } + else + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_intra_add_stub(): where's the interface ?"); + } + } + + rn->info = or; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("ospf_intra_add_stub(): Stop"); +} + +const char *ospf_path_type_str[] = +{ + "unknown-type", + "intra-area", + "inter-area", + "type1-external", + "type2-external" +}; + +void +ospf_route_table_dump (struct route_table *rt) +{ + struct route_node *rn; + struct ospf_route *or; + char buf1[BUFSIZ]; + char buf2[BUFSIZ]; + struct listnode *pnode; + struct ospf_path *path; + +#if 0 + zlog_debug ("Type Dest Area Path Type Cost Next Adv."); + zlog_debug (" Hop(s) Router(s)"); +#endif /* 0 */ + + zlog_debug ("========== OSPF routing table =========="); + for (rn = route_top (rt); rn; rn = route_next (rn)) + if ((or = rn->info) != NULL) + { + if (or->type == OSPF_DESTINATION_NETWORK) + { + zlog_debug ("N %s/%d\t%s\t%s\t%d", + inet_ntop (AF_INET, &rn->p.u.prefix4, buf1, BUFSIZ), + rn->p.prefixlen, + inet_ntop (AF_INET, &or->u.std.area_id, buf2, + BUFSIZ), + ospf_path_type_str[or->path_type], + or->cost); + for (ALL_LIST_ELEMENTS_RO (or->paths, pnode, path)) + zlog_debug (" -> %s", inet_ntoa (path->nexthop)); + } + else + zlog_debug ("R %s\t%s\t%s\t%d", + inet_ntop (AF_INET, &rn->p.u.prefix4, buf1, BUFSIZ), + inet_ntop (AF_INET, &or->u.std.area_id, buf2, + BUFSIZ), + ospf_path_type_str[or->path_type], + or->cost); + } + zlog_debug ("========================================"); +} + +/* This is 16.4.1 implementation. + o Intra-area paths using non-backbone areas are always the most preferred. + o The other paths, intra-area backbone paths and inter-area paths, + are of equal preference. */ +static int +ospf_asbr_route_cmp (struct ospf *ospf, struct ospf_route *r1, + struct ospf_route *r2) +{ + u_char r1_type, r2_type; + + r1_type = r1->path_type; + r2_type = r2->path_type; + + /* r1/r2 itself is backbone, and it's Inter-area path. */ + if (OSPF_IS_AREA_ID_BACKBONE (r1->u.std.area_id)) + r1_type = OSPF_PATH_INTER_AREA; + if (OSPF_IS_AREA_ID_BACKBONE (r2->u.std.area_id)) + r2_type = OSPF_PATH_INTER_AREA; + + return (r1_type - r2_type); +} + +/* Compare two routes. + ret < 0 -- r1 is better. + ret == 0 -- r1 and r2 are the same. + ret > 0 -- r2 is better. */ +int +ospf_route_cmp (struct ospf *ospf, struct ospf_route *r1, + struct ospf_route *r2) +{ + int ret = 0; + + /* Path types of r1 and r2 are not the same. */ + if ((ret = (r1->path_type - r2->path_type))) + return ret; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Route[Compare]: Path types are the same."); + /* Path types are the same, compare any cost. */ + switch (r1->path_type) + { + case OSPF_PATH_INTRA_AREA: + case OSPF_PATH_INTER_AREA: + break; + case OSPF_PATH_TYPE1_EXTERNAL: + if (!CHECK_FLAG (ospf->config, OSPF_RFC1583_COMPATIBLE)) + { + ret = ospf_asbr_route_cmp (ospf, r1->u.ext.asbr, r2->u.ext.asbr); + if (ret != 0) + return ret; + } + break; + case OSPF_PATH_TYPE2_EXTERNAL: + if ((ret = (r1->u.ext.type2_cost - r2->u.ext.type2_cost))) + return ret; + + if (!CHECK_FLAG (ospf->config, OSPF_RFC1583_COMPATIBLE)) + { + ret = ospf_asbr_route_cmp (ospf, r1->u.ext.asbr, r2->u.ext.asbr); + if (ret != 0) + return ret; + } + break; + } + + /* Anyway, compare the costs. */ + return (r1->cost - r2->cost); +} + +static int +ospf_path_exist (struct list *plist, struct in_addr nexthop, + struct ospf_interface *oi) +{ + struct listnode *node, *nnode; + struct ospf_path *path; + + for (ALL_LIST_ELEMENTS (plist, node, nnode, path)) + if (IPV4_ADDR_SAME (&path->nexthop, &nexthop) && + path->ifindex == oi->ifp->ifindex) + return 1; + + return 0; +} + +void +ospf_route_copy_nexthops_from_vertex (struct ospf_route *to, + struct vertex *v) +{ + struct listnode *node; + struct ospf_path *path; + struct vertex_nexthop *nexthop; + struct vertex_parent *vp; + + assert (to->paths); + + for (ALL_LIST_ELEMENTS_RO (v->parents, node, vp)) + { + nexthop = vp->nexthop; + + if (nexthop->oi != NULL) + { + if (! ospf_path_exist (to->paths, nexthop->router, nexthop->oi)) + { + path = ospf_path_new (); + path->nexthop = nexthop->router; + path->ifindex = nexthop->oi->ifp->ifindex; + listnode_add (to->paths, path); + } + } + } +} + +struct ospf_path * +ospf_path_lookup (struct list *plist, struct ospf_path *path) +{ + struct listnode *node; + struct ospf_path *op; + + for (ALL_LIST_ELEMENTS_RO (plist, node, op)) + { + if (!IPV4_ADDR_SAME (&op->nexthop, &path->nexthop)) + continue; + if (!IPV4_ADDR_SAME (&op->adv_router, &path->adv_router)) + continue; + if (op->ifindex != path->ifindex) + continue; + return op; + } + return NULL; +} + +void +ospf_route_copy_nexthops (struct ospf_route *to, struct list *from) +{ + struct listnode *node, *nnode; + struct ospf_path *path; + + assert (to->paths); + + for (ALL_LIST_ELEMENTS (from, node, nnode, path)) + /* The same routes are just discarded. */ + if (!ospf_path_lookup (to->paths, path)) + listnode_add (to->paths, ospf_path_dup (path)); +} + +void +ospf_route_subst_nexthops (struct ospf_route *to, struct list *from) +{ + + list_delete_all_node (to->paths); + ospf_route_copy_nexthops (to, from); +} + +void +ospf_route_subst (struct route_node *rn, struct ospf_route *new_or, + struct ospf_route *over) +{ + route_lock_node (rn); + ospf_route_free (rn->info); + + ospf_route_copy_nexthops (new_or, over->paths); + rn->info = new_or; + route_unlock_node (rn); +} + +void +ospf_route_add (struct route_table *rt, struct prefix_ipv4 *p, + struct ospf_route *new_or, struct ospf_route *over) +{ + struct route_node *rn; + + rn = route_node_get (rt, (struct prefix *) p); + + ospf_route_copy_nexthops (new_or, over->paths); + + if (rn->info) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_route_add(): something's wrong !"); + route_unlock_node (rn); + return; + } + + rn->info = new_or; +} + +void +ospf_prune_unreachable_networks (struct route_table *rt) +{ + struct route_node *rn, *next; + struct ospf_route *or; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Pruning unreachable networks"); + + for (rn = route_top (rt); rn; rn = next) + { + next = route_next (rn); + if (rn->info != NULL) + { + or = rn->info; + if (listcount (or->paths) == 0) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Pruning route to %s/%d", + inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen); + + ospf_route_free (or); + rn->info = NULL; + route_unlock_node (rn); + } + } + } +} + +void +ospf_prune_unreachable_routers (struct route_table *rtrs) +{ + struct route_node *rn, *next; + struct ospf_route *or; + struct listnode *node, *nnode; + struct list *paths; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Pruning unreachable routers"); + + for (rn = route_top (rtrs); rn; rn = next) + { + next = route_next (rn); + if ((paths = rn->info) == NULL) + continue; + + for (ALL_LIST_ELEMENTS (paths, node, nnode, or)) + { + if (listcount (or->paths) == 0) + { + if (IS_DEBUG_OSPF_EVENT) + { + zlog_debug ("Pruning route to rtr %s", + inet_ntoa (rn->p.u.prefix4)); + zlog_debug (" via area %s", + inet_ntoa (or->u.std.area_id)); + } + + listnode_delete (paths, or); + ospf_route_free (or); + } + } + + if (listcount (paths) == 0) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Pruning router node %s", inet_ntoa (rn->p.u.prefix4)); + + list_delete (paths); + rn->info = NULL; + route_unlock_node (rn); + } + } +} + +int +ospf_add_discard_route (struct route_table *rt, struct ospf_area *area, + struct prefix_ipv4 *p) +{ + struct route_node *rn; + struct ospf_route *or, *new_or; + + rn = route_node_get (rt, (struct prefix *) p); + + if (rn == NULL) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_add_discard_route(): router installation error"); + return 0; + } + + if (rn->info) /* If the route to the same destination is found */ + { + route_unlock_node (rn); + + or = rn->info; + + if (or->path_type == OSPF_PATH_INTRA_AREA) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_add_discard_route(): " + "an intra-area route exists"); + return 0; + } + + if (or->type == OSPF_DESTINATION_DISCARD) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_add_discard_route(): " + "discard entry already installed"); + return 0; + } + + 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; + new_or->cost = 0; + new_or->u.std.area_id = area->area_id; + new_or->u.std.external_routing = area->external_routing; + new_or->path_type = OSPF_PATH_INTER_AREA; + rn->info = new_or; + + ospf_zebra_add_discard (p); + + return 1; +} + +void +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 new file mode 100644 index 0000000..d509e4a --- /dev/null +++ b/ospfd/ospf_route.h @@ -0,0 +1,166 @@ +/* + * OSPF routing table. + * Copyright (C) 1999, 2000 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 _ZEBRA_OSPF_ROUTE_H +#define _ZEBRA_OSPF_ROUTE_H + +#define OSPF_DESTINATION_ROUTER 1 +#define OSPF_DESTINATION_NETWORK 2 +#define OSPF_DESTINATION_DISCARD 3 + +#define OSPF_PATH_MIN 0 +#define OSPF_PATH_INTRA_AREA 1 +#define OSPF_PATH_INTER_AREA 2 +#define OSPF_PATH_TYPE1_EXTERNAL 3 +#define OSPF_PATH_TYPE2_EXTERNAL 4 +#define OSPF_PATH_MAX 5 + +/* OSPF Path. */ +struct ospf_path +{ + struct in_addr nexthop; + struct in_addr adv_router; + ifindex_t ifindex; +}; + +/* Below is the structure linked to every + route node. Note that for Network routing + entries a single ospf_route is kept, while + for ABRs and ASBRs (Router routing entries), + we link an instance of ospf_router_route + where a list of paths is maintained, so + + nr->info is a (struct ospf_route *) for OSPF_DESTINATION_NETWORK + but + nr->info is a (struct ospf_router_route *) for OSPF_DESTINATION_ROUTER +*/ + +struct route_standard +{ + /* Link Sate Origin. */ + struct lsa_header *origin; + + /* Associated Area. */ + struct in_addr area_id; /* The area the route belongs to */ + + /* Area Type */ + int external_routing; + + /* Optional Capability. */ + u_char options; /* Get from LSA header. */ + + /* */ + u_char flags; /* From router-LSA */ +}; + +struct route_external +{ + /* Link State Origin. */ + struct ospf_lsa *origin; + + /* Link State Cost Type2. */ + u_int32_t type2_cost; + + /* Tag value. */ + u_int32_t tag; + + /* ASBR route. */ + struct ospf_route *asbr; +}; + +struct ospf_route +{ + /* Create time. */ + time_t ctime; + + /* Modified time. */ + time_t mtime; + + /* Destination Type. */ + u_char type; + + /* Destination ID. */ /* i.e. Link State ID. */ + struct in_addr id; + + /* Address Mask. */ + struct in_addr mask; /* Only valid for networks. */ + + /* Path Type. */ + u_char path_type; + + /* List of Paths. */ + struct list *paths; + + /* Link State Cost. */ + u_int32_t cost; /* i.e. metric. */ + + /* Route specific info. */ + union + { + struct route_standard std; + struct route_external ext; + } u; +}; + +extern struct ospf_path *ospf_path_new (void); +extern void ospf_path_free (struct ospf_path *); +extern struct ospf_path *ospf_path_lookup (struct list *, struct ospf_path *); +extern struct ospf_route *ospf_route_new (void); +extern void ospf_route_free (struct ospf_route *); +extern void ospf_route_delete (struct route_table *); +extern void ospf_route_table_free (struct route_table *); + +extern void ospf_route_install (struct ospf *, struct route_table *); +extern void ospf_route_table_dump (struct route_table *); + +extern void ospf_intra_add_router (struct route_table *, struct vertex *, + struct ospf_area *); + +extern void ospf_intra_add_transit (struct route_table *, struct vertex *, + struct ospf_area *); + +extern void ospf_intra_add_stub (struct route_table *, + struct router_lsa_link *, struct vertex *, + struct ospf_area *, + int parent_is_root, int); + +extern int ospf_route_cmp (struct ospf *, struct ospf_route *, + struct ospf_route *); +extern void ospf_route_copy_nexthops (struct ospf_route *, struct list *); +extern void ospf_route_copy_nexthops_from_vertex (struct ospf_route *, + struct vertex *); + +extern void ospf_route_subst (struct route_node *, struct ospf_route *, + struct ospf_route *); +extern void ospf_route_add (struct route_table *, struct prefix_ipv4 *, + struct ospf_route *, struct ospf_route *); + +extern void ospf_route_subst_nexthops (struct ospf_route *, struct list *); +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 route_table *, struct prefix_ipv4 *); +extern int ospf_route_match_same (struct route_table *, struct prefix_ipv4 *, + struct ospf_route *); + +#endif /* _ZEBRA_OSPF_ROUTE_H */ diff --git a/ospfd/ospf_routemap.c b/ospfd/ospf_routemap.c new file mode 100644 index 0000000..31d7ce2 --- /dev/null +++ b/ospfd/ospf_routemap.c @@ -0,0 +1,972 @@ +/* + * Route map function of ospfd. + * Copyright (C) 2000 IP Infusion Inc. + * + * Written by 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. + */ + +#include + +#include "memory.h" +#include "prefix.h" +#include "table.h" +#include "routemap.h" +#include "command.h" +#include "log.h" +#include "plist.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_zebra.h" + +/* Hook function for updating route_map assignment. */ +static void +ospf_route_map_update (const char *name) +{ + struct ospf *ospf; + int type; + + /* If OSPF instatnce does not exist, return right now. */ + ospf = ospf_lookup (); + if (ospf == NULL) + return; + + /* Update route-map */ + for (type = 0; type <= ZEBRA_ROUTE_MAX; type++) + { + if (ROUTEMAP_NAME (ospf, type) + && strcmp (ROUTEMAP_NAME (ospf, type), name) == 0) + { + /* Keep old route-map. */ + struct route_map *old = ROUTEMAP (ospf, type); + + /* Update route-map. */ + ROUTEMAP (ospf, type) = + route_map_lookup_by_name (ROUTEMAP_NAME (ospf, type)); + + /* No update for this distribute type. */ + if (old == NULL && ROUTEMAP (ospf, type) == NULL) + continue; + + ospf_distribute_list_update (ospf, type); + } + } +} + +static void +ospf_route_map_event (route_map_event_t event, const char *name) +{ + struct ospf *ospf; + int type; + + /* If OSPF instatnce does not exist, return right now. */ + ospf = ospf_lookup (); + if (ospf == NULL) + return; + + /* Update route-map. */ + for (type = 0; type <= ZEBRA_ROUTE_MAX; type++) + { + if (ROUTEMAP_NAME (ospf, type) && ROUTEMAP (ospf, type) + && !strcmp (ROUTEMAP_NAME (ospf, type), name)) + { + ospf_distribute_list_update (ospf, type); + } + } +} + +/* Delete rip route map rule. */ +static int +ospf_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, "%% OSPF Can't find rule.%s", VTY_NEWLINE); + return CMD_WARNING; + case RMAP_COMPILE_ERROR: + vty_out (vty, "%% OSPF Argument is malformed.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + return CMD_SUCCESS; +} + +static int +ospf_route_match_add (struct vty *vty, struct route_map_index *index, + const char *command, const char *arg) +{ + int ret; + + ret = route_map_add_match (index, command, arg); + if (ret) + { + switch (ret) + { + case RMAP_RULE_MISSING: + vty_out (vty, "%% OSPF Can't find rule.%s", VTY_NEWLINE); + return CMD_WARNING; + case RMAP_COMPILE_ERROR: + vty_out (vty, "%% OSPF Argument is malformed.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + return CMD_SUCCESS; +} + +static int +ospf_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, "%% OSPF Can't find rule.%s", VTY_NEWLINE); + return CMD_WARNING; + case RMAP_COMPILE_ERROR: + vty_out (vty, "%% OSPF Argument is malformed.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + return CMD_SUCCESS; +} + +/* Delete rip route map rule. */ +static int +ospf_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, "%% OSPF Can't find rule.%s", VTY_NEWLINE); + return CMD_WARNING; + case RMAP_COMPILE_ERROR: + vty_out (vty, "%% OSPF Argument is malformed.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + return CMD_SUCCESS; +} + +/* `match ip netxthop ' */ +/* Match function return 1 if match is success else return zero. */ +static route_map_result_t +route_match_ip_nexthop (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct access_list *alist; + struct external_info *ei = object; + struct prefix_ipv4 p; + + if (type == RMAP_OSPF) + { + p.family = AF_INET; + p.prefix = ei->nexthop; + p.prefixlen = IPV4_MAX_BITLEN; + + alist = access_list_lookup (AFI_IP, (char *) rule); + if (alist == NULL) + return RMAP_NOMATCH; + + return (access_list_apply (alist, &p) == FILTER_DENY ? + RMAP_NOMATCH : RMAP_MATCH); + } + return RMAP_NOMATCH; +} + +/* Route map `ip next-hop' match statement. `arg' should be + access-list name. */ +static void * +route_match_ip_nexthop_compile (const char *arg) +{ + return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); +} + +/* Free route map's compiled `ip address' value. */ +static void +route_match_ip_nexthop_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for metric matching. */ +struct route_map_rule_cmd route_match_ip_nexthop_cmd = +{ + "ip next-hop", + route_match_ip_nexthop, + route_match_ip_nexthop_compile, + route_match_ip_nexthop_free +}; + +/* `match ip next-hop prefix-list PREFIX_LIST' */ + +static route_map_result_t +route_match_ip_next_hop_prefix_list (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct prefix_list *plist; + struct external_info *ei = object; + struct prefix_ipv4 p; + + if (type == RMAP_OSPF) + { + p.family = AF_INET; + p.prefix = ei->nexthop; + p.prefixlen = IPV4_MAX_BITLEN; + + plist = prefix_list_lookup (AFI_IP, (char *) rule); + if (plist == NULL) + return RMAP_NOMATCH; + + return (prefix_list_apply (plist, &p) == PREFIX_DENY ? + RMAP_NOMATCH : RMAP_MATCH); + } + return RMAP_NOMATCH; +} + +static void * +route_match_ip_next_hop_prefix_list_compile (const char *arg) +{ + return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); +} + +static void +route_match_ip_next_hop_prefix_list_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +struct route_map_rule_cmd route_match_ip_next_hop_prefix_list_cmd = +{ + "ip next-hop prefix-list", + route_match_ip_next_hop_prefix_list, + 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 + zero. */ +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; + /* struct prefix_ipv4 match; */ + + if (type == RMAP_OSPF) + { + alist = access_list_lookup (AFI_IP, (char *) rule); + if (alist == NULL) + return RMAP_NOMATCH; + + return (access_list_apply (alist, prefix) == FILTER_DENY ? + RMAP_NOMATCH : RMAP_MATCH); + } + return RMAP_NOMATCH; +} + +/* Route map `ip address' match statement. `arg' should be + access-list name. */ +static void * +route_match_ip_address_compile (const char *arg) +{ + return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); +} + +/* Free route map's compiled `ip address' value. */ +static void +route_match_ip_address_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for ip address matching. */ +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 +}; + +/* `match ip address prefix-list PREFIX_LIST' */ +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_OSPF) + { + plist = prefix_list_lookup (AFI_IP, (char *) rule); + if (plist == NULL) + return RMAP_NOMATCH; + + return (prefix_list_apply (plist, prefix) == PREFIX_DENY ? + RMAP_NOMATCH : 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 +}; + +/* `match interface IFNAME' */ +/* Match function should 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 interface *ifp; + struct external_info *ei; + + if (type == RMAP_OSPF) + { + ei = object; + ifp = if_lookup_by_name ((char *)rule); + + if (ifp == NULL || ifp->ifindex != ei->ifindex) + return RMAP_NOMATCH; + + return RMAP_MATCH; + } + return RMAP_NOMATCH; +} + +/* Route map `interface' match statement. `arg' should be + interface name. */ +static void * +route_match_interface_compile (const char *arg) +{ + return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); +} + +/* Free route map's compiled `interface' value. */ +static void +route_match_interface_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for ip address matching. */ +struct route_map_rule_cmd route_match_interface_cmd = +{ + "interface", + route_match_interface, + route_match_interface_compile, + 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 +route_set_metric (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + u_int32_t *metric; + struct external_info *ei; + + if (type == RMAP_OSPF) + { + /* Fetch routemap's rule information. */ + metric = rule; + ei = object; + + /* Set metric out value. */ + ei->route_map_set.metric = *metric; + } + return RMAP_OKAY; +} + +/* set metric compilation. */ +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)); + ret = atoi (arg); + + if (ret >= 0) + { + *metric = (u_int32_t)ret; + return metric; + } + + XFREE (MTYPE_ROUTE_MAP_COMPILED, metric); + return NULL; +} + +/* 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, +}; + +/* `set metric-type TYPE' */ +/* Set metric-type to attribute. */ +static route_map_result_t +route_set_metric_type (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + u_int32_t *metric_type; + struct external_info *ei; + + if (type == RMAP_OSPF) + { + /* Fetch routemap's rule information. */ + metric_type = rule; + ei = object; + + /* Set metric out value. */ + ei->route_map_set.metric_type = *metric_type; + } + return RMAP_OKAY; +} + +/* set metric-type compilation. */ +static void * +route_set_metric_type_compile (const char *arg) +{ + u_int32_t *metric_type; + + metric_type = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_int32_t)); + if (strcmp (arg, "type-1") == 0) + *metric_type = EXTERNAL_METRIC_TYPE_1; + else if (strcmp (arg, "type-2") == 0) + *metric_type = EXTERNAL_METRIC_TYPE_2; + + if (*metric_type == EXTERNAL_METRIC_TYPE_1 || + *metric_type == EXTERNAL_METRIC_TYPE_2) + return metric_type; + + XFREE (MTYPE_ROUTE_MAP_COMPILED, metric_type); + return NULL; +} + +/* Free route map's compiled `set metric-type' value. */ +static void +route_set_metric_type_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Set metric rule structure. */ +struct route_map_rule_cmd route_set_metric_type_cmd = +{ + "metric-type", + route_set_metric_type, + route_set_metric_type_compile, + 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)", + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "IP access-list number\n" + "IP access-list number (expanded range)\n" + "IP access-list name\n") +{ + return ospf_route_match_add (vty, vty->index, "ip next-hop", argv[0]); +} + +DEFUN (no_match_ip_nexthop, + no_match_ip_nexthop_cmd, + "no match ip next-hop", + NO_STR + MATCH_STR + IP_STR + "Match next-hop address of route\n") +{ + if (argc == 0) + return ospf_route_match_delete (vty, vty->index, "ip next-hop", NULL); + + return ospf_route_match_delete (vty, vty->index, "ip next-hop", argv[0]); +} + +ALIAS (no_match_ip_nexthop, + no_match_ip_nexthop_val_cmd, + "no match ip next-hop (<1-199>|<1300-2699>|WORD)", + NO_STR + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "IP access-list number\n" + "IP access-list number (expanded range)\n" + "IP access-list name\n") + +DEFUN (match_ip_next_hop_prefix_list, + match_ip_next_hop_prefix_list_cmd, + "match ip next-hop prefix-list WORD", + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") +{ + return ospf_route_match_add (vty, vty->index, "ip next-hop prefix-list", + argv[0]); +} + +DEFUN (no_match_ip_next_hop_prefix_list, + no_match_ip_next_hop_prefix_list_cmd, + "no match ip next-hop prefix-list", + NO_STR + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "Match entries of prefix-lists\n") +{ + if (argc == 0) + return ospf_route_match_delete (vty, vty->index, "ip next-hop prefix-list", + NULL); + return ospf_route_match_delete (vty, vty->index, "ip next-hop prefix-list", + argv[0]); +} + +ALIAS (no_match_ip_next_hop_prefix_list, + no_match_ip_next_hop_prefix_list_val_cmd, + "no match ip next-hop prefix-list WORD", + NO_STR + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") + +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 ospf_route_match_add (vty, vty->index, "ip address", argv[0]); +} + +DEFUN (no_match_ip_address, + no_match_ip_address_cmd, + "no match ip address", + NO_STR + MATCH_STR + IP_STR + "Match address of route\n") +{ + if (argc == 0) + return ospf_route_match_delete (vty, vty->index, "ip address", NULL); + + return ospf_route_match_delete (vty, vty->index, "ip address", argv[0]); +} + +ALIAS (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") + +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 ospf_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 ospf_route_match_delete (vty, vty->index, "ip address prefix-list", + NULL); + return ospf_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_interface, + match_interface_cmd, + "match interface WORD", + MATCH_STR + "Match first hop interface of route\n" + "Interface name\n") +{ + return ospf_route_match_add (vty, vty->index, "interface", argv[0]); +} + +DEFUN (no_match_interface, + no_match_interface_cmd, + "no match interface", + NO_STR + MATCH_STR + "Match first hop interface of route\n") +{ + if (argc == 0) + return ospf_route_match_delete (vty, vty->index, "interface", NULL); + + return ospf_route_match_delete (vty, vty->index, "interface", argv[0]); +} + +ALIAS (no_match_interface, + no_match_interface_val_cmd, + "no match interface WORD", + NO_STR + MATCH_STR + "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>", + SET_STR + "Metric value for destination routing protocol\n" + "Metric value\n") +{ + return ospf_route_set_add (vty, vty->index, "metric", argv[0]); +} + +DEFUN (no_set_metric, + no_set_metric_cmd, + "no set metric", + NO_STR + SET_STR + "Metric value for destination routing protocol\n") +{ + if (argc == 0) + return ospf_route_set_delete (vty, vty->index, "metric", NULL); + + return ospf_route_set_delete (vty, vty->index, "metric", argv[0]); +} + +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") + +DEFUN (set_metric_type, + set_metric_type_cmd, + "set metric-type (type-1|type-2)", + SET_STR + "Type of metric for destination routing protocol\n" + "OSPF[6] external type 1 metric\n" + "OSPF[6] external type 2 metric\n") +{ + if (strcmp (argv[0], "1") == 0) + return ospf_route_set_add (vty, vty->index, "metric-type", "type-1"); + if (strcmp (argv[0], "2") == 0) + return ospf_route_set_add (vty, vty->index, "metric-type", "type-2"); + + return ospf_route_set_add (vty, vty->index, "metric-type", argv[0]); +} + +DEFUN (no_set_metric_type, + no_set_metric_type_cmd, + "no set metric-type", + NO_STR + SET_STR + "Type of metric for destination routing protocol\n") +{ + if (argc == 0) + return ospf_route_set_delete (vty, vty->index, "metric-type", NULL); + + return ospf_route_set_delete (vty, vty->index, "metric-type", argv[0]); +} + +ALIAS (no_set_metric_type, + no_set_metric_type_val_cmd, + "no set metric-type (type-1|type-2)", + NO_STR + SET_STR + "Type of metric for destination routing protocol\n" + "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) +{ + route_map_init (); + route_map_init_vty (); + + route_map_add_hook (ospf_route_map_update); + route_map_delete_hook (ospf_route_map_update); + route_map_event_hook (ospf_route_map_event); + + route_map_install_match (&route_match_ip_nexthop_cmd); + route_map_install_match (&route_match_ip_next_hop_prefix_list_cmd); + 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); + install_element (RMAP_NODE, &no_match_ip_nexthop_val_cmd); + install_element (RMAP_NODE, &match_ip_next_hop_prefix_list_cmd); + install_element (RMAP_NODE, &no_match_ip_next_hop_prefix_list_cmd); + install_element (RMAP_NODE, &no_match_ip_next_hop_prefix_list_val_cmd); + install_element (RMAP_NODE, &match_ip_address_cmd); + install_element (RMAP_NODE, &no_match_ip_address_cmd); + install_element (RMAP_NODE, &no_match_ip_address_val_cmd); + install_element (RMAP_NODE, &match_ip_address_prefix_list_cmd); + install_element (RMAP_NODE, &no_match_ip_address_prefix_list_cmd); + install_element (RMAP_NODE, &no_match_ip_address_prefix_list_val_cmd); + 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); + install_element (RMAP_NODE, &no_set_metric_val_cmd); + 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 new file mode 100644 index 0000000..1f9b851 --- /dev/null +++ b/ospfd/ospf_snmp.c @@ -0,0 +1,2743 @@ +/* OSPFv2 SNMP support + * Copyright (C) 2005 6WIND + * Copyright (C) 2000 IP Infusion Inc. + * + * Written by 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 "if.h" +#include "log.h" +#include "prefix.h" +#include "table.h" +#include "command.h" +#include "memory.h" +#include "smux.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_abr.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_flood.h" +#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 + +/* OSPF MIB General Group values. */ +#define OSPFROUTERID 1 +#define OSPFADMINSTAT 2 +#define OSPFVERSIONNUMBER 3 +#define OSPFAREABDRRTRSTATUS 4 +#define OSPFASBDRRTRSTATUS 5 +#define OSPFEXTERNLSACOUNT 6 +#define OSPFEXTERNLSACKSUMSUM 7 +#define OSPFTOSSUPPORT 8 +#define OSPFORIGINATENEWLSAS 9 +#define OSPFRXNEWLSAS 10 +#define OSPFEXTLSDBLIMIT 11 +#define OSPFMULTICASTEXTENSIONS 12 +#define OSPFEXITOVERFLOWINTERVAL 13 +#define OSPFDEMANDEXTENSIONS 14 + +/* OSPF MIB ospfAreaTable. */ +#define OSPFAREAID 1 +#define OSPFAUTHTYPE 2 +#define OSPFIMPORTASEXTERN 3 +#define OSPFSPFRUNS 4 +#define OSPFAREABDRRTRCOUNT 5 +#define OSPFASBDRRTRCOUNT 6 +#define OSPFAREALSACOUNT 7 +#define OSPFAREALSACKSUMSUM 8 +#define OSPFAREASUMMARY 9 +#define OSPFAREASTATUS 10 + +/* OSPF MIB ospfStubAreaTable. */ +#define OSPFSTUBAREAID 1 +#define OSPFSTUBTOS 2 +#define OSPFSTUBMETRIC 3 +#define OSPFSTUBSTATUS 4 +#define OSPFSTUBMETRICTYPE 5 + +/* OSPF MIB ospfLsdbTable. */ +#define OSPFLSDBAREAID 1 +#define OSPFLSDBTYPE 2 +#define OSPFLSDBLSID 3 +#define OSPFLSDBROUTERID 4 +#define OSPFLSDBSEQUENCE 5 +#define OSPFLSDBAGE 6 +#define OSPFLSDBCHECKSUM 7 +#define OSPFLSDBADVERTISEMENT 8 + +/* OSPF MIB ospfAreaRangeTable. */ +#define OSPFAREARANGEAREAID 1 +#define OSPFAREARANGENET 2 +#define OSPFAREARANGEMASK 3 +#define OSPFAREARANGESTATUS 4 +#define OSPFAREARANGEEFFECT 5 + +/* OSPF MIB ospfHostTable. */ +#define OSPFHOSTIPADDRESS 1 +#define OSPFHOSTTOS 2 +#define OSPFHOSTMETRIC 3 +#define OSPFHOSTSTATUS 4 +#define OSPFHOSTAREAID 5 + +/* OSPF MIB ospfIfTable. */ +#define OSPFIFIPADDRESS 1 +#define OSPFADDRESSLESSIF 2 +#define OSPFIFAREAID 3 +#define OSPFIFTYPE 4 +#define OSPFIFADMINSTAT 5 +#define OSPFIFRTRPRIORITY 6 +#define OSPFIFTRANSITDELAY 7 +#define OSPFIFRETRANSINTERVAL 8 +#define OSPFIFHELLOINTERVAL 9 +#define OSPFIFRTRDEADINTERVAL 10 +#define OSPFIFPOLLINTERVAL 11 +#define OSPFIFSTATE 12 +#define OSPFIFDESIGNATEDROUTER 13 +#define OSPFIFBACKUPDESIGNATEDROUTER 14 +#define OSPFIFEVENTS 15 +#define OSPFIFAUTHKEY 16 +#define OSPFIFSTATUS 17 +#define OSPFIFMULTICASTFORWARDING 18 +#define OSPFIFDEMAND 19 +#define OSPFIFAUTHTYPE 20 + +/* OSPF MIB ospfIfMetricTable. */ +#define OSPFIFMETRICIPADDRESS 1 +#define OSPFIFMETRICADDRESSLESSIF 2 +#define OSPFIFMETRICTOS 3 +#define OSPFIFMETRICVALUE 4 +#define OSPFIFMETRICSTATUS 5 + +/* OSPF MIB ospfVirtIfTable. */ +#define OSPFVIRTIFAREAID 1 +#define OSPFVIRTIFNEIGHBOR 2 +#define OSPFVIRTIFTRANSITDELAY 3 +#define OSPFVIRTIFRETRANSINTERVAL 4 +#define OSPFVIRTIFHELLOINTERVAL 5 +#define OSPFVIRTIFRTRDEADINTERVAL 6 +#define OSPFVIRTIFSTATE 7 +#define OSPFVIRTIFEVENTS 8 +#define OSPFVIRTIFAUTHKEY 9 +#define OSPFVIRTIFSTATUS 10 +#define OSPFVIRTIFAUTHTYPE 11 + +/* OSPF MIB ospfNbrTable. */ +#define OSPFNBRIPADDR 1 +#define OSPFNBRADDRESSLESSINDEX 2 +#define OSPFNBRRTRID 3 +#define OSPFNBROPTIONS 4 +#define OSPFNBRPRIORITY 5 +#define OSPFNBRSTATE 6 +#define OSPFNBREVENTS 7 +#define OSPFNBRLSRETRANSQLEN 8 +#define OSPFNBMANBRSTATUS 9 +#define OSPFNBMANBRPERMANENCE 10 +#define OSPFNBRHELLOSUPPRESSED 11 + +/* OSPF MIB ospfVirtNbrTable. */ +#define OSPFVIRTNBRAREA 1 +#define OSPFVIRTNBRRTRID 2 +#define OSPFVIRTNBRIPADDR 3 +#define OSPFVIRTNBROPTIONS 4 +#define OSPFVIRTNBRSTATE 5 +#define OSPFVIRTNBREVENTS 6 +#define OSPFVIRTNBRLSRETRANSQLEN 7 +#define OSPFVIRTNBRHELLOSUPPRESSED 8 + +/* OSPF MIB ospfExtLsdbTable. */ +#define OSPFEXTLSDBTYPE 1 +#define OSPFEXTLSDBLSID 2 +#define OSPFEXTLSDBROUTERID 3 +#define OSPFEXTLSDBSEQUENCE 4 +#define OSPFEXTLSDBAGE 5 +#define OSPFEXTLSDBCHECKSUM 6 +#define OSPFEXTLSDBADVERTISEMENT 7 + +/* OSPF MIB ospfAreaAggregateTable. */ +#define OSPFAREAAGGREGATEAREAID 1 +#define OSPFAREAAGGREGATELSDBTYPE 2 +#define OSPFAREAAGGREGATENET 3 +#define OSPFAREAAGGREGATEMASK 4 +#define OSPFAREAAGGREGATESTATUS 5 +#define OSPFAREAAGGREGATEEFFECT 6 + +/* SYNTAX Status from OSPF-MIB. */ +#define OSPF_STATUS_ENABLED 1 +#define OSPF_STATUS_DISABLED 2 + +/* SNMP value hack. */ +#define COUNTER ASN_COUNTER +#define INTEGER ASN_INTEGER +#define GAUGE ASN_GAUGE +#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 = { .s_addr = 0 }; + +/* Hook functions. */ +static u_char *ospfGeneralGroup (struct variable *, oid *, size_t *, + int, size_t *, WriteMethod **); +static u_char *ospfAreaEntry (struct variable *, oid *, size_t *, int, + size_t *, WriteMethod **); +static u_char *ospfStubAreaEntry (struct variable *, oid *, size_t *, + int, size_t *, WriteMethod **); +static u_char *ospfLsdbEntry (struct variable *, oid *, size_t *, int, + size_t *, WriteMethod **); +static u_char *ospfAreaRangeEntry (struct variable *, oid *, size_t *, int, + size_t *, WriteMethod **); +static u_char *ospfHostEntry (struct variable *, oid *, size_t *, int, + size_t *, WriteMethod **); +static u_char *ospfIfEntry (struct variable *, oid *, size_t *, int, + size_t *, WriteMethod **); +static u_char *ospfIfMetricEntry (struct variable *, oid *, size_t *, int, + size_t *, WriteMethod **); +static u_char *ospfVirtIfEntry (struct variable *, oid *, size_t *, int, + size_t *, WriteMethod **); +static u_char *ospfNbrEntry (struct variable *, oid *, size_t *, int, + size_t *, WriteMethod **); +static u_char *ospfVirtNbrEntry (struct variable *, oid *, size_t *, int, + size_t *, WriteMethod **); +static u_char *ospfExtLsdbEntry (struct variable *, oid *, size_t *, int, + size_t *, WriteMethod **); +static u_char *ospfAreaAggregateEntry (struct variable *, oid *, size_t *, + int, size_t *, WriteMethod **); + +struct variable ospf_variables[] = +{ + /* OSPF general variables */ + {OSPFROUTERID, IPADDRESS, RWRITE, ospfGeneralGroup, + 2, {1, 1}}, + {OSPFADMINSTAT, INTEGER, RWRITE, ospfGeneralGroup, + 2, {1, 2}}, + {OSPFVERSIONNUMBER, INTEGER, RONLY, ospfGeneralGroup, + 2, {1, 3}}, + {OSPFAREABDRRTRSTATUS, INTEGER, RONLY, ospfGeneralGroup, + 2, {1, 4}}, + {OSPFASBDRRTRSTATUS, INTEGER, RWRITE, ospfGeneralGroup, + 2, {1, 5}}, + {OSPFEXTERNLSACOUNT, GAUGE, RONLY, ospfGeneralGroup, + 2, {1, 6}}, + {OSPFEXTERNLSACKSUMSUM, INTEGER, RONLY, ospfGeneralGroup, + 2, {1, 7}}, + {OSPFTOSSUPPORT, INTEGER, RWRITE, ospfGeneralGroup, + 2, {1, 8}}, + {OSPFORIGINATENEWLSAS, COUNTER, RONLY, ospfGeneralGroup, + 2, {1, 9}}, + {OSPFRXNEWLSAS, COUNTER, RONLY, ospfGeneralGroup, + 2, {1, 10}}, + {OSPFEXTLSDBLIMIT, INTEGER, RWRITE, ospfGeneralGroup, + 2, {1, 11}}, + {OSPFMULTICASTEXTENSIONS, INTEGER, RWRITE, ospfGeneralGroup, + 2, {1, 12}}, + {OSPFEXITOVERFLOWINTERVAL, INTEGER, RWRITE, ospfGeneralGroup, + 2, {1, 13}}, + {OSPFDEMANDEXTENSIONS, INTEGER, RWRITE, ospfGeneralGroup, + 2, {1, 14}}, + + /* OSPF area data structure. */ + {OSPFAREAID, IPADDRESS, RONLY, ospfAreaEntry, + 3, {2, 1, 1}}, + {OSPFAUTHTYPE, INTEGER, RWRITE, ospfAreaEntry, + 3, {2, 1, 2}}, + {OSPFIMPORTASEXTERN, INTEGER, RWRITE, ospfAreaEntry, + 3, {2, 1, 3}}, + {OSPFSPFRUNS, COUNTER, RONLY, ospfAreaEntry, + 3, {2, 1, 4}}, + {OSPFAREABDRRTRCOUNT, GAUGE, RONLY, ospfAreaEntry, + 3, {2, 1, 5}}, + {OSPFASBDRRTRCOUNT, GAUGE, RONLY, ospfAreaEntry, + 3, {2, 1, 6}}, + {OSPFAREALSACOUNT, GAUGE, RONLY, ospfAreaEntry, + 3, {2, 1, 7}}, + {OSPFAREALSACKSUMSUM, INTEGER, RONLY, ospfAreaEntry, + 3, {2, 1, 8}}, + {OSPFAREASUMMARY, INTEGER, RWRITE, ospfAreaEntry, + 3, {2, 1, 9}}, + {OSPFAREASTATUS, INTEGER, RWRITE, ospfAreaEntry, + 3, {2, 1, 10}}, + + /* OSPF stub area information. */ + {OSPFSTUBAREAID, IPADDRESS, RONLY, ospfStubAreaEntry, + 3, {3, 1, 1}}, + {OSPFSTUBTOS, INTEGER, RONLY, ospfStubAreaEntry, + 3, {3, 1, 2}}, + {OSPFSTUBMETRIC, INTEGER, RWRITE, ospfStubAreaEntry, + 3, {3, 1, 3}}, + {OSPFSTUBSTATUS, INTEGER, RWRITE, ospfStubAreaEntry, + 3, {3, 1, 4}}, + {OSPFSTUBMETRICTYPE, INTEGER, RWRITE, ospfStubAreaEntry, + 3, {3, 1, 5}}, + + /* OSPF link state database. */ + {OSPFLSDBAREAID, IPADDRESS, RONLY, ospfLsdbEntry, + 3, {4, 1, 1}}, + {OSPFLSDBTYPE, INTEGER, RONLY, ospfLsdbEntry, + 3, {4, 1, 2}}, + {OSPFLSDBLSID, IPADDRESS, RONLY, ospfLsdbEntry, + 3, {4, 1, 3}}, + {OSPFLSDBROUTERID, IPADDRESS, RONLY, ospfLsdbEntry, + 3, {4, 1, 4}}, + {OSPFLSDBSEQUENCE, INTEGER, RONLY, ospfLsdbEntry, + 3, {4, 1, 5}}, + {OSPFLSDBAGE, INTEGER, RONLY, ospfLsdbEntry, + 3, {4, 1, 6}}, + {OSPFLSDBCHECKSUM, INTEGER, RONLY, ospfLsdbEntry, + 3, {4, 1, 7}}, + {OSPFLSDBADVERTISEMENT, STRING, RONLY, ospfLsdbEntry, + 3, {4, 1, 8}}, + + /* Area range table. */ + {OSPFAREARANGEAREAID, IPADDRESS, RONLY, ospfAreaRangeEntry, + 3, {5, 1, 1}}, + {OSPFAREARANGENET, IPADDRESS, RONLY, ospfAreaRangeEntry, + 3, {5, 1, 2}}, + {OSPFAREARANGEMASK, IPADDRESS, RWRITE, ospfAreaRangeEntry, + 3, {5, 1, 3}}, + {OSPFAREARANGESTATUS, INTEGER, RWRITE, ospfAreaRangeEntry, + 3, {5, 1, 4}}, + {OSPFAREARANGEEFFECT, INTEGER, RWRITE, ospfAreaRangeEntry, + 3, {5, 1, 5}}, + + /* OSPF host table. */ + {OSPFHOSTIPADDRESS, IPADDRESS, RONLY, ospfHostEntry, + 3, {6, 1, 1}}, + {OSPFHOSTTOS, INTEGER, RONLY, ospfHostEntry, + 3, {6, 1, 2}}, + {OSPFHOSTMETRIC, INTEGER, RWRITE, ospfHostEntry, + 3, {6, 1, 3}}, + {OSPFHOSTSTATUS, INTEGER, RWRITE, ospfHostEntry, + 3, {6, 1, 4}}, + {OSPFHOSTAREAID, IPADDRESS, RONLY, ospfHostEntry, + 3, {6, 1, 5}}, + + /* OSPF interface table. */ + {OSPFIFIPADDRESS, IPADDRESS, RONLY, ospfIfEntry, + 3, {7, 1, 1}}, + {OSPFADDRESSLESSIF, INTEGER, RONLY, ospfIfEntry, + 3, {7, 1, 2}}, + {OSPFIFAREAID, IPADDRESS, RWRITE, ospfIfEntry, + 3, {7, 1, 3}}, + {OSPFIFTYPE, INTEGER, RWRITE, ospfIfEntry, + 3, {7, 1, 4}}, + {OSPFIFADMINSTAT, INTEGER, RWRITE, ospfIfEntry, + 3, {7, 1, 5}}, + {OSPFIFRTRPRIORITY, INTEGER, RWRITE, ospfIfEntry, + 3, {7, 1, 6}}, + {OSPFIFTRANSITDELAY, INTEGER, RWRITE, ospfIfEntry, + 3, {7, 1, 7}}, + {OSPFIFRETRANSINTERVAL, INTEGER, RWRITE, ospfIfEntry, + 3, {7, 1, 8}}, + {OSPFIFHELLOINTERVAL, INTEGER, RWRITE, ospfIfEntry, + 3, {7, 1, 9}}, + {OSPFIFRTRDEADINTERVAL, INTEGER, RWRITE, ospfIfEntry, + 3, {7, 1, 10}}, + {OSPFIFPOLLINTERVAL, INTEGER, RWRITE, ospfIfEntry, + 3, {7, 1, 11}}, + {OSPFIFSTATE, INTEGER, RONLY, ospfIfEntry, + 3, {7, 1, 12}}, + {OSPFIFDESIGNATEDROUTER, IPADDRESS, RONLY, ospfIfEntry, + 3, {7, 1, 13}}, + {OSPFIFBACKUPDESIGNATEDROUTER, IPADDRESS, RONLY, ospfIfEntry, + 3, {7, 1, 14}}, + {OSPFIFEVENTS, COUNTER, RONLY, ospfIfEntry, + 3, {7, 1, 15}}, + {OSPFIFAUTHKEY, STRING, RWRITE, ospfIfEntry, + 3, {7, 1, 16}}, + {OSPFIFSTATUS, INTEGER, RWRITE, ospfIfEntry, + 3, {7, 1, 17}}, + {OSPFIFMULTICASTFORWARDING, INTEGER, RWRITE, ospfIfEntry, + 3, {7, 1, 18}}, + {OSPFIFDEMAND, INTEGER, RWRITE, ospfIfEntry, + 3, {7, 1, 19}}, + {OSPFIFAUTHTYPE, INTEGER, RWRITE, ospfIfEntry, + 3, {7, 1, 20}}, + + /* OSPF interface metric table. */ + {OSPFIFMETRICIPADDRESS, IPADDRESS, RONLY, ospfIfMetricEntry, + 3, {8, 1, 1}}, + {OSPFIFMETRICADDRESSLESSIF, INTEGER, RONLY, ospfIfMetricEntry, + 3, {8, 1, 2}}, + {OSPFIFMETRICTOS, INTEGER, RONLY, ospfIfMetricEntry, + 3, {8, 1, 3}}, + {OSPFIFMETRICVALUE, INTEGER, RWRITE, ospfIfMetricEntry, + 3, {8, 1, 4}}, + {OSPFIFMETRICSTATUS, INTEGER, RWRITE, ospfIfMetricEntry, + 3, {8, 1, 5}}, + + /* OSPF virtual interface table. */ + {OSPFVIRTIFAREAID, IPADDRESS, RONLY, ospfVirtIfEntry, + 3, {9, 1, 1}}, + {OSPFVIRTIFNEIGHBOR, IPADDRESS, RONLY, ospfVirtIfEntry, + 3, {9, 1, 2}}, + {OSPFVIRTIFTRANSITDELAY, INTEGER, RWRITE, ospfVirtIfEntry, + 3, {9, 1, 3}}, + {OSPFVIRTIFRETRANSINTERVAL, INTEGER, RWRITE, ospfVirtIfEntry, + 3, {9, 1, 4}}, + {OSPFVIRTIFHELLOINTERVAL, INTEGER, RWRITE, ospfVirtIfEntry, + 3, {9, 1, 5}}, + {OSPFVIRTIFRTRDEADINTERVAL, INTEGER, RWRITE, ospfVirtIfEntry, + 3, {9, 1, 6}}, + {OSPFVIRTIFSTATE, INTEGER, RONLY, ospfVirtIfEntry, + 3, {9, 1, 7}}, + {OSPFVIRTIFEVENTS, COUNTER, RONLY, ospfVirtIfEntry, + 3, {9, 1, 8}}, + {OSPFVIRTIFAUTHKEY, STRING, RWRITE, ospfVirtIfEntry, + 3, {9, 1, 9}}, + {OSPFVIRTIFSTATUS, INTEGER, RWRITE, ospfVirtIfEntry, + 3, {9, 1, 10}}, + {OSPFVIRTIFAUTHTYPE, INTEGER, RWRITE, ospfVirtIfEntry, + 3, {9, 1, 11}}, + + /* OSPF neighbor table. */ + {OSPFNBRIPADDR, IPADDRESS, RONLY, ospfNbrEntry, + 3, {10, 1, 1}}, + {OSPFNBRADDRESSLESSINDEX, INTEGER, RONLY, ospfNbrEntry, + 3, {10, 1, 2}}, + {OSPFNBRRTRID, IPADDRESS, RONLY, ospfNbrEntry, + 3, {10, 1, 3}}, + {OSPFNBROPTIONS, INTEGER, RONLY, ospfNbrEntry, + 3, {10, 1, 4}}, + {OSPFNBRPRIORITY, INTEGER, RWRITE, ospfNbrEntry, + 3, {10, 1, 5}}, + {OSPFNBRSTATE, INTEGER, RONLY, ospfNbrEntry, + 3, {10, 1, 6}}, + {OSPFNBREVENTS, COUNTER, RONLY, ospfNbrEntry, + 3, {10, 1, 7}}, + {OSPFNBRLSRETRANSQLEN, GAUGE, RONLY, ospfNbrEntry, + 3, {10, 1, 8}}, + {OSPFNBMANBRSTATUS, INTEGER, RWRITE, ospfNbrEntry, + 3, {10, 1, 9}}, + {OSPFNBMANBRPERMANENCE, INTEGER, RONLY, ospfNbrEntry, + 3, {10, 1, 10}}, + {OSPFNBRHELLOSUPPRESSED, INTEGER, RONLY, ospfNbrEntry, + 3, {10, 1, 11}}, + + /* OSPF virtual neighbor table. */ + {OSPFVIRTNBRAREA, IPADDRESS, RONLY, ospfVirtNbrEntry, + 3, {11, 1, 1}}, + {OSPFVIRTNBRRTRID, IPADDRESS, RONLY, ospfVirtNbrEntry, + 3, {11, 1, 2}}, + {OSPFVIRTNBRIPADDR, IPADDRESS, RONLY, ospfVirtNbrEntry, + 3, {11, 1, 3}}, + {OSPFVIRTNBROPTIONS, INTEGER, RONLY, ospfVirtNbrEntry, + 3, {11, 1, 4}}, + {OSPFVIRTNBRSTATE, INTEGER, RONLY, ospfVirtNbrEntry, + 3, {11, 1, 5}}, + {OSPFVIRTNBREVENTS, COUNTER, RONLY, ospfVirtNbrEntry, + 3, {11, 1, 6}}, + {OSPFVIRTNBRLSRETRANSQLEN, INTEGER, RONLY, ospfVirtNbrEntry, + 3, {11, 1, 7}}, + {OSPFVIRTNBRHELLOSUPPRESSED, INTEGER, RONLY, ospfVirtNbrEntry, + 3, {11, 1, 8}}, + + /* OSPF link state database, external. */ + {OSPFEXTLSDBTYPE, INTEGER, RONLY, ospfExtLsdbEntry, + 3, {12, 1, 1}}, + {OSPFEXTLSDBLSID, IPADDRESS, RONLY, ospfExtLsdbEntry, + 3, {12, 1, 2}}, + {OSPFEXTLSDBROUTERID, IPADDRESS, RONLY, ospfExtLsdbEntry, + 3, {12, 1, 3}}, + {OSPFEXTLSDBSEQUENCE, INTEGER, RONLY, ospfExtLsdbEntry, + 3, {12, 1, 4}}, + {OSPFEXTLSDBAGE, INTEGER, RONLY, ospfExtLsdbEntry, + 3, {12, 1, 5}}, + {OSPFEXTLSDBCHECKSUM, INTEGER, RONLY, ospfExtLsdbEntry, + 3, {12, 1, 6}}, + {OSPFEXTLSDBADVERTISEMENT, STRING, RONLY, ospfExtLsdbEntry, + 3, {12, 1, 7}}, + + /* OSPF area aggregate table. */ + {OSPFAREAAGGREGATEAREAID, IPADDRESS, RONLY, ospfAreaAggregateEntry, + 3, {14, 1, 1}}, + {OSPFAREAAGGREGATELSDBTYPE, INTEGER, RONLY, ospfAreaAggregateEntry, + 3, {14, 1, 2}}, + {OSPFAREAAGGREGATENET, IPADDRESS, RONLY, ospfAreaAggregateEntry, + 3, {14, 1, 3}}, + {OSPFAREAAGGREGATEMASK, IPADDRESS, RONLY, ospfAreaAggregateEntry, + 3, {14, 1, 4}}, + {OSPFAREAAGGREGATESTATUS, INTEGER, RWRITE, ospfAreaAggregateEntry, + 3, {14, 1, 5}}, + {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 +ospf_admin_stat (struct ospf *ospf) +{ + struct listnode *node; + struct ospf_interface *oi; + + if (ospf == NULL) + return 0; + + for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi)) + if (oi && oi->address) + return 1; + + return 0; +} + +static u_char * +ospfGeneralGroup (struct variable *v, oid *name, size_t *length, + int exact, size_t *var_len, WriteMethod **write_method) +{ + struct ospf *ospf; + + ospf = ospf_lookup (); + + /* Check whether the instance identifier is valid */ + if (smux_header_generic (v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + /* Return the current value of the variable */ + switch (v->magic) + { + case OSPFROUTERID: /* 1 */ + /* Router-ID of this OSPF instance. */ + if (ospf) + return SNMP_IPADDRESS (ospf->router_id); + else + return SNMP_IPADDRESS (ospf_empty_addr); + break; + case OSPFADMINSTAT: /* 2 */ + /* The administrative status of OSPF in the router. */ + if (ospf_admin_stat (ospf)) + return SNMP_INTEGER (OSPF_STATUS_ENABLED); + else + return SNMP_INTEGER (OSPF_STATUS_DISABLED); + break; + case OSPFVERSIONNUMBER: /* 3 */ + /* OSPF version 2. */ + return SNMP_INTEGER (OSPF_VERSION); + break; + case OSPFAREABDRRTRSTATUS: /* 4 */ + /* Area Border router status. */ + if (ospf && CHECK_FLAG (ospf->flags, OSPF_FLAG_ABR)) + return SNMP_INTEGER (SNMP_TRUE); + else + return SNMP_INTEGER (SNMP_FALSE); + break; + case OSPFASBDRRTRSTATUS: /* 5 */ + /* AS Border router status. */ + if (ospf && CHECK_FLAG (ospf->flags, OSPF_FLAG_ASBR)) + return SNMP_INTEGER (SNMP_TRUE); + else + return SNMP_INTEGER (SNMP_FALSE); + break; + case OSPFEXTERNLSACOUNT: /* 6 */ + /* External LSA counts. */ + if (ospf) + return SNMP_INTEGER (ospf_lsdb_count_all (ospf->lsdb)); + else + return SNMP_INTEGER (0); + break; + case OSPFEXTERNLSACKSUMSUM: /* 7 */ + /* External LSA checksum. */ + return SNMP_INTEGER (0); + break; + case OSPFTOSSUPPORT: /* 8 */ + /* TOS is not supported. */ + return SNMP_INTEGER (SNMP_FALSE); + break; + case OSPFORIGINATENEWLSAS: /* 9 */ + /* The number of new link-state advertisements. */ + if (ospf) + return SNMP_INTEGER (ospf->lsa_originate_count); + else + return SNMP_INTEGER (0); + break; + case OSPFRXNEWLSAS: /* 10 */ + /* The number of link-state advertisements received determined + to be new instantiations. */ + if (ospf) + return SNMP_INTEGER (ospf->rx_lsa_count); + else + return SNMP_INTEGER (0); + break; + case OSPFEXTLSDBLIMIT: /* 11 */ + /* There is no limit for the number of non-default + AS-external-LSAs. */ + return SNMP_INTEGER (-1); + break; + case OSPFMULTICASTEXTENSIONS: /* 12 */ + /* Multicast Extensions to OSPF is not supported. */ + return SNMP_INTEGER (0); + break; + case OSPFEXITOVERFLOWINTERVAL: /* 13 */ + /* Overflow is not supported. */ + return SNMP_INTEGER (0); + break; + case OSPFDEMANDEXTENSIONS: /* 14 */ + /* Demand routing is not supported. */ + return SNMP_INTEGER (SNMP_FALSE); + break; + default: + return NULL; + } + return NULL; +} + +static struct ospf_area * +ospf_area_lookup_next (struct ospf *ospf, struct in_addr *area_id, int first) +{ + struct ospf_area *area; + struct listnode *node; + + if (ospf == NULL) + return NULL; + + if (first) + { + node = listhead (ospf->areas); + if (node) + { + area = listgetdata (node); + *area_id = area->area_id; + return area; + } + return NULL; + } + for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area)) + { + if (ntohl (area->area_id.s_addr) > ntohl (area_id->s_addr)) + { + *area_id = area->area_id; + return area; + } + } + return NULL; +} + +static struct ospf_area * +ospfAreaLookup (struct variable *v, oid name[], size_t *length, + struct in_addr *addr, int exact) +{ + struct ospf *ospf; + struct ospf_area *area; + int len; + + ospf = ospf_lookup (); + if (ospf == NULL) + return NULL; + + if (exact) + { + /* Length is insufficient to lookup OSPF area. */ + if (*length - v->namelen != sizeof (struct in_addr)) + return NULL; + + oid2in_addr (name + v->namelen, sizeof (struct in_addr), addr); + + area = ospf_area_lookup_by_area_id (ospf, *addr); + + return area; + } + else + { + len = *length - v->namelen; + if (len > 4) + len = 4; + + oid2in_addr (name + v->namelen, len, addr); + + area = ospf_area_lookup_next (ospf, addr, len == 0 ? 1 : 0); + + if (area == NULL) + return NULL; + + oid_copy_addr (name + v->namelen, addr, sizeof (struct in_addr)); + *length = sizeof (struct in_addr) + v->namelen; + + return area; + } + return NULL; +} + +static u_char * +ospfAreaEntry (struct variable *v, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + 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); + if (! area) + return NULL; + + /* Return the current value of the variable */ + switch (v->magic) + { + case OSPFAREAID: /* 1 */ + return SNMP_IPADDRESS (area->area_id); + break; + case OSPFAUTHTYPE: /* 2 */ + return SNMP_INTEGER (area->auth_type); + break; + case OSPFIMPORTASEXTERN: /* 3 */ + return SNMP_INTEGER (area->external_routing + 1); + break; + case OSPFSPFRUNS: /* 4 */ + return SNMP_INTEGER (area->spf_calculation); + break; + case OSPFAREABDRRTRCOUNT: /* 5 */ + return SNMP_INTEGER (area->abr_count); + break; + case OSPFASBDRRTRCOUNT: /* 6 */ + return SNMP_INTEGER (area->asbr_count); + break; + case OSPFAREALSACOUNT: /* 7 */ + return SNMP_INTEGER (area->lsdb->total); + break; + case OSPFAREALSACKSUMSUM: /* 8 */ + return SNMP_INTEGER (0); + break; + case OSPFAREASUMMARY: /* 9 */ +#define OSPF_noAreaSummary 1 +#define OSPF_sendAreaSummary 2 + if (area->no_summary) + return SNMP_INTEGER (OSPF_noAreaSummary); + else + return SNMP_INTEGER (OSPF_sendAreaSummary); + break; + case OSPFAREASTATUS: /* 10 */ + return SNMP_INTEGER (SNMP_VALID); + break; + default: + return NULL; + break; + } + return NULL; +} + +static struct ospf_area * +ospf_stub_area_lookup_next (struct in_addr *area_id, int first) +{ + struct ospf_area *area; + struct listnode *node; + struct ospf *ospf; + + ospf = ospf_lookup (); + if (ospf == NULL) + return NULL; + + for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area)) + { + if (area->external_routing == OSPF_AREA_STUB) + { + if (first) + { + *area_id = area->area_id; + return area; + } + else if (ntohl (area->area_id.s_addr) > ntohl (area_id->s_addr)) + { + *area_id = area->area_id; + return area; + } + } + } + return NULL; +} + +static struct ospf_area * +ospfStubAreaLookup (struct variable *v, oid name[], size_t *length, + struct in_addr *addr, int exact) +{ + struct ospf *ospf; + struct ospf_area *area; + int len; + + ospf = ospf_lookup (); + if (ospf == NULL) + return NULL; + + /* Exact lookup. */ + if (exact) + { + /* ospfStubAreaID + ospfStubTOS. */ + if (*length != v->namelen + sizeof (struct in_addr) + 1) + return NULL; + + /* Check ospfStubTOS is zero. */ + if (name[*length - 1] != 0) + return NULL; + + oid2in_addr (name + v->namelen, sizeof (struct in_addr), addr); + + area = ospf_area_lookup_by_area_id (ospf, *addr); + + if (area->external_routing == OSPF_AREA_STUB) + return area; + else + return NULL; + } + else + { + len = *length - v->namelen; + if (len > 4) + len = 4; + + oid2in_addr (name + v->namelen, len, addr); + + area = ospf_stub_area_lookup_next (addr, len == 0 ? 1 : 0); + + if (area == NULL) + return NULL; + + oid_copy_addr (name + v->namelen, addr, sizeof (struct in_addr)); + /* Set TOS 0. */ + name[v->namelen + sizeof (struct in_addr)] = 0; + *length = v->namelen + sizeof (struct in_addr) + 1; + + return area; + } + return NULL; +} + +static u_char * +ospfStubAreaEntry (struct variable *v, oid *name, size_t *length, + int exact, size_t *var_len, WriteMethod **write_method) +{ + 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); + if (! area) + return NULL; + + /* Return the current value of the variable */ + switch (v->magic) + { + case OSPFSTUBAREAID: /* 1 */ + /* OSPF stub area id. */ + return SNMP_IPADDRESS (area->area_id); + break; + case OSPFSTUBTOS: /* 2 */ + /* TOS value is not supported. */ + return SNMP_INTEGER (0); + break; + case OSPFSTUBMETRIC: /* 3 */ + /* Default cost to stub area. */ + return SNMP_INTEGER (area->default_cost); + break; + case OSPFSTUBSTATUS: /* 4 */ + /* Status of the stub area. */ + return SNMP_INTEGER (SNMP_VALID); + break; + case OSPFSTUBMETRICTYPE: /* 5 */ + /* OSPF Metric type. */ +#define OSPF_ospfMetric 1 +#define OSPF_comparableCost 2 +#define OSPF_nonComparable 3 + return SNMP_INTEGER (OSPF_ospfMetric); + break; + default: + return NULL; + break; + } + return NULL; +} + +static struct ospf_lsa * +lsdb_lookup_next (struct ospf_area *area, u_char *type, int type_next, + struct in_addr *ls_id, int ls_id_next, + struct in_addr *router_id, int router_id_next) +{ + struct ospf_lsa *lsa; + int i; + + if (type_next) + i = OSPF_MIN_LSA; + else + i = *type; + + /* Sanity check, if LSA type unknwon + merley skip any LSA */ + if ((i < OSPF_MIN_LSA) || (i >= OSPF_MAX_LSA)) + { + zlog_debug("Strange request with LSA type %d\n", i); + return NULL; + } + + for (; i < OSPF_MAX_LSA; i++) + { + *type = i; + + lsa = ospf_lsdb_lookup_by_id_next (area->lsdb, *type, *ls_id, *router_id, + ls_id_next); + if (lsa) + return lsa; + + ls_id_next = 1; + } + return NULL; +} + +static struct ospf_lsa * +ospfLsdbLookup (struct variable *v, oid *name, size_t *length, + struct in_addr *area_id, u_char *type, + struct in_addr *ls_id, struct in_addr *router_id, int exact) +{ + struct ospf *ospf; + struct ospf_area *area; + struct ospf_lsa *lsa; + int len; + int type_next; + int ls_id_next; + int router_id_next; + oid *offset; + int offsetlen; + + ospf = ospf_lookup (); + +#define OSPF_LSDB_ENTRY_OFFSET \ + (IN_ADDR_SIZE + 1 + IN_ADDR_SIZE + IN_ADDR_SIZE) + + if (exact) + { + /* Area ID + Type + LS ID + Router ID. */ + if (*length - v->namelen != OSPF_LSDB_ENTRY_OFFSET) + return NULL; + + /* Set OID offset for Area ID. */ + offset = name + v->namelen; + + /* Lookup area first. */ + oid2in_addr (offset, IN_ADDR_SIZE, area_id); + area = ospf_area_lookup_by_area_id (ospf, *area_id); + if (! area) + return NULL; + offset += IN_ADDR_SIZE; + + /* Type. */ + *type = *offset; + offset++; + + /* LS ID. */ + oid2in_addr (offset, IN_ADDR_SIZE, ls_id); + offset += IN_ADDR_SIZE; + + /* Router ID. */ + oid2in_addr (offset, IN_ADDR_SIZE, router_id); + + /* Lookup LSDB. */ + return ospf_lsdb_lookup_by_id (area->lsdb, *type, *ls_id, *router_id); + } + else + { + /* Get variable length. */ + offset = name + v->namelen; + offsetlen = *length - v->namelen; + len = offsetlen; + + if (len > (int)IN_ADDR_SIZE) + len = IN_ADDR_SIZE; + + oid2in_addr (offset, len, area_id); + + /* First we search area. */ + if (len == IN_ADDR_SIZE) + area = ospf_area_lookup_by_area_id (ospf, *area_id); + else + area = ospf_area_lookup_next (ospf, area_id, 1); + + if (area == NULL) + return NULL; + + do + { + /* Next we lookup type. */ + offset += len; + offsetlen -= len; + len = offsetlen; + + if (len <= 0) + type_next = 1; + else + { + len = 1; + type_next = 0; + *type = *offset; + } + + /* LS ID. */ + offset++; + offsetlen--; + len = offsetlen; + + if (len <= 0) + ls_id_next = 1; + else + { + ls_id_next = 0; + if (len > (int)IN_ADDR_SIZE) + len = IN_ADDR_SIZE; + + oid2in_addr (offset, len, ls_id); + } + + /* Router ID. */ + offset += IN_ADDR_SIZE; + offsetlen -= IN_ADDR_SIZE; + len = offsetlen; + + if (len <= 0) + router_id_next = 1; + else + { + router_id_next = 0; + if (len > (int)IN_ADDR_SIZE) + len = IN_ADDR_SIZE; + + oid2in_addr (offset, len, router_id); + } + + lsa = lsdb_lookup_next (area, type, type_next, ls_id, ls_id_next, + router_id, router_id_next); + + if (lsa) + { + /* Fill in length. */ + *length = v->namelen + OSPF_LSDB_ENTRY_OFFSET; + + /* Fill in value. */ + offset = name + v->namelen; + oid_copy_addr (offset, area_id, IN_ADDR_SIZE); + offset += IN_ADDR_SIZE; + *offset = lsa->data->type; + offset++; + oid_copy_addr (offset, &lsa->data->id, IN_ADDR_SIZE); + offset += IN_ADDR_SIZE; + oid_copy_addr (offset, &lsa->data->adv_router, IN_ADDR_SIZE); + + return lsa; + } + } + while ((area = ospf_area_lookup_next (ospf, area_id, 0)) != NULL); + } + return NULL; +} + +static u_char * +ospfLsdbEntry (struct variable *v, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct ospf_lsa *lsa; + struct lsa_header *lsah; + struct in_addr area_id; + u_char type; + struct in_addr ls_id; + 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 } */ + + memset (&area_id, 0, sizeof (struct in_addr)); + type = 0; + memset (&ls_id, 0, sizeof (struct in_addr)); + memset (&router_id, 0, sizeof (struct in_addr)); + + /* Check OSPF instance. */ + ospf = ospf_lookup (); + if (ospf == NULL) + return NULL; + + lsa = ospfLsdbLookup (v, name, length, &area_id, &type, &ls_id, &router_id, + exact); + if (! lsa) + return NULL; + + lsah = lsa->data; + + /* Return the current value of the variable */ + switch (v->magic) + { + case OSPFLSDBAREAID: /* 1 */ + return SNMP_IPADDRESS (lsa->area->area_id); + break; + case OSPFLSDBTYPE: /* 2 */ + return SNMP_INTEGER (lsah->type); + break; + case OSPFLSDBLSID: /* 3 */ + return SNMP_IPADDRESS (lsah->id); + break; + case OSPFLSDBROUTERID: /* 4 */ + return SNMP_IPADDRESS (lsah->adv_router); + break; + case OSPFLSDBSEQUENCE: /* 5 */ + return SNMP_INTEGER (lsah->ls_seqnum); + break; + case OSPFLSDBAGE: /* 6 */ + return SNMP_INTEGER (lsah->ls_age); + break; + case OSPFLSDBCHECKSUM: /* 7 */ + return SNMP_INTEGER (lsah->checksum); + break; + case OSPFLSDBADVERTISEMENT: /* 8 */ + *var_len = ntohs (lsah->length); + return (u_char *) lsah; + break; + default: + return NULL; + break; + } + return NULL; +} + +static struct ospf_area_range * +ospfAreaRangeLookup (struct variable *v, oid *name, size_t *length, + struct in_addr *area_id, struct in_addr *range_net, + int exact) +{ + oid *offset; + int offsetlen; + int len; + struct ospf *ospf; + struct ospf_area *area; + struct ospf_area_range *range; + struct prefix_ipv4 p; + p.family = AF_INET; + p.prefixlen = IPV4_MAX_BITLEN; + + ospf = ospf_lookup (); + + if (exact) + { + /* Area ID + Range Network. */ + if (v->namelen + IN_ADDR_SIZE + IN_ADDR_SIZE != *length) + return NULL; + + /* Set OID offset for Area ID. */ + offset = name + v->namelen; + + /* Lookup area first. */ + oid2in_addr (offset, IN_ADDR_SIZE, area_id); + + area = ospf_area_lookup_by_area_id (ospf, *area_id); + if (! area) + return NULL; + + offset += IN_ADDR_SIZE; + + /* Lookup area range. */ + oid2in_addr (offset, IN_ADDR_SIZE, range_net); + p.prefix = *range_net; + + return ospf_area_range_lookup (area, &p); + } + else + { + /* Set OID offset for Area ID. */ + offset = name + v->namelen; + offsetlen = *length - v->namelen; + + len = offsetlen; + if (len > (int)IN_ADDR_SIZE) + len = IN_ADDR_SIZE; + + oid2in_addr (offset, len, area_id); + + /* First we search area. */ + 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); + + if (area == NULL) + return NULL; + + do + { + offset += IN_ADDR_SIZE; + offsetlen -= IN_ADDR_SIZE; + len = offsetlen; + + if (len < 0) + len = 0; + if (len > (int)IN_ADDR_SIZE) + len = IN_ADDR_SIZE; + + oid2in_addr (offset, len, range_net); + + range = ospf_area_range_lookup_next (area, range_net, + len == 0 ? 1 : 0); + + if (range) + { + /* Fill in length. */ + *length = v->namelen + IN_ADDR_SIZE + IN_ADDR_SIZE; + + /* Fill in value. */ + offset = name + v->namelen; + oid_copy_addr (offset, area_id, IN_ADDR_SIZE); + offset += IN_ADDR_SIZE; + oid_copy_addr (offset, range_net, IN_ADDR_SIZE); + + return range; + } + } + while ((area = ospf_area_lookup_next (ospf, area_id, 0)) != NULL); + } + return NULL; +} + +static u_char * +ospfAreaRangeEntry (struct variable *v, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct ospf_area_range *range; + struct in_addr area_id; + struct in_addr range_net; + 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) + return NULL; + + memset (&area_id, 0, IN_ADDR_SIZE); + memset (&range_net, 0, IN_ADDR_SIZE); + + range = ospfAreaRangeLookup (v, name, length, &area_id, &range_net, exact); + if (! range) + return NULL; + + /* Convert prefixlen to network mask format. */ + masklen2ip (range->subst_masklen, &mask); + + /* Return the current value of the variable */ + switch (v->magic) + { + case OSPFAREARANGEAREAID: /* 1 */ + return SNMP_IPADDRESS (area_id); + break; + case OSPFAREARANGENET: /* 2 */ + return SNMP_IPADDRESS (range_net); + break; + case OSPFAREARANGEMASK: /* 3 */ + return SNMP_IPADDRESS (mask); + break; + case OSPFAREARANGESTATUS: /* 4 */ + return SNMP_INTEGER (SNMP_VALID); + break; + case OSPFAREARANGEEFFECT: /* 5 */ +#define OSPF_advertiseMatching 1 +#define OSPF_doNotAdvertiseMatching 2 + return SNMP_INTEGER (OSPF_advertiseMatching); + break; + default: + return NULL; + break; + } + return NULL; +} + +static struct ospf_nbr_nbma * +ospfHostLookup (struct variable *v, oid *name, size_t *length, + struct in_addr *addr, int exact) +{ + int len; + struct ospf_nbr_nbma *nbr_nbma; + struct ospf *ospf; + + ospf = ospf_lookup (); + if (ospf == NULL) + return NULL; + + if (exact) + { + /* INDEX { ospfHostIpAddress, ospfHostTOS } */ + if (*length != v->namelen + IN_ADDR_SIZE + 1) + return NULL; + + /* Check ospfHostTOS. */ + if (name[*length - 1] != 0) + return NULL; + + oid2in_addr (name + v->namelen, IN_ADDR_SIZE, addr); + + nbr_nbma = ospf_nbr_nbma_lookup (ospf, *addr); + + return nbr_nbma; + } + else + { + len = *length - v->namelen; + if (len > 4) + len = 4; + + oid2in_addr (name + v->namelen, len, addr); + + nbr_nbma = ospf_nbr_nbma_lookup_next (ospf, addr, len == 0 ? 1 : 0); + + if (nbr_nbma == NULL) + return NULL; + + oid_copy_addr (name + v->namelen, addr, IN_ADDR_SIZE); + + /* Set TOS 0. */ + name[v->namelen + IN_ADDR_SIZE] = 0; + + *length = v->namelen + IN_ADDR_SIZE + 1; + + return nbr_nbma; + } + return NULL; +} + +static u_char * +ospfHostEntry (struct variable *v, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct ospf_nbr_nbma *nbr_nbma; + struct ospf_interface *oi; + 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) + return NULL; + + memset (&addr, 0, sizeof (struct in_addr)); + + nbr_nbma = ospfHostLookup (v, name, length, &addr, exact); + if (nbr_nbma == NULL) + return NULL; + + oi = nbr_nbma->oi; + + /* Return the current value of the variable */ + switch (v->magic) + { + case OSPFHOSTIPADDRESS: /* 1 */ + return SNMP_IPADDRESS (nbr_nbma->addr); + break; + case OSPFHOSTTOS: /* 2 */ + return SNMP_INTEGER (0); + break; + case OSPFHOSTMETRIC: /* 3 */ + if (oi) + return SNMP_INTEGER (oi->output_cost); + else + return SNMP_INTEGER (1); + break; + case OSPFHOSTSTATUS: /* 4 */ + return SNMP_INTEGER (SNMP_VALID); + break; + case OSPFHOSTAREAID: /* 5 */ + if (oi && oi->area) + return SNMP_IPADDRESS (oi->area->area_id); + else + return SNMP_IPADDRESS (ospf_empty_addr); + break; + default: + return NULL; + break; + } + return NULL; +} + +struct list *ospf_snmp_iflist; + +struct ospf_snmp_if +{ + struct in_addr addr; + ifindex_t ifindex; + struct interface *ifp; +}; + +static struct ospf_snmp_if * +ospf_snmp_if_new (void) +{ + return XCALLOC (MTYPE_TMP, sizeof (struct ospf_snmp_if)); +} + +static void +ospf_snmp_if_free (struct ospf_snmp_if *osif) +{ + XFREE (MTYPE_TMP, osif); +} + +void +ospf_snmp_if_delete (struct interface *ifp) +{ + struct listnode *node, *nnode; + struct ospf_snmp_if *osif; + + for (ALL_LIST_ELEMENTS (ospf_snmp_iflist, node, nnode, osif)) + { + if (osif->ifp == ifp) + { + list_delete_node (ospf_snmp_iflist, node); + ospf_snmp_if_free (osif); + return; + } + } +} + +void +ospf_snmp_if_update (struct interface *ifp) +{ + struct listnode *node; + struct listnode *pn; + struct connected *ifc; + struct prefix *p; + struct ospf_snmp_if *osif; + struct in_addr *addr; + ifindex_t ifindex; + + ospf_snmp_if_delete (ifp); + + p = NULL; + addr = NULL; + ifindex = 0; + + /* Lookup first IPv4 address entry. */ + for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, ifc)) + { + p = CONNECTED_ID(ifc); + + if (p->family == AF_INET) + { + addr = &p->u.prefix4; + break; + } + } + if (! addr) + ifindex = ifp->ifindex; + + /* Add interface to the list. */ + pn = NULL; + for (ALL_LIST_ELEMENTS_RO (ospf_snmp_iflist, node, osif)) + { + if (addr) + { + /* Usual interfaces --> Sort them based on interface IPv4 addresses */ + if (ntohl (osif->addr.s_addr) > ntohl (addr->s_addr)) + break; + } + else + { + /* Unnumbered interfaces --> Sort them based on interface indexes */ + if (osif->addr.s_addr != 0 || osif->ifindex > ifindex) + break; + } + pn = node; + } + + osif = ospf_snmp_if_new (); + if (addr) /* Usual interface */ + { + osif->addr = *addr; + + /* This field is used for storing ospfAddressLessIf OID value, + * conform to RFC1850 OSPF-MIB specification, it must be 0 for + * usual interface */ + osif->ifindex = 0; + } + else /* Unnumbered interface */ + osif->ifindex = ifindex; + osif->ifp = ifp; + + listnode_add_after (ospf_snmp_iflist, pn, osif); +} + +static int +ospf_snmp_is_if_have_addr (struct interface *ifp) +{ + struct listnode *nn; + struct connected *ifc; + + /* Is this interface having any connected IPv4 address ? */ + for (ALL_LIST_ELEMENTS_RO (ifp->connected, nn, ifc)) + { + if (CONNECTED_PREFIX(ifc)->family == AF_INET) + return 1; + } + + return 0; +} + +static struct ospf_interface * +ospf_snmp_if_lookup (struct in_addr *ifaddr, ifindex_t *ifindex) +{ + struct listnode *node; + struct ospf_snmp_if *osif; + struct ospf_interface *oi = NULL; + struct ospf *ospf = ospf_lookup (); + + for (ALL_LIST_ELEMENTS_RO (ospf_snmp_iflist, node, osif)) + { + if (ifaddr->s_addr) + { + if (IPV4_ADDR_SAME (&osif->addr, ifaddr)) + oi = ospf_if_lookup_by_local_addr (ospf, osif->ifp, *ifaddr); + } + else + { + if (osif->ifindex == *ifindex) + oi = ospf_if_lookup_by_local_addr (ospf, osif->ifp, *ifaddr); + } + } + return oi; +} + +static struct ospf_interface * +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; + struct ospf *ospf = ospf_lookup (); + struct ospf_interface *oi = NULL; + + if (ospf == NULL) + return NULL; + + /* No instance is specified --> Return the first OSPF interface */ + if (ifaddr_next) + { + for (ALL_LIST_ELEMENTS_RO (ospf_snmp_iflist, nn, osif)) + { + osif = listgetdata (nn); + *ifaddr = osif->addr; + *ifindex = osif->ifindex; + /* Because no instance is specified, we don't care about the kind of + * interface (usual or unnumbered), just returning the first valid + * OSPF interface */ + oi = ospf_if_lookup_by_local_addr (ospf, osif->ifp, *ifaddr); + if (oi) + return (oi); + } + return NULL; + } + + /* An instance is specified --> Return the next OSPF interface */ + for (ALL_LIST_ELEMENTS_RO (ospf_snmp_iflist, nn, osif)) + { + /* Usual interface */ + if (ifaddr->s_addr) + { + /* The interface must have valid AF_INET connected address */ + /* it must have lager IPv4 address value than the lookup entry */ + if ((ospf_snmp_is_if_have_addr(osif->ifp)) && + (ntohl (osif->addr.s_addr) > ntohl (ifaddr->s_addr))) + { + *ifaddr = osif->addr; + *ifindex = osif->ifindex; + + /* and it must be an OSPF interface */ + oi = ospf_if_lookup_by_local_addr (ospf, osif->ifp, *ifaddr); + if (oi) + return oi; + } + } + /* Unnumbered interface */ + else + /* The interface must NOT have valid AF_INET connected address */ + /* it must have lager interface index than the lookup entry */ + if ((!ospf_snmp_is_if_have_addr(osif->ifp)) && + (osif->ifindex > *ifindex)) + { + *ifaddr = osif->addr; + *ifindex = osif->ifindex; + + /* and it must be an OSPF interface */ + oi = ospf_if_lookup_by_local_addr (ospf, osif->ifp, *ifaddr); + if (oi) + return oi; + } + } + return NULL; +} + +static int +ospf_snmp_iftype (struct interface *ifp) +{ +#define ospf_snmp_iftype_broadcast 1 +#define ospf_snmp_iftype_nbma 2 +#define ospf_snmp_iftype_pointToPoint 3 +#define ospf_snmp_iftype_pointToMultipoint 5 + if (if_is_broadcast (ifp)) + return ospf_snmp_iftype_broadcast; + if (if_is_pointopoint (ifp)) + return ospf_snmp_iftype_pointToPoint; + return ospf_snmp_iftype_broadcast; +} + +static struct ospf_interface * +ospfIfLookup (struct variable *v, oid *name, size_t *length, + struct in_addr *ifaddr, ifindex_t *ifindex, int exact) +{ + unsigned int len; + int ifaddr_next = 0; + ifindex_t ifindex_next = 0; + struct ospf_interface *oi; + oid *offset; + + if (exact) + { + if (*length != v->namelen + IN_ADDR_SIZE + 1) + return NULL; + + oid2in_addr (name + v->namelen, IN_ADDR_SIZE, ifaddr); + *ifindex = name[v->namelen + IN_ADDR_SIZE]; + + return ospf_snmp_if_lookup (ifaddr, ifindex); + } + else + { + len = *length - v->namelen; + if (len >= IN_ADDR_SIZE) + len = IN_ADDR_SIZE; + if (len <= 0) + ifaddr_next = 1; + + oid2in_addr (name + v->namelen, len, ifaddr); + + len = *length - v->namelen - IN_ADDR_SIZE; + if (len >= 1) + len = 1; + else + ifindex_next = 1; + + if (len == 1) + *ifindex = name[v->namelen + IN_ADDR_SIZE]; + + oi = ospf_snmp_if_lookup_next (ifaddr, ifindex, ifaddr_next, + ifindex_next); + if (oi) + { + *length = v->namelen + IN_ADDR_SIZE + 1; + offset = name + v->namelen; + oid_copy_addr (offset, ifaddr, IN_ADDR_SIZE); + offset += IN_ADDR_SIZE; + *offset = *ifindex; + return oi; + } + } + return NULL; +} + +static u_char * +ospfIfEntry (struct variable *v, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + 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)); + + /* Check OSPF instance. */ + ospf = ospf_lookup (); + if (ospf == NULL) + return NULL; + + oi = ospfIfLookup (v, name, length, &ifaddr, &ifindex, exact); + if (oi == NULL) + return NULL; + + /* Return the current value of the variable */ + switch (v->magic) + { + case OSPFIFIPADDRESS: /* 1 */ + return SNMP_IPADDRESS (ifaddr); + break; + case OSPFADDRESSLESSIF: /* 2 */ + return SNMP_INTEGER (ifindex); + break; + case OSPFIFAREAID: /* 3 */ + if (oi->area) + return SNMP_IPADDRESS (oi->area->area_id); + else + return SNMP_IPADDRESS (ospf_empty_addr); + break; + case OSPFIFTYPE: /* 4 */ + return SNMP_INTEGER (ospf_snmp_iftype (oi->ifp)); + break; + case OSPFIFADMINSTAT: /* 5 */ + if (oi) + return SNMP_INTEGER (OSPF_STATUS_ENABLED); + else + return SNMP_INTEGER (OSPF_STATUS_DISABLED); + break; + case OSPFIFRTRPRIORITY: /* 6 */ + return SNMP_INTEGER (PRIORITY (oi)); + break; + case OSPFIFTRANSITDELAY: /* 7 */ + return SNMP_INTEGER (OSPF_IF_PARAM (oi, transmit_delay)); + break; + case OSPFIFRETRANSINTERVAL: /* 8 */ + return SNMP_INTEGER (OSPF_IF_PARAM (oi, retransmit_interval)); + break; + case OSPFIFHELLOINTERVAL: /* 9 */ + return SNMP_INTEGER (OSPF_IF_PARAM (oi, v_hello)); + break; + case OSPFIFRTRDEADINTERVAL: /* 10 */ + return SNMP_INTEGER (OSPF_IF_PARAM (oi, v_wait)); + break; + case OSPFIFPOLLINTERVAL: /* 11 */ + return SNMP_INTEGER (OSPF_POLL_INTERVAL_DEFAULT); + break; + case OSPFIFSTATE: /* 12 */ + return SNMP_INTEGER (ISM_SNMP(oi->state)); + break; + case OSPFIFDESIGNATEDROUTER: /* 13 */ + return SNMP_IPADDRESS (DR (oi)); + break; + case OSPFIFBACKUPDESIGNATEDROUTER: /* 14 */ + return SNMP_IPADDRESS (BDR (oi)); + break; + case OSPFIFEVENTS: /* 15 */ + return SNMP_INTEGER (oi->state_change); + break; + case OSPFIFAUTHKEY: /* 16 */ + *var_len = 0; + return (u_char *) OSPF_IF_PARAM (oi, auth_simple); + break; + case OSPFIFSTATUS: /* 17 */ + return SNMP_INTEGER (SNMP_VALID); + break; + case OSPFIFMULTICASTFORWARDING: /* 18 */ +#define ospf_snmp_multiforward_blocked 1 +#define ospf_snmp_multiforward_multicast 2 +#define ospf_snmp_multiforward_unicast 3 + return SNMP_INTEGER (ospf_snmp_multiforward_blocked); + break; + case OSPFIFDEMAND: /* 19 */ + return SNMP_INTEGER (SNMP_FALSE); + break; + case OSPFIFAUTHTYPE: /* 20 */ + if (oi->area) + return SNMP_INTEGER (oi->area->auth_type); + else + return SNMP_INTEGER (0); + break; + default: + return NULL; + break; + } + return NULL; +} + +#define OSPF_SNMP_METRIC_VALUE 1 + +static struct ospf_interface * +ospfIfMetricLookup (struct variable *v, oid *name, size_t *length, + struct in_addr *ifaddr, ifindex_t *ifindex, int exact) +{ + unsigned int len; + int ifaddr_next = 0; + ifindex_t ifindex_next = 0; + struct ospf_interface *oi; + oid *offset; + int metric; + + if (exact) + { + if (*length != v->namelen + IN_ADDR_SIZE + 1 + 1) + return NULL; + + oid2in_addr (name + v->namelen, IN_ADDR_SIZE, ifaddr); + *ifindex = name[v->namelen + IN_ADDR_SIZE]; + metric = name[v->namelen + IN_ADDR_SIZE + 1]; + + if (metric != OSPF_SNMP_METRIC_VALUE) + return NULL; + + return ospf_snmp_if_lookup (ifaddr, ifindex); + } + else + { + len = *length - v->namelen; + if (len >= IN_ADDR_SIZE) + len = IN_ADDR_SIZE; + else + ifaddr_next = 1; + + oid2in_addr (name + v->namelen, len, ifaddr); + + len = *length - v->namelen - IN_ADDR_SIZE; + if (len >= 1) + len = 1; + else + ifindex_next = 1; + + if (len == 1) + *ifindex = name[v->namelen + IN_ADDR_SIZE]; + + oi = ospf_snmp_if_lookup_next (ifaddr, ifindex, ifaddr_next, + ifindex_next); + if (oi) + { + *length = v->namelen + IN_ADDR_SIZE + 1 + 1; + offset = name + v->namelen; + oid_copy_addr (offset, ifaddr, IN_ADDR_SIZE); + offset += IN_ADDR_SIZE; + *offset = *ifindex; + offset++; + *offset = OSPF_SNMP_METRIC_VALUE; + return oi; + } + } + return NULL; +} + +static u_char * +ospfIfMetricEntry (struct variable *v, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + /* Currently we support metric 1 only. */ + 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)); + + /* Check OSPF instance. */ + ospf = ospf_lookup (); + if (ospf == NULL) + return NULL; + + oi = ospfIfMetricLookup (v, name, length, &ifaddr, &ifindex, exact); + if (oi == NULL) + return NULL; + + /* Return the current value of the variable */ + switch (v->magic) + { + case OSPFIFMETRICIPADDRESS: + return SNMP_IPADDRESS (ifaddr); + break; + case OSPFIFMETRICADDRESSLESSIF: + return SNMP_INTEGER (ifindex); + break; + case OSPFIFMETRICTOS: + return SNMP_INTEGER (0); + break; + case OSPFIFMETRICVALUE: + return SNMP_INTEGER (OSPF_SNMP_METRIC_VALUE); + break; + case OSPFIFMETRICSTATUS: + return SNMP_INTEGER (1); + break; + default: + return NULL; + break; + } + return NULL; +} + +struct route_table *ospf_snmp_vl_table; + +void +ospf_snmp_vl_add (struct ospf_vl_data *vl_data) +{ + struct prefix_ls lp; + struct route_node *rn; + + memset (&lp, 0, sizeof (struct prefix_ls)); + lp.family = 0; + lp.prefixlen = 64; + lp.id = vl_data->vl_area_id; + 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; +} + +void +ospf_snmp_vl_delete (struct ospf_vl_data *vl_data) +{ + struct prefix_ls lp; + struct route_node *rn; + + memset (&lp, 0, sizeof (struct prefix_ls)); + lp.family = 0; + lp.prefixlen = 64; + lp.id = vl_data->vl_area_id; + lp.adv_router = vl_data->vl_peer; + + rn = route_node_lookup (ospf_snmp_vl_table, (struct prefix *) &lp); + if (! rn) + return; + rn->info = NULL; + route_unlock_node (rn); + route_unlock_node (rn); +} + +static struct ospf_vl_data * +ospf_snmp_vl_lookup (struct in_addr *area_id, struct in_addr *neighbor) +{ + struct prefix_ls lp; + struct route_node *rn; + struct ospf_vl_data *vl_data; + + memset (&lp, 0, sizeof (struct prefix_ls)); + lp.family = 0; + lp.prefixlen = 64; + lp.id = *area_id; + lp.adv_router = *neighbor; + + rn = route_node_lookup (ospf_snmp_vl_table, (struct prefix *) &lp); + if (rn) + { + vl_data = rn->info; + route_unlock_node (rn); + return vl_data; + } + return NULL; +} + +static struct ospf_vl_data * +ospf_snmp_vl_lookup_next (struct in_addr *area_id, struct in_addr *neighbor, + int first) +{ + struct prefix_ls lp; + struct route_node *rn; + struct ospf_vl_data *vl_data; + + memset (&lp, 0, sizeof (struct prefix_ls)); + lp.family = 0; + lp.prefixlen = 64; + lp.id = *area_id; + lp.adv_router = *neighbor; + + if (first) + rn = route_top (ospf_snmp_vl_table); + else + { + rn = route_node_get (ospf_snmp_vl_table, (struct prefix *) &lp); + rn = route_next (rn); + } + + for (; rn; rn = route_next (rn)) + if (rn->info) + break; + + if (rn && rn->info) + { + vl_data = rn->info; + *area_id = vl_data->vl_area_id; + *neighbor = vl_data->vl_peer; + route_unlock_node (rn); + return vl_data; + } + return NULL; +} + +static struct ospf_vl_data * +ospfVirtIfLookup (struct variable *v, oid *name, size_t *length, + struct in_addr *area_id, struct in_addr *neighbor, int exact) +{ + int first; + unsigned int len; + struct ospf_vl_data *vl_data; + + if (exact) + { + if (*length != v->namelen + IN_ADDR_SIZE + IN_ADDR_SIZE) + return NULL; + + oid2in_addr (name + v->namelen, IN_ADDR_SIZE, area_id); + oid2in_addr (name + v->namelen + IN_ADDR_SIZE, IN_ADDR_SIZE, neighbor); + + return ospf_snmp_vl_lookup (area_id, neighbor); + } + else + { + first = 0; + + len = *length - v->namelen; + if (len <= 0) + first = 1; + if (len > IN_ADDR_SIZE) + len = IN_ADDR_SIZE; + oid2in_addr (name + v->namelen, len, area_id); + + len = *length - v->namelen - IN_ADDR_SIZE; + if (len > IN_ADDR_SIZE) + len = IN_ADDR_SIZE; + oid2in_addr (name + v->namelen + IN_ADDR_SIZE, len, neighbor); + + vl_data = ospf_snmp_vl_lookup_next (area_id, neighbor, first); + + if (vl_data) + { + *length = v->namelen + IN_ADDR_SIZE + IN_ADDR_SIZE; + oid_copy_addr (name + v->namelen, area_id, IN_ADDR_SIZE); + oid_copy_addr (name + v->namelen + IN_ADDR_SIZE, neighbor, + IN_ADDR_SIZE); + return vl_data; + } + } + return NULL; +} + +static u_char * +ospfVirtIfEntry (struct variable *v, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct ospf_vl_data *vl_data; + struct ospf_interface *oi; + 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)); + + vl_data = ospfVirtIfLookup (v, name, length, &area_id, &neighbor, exact); + if (! vl_data) + return NULL; + oi = vl_data->vl_oi; + if (! oi) + return NULL; + + /* Return the current value of the variable */ + switch (v->magic) + { + case OSPFVIRTIFAREAID: + return SNMP_IPADDRESS (area_id); + break; + case OSPFVIRTIFNEIGHBOR: + return SNMP_IPADDRESS (neighbor); + break; + case OSPFVIRTIFTRANSITDELAY: + return SNMP_INTEGER (OSPF_IF_PARAM (oi, transmit_delay)); + break; + case OSPFVIRTIFRETRANSINTERVAL: + return SNMP_INTEGER (OSPF_IF_PARAM (oi, retransmit_interval)); + break; + case OSPFVIRTIFHELLOINTERVAL: + return SNMP_INTEGER (OSPF_IF_PARAM (oi, v_hello)); + break; + case OSPFVIRTIFRTRDEADINTERVAL: + return SNMP_INTEGER (OSPF_IF_PARAM (oi, v_wait)); + break; + case OSPFVIRTIFSTATE: + return SNMP_INTEGER (oi->state); + break; + case OSPFVIRTIFEVENTS: + return SNMP_INTEGER (oi->state_change); + break; + case OSPFVIRTIFAUTHKEY: + *var_len = 0; + return (u_char *) OSPF_IF_PARAM (oi, auth_simple); + break; + case OSPFVIRTIFSTATUS: + return SNMP_INTEGER (SNMP_VALID); + break; + case OSPFVIRTIFAUTHTYPE: + if (oi->area) + return SNMP_INTEGER (oi->area->auth_type); + else + return SNMP_INTEGER (0); + break; + default: + return NULL; + break; + } + return NULL; +} + +static struct ospf_neighbor * +ospf_snmp_nbr_lookup (struct ospf *ospf, struct in_addr *nbr_addr, + ifindex_t *ifindex) +{ + struct listnode *node, *nnode; + struct ospf_interface *oi; + struct ospf_neighbor *nbr; + struct route_node *rn; + + for (ALL_LIST_ELEMENTS (ospf->oiflist, node, nnode, oi)) + { + for (rn = route_top (oi->nbrs); rn; rn = route_next (rn)) + if ((nbr = rn->info) != NULL + && nbr != oi->nbr_self +/* If EXACT match is needed, provide ALL entry found + && nbr->state != NSM_Down + */ + && nbr->src.s_addr != 0) + { + if (IPV4_ADDR_SAME (&nbr->src, nbr_addr)) + { + route_unlock_node (rn); + return nbr; + } + } + } + return NULL; +} + +static struct ospf_neighbor * +ospf_snmp_nbr_lookup_next (struct in_addr *nbr_addr, ifindex_t *ifindex, + int first) +{ + struct listnode *nn; + struct ospf_interface *oi; + struct ospf_neighbor *nbr; + struct route_node *rn; + struct ospf_neighbor *min = NULL; + struct ospf *ospf = ospf; + + ospf = ospf_lookup (); + + for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, nn, oi)) + { + for (rn = route_top (oi->nbrs); rn; rn = route_next (rn)) + if ((nbr = rn->info) != NULL + && nbr != oi->nbr_self + && nbr->state != NSM_Down + && nbr->src.s_addr != 0) + { + if (first) + { + if (! min) + min = nbr; + else if (ntohl (nbr->src.s_addr) < ntohl (min->src.s_addr)) + min = nbr; + } + else if (ntohl (nbr->src.s_addr) > ntohl (nbr_addr->s_addr)) + { + if (! min) + min = nbr; + else if (ntohl (nbr->src.s_addr) < ntohl (min->src.s_addr)) + min = nbr; + } + } + } + if (min) + { + *nbr_addr = min->src; + *ifindex = 0; + return min; + } + return NULL; +} + +static struct ospf_neighbor * +ospfNbrLookup (struct variable *v, oid *name, size_t *length, + struct in_addr *nbr_addr, ifindex_t *ifindex, int exact) +{ + unsigned int len; + int first; + struct ospf_neighbor *nbr; + struct ospf *ospf; + + ospf = ospf_lookup (); + + if (! ospf) + return NULL; + + if (exact) + { + if (*length != v->namelen + IN_ADDR_SIZE + 1) + return NULL; + + oid2in_addr (name + v->namelen, IN_ADDR_SIZE, nbr_addr); + *ifindex = name[v->namelen + IN_ADDR_SIZE]; + + return ospf_snmp_nbr_lookup (ospf, nbr_addr, ifindex); + } + else + { + first = 0; + len = *length - v->namelen; + + if (len <= 0) + first = 1; + + if (len > IN_ADDR_SIZE) + len = IN_ADDR_SIZE; + + oid2in_addr (name + v->namelen, len, nbr_addr); + + len = *length - v->namelen - IN_ADDR_SIZE; + if (len >= 1) + *ifindex = name[v->namelen + IN_ADDR_SIZE]; + + nbr = ospf_snmp_nbr_lookup_next (nbr_addr, ifindex, first); + + if (nbr) + { + *length = v->namelen + IN_ADDR_SIZE + 1; + oid_copy_addr (name + v->namelen, nbr_addr, IN_ADDR_SIZE); + name[v->namelen + IN_ADDR_SIZE] = *ifindex; + return nbr; + } + } + return NULL; +} + +/* map internal quagga neighbor states to official MIB values: + +ospfNbrState OBJECT-TYPE + SYNTAX INTEGER { + down (1), + attempt (2), + init (3), + twoWay (4), + exchangeStart (5), + exchange (6), + loading (7), + full (8) + } +*/ +static int32_t +ospf_snmp_neighbor_state(u_char nst) +{ + switch (nst) + { + case NSM_Attempt: + return 2; + case NSM_Init: + return 3; + case NSM_TwoWay: + return 4; + case NSM_ExStart: + return 5; + case NSM_Exchange: + return 6; + case NSM_Loading: + return 7; + case NSM_Full: + return 8; + default: + return 1; /* down */ + } +} + +static u_char * +ospfNbrEntry (struct variable *v, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct in_addr nbr_addr; + 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; + + nbr = ospfNbrLookup (v, name, length, &nbr_addr, &ifindex, exact); + if (! nbr) + return NULL; + oi = nbr->oi; + if (! oi) + return NULL; + + /* Return the current value of the variable */ + switch (v->magic) + { + case OSPFNBRIPADDR: + return SNMP_IPADDRESS (nbr_addr); + break; + case OSPFNBRADDRESSLESSINDEX: + return SNMP_INTEGER (ifindex); + break; + case OSPFNBRRTRID: + return SNMP_IPADDRESS (nbr->router_id); + break; + case OSPFNBROPTIONS: + return SNMP_INTEGER (oi->nbr_self->options); + break; + case OSPFNBRPRIORITY: + return SNMP_INTEGER (nbr->priority); + break; + case OSPFNBRSTATE: + return SNMP_INTEGER (ospf_snmp_neighbor_state(nbr->state)); + break; + case OSPFNBREVENTS: + return SNMP_INTEGER (nbr->state_change); + break; + case OSPFNBRLSRETRANSQLEN: + return SNMP_INTEGER (ospf_ls_retransmit_count (nbr)); + break; + case OSPFNBMANBRSTATUS: + return SNMP_INTEGER (SNMP_VALID); + break; + case OSPFNBMANBRPERMANENCE: + return SNMP_INTEGER (2); + break; + case OSPFNBRHELLOSUPPRESSED: + return SNMP_INTEGER (SNMP_FALSE); + break; + default: + return NULL; + break; + } + return NULL; +} + +static u_char * +ospfVirtNbrEntry (struct variable *v, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct ospf_vl_data *vl_data; + struct in_addr area_id; + 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)); + + /* Check OSPF instance. */ + ospf = ospf_lookup (); + if (ospf == NULL) + return NULL; + + vl_data = ospfVirtIfLookup (v, name, length, &area_id, &neighbor, exact); + if (! vl_data) + return NULL; + + /* Return the current value of the variable */ + switch (v->magic) + { + case OSPFVIRTNBRAREA: + return (u_char *) NULL; + break; + case OSPFVIRTNBRRTRID: + return (u_char *) NULL; + break; + case OSPFVIRTNBRIPADDR: + return (u_char *) NULL; + break; + case OSPFVIRTNBROPTIONS: + return (u_char *) NULL; + break; + case OSPFVIRTNBRSTATE: + return (u_char *) NULL; + break; + case OSPFVIRTNBREVENTS: + return (u_char *) NULL; + break; + case OSPFVIRTNBRLSRETRANSQLEN: + return (u_char *) NULL; + break; + case OSPFVIRTNBRHELLOSUPPRESSED: + return (u_char *) NULL; + break; + default: + return NULL; + break; + } + 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) +{ + int first; + oid *offset; + int offsetlen; + u_char lsa_type; + unsigned int len; + struct ospf_lsa *lsa; + struct ospf *ospf; + + ospf = ospf_lookup (); + if (exact) + { + if (*length != v->namelen + 1 + IN_ADDR_SIZE + IN_ADDR_SIZE) + return NULL; + + offset = name + v->namelen; + + /* Make it sure given value match to type. */ + lsa_type = *offset; + offset++; + + if (lsa_type != *type) + return NULL; + + /* LS ID. */ + oid2in_addr (offset, IN_ADDR_SIZE, ls_id); + offset += IN_ADDR_SIZE; + + /* Router ID. */ + oid2in_addr (offset, IN_ADDR_SIZE, router_id); + + return ospf_lsdb_lookup_by_id (ospf->lsdb, *type, *ls_id, *router_id); + } + else + { + /* Get variable length. */ + first = 0; + offset = name + v->namelen; + offsetlen = *length - v->namelen; + + /* LSA type value. */ + lsa_type = *offset; + offset++; + offsetlen--; + + if (offsetlen <= 0 || lsa_type < OSPF_AS_EXTERNAL_LSA) + first = 1; + + /* LS ID. */ + len = offsetlen; + if (len > IN_ADDR_SIZE) + len = IN_ADDR_SIZE; + + oid2in_addr (offset, len, ls_id); + + offset += IN_ADDR_SIZE; + offsetlen -= IN_ADDR_SIZE; + + /* Router ID. */ + len = offsetlen; + if (len > IN_ADDR_SIZE) + len = IN_ADDR_SIZE; + + oid2in_addr (offset, len, router_id); + + lsa = ospf_lsdb_lookup_by_id_next (ospf->lsdb, *type, *ls_id, + *router_id, first); + + if (lsa) + { + /* Fill in length. */ + *length = v->namelen + 1 + IN_ADDR_SIZE + IN_ADDR_SIZE; + + /* Fill in value. */ + offset = name + v->namelen; + + *offset = OSPF_AS_EXTERNAL_LSA; + offset++; + oid_copy_addr (offset, &lsa->data->id, IN_ADDR_SIZE); + offset += IN_ADDR_SIZE; + oid_copy_addr (offset, &lsa->data->adv_router, IN_ADDR_SIZE); + + return lsa; + } + } + return NULL; +} + +static u_char * +ospfExtLsdbEntry (struct variable *v, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + struct ospf_lsa *lsa; + struct lsa_header *lsah; + u_char type; + struct in_addr ls_id; + 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)); + + /* Check OSPF instance. */ + ospf = ospf_lookup (); + if (ospf == NULL) + return NULL; + + lsa = ospfExtLsdbLookup (v, name, length, &type, &ls_id, &router_id, exact); + if (! lsa) + return NULL; + + lsah = lsa->data; + + /* Return the current value of the variable */ + switch (v->magic) + { + case OSPFEXTLSDBTYPE: + return SNMP_INTEGER (OSPF_AS_EXTERNAL_LSA); + break; + case OSPFEXTLSDBLSID: + return SNMP_IPADDRESS (lsah->id); + break; + case OSPFEXTLSDBROUTERID: + return SNMP_IPADDRESS (lsah->adv_router); + break; + case OSPFEXTLSDBSEQUENCE: + return SNMP_INTEGER (lsah->ls_seqnum); + break; + case OSPFEXTLSDBAGE: + return SNMP_INTEGER (lsah->ls_age); + break; + case OSPFEXTLSDBCHECKSUM: + return SNMP_INTEGER (lsah->checksum); + break; + case OSPFEXTLSDBADVERTISEMENT: + *var_len = ntohs (lsah->length); + return (u_char *) lsah; + break; + default: + return NULL; + break; + } + 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) + { + case OSPFAREAAGGREGATEAREAID: + return (u_char *) NULL; + break; + case OSPFAREAAGGREGATELSDBTYPE: + return (u_char *) NULL; + break; + case OSPFAREAAGGREGATENET: + return (u_char *) NULL; + break; + case OSPFAREAAGGREGATEMASK: + return (u_char *) NULL; + break; + case OSPFAREAAGGREGATESTATUS: + return (u_char *) NULL; + break; + case OSPFAREAAGGREGATEEFFECT: + return (u_char *) NULL; + break; + default: + return NULL; + break; + } + return NULL; +} + +/* OSPF Traps. */ +#define IFSTATECHANGE 16 +#define VIRTIFSTATECHANGE 1 +#define NBRSTATECHANGE 2 +#define VIRTNBRSTATECHANGE 3 + +struct trap_object ospfNbrTrapList[] = +{ + {-2, {1, OSPFROUTERID}}, + {3, {10, 1, OSPFNBRIPADDR}}, + {3, {10, 1, OSPFNBRRTRID}}, + {3, {10, 1, OSPFNBRSTATE}} +}; + + +struct trap_object ospfVirtNbrTrapList[] = +{ + {-2, {1, 1}}, + {3, {11, 1, OSPFVIRTNBRAREA}}, + {3, {11, 1, OSPFVIRTNBRRTRID}}, + {3, {11, 1, OSPFVIRTNBRSTATE}} +}; + +struct trap_object ospfIfTrapList[] = +{ + {-2, {1, OSPFROUTERID}}, + {3, {7, 1, OSPFIFIPADDRESS}}, + {3, {7, 1, OSPFADDRESSLESSIF}}, + {3, {7, 1, OSPFIFSTATE}} +}; + +struct trap_object ospfVirtIfTrapList[] = +{ + {-2, {1, OSPFROUTERID}}, + {3, {9, 1, OSPFVIRTIFAREAID}}, + {3, {9, 1, OSPFVIRTIFNEIGHBOR}}, + {3, {9, 1, OSPFVIRTIFSTATE}} +}; + +void +ospfTrapNbrStateChange (struct ospf_neighbor *on) +{ + oid index[sizeof (oid) * (IN_ADDR_SIZE + 1)]; + char msgbuf[16]; + + ospf_nbr_state_message(on, msgbuf, sizeof(msgbuf)); + zlog (NULL, LOG_INFO, "ospfTrapNbrStateChange trap sent: %s now %s", + inet_ntoa(on->address.u.prefix4), msgbuf); + + oid_copy_addr (index, &(on->address.u.prefix4), IN_ADDR_SIZE); + index[IN_ADDR_SIZE] = 0; + + 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), + NBRSTATECHANGE); +} + +void +ospfTrapVirtNbrStateChange (struct ospf_neighbor *on) +{ + oid index[sizeof (oid) * (IN_ADDR_SIZE + 1)]; + + zlog (NULL, LOG_INFO, "ospfTrapVirtNbrStateChange trap sent"); + + oid_copy_addr (index, &(on->address.u.prefix4), IN_ADDR_SIZE); + index[IN_ADDR_SIZE] = 0; + + 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), + VIRTNBRSTATECHANGE); +} + +void +ospfTrapIfStateChange (struct ospf_interface *oi) +{ + oid index[sizeof (oid) * (IN_ADDR_SIZE + 1)]; + + zlog (NULL, LOG_INFO, "ospfTrapIfStateChange trap sent: %s now %s", + inet_ntoa(oi->address->u.prefix4), + LOOKUP(ospf_ism_state_msg, oi->state)); + + oid_copy_addr (index, &(oi->address->u.prefix4), IN_ADDR_SIZE); + index[IN_ADDR_SIZE] = 0; + + 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), + IFSTATECHANGE); +} + +void +ospfTrapVirtIfStateChange (struct ospf_interface *oi) +{ + oid index[sizeof (oid) * (IN_ADDR_SIZE + 1)]; + + zlog (NULL, LOG_INFO, "ospfTrapVirtIfStateChange trap sent"); + + oid_copy_addr (index, &(oi->address->u.prefix4), IN_ADDR_SIZE); + index[IN_ADDR_SIZE] = 0; + + 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), + VIRTIFSTATECHANGE); +} +/* Register OSPF2-MIB. */ +void +ospf_snmp_init () +{ + ospf_snmp_iflist = list_new (); + ospf_snmp_vl_table = route_table_init (); + smux_init (om->master); + REGISTER_MIB("mibII/ospf", ospf_variables, variable, ospf_oid); +} +#endif /* HAVE_SNMP */ diff --git a/ospfd/ospf_snmp.h b/ospfd/ospf_snmp.h new file mode 100644 index 0000000..413d1d7 --- /dev/null +++ b/ospfd/ospf_snmp.h @@ -0,0 +1,38 @@ +/* OSPFv2 SNMP support + * Copyright (C) 2000 IP Infusion Inc. + * + * Written by 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_OSPF_SNMP_H +#define _ZEBRA_OSPF_SNMP_H + +extern void ospf_snmp_if_update (struct interface *); +extern void ospf_snmp_if_delete (struct interface *); + +extern void ospf_snmp_vl_add (struct ospf_vl_data *); +extern void ospf_snmp_vl_delete (struct ospf_vl_data *); + +extern void ospfTrapIfStateChange (struct ospf_interface *); +extern void ospfTrapVirtIfStateChange (struct ospf_interface *); +extern void ospfTrapNbrStateChange (struct ospf_neighbor *); +extern void ospfTrapVirtNbrStateChange (struct ospf_neighbor *); + +#endif /* _ZEBRA_OSPF_SNMP_H */ diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c new file mode 100644 index 0000000..e1c290e --- /dev/null +++ b/ospfd/ospf_spf.c @@ -0,0 +1,1490 @@ +/* OSPF SPF calculation. + Copyright (C) 1999, 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. */ + +#include + +#include "thread.h" +#include "memory.h" +#include "hash.h" +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "log.h" +#include "sockunion.h" /* for inet_ntop () */ +#include "pqueue.h" + +#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_spf.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_ia.h" +#include "ospfd/ospf_ase.h" +#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 +cmp (void * node1 , void * node2) +{ + struct vertex * v1 = (struct vertex *) node1; + struct vertex * v2 = (struct vertex *) node2; + if (v1 != NULL && v2 != NULL ) + { + /* network vertices must be chosen before router vertices of same + * cost in order to find all shortest paths + */ + if ( ((v1->distance - v2->distance) == 0) + && (v1->type != v2->type)) + { + switch (v1->type) + { + case OSPF_VERTEX_NETWORK: + return -1; + case OSPF_VERTEX_ROUTER: + return 1; + } + } + else + return (v1->distance - v2->distance); + } + return 0; +} + +static void +update_stat (void *node , int position) +{ + struct vertex *v = node; + + /* Set the status of the vertex, when its position changes. */ + *(v->stat) = position; +} + +static struct vertex_nexthop * +vertex_nexthop_new (void) +{ + return XCALLOC (MTYPE_OSPF_NEXTHOP, sizeof (struct vertex_nexthop)); +} + +static void +vertex_nexthop_free (struct vertex_nexthop *nh) +{ + XFREE (MTYPE_OSPF_NEXTHOP, nh); +} + +/* Free the canonical nexthop objects for an area, ie the nexthop objects + * attached to the first-hop router vertices, and any intervening network + * vertices. + */ +static void +ospf_canonical_nexthops_free (struct vertex *root) +{ + struct listnode *node, *nnode; + struct vertex *child; + + for (ALL_LIST_ELEMENTS (root->children, node, nnode, child)) + { + struct listnode *n2, *nn2; + struct vertex_parent *vp; + + /* router vertices through an attached network each + * have a distinct (canonical / not inherited) nexthop + * which must be freed. + * + * A network vertex can only have router vertices as its + * children, so only one level of recursion is possible. + */ + if (child->type == OSPF_VERTEX_NETWORK) + ospf_canonical_nexthops_free (child); + + /* Free child nexthops pointing back to this root vertex */ + for (ALL_LIST_ELEMENTS (child->parents, n2, nn2, vp)) + if (vp->parent == root && vp->nexthop) + vertex_nexthop_free (vp->nexthop); + } +} + +/* TODO: Parent list should be excised, in favour of maintaining only + * vertex_nexthop, with refcounts. + */ +static struct vertex_parent * +vertex_parent_new (struct vertex *v, int backlink, struct vertex_nexthop *hop) +{ + struct vertex_parent *new; + + new = XMALLOC (MTYPE_OSPF_VERTEX_PARENT, sizeof (struct vertex_parent)); + + if (new == NULL) + return NULL; + + new->parent = v; + new->backlink = backlink; + new->nexthop = hop; + return new; +} + +static void +vertex_parent_free (void *p) +{ + XFREE (MTYPE_OSPF_VERTEX_PARENT, p); +} + +static struct vertex * +ospf_vertex_new (struct ospf_lsa *lsa) +{ + struct vertex *new; + + new = XCALLOC (MTYPE_OSPF_VERTEX, sizeof (struct vertex)); + + new->flags = 0; + new->stat = &(lsa->stat); + new->type = lsa->data->type; + new->id = lsa->data->id; + new->lsa = lsa->data; + new->children = list_new (); + new->parents = list_new (); + new->parents->del = vertex_parent_free; + + listnode_add (&vertex_list, new); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("%s: Created %s vertex %s", __func__, + new->type == OSPF_VERTEX_ROUTER ? "Router" : "Network", + inet_ntoa (new->lsa->id)); + return new; +} + +static void +ospf_vertex_free (void *data) +{ + struct vertex *v = data; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("%s: Free %s vertex %s", __func__, + v->type == OSPF_VERTEX_ROUTER ? "Router" : "Network", + inet_ntoa (v->lsa->id)); + + /* There should be no parents potentially holding references to this vertex + * Children however may still be there, but presumably referenced by other + * vertices + */ + //assert (listcount (v->parents) == 0); + + if (v->children) + list_delete (v->children); + v->children = NULL; + + if (v->parents) + list_delete (v->parents); + v->parents = NULL; + + v->lsa = NULL; + + XFREE (MTYPE_OSPF_VERTEX, v); +} + +static void +ospf_vertex_dump(const char *msg, struct vertex *v, + int print_parents, int print_children) +{ + if ( ! IS_DEBUG_OSPF_EVENT) + return; + + zlog_debug("%s %s vertex %s distance %u flags %u", + msg, + v->type == OSPF_VERTEX_ROUTER ? "Router" : "Network", + inet_ntoa(v->lsa->id), + v->distance, + (unsigned int)v->flags); + + if (print_parents) + { + struct listnode *node; + struct vertex_parent *vp; + + for (ALL_LIST_ELEMENTS_RO (v->parents, node, vp)) + { + char buf1[BUFSIZ]; + + if (vp) + { + zlog_debug ("parent %s backlink %d nexthop %s interface %s", + inet_ntoa(vp->parent->lsa->id), vp->backlink, + inet_ntop(AF_INET, &vp->nexthop->router, buf1, BUFSIZ), + vp->nexthop->oi ? IF_NAME(vp->nexthop->oi) : "NULL"); + } + } + } + + if (print_children) + { + struct listnode *cnode; + struct vertex *cv; + + for (ALL_LIST_ELEMENTS_RO (v->children, cnode, cv)) + ospf_vertex_dump(" child:", cv, 0, 0); + } +} + + +/* Add a vertex to the list of children in each of its parents. */ +static void +ospf_vertex_add_parent (struct vertex *v) +{ + struct vertex_parent *vp; + struct listnode *node; + + assert (v && v->parents); + + for (ALL_LIST_ELEMENTS_RO (v->parents, node, vp)) + { + assert (vp->parent && vp->parent->children); + + /* No need to add two links from the same parent. */ + if (listnode_lookup (vp->parent->children, v) == NULL) + listnode_add (vp->parent->children, v); + } +} + +static void +ospf_spf_init (struct ospf_area *area) +{ + struct vertex *v; + + /* Create root node. */ + v = ospf_vertex_new (area->router_lsa_self); + + area->spf = v; + + /* Reset ABR and ASBR router counts. */ + area->abr_count = 0; + area->asbr_count = 0; +} + +/* return index of link back to V from W, or -1 if no link found */ +static int +ospf_lsa_has_link (struct lsa_header *w, struct lsa_header *v) +{ + unsigned int i, length; + struct router_lsa *rl; + struct network_lsa *nl; + + /* In case of W is Network LSA. */ + if (w->type == OSPF_NETWORK_LSA) + { + if (v->type == OSPF_NETWORK_LSA) + return -1; + + nl = (struct network_lsa *) w; + length = (ntohs (w->length) - OSPF_LSA_HEADER_SIZE - 4) / 4; + + for (i = 0; i < length; i++) + if (IPV4_ADDR_SAME (&nl->routers[i], &v->id)) + return i; + return -1; + } + + /* In case of W is Router LSA. */ + if (w->type == OSPF_ROUTER_LSA) + { + rl = (struct router_lsa *) w; + + length = ntohs (w->length); + + for (i = 0; + i < ntohs (rl->links) && length >= sizeof (struct router_lsa); + i++, length -= 12) + { + switch (rl->link[i].type) + { + case LSA_LINK_TYPE_POINTOPOINT: + case LSA_LINK_TYPE_VIRTUALLINK: + /* Router LSA ID. */ + if (v->type == OSPF_ROUTER_LSA && + IPV4_ADDR_SAME (&rl->link[i].link_id, &v->id)) + { + return i; + } + break; + case LSA_LINK_TYPE_TRANSIT: + /* Network LSA ID. */ + if (v->type == OSPF_NETWORK_LSA && + IPV4_ADDR_SAME (&rl->link[i].link_id, &v->id)) + { + return i; + } + break; + case LSA_LINK_TYPE_STUB: + /* Stub can't lead anywhere, carry on */ + continue; + default: + break; + } + } + } + return -1; +} + +/* Find the next link after prev_link from v to w. If prev_link is + * NULL, return the first link from v to w. Ignore stub and virtual links; + * these link types will never be returned. + */ +static struct router_lsa_link * +ospf_get_next_link (struct vertex *v, struct vertex *w, + struct router_lsa_link *prev_link) +{ + u_char *p; + u_char *lim; + u_char lsa_type = LSA_LINK_TYPE_TRANSIT; + struct router_lsa_link *l; + + if (w->type == OSPF_VERTEX_ROUTER) + lsa_type = LSA_LINK_TYPE_POINTOPOINT; + + if (prev_link == NULL) + p = ((u_char *) v->lsa) + OSPF_LSA_HEADER_SIZE + 4; + else + { + p = (u_char *) prev_link; + p += (OSPF_ROUTER_LSA_LINK_SIZE + + (prev_link->m[0].tos_count * OSPF_ROUTER_LSA_TOS_SIZE)); + } + + lim = ((u_char *) v->lsa) + ntohs (v->lsa->length); + + while (p < lim) + { + l = (struct router_lsa_link *) p; + + p += (OSPF_ROUTER_LSA_LINK_SIZE + (l->m[0].tos_count * OSPF_ROUTER_LSA_TOS_SIZE)); + + if (l->m[0].type != lsa_type) + continue; + + if (IPV4_ADDR_SAME (&l->link_id, &w->id)) + return l; + } + + return NULL; +} + +static void +ospf_spf_flush_parents (struct vertex *w) +{ + struct vertex_parent *vp; + struct listnode *ln, *nn; + + /* delete the existing nexthops */ + for (ALL_LIST_ELEMENTS (w->parents, ln, nn, vp)) + { + list_delete_node (w->parents, ln); + vertex_parent_free (vp); + } +} + +/* + * Consider supplied next-hop for inclusion to the supplied list of + * equal-cost next-hops, adjust list as neccessary. + */ +static void +ospf_spf_add_parent (struct vertex *v, struct vertex *w, + struct vertex_nexthop *newhop, + unsigned int distance) +{ + struct vertex_parent *vp, *wp; + struct listnode *node; + + /* we must have a newhop, and a distance */ + assert (v && w && newhop); + assert (distance); + + /* IFF w has already been assigned a distance, then we shouldn't get here + * unless callers have determined V(l)->W is shortest / equal-shortest + * path (0 is a special case distance (no distance yet assigned)). + */ + if (w->distance) + assert (distance <= w->distance); + else + w->distance = distance; + + if (IS_DEBUG_OSPF_EVENT) + { + char buf[2][INET_ADDRSTRLEN]; + zlog_debug ("%s: Adding %s as parent of %s", + __func__, + inet_ntop(AF_INET, &v->lsa->id, buf[0], sizeof(buf[0])), + inet_ntop(AF_INET, &w->lsa->id, buf[1], sizeof(buf[1]))); + } + + /* Adding parent for a new, better path: flush existing parents from W. */ + if (distance < w->distance) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("%s: distance %d better than %d, flushing existing parents", + __func__, distance, w->distance); + ospf_spf_flush_parents (w); + w->distance = distance; + } + + /* 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); + + return; +} + +/* 16.1.1. Calculate nexthop from root through V (parent) to + * vertex W (destination), with given distance from root->W. + * + * The link must be supplied if V is the root vertex. In all other cases + * it may be NULL. + * + * Note that this function may fail, hence the state of the destination + * vertex, W, should /not/ be modified in a dependent manner until + * this function returns. This function will update the W vertex with the + * provided distance as appropriate. + */ +static unsigned int +ospf_nexthop_calculation (struct ospf_area *area, struct vertex *v, + struct vertex *w, struct router_lsa_link *l, + 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) + { + zlog_debug ("ospf_nexthop_calculation(): Start"); + ospf_vertex_dump("V (parent):", v, 1, 1); + ospf_vertex_dump("W (dest) :", w, 1, 1); + zlog_debug ("V->W distance: %d", distance); + } + + if (v == area->spf) + { + /* 16.1.1 para 4. In the first case, the parent vertex (V) is the + root (the calculating router itself). This means that the + destination is either a directly connected network or directly + connected router. The outgoing interface in this case is simply + 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; + + 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) + can be determined by examining the destination's + router-LSA: each link pointing back to the + calculating router and having a Link Data field + belonging to the Point-to-MultiPoint network + 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"). 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 + a nexthop address for V. + */ + 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 = nexthop; + ospf_spf_add_parent (v, w, nh, distance); + return 1; + } + else + 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) + { + struct ospf_vl_data *vl_data; + + /* VLink implementation limitations: + * a) vl_data can only reference one nexthop, so no ECMP + * to backbone through VLinks. Though transit-area + * summaries may be considered, and those can be ECMP. + * b) We can only use /one/ VLink, even if multiple ones + * exist this router through multiple transit-areas. + */ + vl_data = ospf_vl_lookup (area->ospf, NULL, l->link_id); + + if (vl_data + && CHECK_FLAG (vl_data->flags, OSPF_VL_FLAG_APPROVED)) + { + nh = vertex_nexthop_new (); + nh->oi = vl_data->nexthop.oi; + nh->router = vl_data->nexthop.router; + ospf_spf_add_parent (v, w, nh, distance); + return 1; + } + else + zlog_info("ospf_nexthop_calculation(): " + "vl_data for VL link not found"); + } /* end virtual-link from V to W */ + return 0; + } /* end W is a Router vertex */ + else + { + assert(w->type == OSPF_VERTEX_NETWORK); + + 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; + } + } /* end V is the root */ + /* Check if W's parent is a network connected to root. */ + else if (v->type == OSPF_VERTEX_NETWORK) + { + /* See if any of V's parents are the root. */ + for (ALL_LIST_ELEMENTS (v->parents, node, nnode, vp)) + { + if (vp->parent == area->spf) /* connects to root? */ + { + /* 16.1.1 para 5. ...the parent vertex is a network that + * directly connects the calculating router to the destination + * router. The list of next hops is then determined by + * examining the destination's router-LSA... + */ + + assert(w->type == OSPF_VERTEX_ROUTER); + while ((l = ospf_get_next_link (w, v, l))) + { + /* ...For each link in the router-LSA that points back to the + * parent network, the link's Link Data field provides the IP + * address of a next hop router. The outgoing interface to + * use can then be derived from the next hop IP address (or + * it can be inherited from the parent network). + */ + nh = vertex_nexthop_new (); + nh->oi = vp->nexthop->oi; + nh->router = l->link_data; + 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 (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 (required reading!), and: + * + * http://blogs.oracle.com/paulj/entry/the_difference_a_line_makes + */ + if (added) + return added; + } + + /* 16.1.1 para 4. If there is at least one intervening router in the + * current shortest path between the destination and the root, the + * destination simply inherits the set of next hops from the + * parent. + */ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("%s: Intervening routers, adding parent(s)", __func__); + + for (ALL_LIST_ELEMENTS (v->parents, node, nnode, vp)) + { + added = 1; + ospf_spf_add_parent (v, w, vp->nexthop, distance); + } + + return added; +} + +/* RFC2328 Section 16.1 (2). + * v is on the SPF tree. Examine the links in v's LSA. Update the list + * of candidates with any vertices not already on the list. If a lower-cost + * path is found to a vertex already on the candidate list, store the new cost. + */ +static void +ospf_spf_next (struct vertex *v, struct ospf_area *area, + struct pqueue * candidate) +{ + struct ospf_lsa *w_lsa = NULL; + u_char *p; + u_char *lim; + struct router_lsa_link *l = NULL; + struct in_addr *r; + 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. */ + if (v->type == OSPF_VERTEX_ROUTER) + { + if (IS_ROUTER_LSA_VIRTUAL ((struct router_lsa *) v->lsa)) + area->transit = OSPF_TRANSIT_TRUE; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("%s: Next vertex of %s vertex %s", + __func__, + v->type == OSPF_VERTEX_ROUTER ? "Router" : "Network", + inet_ntoa(v->lsa->id)); + + p = ((u_char *) v->lsa) + OSPF_LSA_HEADER_SIZE + 4; + lim = ((u_char *) v->lsa) + ntohs (v->lsa->length); + + while (p < lim) + { + struct vertex *w; + unsigned int distance; + + /* In case of V is Router-LSA. */ + if (v->lsa->type == OSPF_ROUTER_LSA) + { + 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)); + + /* (a) If this is a link to a stub network, examine the next + link in V's LSA. Links to stub networks will be + considered in the second stage of the shortest path + calculation. */ + if ((type = l->m[0].type) == LSA_LINK_TYPE_STUB) + continue; + + /* Infinite distance links shouldn't be followed, except + * for local links (a stub-routed router still wants to + * calculate tree, so must follow its own links). + */ + if ((v != area->spf) && l->m[0].metric >= OSPF_OUTPUT_COST_INFINITE) + continue; + + /* (b) Otherwise, W is a transit vertex (router or transit + network). Look up the vertex W's LSA (router-LSA or + network-LSA) in Area A's link state database. */ + switch (type) + { + case LSA_LINK_TYPE_POINTOPOINT: + case LSA_LINK_TYPE_VIRTUALLINK: + if (type == LSA_LINK_TYPE_VIRTUALLINK) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("looking up LSA through VL: %s", + inet_ntoa (l->link_id)); + } + + w_lsa = ospf_lsa_lookup (area, OSPF_ROUTER_LSA, l->link_id, + l->link_id); + if (w_lsa) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("found Router LSA %s", inet_ntoa (l->link_id)); + } + break; + case LSA_LINK_TYPE_TRANSIT: + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Looking up Network LSA, ID: %s", + inet_ntoa (l->link_id)); + w_lsa = ospf_lsa_lookup_by_id (area, OSPF_NETWORK_LSA, + l->link_id); + if (w_lsa) + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("found the LSA"); + break; + default: + zlog_warn ("Invalid LSA link type %d", type); + continue; + } + } + else + { + /* In case of V is Network-LSA. */ + r = (struct in_addr *) p; + p += sizeof (struct in_addr); + + /* Lookup the vertex W's LSA. */ + w_lsa = ospf_lsa_lookup_by_id (area, OSPF_ROUTER_LSA, *r); + if (w_lsa) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("found Router LSA %s", inet_ntoa (w_lsa->data->id)); + } + } + + /* (b cont.) If the LSA does not exist, or its LS age is equal + to MaxAge, or it does not have a link back to vertex V, + examine the next link in V's LSA.[23] */ + if (w_lsa == NULL) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("No LSA found"); + continue; + } + + if (IS_LSA_MAXAGE (w_lsa)) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("LSA is MaxAge"); + continue; + } + + if (ospf_lsa_has_link (w_lsa->data, v->lsa) < 0 ) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("The LSA doesn't have a link back"); + continue; + } + + /* (c) If vertex W is already on the shortest-path tree, examine + the next link in the LSA. */ + if (w_lsa->stat == LSA_SPF_IN_SPFTREE) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("The LSA is already in SPF"); + continue; + } + + /* (d) Calculate the link state cost D of the resulting path + from the root to vertex W. D is equal to the sum of the link + state cost of the (already calculated) shortest path to + vertex V and the advertised cost of the link between vertices + V and W. If D is: */ + + /* calculate link cost D. */ + if (v->lsa->type == OSPF_ROUTER_LSA) + distance = v->distance + ntohs (l->m[0].metric); + else /* v is not a Router-LSA */ + distance = v->distance; + + /* Is there already vertex W in candidate list? */ + if (w_lsa->stat == LSA_SPF_NOT_EXPLORED) + { + /* prepare vertex W. */ + w = ospf_vertex_new (w_lsa); + + /* Calculate nexthop to W. */ + 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"); + } + else if (w_lsa->stat >= 0) + { + /* Get the vertex from candidates. */ + w = candidate->array[w_lsa->stat]; + + /* if D is greater than. */ + if (w->distance < distance) + { + continue; + } + /* equal to. */ + else if (w->distance == distance) + { + /* Found an equal-cost path to W. + * Calculate nexthop of to W from V. */ + ospf_nexthop_calculation (area, v, w, l, distance, lsa_pos); + } + /* less than. */ + else + { + /* Found a lower-cost path to W. + * nexthop_calculation is conditional, if it finds + * valid nexthop it will call spf_add_parents, which + * will flush the old parents + */ + 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. + * (next pqueu_{de,en}queue will fully re-heap the queue). + */ + trickle_up (w_lsa->stat, candidate); + } + } /* end W is already on the candidate list */ + } /* end loop over the links in V's LSA */ +} + +static void +ospf_spf_dump (struct vertex *v, int i) +{ + struct listnode *cnode; + struct listnode *nnode; + struct vertex_parent *parent; + + if (v->type == OSPF_VERTEX_ROUTER) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("SPF Result: %d [R] %s", i, inet_ntoa (v->lsa->id)); + } + else + { + struct network_lsa *lsa = (struct network_lsa *) v->lsa; + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("SPF Result: %d [N] %s/%d", i, inet_ntoa (v->lsa->id), + ip_masklen (lsa->mask)); + } + + if (IS_DEBUG_OSPF_EVENT) + for (ALL_LIST_ELEMENTS_RO (v->parents, nnode, parent)) + { + zlog_debug (" nexthop %p %s %s", + (void *)parent->nexthop, + inet_ntoa (parent->nexthop->router), + parent->nexthop->oi ? IF_NAME(parent->nexthop->oi) + : "NULL"); + } + + i++; + + for (ALL_LIST_ELEMENTS_RO (v->children, cnode, v)) + ospf_spf_dump (v, i); +} + +/* Second stage of SPF calculation. */ +static void +ospf_spf_process_stubs (struct ospf_area *area, struct vertex *v, + struct route_table *rt, + int parent_is_root) +{ + struct listnode *cnode, *cnnode; + struct vertex *child; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_process_stub():processing stubs for area %s", + inet_ntoa (area->area_id)); + if (v->type == OSPF_VERTEX_ROUTER) + { + u_char *p; + 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", + inet_ntoa (v->lsa->id)); + rlsa = (struct router_lsa *) v->lsa; + + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_process_stubs(): we have %d links to process", + ntohs (rlsa->links)); + p = ((u_char *) v->lsa) + OSPF_LSA_HEADER_SIZE + 4; + lim = ((u_char *) v->lsa) + ntohs (v->lsa->length); + + while (p < lim) + { + l = (struct router_lsa_link *) p; + + p += (OSPF_ROUTER_LSA_LINK_SIZE + + (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, lsa_pos); + lsa_pos++; + } + } + + ospf_vertex_dump("ospf_process_stubs(): after examining links: ", v, 1, 1); + + for (ALL_LIST_ELEMENTS (v->children, cnode, cnnode, child)) + { + if (CHECK_FLAG (child->flags, OSPF_VERTEX_PROCESSED)) + continue; + + /* the first level of routers connected to the root + * should have 'parent_is_root' set, including those + * connected via a network vertex. + */ + if (area->spf == v) + parent_is_root = 1; + else if (v->type == OSPF_VERTEX_ROUTER) + parent_is_root = 0; + + ospf_spf_process_stubs (area, child, rt, parent_is_root); + + SET_FLAG (child->flags, OSPF_VERTEX_PROCESSED); + } +} + +void +ospf_rtrs_free (struct route_table *rtrs) +{ + struct route_node *rn; + struct list *or_list; + struct ospf_route *or; + struct listnode *node, *nnode; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Route: Router Routing Table free"); + + for (rn = route_top (rtrs); rn; rn = route_next (rn)) + if ((or_list = rn->info) != NULL) + { + for (ALL_LIST_ELEMENTS (or_list, node, nnode, or)) + ospf_route_free (or); + + list_delete (or_list); + + /* Unlock the node. */ + rn->info = NULL; + route_unlock_node (rn); + } + route_table_finish (rtrs); +} + +#if 0 +static void +ospf_rtrs_print (struct route_table *rtrs) +{ + struct route_node *rn; + struct list *or_list; + struct listnode *ln; + struct listnode *pnode; + struct ospf_route *or; + struct ospf_path *path; + char buf1[BUFSIZ]; + char buf2[BUFSIZ]; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_rtrs_print() start"); + + for (rn = route_top (rtrs); rn; rn = route_next (rn)) + if ((or_list = rn->info) != NULL) + for (ALL_LIST_ELEMENTS_RO (or_list, ln, or)) + { + switch (or->path_type) + { + case OSPF_PATH_INTRA_AREA: + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("%s [%d] area: %s", + inet_ntop (AF_INET, &or->id, buf1, BUFSIZ), + or->cost, inet_ntop (AF_INET, &or->u.std.area_id, + buf2, BUFSIZ)); + break; + case OSPF_PATH_INTER_AREA: + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("%s IA [%d] area: %s", + inet_ntop (AF_INET, &or->id, buf1, BUFSIZ), + or->cost, inet_ntop (AF_INET, &or->u.std.area_id, + buf2, BUFSIZ)); + break; + default: + break; + } + + for (ALL_LIST_ELEMENTS_RO (or->paths, pnode, path)) + { + if (path->nexthop.s_addr == 0) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug (" directly attached to %s\r\n", + ifindex2ifname (path->ifindex)); + } + else + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug (" via %s, %s\r\n", + inet_ntoa (path->nexthop), + ifindex2ifname (path->ifindex)); + } + } + } + + zlog_debug ("ospf_rtrs_print() end"); +} +#endif + +/* Calculating the shortest-path tree for an area. */ +static void +ospf_spf_calculate (struct ospf_area *area, struct route_table *new_table, + struct route_table *new_rtrs) +{ + struct pqueue *candidate; + struct vertex *v; + + if (IS_DEBUG_OSPF_EVENT) + { + zlog_debug ("ospf_spf_calculate: Start"); + zlog_debug ("ospf_spf_calculate: running Dijkstra for area %s", + inet_ntoa (area->area_id)); + } + + /* Check router-lsa-self. If self-router-lsa is not yet allocated, + return this area's calculation. */ + if (!area->router_lsa_self) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_spf_calculate: " + "Skip area %s's calculation due to empty router_lsa_self", + inet_ntoa (area->area_id)); + return; + } + + /* RFC2328 16.1. (1). */ + /* Initialize the algorithm's data structures. */ + + /* This function scans all the LSA database and set the stat field to + * LSA_SPF_NOT_EXPLORED. */ + ospf_lsdb_clean_stat (area->lsdb); + /* Create a new heap for the candidates. */ + candidate = pqueue_create(); + candidate->cmp = cmp; + candidate->update = update_stat; + + /* Initialize the shortest-path tree to only the root (which is the + router doing the calculation). */ + ospf_spf_init (area); + v = area->spf; + /* Set LSA position to LSA_SPF_IN_SPFTREE. This vertex is the root of the + * spanning tree. */ + *(v->stat) = LSA_SPF_IN_SPFTREE; + + /* Set Area A's TransitCapability to FALSE. */ + area->transit = OSPF_TRANSIT_FALSE; + area->shortcut_capability = 1; + + for (;;) + { + /* RFC2328 16.1. (2). */ + ospf_spf_next (v, area, candidate); + + /* RFC2328 16.1. (3). */ + /* If at this step the candidate list is empty, the shortest- + path tree (of transit vertices) has been completely built and + this stage of the procedure terminates. */ + if (candidate->size == 0) + break; + + /* Otherwise, choose the vertex belonging to the candidate list + that is closest to the root, and add it to the shortest-path + tree (removing it from the candidate list in the + process). */ + /* Extract from the candidates the node with the lower key. */ + v = (struct vertex *) pqueue_dequeue (candidate); + /* Update stat field in vertex. */ + *(v->stat) = LSA_SPF_IN_SPFTREE; + + ospf_vertex_add_parent (v); + + /* RFC2328 16.1. (4). */ + if (v->type == OSPF_VERTEX_ROUTER) + ospf_intra_add_router (new_rtrs, v, area); + else + ospf_intra_add_transit (new_table, v, area); + + /* RFC2328 16.1. (5). */ + /* Iterate the algorithm by returning to Step 2. */ + + } /* end loop until no more candidate vertices */ + + if (IS_DEBUG_OSPF_EVENT) + { + ospf_spf_dump (area->spf, 0); + ospf_route_table_dump (new_table); + } + + /* Second stage of SPF calculation procedure's */ + ospf_spf_process_stubs (area, area->spf, new_table, 0); + + /* 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); + + /* 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. %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) +{ + struct ospf *ospf = THREAD_ARG (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 (); + + ospf_vl_unapprove (ospf); + + /* Calculate SPF for each area. */ + for (ALL_LIST_ELEMENTS (ospf->areas, node, nnode, area)) + { + /* Do backbone last, so as to first discover intra-area paths + * for any back-bone virtual-links + */ + 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); + 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, + then schedule re-calculate External routes. */ + if (1) + ospf_ase_calculate_schedule (ospf); + + 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) + { + /* old_rtrs's node holds linked list of ospf_route. --kunihiro. */ + /* ospf_route_delete (ospf->old_rtrs); */ + ospf_rtrs_free (ospf->old_rtrs); + } + + 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_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; +} + +/* 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_reason_t reason) +{ + unsigned long delay, elapsed, ht; + struct timeval result; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("SPF: calculation timer scheduled"); + + /* OSPF instance does not exist. */ + 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", + (void *)ospf->t_spf_calc); + return; + } + + /* XXX Monotic timers: we only care about relative time here. */ + result = tv_sub (recent_relative_time (), ospf->ts_spf); + + elapsed = (result.tv_sec * 1000) + (result.tv_usec / 1000); + ht = ospf->spf_holdtime * ospf->spf_hold_multiplier; + + if (ht > ospf->spf_max_holdtime) + ht = ospf->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 < ospf->spf_max_holdtime) + ospf->spf_hold_multiplier++; + + /* always honour the SPF initial delay */ + if ( (ht - elapsed) < ospf->spf_delay) + delay = ospf->spf_delay; + else + delay = ht - elapsed; + } + else + { + /* Event is past required hold-time of last SPF */ + delay = ospf->spf_delay; + ospf->spf_hold_multiplier = 1; + } + + 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 new file mode 100644 index 0000000..e33b3e5 --- /dev/null +++ b/ospfd/ospf_spf.h @@ -0,0 +1,78 @@ +/* + * OSPF calculation. + * 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_OSPF_SPF_H +#define _QUAGGA_OSPF_SPF_H + +/* values for vertex->type */ +#define OSPF_VERTEX_ROUTER 1 /* for a Router-LSA */ +#define OSPF_VERTEX_NETWORK 2 /* for a Network-LSA */ + +/* values for vertex->flags */ +#define OSPF_VERTEX_PROCESSED 0x01 + +/* The "root" is the node running the SPF calculation */ + +/* A router or network in an area */ +struct vertex +{ + u_char flags; + u_char type; /* copied from LSA header */ + struct in_addr id; /* copied from LSA header */ + struct lsa_header *lsa; /* Router or Network LSA */ + int *stat; /* Link to LSA status. */ + u_int32_t distance; /* from root to this vertex */ + struct list *parents; /* list of parents in SPF tree */ + struct list *children; /* list of children in SPF tree*/ +}; + +/* A nexthop taken on the root node to get to this (parent) vertex */ +struct vertex_nexthop +{ + struct ospf_interface *oi; /* output intf on root node */ + struct in_addr router; /* router address to send to */ +}; + +struct vertex_parent +{ + struct vertex_nexthop *nexthop; /* link to nexthop info for this parent */ + struct vertex *parent; /* parent vertex */ + int backlink; /* index back to parent for router-lsa's */ +}; + +/* 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 new file mode 100644 index 0000000..47771a1 --- /dev/null +++ b/ospfd/ospf_te.c @@ -0,0 +1,2639 @@ +/* + * 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 + * 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. + */ + +/* Add support of RFC7471 */ +/* Add support of RFC5392, RFC6827 */ + +#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 "network.h" + +#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_te.h" +#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. + */ +struct ospf_mpls_te OspfMplsTE; + +const char *mode2text[] = { "Disable", "AS", "Area", "Emulate" }; + +enum oifstate +{ + OI_ANY, OI_DOWN, OI_UP +}; + +/*------------------------------------------------------------------------* + * Followings are initialize/terminate functions for MPLS-TE handling. + *------------------------------------------------------------------------*/ + +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_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_show_info (struct vty *vty, struct ospf_lsa *lsa); +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 del_mpls_te_link (void *val); +static void ospf_mpls_te_register_vty (void); + +int +ospf_mpls_te_init (void) +{ + int rc; + + rc = ospf_register_opaque_functab ( + OSPF_OPAQUE_AREA_LSA, + OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA, + ospf_mpls_te_new_if, + ospf_mpls_te_del_if, + ospf_mpls_te_ism_change, + ospf_mpls_te_nsm_change, + ospf_mpls_te_config_write_router, + NULL,/*ospf_mpls_te_config_write_if */ + NULL,/* ospf_mpls_te_config_write_debug */ + ospf_mpls_te_show_info, + 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 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; + + ospf_mpls_te_register_vty (); + +out: + 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; + + 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; +} + +/*------------------------------------------------------------------------* + * Followings are control functions for MPLS-TE parameters management. + *------------------------------------------------------------------------*/ + +static void +del_mpls_te_link (void *val) +{ + XFREE (MTYPE_OSPF_MPLS_TE, val); + return; +} + +u_int32_t +get_mpls_te_instance_value (void) +{ + static u_int32_t seqno = 0; + + if (seqno < MAX_LEGAL_TE_INSTANCE_NUM ) + seqno += 1; + else + seqno = 1; /* Avoid zero. */ + + return seqno; +} + +static struct ospf_interface * +lookup_oi_by_ifp (struct interface *ifp, + struct ospf_area *area, enum oifstate oifstate) +{ + struct ospf_interface *oi = NULL; + struct route_node *rn; + + for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn)) + { + if ((oi = rn->info) == NULL) + continue; + + switch (oifstate) + { + case OI_ANY: + break; + case OI_DOWN: + if (ospf_if_is_enable (oi)) + continue; + break; + case OI_UP: + if (! ospf_if_is_enable (oi)) + continue; + break; + default: + zlog_warn ("lookup_oi_by_ifp: Unknown oifstate: %x", oifstate); + goto out; + } + + if (area == NULL || oi->area == area) + return oi; + } +out: + return NULL; +} + +static struct mpls_te_link * +lookup_linkparams_by_ifp (struct interface *ifp) +{ + struct listnode *node, *nnode; + struct mpls_te_link *lp; + + for (ALL_LIST_ELEMENTS (OspfMplsTE.iflist, node, nnode, lp)) + if (lp->ifp == ifp) + return lp; + + return NULL; +} + +static struct mpls_te_link * +lookup_linkparams_by_instance (struct ospf_lsa *lsa) +{ + struct listnode *node; + struct mpls_te_link *lp; + unsigned int key = GET_OPAQUE_ID (ntohl (lsa->data->id.s_addr)); + + for (ALL_LIST_ELEMENTS_RO (OspfMplsTE.iflist, node, lp)) + if (lp->instance == key) + return lp; + + zlog_warn ("lookup_linkparams_by_instance: Entry not found: key(%x)", key); + return NULL; +} + +static void +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; + struct mpls_te_link *lp; + struct ospf_area *area; + + for (ALL_LIST_ELEMENTS (OspfMplsTE.iflist, node, nnode, lp)) + { + /* Skip Inter-AS TEv2 Links */ + if (IS_INTER_AS (lp->type)) + continue; + if ((area = lp->area) == NULL) + continue; + if CHECK_FLAG (lp->flags, LPFLG_LOOKUP_DONE) continue; + + if (func != NULL) + (* func)(lp, sched_opcode); + + for (node2 = listnextnode (node); node2; node2 = listnextnode (node2)) + if ((lp = listgetdata (node2)) != NULL) + if (lp->area != NULL) + if (IPV4_ADDR_SAME (&lp->area->area_id, &area->area_id)) + SET_FLAG (lp->flags, LPFLG_LOOKUP_DONE); + } + + for (ALL_LIST_ELEMENTS_RO (OspfMplsTE.iflist, node, lp)) + if (lp->area != NULL) + UNSET_FLAG (lp->flags, LPFLG_LOOKUP_DONE); + + return; +} + +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 (TE_LINK_SUBTLV_DEF_SIZE); + OspfMplsTE.router_addr.value = ipv4; + return; +} + +static void +set_linkparams_link_header (struct mpls_te_link *lp) +{ + u_int16_t length = 0; + + /* TE_LINK_SUBTLV_LINK_TYPE */ + if (ntohs (lp->link_type.header.type) != 0) + length += TLV_SIZE (&lp->link_type.header); + + /* TE_LINK_SUBTLV_LINK_ID */ + if (ntohs (lp->link_id.header.type) != 0) + length += TLV_SIZE (&lp->link_id.header); + + /* TE_LINK_SUBTLV_LCLIF_IPADDR */ + if (lp->lclif_ipaddr.header.type != 0) + length += TLV_SIZE (&lp->lclif_ipaddr.header); + + /* TE_LINK_SUBTLV_RMTIF_IPADDR */ + 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) + length += TLV_SIZE (&lp->te_metric.header); + + /* TE_LINK_SUBTLV_MAX_BW */ + if (ntohs (lp->max_bw.header.type) != 0) + length += TLV_SIZE (&lp->max_bw.header); + + /* TE_LINK_SUBTLV_MAX_RSV_BW */ + if (ntohs (lp->max_rsv_bw.header.type) != 0) + length += TLV_SIZE (&lp->max_rsv_bw.header); + + /* TE_LINK_SUBTLV_UNRSV_BW */ + if (ntohs (lp->unrsv_bw.header.type) != 0) + length += TLV_SIZE (&lp->unrsv_bw.header); + + /* TE_LINK_SUBTLV_RSC_CLSCLR */ + 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); + + return; +} + +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 (TE_LINK_SUBTLV_TYPE_SIZE); + + switch (oi->type) + { + case OSPF_IFTYPE_POINTOPOINT: + lp->link_type.link_type.value = LINK_TYPE_SUBTLV_VALUE_PTP; + break; + case OSPF_IFTYPE_BROADCAST: + case OSPF_IFTYPE_NBMA: + lp->link_type.link_type.value = LINK_TYPE_SUBTLV_VALUE_MA; + break; + default: + /* Not supported yet. *//* XXX */ + lp->link_type.header.type = htons (0); + break; + } + return; +} + +static void +set_linkparams_link_id (struct ospf_interface *oi, struct mpls_te_link *lp) +{ + struct ospf_neighbor *nbr; + int done = 0; + + lp->link_id.header.type = htons (TE_LINK_SUBTLV_LINK_ID); + lp->link_id.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE); + + /* + * The Link ID is identical to the contents of the Link ID field + * in the Router LSA for these link types. + */ + switch (oi->type) + { + case OSPF_IFTYPE_POINTOPOINT: + /* Take the router ID of the neighbor. */ + if ((nbr = ospf_nbr_lookup_ptop (oi)) && nbr->state == NSM_Full) + { + lp->link_id.value = nbr->router_id; + done = 1; + } + break; + case OSPF_IFTYPE_BROADCAST: + case OSPF_IFTYPE_NBMA: + /* Take the interface address of the designated router. */ + if ((nbr = ospf_nbr_lookup_by_addr (oi->nbrs, &DR (oi))) == NULL) + break; + + if (nbr->state == NSM_Full + || (IPV4_ADDR_SAME (&oi->address->u.prefix4, &DR (oi)) + && ospf_nbr_count (oi, NSM_Full) > 0)) + { + lp->link_id.value = DR (oi); + done = 1; + } + break; + default: + /* Not supported yet. *//* XXX */ + lp->link_id.header.type = htons (0); + break; + } + + if (! done) + { + struct in_addr mask; + masklen2ip (oi->address->prefixlen, &mask); + lp->link_id.value.s_addr = oi->address->u.prefix4.s_addr & mask.s_addr; + } + 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 (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) +{ + lp->max_bw.header.type = htons (TE_LINK_SUBTLV_MAX_BW); + 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) +{ + lp->max_rsv_bw.header.type = htons (TE_LINK_SUBTLV_MAX_RSV_BW); + 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) +{ + /* 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 (TE_LINK_SUBTLV_UNRSV_SIZE); + lp->unrsv_bw.value [priority] = htonf (fp); + return; +} + +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 (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; + + 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) + { + 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 + * zebra-interface information. + */ + set_linkparams_link_type (oi, lp); + + /* Set local IP addr */ + set_linkparams_lclif_ipaddr (lp, oi->address->u.prefix4); + + /* 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); + } + + /* Keep Area information in combination with link parameters. */ + lp->area = oi->area; + + return; +} + +static int +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 (!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: + return rc; +} + +/*------------------------------------------------------------------------* + * Followings are callback functions against generic Opaque-LSAs handling. + *------------------------------------------------------------------------*/ + +static int +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?", (void *)ifp); + rc = 0; /* Do nothing here. */ + goto out; + } + + 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->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 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; +out: + return rc; +} + +static int +ospf_mpls_te_del_if (struct interface *ifp) +{ + struct mpls_te_link *lp; + int rc = -1; + + if ((lp = lookup_linkparams_by_ifp (ifp)) != NULL) + { + struct list *iflist = OspfMplsTE.iflist; + + /* Dequeue listnode entry from the list. */ + listnode_delete (iflist, lp); + + /* Avoid misjudgement in the next lookup. */ + if (listcount (iflist) == 0) + iflist->head = iflist->tail = NULL; + + XFREE (MTYPE_OSPF_MPLS_TE, lp); + } + + /* Schedule Opaque-LSA refresh. *//* XXX */ + + rc = 0; +/*out:*/ + 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) +{ + struct te_link_subtlv_link_type old_type; + struct te_link_subtlv_link_id old_id; + struct mpls_te_link *lp; + + if ((lp = lookup_linkparams_by_ifp (oi->ifp)) == NULL) + { + 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)); + goto out; + } +#ifdef notyet + if ((lp->area != NULL + && ! IPV4_ADDR_SAME (&lp->area->area_id, &oi->area->area_id)) + || (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"); + ospf_mpls_te_lsa_schedule (lp, FLUSH_THIS_LSA); + } +#endif + /* 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: + case ISM_DROther: + case ISM_Backup: + case ISM_DR: + 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))) + { + 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); + + } + break; + default: + lp->link_type.header.type = htons (0); + lp->link_id.header.type = htons (0); + + 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) +{ + /* Nothing to do here */ + return; +} + +/*------------------------------------------------------------------------* + * Followings are OSPF protocol processing functions for MPLS-TE. + *------------------------------------------------------------------------*/ + +static void +build_tlv_header (struct stream *s, struct te_tlv_header *tlvh) +{ + stream_put (s, tlvh, sizeof (struct te_tlv_header)); + return; +} + +static void +build_router_tlv (struct stream *s) +{ + struct te_tlv_header *tlvh = &OspfMplsTE.router_addr.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 (struct stream *s, struct te_tlv_header *tlvh) + { + + 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_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 (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; +} + +static void +ospf_mpls_te_lsa_body_set (struct stream *s, struct mpls_te_link *lp) +{ + /* + * The router address TLV is type 1, and ... + * It must appear in exactly one + * Traffic Engineering LSA originated by a router. + */ + build_router_tlv (s); + + /* + * Only one Link TLV shall be carried in each LSA, allowing for fine + * granularity changes in topology. + */ + build_link_tlv (s, lp); + return; +} + +/* Create new opaque-LSA. */ +static struct ospf_lsa * +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 = 0; + 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_mpls_te_lsa_new: stream_new() ?"); + goto out; + } + lsah = (struct lsa_header *) STREAM_DATA (s); + + options = OSPF_OPTION_O; /* Don't forget this :-) */ + + /* 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); + + struct ospf *top = ospf_lookup (); + + 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); + + /* 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_mpls_te_lsa_new: ospf_lsa_new() ?"); + stream_free (s); + goto out; + } + if ((new->data = ospf_lsa_data_new (length)) == NULL) + { + zlog_warn ("ospf_mpls_te_lsa_new: ospf_lsa_data_new() ?"); + ospf_lsa_unlock (&new); + new = NULL; + stream_free (s); + goto out; + } + + new->area = area; + SET_FLAG (new->flags, OSPF_LSA_SELF); + memcpy (new->data, lsah, length); + stream_free (s); + +out: + return new; +} + +static int +ospf_mpls_te_lsa_originate1 (struct ospf_area *area, struct mpls_te_link *lp) +{ + struct ospf_lsa *new; + int rc = -1; + + /* Create new Opaque-LSA/MPLS-TE instance. */ + if ((new = ospf_mpls_te_lsa_new (area, lp)) == NULL) + { + zlog_warn ("ospf_mpls_te_lsa_originate1: ospf_mpls_te_lsa_new() ?"); + goto out; + } + + /* Install this LSA into LSDB. */ + if (ospf_lsa_install (area->ospf, NULL/*oi*/, new) == NULL) + { + zlog_warn ("ospf_mpls_te_lsa_originate1: ospf_lsa_install() ?"); + ospf_lsa_unlock (&new); + goto out; + } + + /* 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++; + + /* Flood new LSA through area. */ + ospf_flood_through_area (area, NULL/*nbr*/, new); + + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + { + 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); + ospf_lsa_header_dump (new->data); + } + + rc = 0; +out: + return rc; +} + +static int +ospf_mpls_te_lsa_originate_area (void *arg) +{ + struct ospf_area *area = (struct ospf_area *) arg; + struct listnode *node, *nnode; + struct mpls_te_link *lp; + int rc = -1; + + if (OspfMplsTE.status == disabled) + { + 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 CHECK_FLAG (lp->flags, LPFLG_LSA_ENGAGED) + { + if CHECK_FLAG (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_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; + } + + rc = 0; +out: + 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) + { + /* + * This LSA must have flushed before due to MPLS-TE status change. + * It seems a slip among routers in the routing domain. + */ + zlog_info ("ospf_mpls_te_lsa_refresh: MPLS-TE is disabled now."); + lsa->data->ls_age = htons (OSPF_LSA_MAXAGE); /* Flush it anyway. */ + } + + /* At first, resolve lsa/lp relationship. */ + if ((lp = lookup_linkparams_by_instance (lsa)) == NULL) + { + zlog_warn ("ospf_mpls_te_lsa_refresh: Invalid parameter?"); + 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)) + { + if (lp) + UNSET_FLAG (lp->flags, LPFLG_LSA_ENGAGED); + ospf_opaque_lsa_flush_schedule (lsa); + goto out; + } + + /* Create new Opaque-LSA/MPLS-TE instance. */ + if ((new = ospf_mpls_te_lsa_new (area, lp)) == NULL) + { + zlog_warn ("ospf_mpls_te_lsa_refresh: ospf_mpls_te_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. */ + /* 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 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)) + { + zlog_debug ("LSA[Type%d:%s]: Refresh Opaque-LSA/MPLS-TE", + new->data->type, inet_ntoa (new->data->id)); + ospf_lsa_header_dump (new->data); + } + +out: + return new; +} + +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; + 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_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: + /* Reset Activity flag */ + lp->flags = LPFLG_LSA_INACTIVE; + ospf_opaque_lsa_flush_schedule (&lsa); + break; + default: + zlog_warn ("ospf_mpls_te_lsa_schedule: Unknown opcode (%u)", opcode); + break; + } + + return; +} + + +/*------------------------------------------------------------------------* + * Followings are vty session control functions. + *------------------------------------------------------------------------*/ + +static u_int16_t +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); + else + zlog_debug (" Router-Address: %s", inet_ntoa (top->value)); + + return TLV_SIZE (tlvh); +} + +static u_int16_t +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); + else + zlog_debug (" Link: %u octets of data", ntohs (top->header.length)); + + return TLV_HDR_SIZE; /* Here is special, not "TLV_SIZE". */ +} + +static u_int16_t +show_vty_link_subtlv_link_type (struct vty *vty, struct te_tlv_header *tlvh) +{ + struct te_link_subtlv_link_type *top; + const char *cp = "Unknown"; + + top = (struct te_link_subtlv_link_type *) tlvh; + switch (top->link_type.value) + { + case LINK_TYPE_SUBTLV_VALUE_PTP: + cp = "Point-to-point"; + break; + case LINK_TYPE_SUBTLV_VALUE_MA: + cp = "Multiaccess"; + break; + default: + break; + } + + if (vty != NULL) + 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); + + return TLV_SIZE (tlvh); +} + +static u_int16_t +show_vty_link_subtlv_link_id (struct vty *vty, struct te_tlv_header *tlvh) +{ + struct te_link_subtlv_link_id *top; + + top = (struct te_link_subtlv_link_id *) tlvh; + if (vty != NULL) + vty_out (vty, " Link-ID: %s%s", inet_ntoa (top->value), VTY_NEWLINE); + else + zlog_debug (" Link-ID: %s", inet_ntoa (top->value)); + + return TLV_SIZE (tlvh); +} + +static u_int16_t +show_vty_link_subtlv_lclif_ipaddr (struct vty *vty, + struct te_tlv_header *tlvh) +{ + struct te_link_subtlv_lclif_ipaddr *top; + int i, n; + + top = (struct te_link_subtlv_lclif_ipaddr *) tlvh; + n = ntohs (tlvh->length) / sizeof (top->value[0]); + + if (vty != NULL) + vty_out (vty, " Local Interface IP Address(es): %d%s", n, VTY_NEWLINE); + else + zlog_debug (" Local Interface IP Address(es): %d", n); + + for (i = 0; i < n; i++) + { + if (vty != NULL) + 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])); + } + return TLV_SIZE (tlvh); +} + +static u_int16_t +show_vty_link_subtlv_rmtif_ipaddr (struct vty *vty, + struct te_tlv_header *tlvh) +{ + struct te_link_subtlv_rmtif_ipaddr *top; + int i, n; + + top = (struct te_link_subtlv_rmtif_ipaddr *) tlvh; + n = ntohs (tlvh->length) / sizeof (top->value[0]); + if (vty != NULL) + vty_out (vty, " Remote Interface IP Address(es): %d%s", n, VTY_NEWLINE); + else + zlog_debug (" Remote Interface IP Address(es): %d", n); + + for (i = 0; i < n; i++) + { + if (vty != NULL) + 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])); + } + return TLV_SIZE (tlvh); +} + +static u_int16_t +show_vty_link_subtlv_te_metric (struct vty *vty, struct te_tlv_header *tlvh) +{ + struct te_link_subtlv_te_metric *top; + + 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); + else + zlog_debug (" Traffic Engineering Metric: %u", + (u_int32_t) ntohl (top->value)); + + return TLV_SIZE (tlvh); +} + +static u_int16_t +show_vty_link_subtlv_max_bw (struct vty *vty, struct te_tlv_header *tlvh) +{ + struct te_link_subtlv_max_bw *top; + float fval; + + top = (struct te_link_subtlv_max_bw *) tlvh; + fval = ntohf (top->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 TLV_SIZE (tlvh); +} + +static u_int16_t +show_vty_link_subtlv_max_rsv_bw (struct vty *vty, struct te_tlv_header *tlvh) +{ + struct te_link_subtlv_max_rsv_bw *top; + float fval; + + top = (struct te_link_subtlv_max_rsv_bw *) tlvh; + fval = ntohf (top->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 TLV_SIZE (tlvh); +} + +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 fval1, fval2; + int i; + + top = (struct te_link_subtlv_unrsv_bw *) tlvh; + 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) + { + fval1 = ntohf (top->value[i]); + fval2 = ntohf (top->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 TLV_SIZE (tlvh); +} + +static u_int16_t +show_vty_link_subtlv_rsc_clsclr (struct vty *vty, struct te_tlv_header *tlvh) +{ + struct te_link_subtlv_rsc_clsclr *top; + + 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); + 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 (" 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); +} + +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); + else + zlog_debug (" Unknown TLV: [type(0x%x), length(0x%x)]", + ntohs (tlvh->type), ntohs (tlvh->length)); + + return TLV_SIZE (tlvh); +} + +static u_int16_t +ospf_mpls_te_show_link_subtlv (struct vty *vty, struct te_tlv_header *tlvh0, + u_int16_t subtotal, u_int16_t total) +{ + struct te_tlv_header *tlvh, *next; + u_int16_t sum = subtotal; + + for (tlvh = tlvh0; sum < total; tlvh = (next ? next : TLV_HDR_NEXT (tlvh))) + { + next = NULL; + switch (ntohs (tlvh->type)) + { + case TE_LINK_SUBTLV_LINK_TYPE: + sum += show_vty_link_subtlv_link_type (vty, tlvh); + break; + case TE_LINK_SUBTLV_LINK_ID: + sum += show_vty_link_subtlv_link_id (vty, tlvh); + break; + case TE_LINK_SUBTLV_LCLIF_IPADDR: + sum += show_vty_link_subtlv_lclif_ipaddr (vty, tlvh); + break; + case TE_LINK_SUBTLV_RMTIF_IPADDR: + sum += show_vty_link_subtlv_rmtif_ipaddr (vty, tlvh); + break; + case TE_LINK_SUBTLV_TE_METRIC: + sum += show_vty_link_subtlv_te_metric (vty, tlvh); + break; + case TE_LINK_SUBTLV_MAX_BW: + sum += show_vty_link_subtlv_max_bw (vty, tlvh); + break; + case TE_LINK_SUBTLV_MAX_RSV_BW: + sum += show_vty_link_subtlv_max_rsv_bw (vty, tlvh); + break; + case TE_LINK_SUBTLV_UNRSV_BW: + sum += show_vty_link_subtlv_unrsv_bw (vty, tlvh); + break; + 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; + } + } + return sum; +} + +static void +ospf_mpls_te_show_info (struct vty *vty, struct ospf_lsa *lsa) +{ + struct lsa_header *lsah = (struct lsa_header *) lsa->data; + struct te_tlv_header *tlvh, *next; + u_int16_t sum, total; + u_int16_t (* subfunc)(struct vty *vty, struct te_tlv_header *tlvh, + u_int16_t subtotal, u_int16_t total) = NULL; + + sum = 0; + total = ntohs (lsah->length) - OSPF_LSA_HEADER_SIZE; + + for (tlvh = TLV_HDR_TOP (lsah); sum < total; + tlvh = (next ? next : TLV_HDR_NEXT (tlvh))) + { + if (subfunc != NULL) + { + sum = (* subfunc)(vty, tlvh, sum, total); + next = (struct te_tlv_header *)((char *) tlvh + sum); + subfunc = NULL; + continue; + } + + next = NULL; + switch (ntohs (tlvh->type)) + { + case TE_TLV_ROUTER_ADDR: + sum += show_vty_router_addr (vty, tlvh); + break; + case TE_TLV_LINK: + sum += show_vty_link_header (vty, tlvh); + subfunc = ospf_mpls_te_show_link_subtlv; + next = tlvh + 1; + break; + default: + sum += show_vty_unknown_tlv (vty, tlvh); + break; + } + } + return; +} + +static void +ospf_mpls_te_config_write_router (struct vty *vty) +{ + + if (OspfMplsTE.status == enabled) + { + 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); + } + + 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); + + return; +} + +/*------------------------------------------------------------------------* + * Followings are vty command functions. + *------------------------------------------------------------------------*/ + +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; + struct mpls_te_link *lp; + + if (OspfMplsTE.status == enabled) + return CMD_SUCCESS; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("MPLS-TE: OFF -> ON"); + + OspfMplsTE.status = enabled; + + /* Reoriginate RFC3630 & RFC6827 Links */ + ospf_mpls_te_foreach_area (ospf_mpls_te_lsa_schedule, REORIGINATE_THIS_LSA); + + /* 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; +} + +DEFUN (no_ospf_mpls_te, + no_ospf_mpls_te_cmd, + "no mpls-te", + NO_STR + "Disable the MPLS-TE functionality\n") +{ + struct listnode *node, *nnode; + struct mpls_te_link *lp; + + if (OspfMplsTE.status == disabled) + return CMD_SUCCESS; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("MPLS-TE: ON -> OFF"); + + OspfMplsTE.status = disabled; + + for (ALL_LIST_ELEMENTS (OspfMplsTE.iflist, node, nnode, lp)) + if CHECK_FLAG (lp->flags, LPFLG_LSA_ENGAGED) + ospf_mpls_te_lsa_schedule (lp, FLUSH_THIS_LSA); + + return CMD_SUCCESS; +} + +DEFUN (ospf_mpls_te_router_addr, + ospf_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 te_tlv_router_addr *ra = &OspfMplsTE.router_addr; + struct in_addr value; + + if (! inet_aton (argv[0], &value)) + { + vty_out (vty, "Please specify Router-Addr by A.B.C.D%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (ntohs (ra->header.type) == 0 + || ntohl (ra->value.s_addr) != ntohl (value.s_addr)) + { + struct listnode *node, *nnode; + struct mpls_te_link *lp; + int need_to_reoriginate = 0; + + set_mpls_te_router_addr (value); + + if (OspfMplsTE.status == disabled) + goto out; + + for (ALL_LIST_ELEMENTS (OspfMplsTE.iflist, node, nnode, lp)) + { + if ((lp->area == NULL) || IS_FLOOD_AS (lp->type)) + continue; + + if (!CHECK_FLAG (lp->flags, LPFLG_LSA_ENGAGED)) + { + need_to_reoriginate = 1; + break; + } + } + + for (ALL_LIST_ELEMENTS (OspfMplsTE.iflist, node, nnode, lp)) + { + if ((lp->area == NULL) || IS_FLOOD_AS (lp->type)) + continue; + + if (need_to_reoriginate) + 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_THIS_LSA); + } +out: + return CMD_SUCCESS; +} + +static int +set_inter_as_mode (struct vty *vty, const char *mode_name, + const char *area_id) +{ + enum inter_as_mode mode; + struct listnode *node; + struct mpls_te_link *lp; + int format; + + if (OspfMplsTE.status == enabled) + { + + /* 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; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("MPLS-TE: Inter-AS enable with %s flooding support", + mode2text[mode]); + + /* 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; + } + + /* 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 (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") +{ + return set_inter_as_mode (vty, "as", ""); +} + +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") +{ + return set_inter_as_mode (vty, "area", argv[0]); +} + +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 listnode *node, *nnode; + struct mpls_te_link *lp; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("MPLS-TE: Inter-AS support OFF"); + + if ((OspfMplsTE.status == enabled) && (OspfMplsTE.inter_as != Disable)) + { + 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); + } + + /* Deregister the Callbacks for Inter-AS suport */ + ospf_mpls_te_unregister (); + + return CMD_SUCCESS; +} + +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" + "MPLS-TE Router parameters\n") +{ + if (OspfMplsTE.status == enabled) + { + 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); + else if (vty != NULL) + vty_out (vty, " N/A%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +static void +show_mpls_te_link_sub (struct vty *vty, struct interface *ifp) +{ + struct mpls_te_link *lp; + + 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); + } + + 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 + { + vty_out (vty, " %s: MPLS-TE is disabled on this interface%s", + ifp->name, VTY_NEWLINE); + } + + return; +} + +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") +{ + struct interface *ifp; + struct listnode *node, *nnode; + + /* Show All Interfaces. */ + if (argc == 0) + { + for (ALL_LIST_ELEMENTS (iflist, node, nnode, ifp)) + show_mpls_te_link_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_link_sub (vty, ifp); + } + + return CMD_SUCCESS; +} + +static void +ospf_mpls_te_register_vty (void) +{ + install_element (VIEW_NODE, &show_ip_ospf_mpls_te_router_cmd); + install_element (VIEW_NODE, &show_ip_ospf_mpls_te_link_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; +} diff --git a/ospfd/ospf_te.h b/ospfd/ospf_te.h new file mode 100644 index 0000000..8bb77c4 --- /dev/null +++ b/ospfd/ospf_te.h @@ -0,0 +1,466 @@ +/* + * 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 + * 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. + */ + +/* 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 + +/* + * Opaque LSA's link state ID for Traffic Engineering is + * structured as follows. + * + * 24 16 8 0 + * +--------+--------+--------+--------+ + * | 1 | MBZ |........|........| + * +--------+--------+--------+--------+ + * |<-Type->||<-- Instance --->| + * + * + * Type: IANA has assigned '1' for Traffic Engineering. + * MBZ: Reserved, must be set to zero. + * Instance: User may select an arbitrary 16-bit value. + * + */ + +#define MAX_LEGAL_TE_INSTANCE_NUM (0xffff) +#define LEGAL_TE_INSTANCE_RANGE(i) (0 <= (i) && (i) <= 0xffff) + +/* + * 24 16 8 0 + * +--------+--------+--------+--------+ --- + * | LS age |Options | 10 | A + * +--------+--------+--------+--------+ | + * | 1 | 0 | Instance | | + * +--------+--------+--------+--------+ | + * | Advertising router | | Standard (Opaque) LSA header; + * +--------+--------+--------+--------+ | Only type-10 is used. + * | LS sequence number | | + * +--------+--------+--------+--------+ | + * | LS checksum | Length | V + * +--------+--------+--------+--------+ --- + * | Type | Length | A + * +--------+--------+--------+--------+ | TLV part for TE; Values might be + * | Values ... | V structured as a set of sub-TLVs. + * +--------+--------+--------+--------+ --- + */ + +/* 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. + */ +struct te_tlv_header +{ + u_int16_t type; /* TE_TLV_XXX (see below) */ + u_int16_t length; /* Value portion only, in octets */ +}; + +#define TLV_HDR_SIZE \ + (sizeof (struct te_tlv_header)) + +#define TLV_BODY_SIZE(tlvh) \ + (ROUNDUP (ntohs ((tlvh)->length), sizeof (u_int32_t))) + +#define TLV_SIZE(tlvh) \ + (TLV_HDR_SIZE + TLV_BODY_SIZE(tlvh)) + +#define TLV_HDR_TOP(lsah) \ + (struct te_tlv_header *)((char *)(lsah) + OSPF_LSA_HEADER_SIZE) + +#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 */ +#define TE_TLV_ROUTER_ADDR 1 +struct te_tlv_router_addr +{ + struct te_tlv_header header; /* Value length is 4 octets. */ + struct in_addr value; +}; + +/* Link TLV */ +#define TE_TLV_LINK 2 +struct te_tlv_link +{ + struct te_tlv_header header; + /* A set of link-sub-TLVs will follow. */ +}; + +#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 + { +#define LINK_TYPE_SUBTLV_VALUE_PTP 1 +#define LINK_TYPE_SUBTLV_VALUE_MA 2 + u_char value; + u_char padding[3]; + } link_type; +}; + +/* 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 +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 +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 +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 +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 +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 +#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[MAX_CLASS_TYPE]; /* One for each priority level. */ +}; + +/* 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. */ +}; + +/* 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 new file mode 100644 index 0000000..478d4ff --- /dev/null +++ b/ospfd/ospf_vty.c @@ -0,0 +1,7923 @@ +/* OSPF VTY interface. + * Copyright (C) 2005 6WIND + * Copyright (C) 2000 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. + */ + +#include + +#include "memory.h" +#include "thread.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" +#include "command.h" +#include "plist.h" +#include "log.h" +#include "zclient.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_ism.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_abr.h" +#include "ospfd/ospf_spf.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_zebra.h" +/*#include "ospfd/ospf_routemap.h" */ +#include "ospfd/ospf_vty.h" +#include "ospfd/ospf_dump.h" + + +static const char *ospf_network_type_str[] = +{ + "Null", + "POINTOPOINT", + "BROADCAST", + "NBMA", + "POINTOMULTIPOINT", + "VIRTUALLINK", + "LOOPBACK" +}; + + +/* Utility functions. */ +int +ospf_str2area_id (const char *str, struct in_addr *area_id, int *format) +{ + char *endptr = NULL; + unsigned long ret; + + /* match "A.B.C.D". */ + if (strchr (str, '.') != NULL) + { + ret = inet_aton (str, area_id); + if (!ret) + return -1; + *format = OSPF_AREA_ID_FORMAT_ADDRESS; + } + /* match "<0-4294967295>". */ + else + { + if (*str == '-') + return -1; + errno = 0; + ret = strtoul (str, &endptr, 10); + if (*endptr != '\0' || errno || ret > UINT32_MAX) + return -1; + + area_id->s_addr = htonl (ret); + *format = OSPF_AREA_ID_FORMAT_DECIMAL; + } + + return 0; +} + + +static int +str2metric (const char *str, int *metric) +{ + /* Sanity check. */ + if (str == NULL) + return 0; + + *metric = strtol (str, NULL, 10); + if (*metric < 0 && *metric > 16777214) + { + /* vty_out (vty, "OSPF metric value is invalid%s", VTY_NEWLINE); */ + return 0; + } + + return 1; +} + +static int +str2metric_type (const char *str, int *metric_type) +{ + /* Sanity check. */ + if (str == NULL) + return 0; + + if (strncmp (str, "1", 1) == 0) + *metric_type = EXTERNAL_METRIC_TYPE_1; + else if (strncmp (str, "2", 1) == 0) + *metric_type = EXTERNAL_METRIC_TYPE_2; + else + return 0; + + return 1; +} + +int +ospf_oi_count (struct interface *ifp) +{ + struct route_node *rn; + int i = 0; + + for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn)) + if (rn->info) + i++; + + return i; +} + + +DEFUN (router_ospf, + router_ospf_cmd, + "router ospf", + "Enable a routing process\n" + "Start OSPF configuration\n") +{ + vty->node = OSPF_NODE; + vty->index = ospf_get (); + + return CMD_SUCCESS; +} + +DEFUN (no_router_ospf, + no_router_ospf_cmd, + "no router ospf", + NO_STR + "Enable a routing process\n" + "Start OSPF configuration\n") +{ + struct ospf *ospf; + + ospf = ospf_lookup (); + if (ospf == NULL) + { + vty_out (vty, "There isn't active ospf instance%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ospf_finish (ospf); + + return CMD_SUCCESS; +} + +DEFUN (ospf_router_id, + ospf_router_id_cmd, + "ospf router-id A.B.C.D", + "OSPF specific commands\n" + "router-id for the OSPF process\n" + "OSPF router-id in IP address format\n") +{ + struct ospf *ospf = vty->index; + struct in_addr router_id; + int ret; + + ret = inet_aton (argv[0], &router_id); + if (!ret) + { + vty_out (vty, "Please specify Router ID by A.B.C.D%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ospf->router_id_static = router_id; + + ospf_router_id_update (ospf); + + return CMD_SUCCESS; +} + +ALIAS (ospf_router_id, + router_ospf_id_cmd, + "router-id A.B.C.D", + "router-id for the OSPF process\n" + "OSPF router-id in IP address format\n") + +DEFUN (no_ospf_router_id, + no_ospf_router_id_cmd, + "no ospf router-id", + NO_STR + "OSPF specific commands\n" + "router-id for the OSPF process\n") +{ + struct ospf *ospf = vty->index; + + ospf->router_id_static.s_addr = 0; + + ospf_router_id_update (ospf); + + return CMD_SUCCESS; +} + +ALIAS (no_ospf_router_id, + no_router_ospf_id_cmd, + "no router-id", + NO_STR + "router-id for the OSPF process\n") + +static void +ospf_passive_interface_default (struct ospf *ospf, u_char newval) +{ + struct listnode *ln; + struct interface *ifp; + struct ospf_interface *oi; + + ospf->passive_interface_default = newval; + + for (ALL_LIST_ELEMENTS_RO (om->iflist, ln, ifp)) + { + if (ifp && + OSPF_IF_PARAM_CONFIGURED (IF_DEF_PARAMS (ifp), passive_interface)) + UNSET_IF_PARAM (IF_DEF_PARAMS (ifp), passive_interface); + } + for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, ln, oi)) + { + if (OSPF_IF_PARAM_CONFIGURED (oi->params, passive_interface)) + UNSET_IF_PARAM (oi->params, passive_interface); + /* update multicast memberships */ + ospf_if_set_multicast(oi); + } +} + +static void +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; + + params->passive_interface = value; + if (params != IF_DEF_PARAMS (ifp)) + { + if (OSPF_IF_PARAM_CONFIGURED (IF_DEF_PARAMS (ifp), passive_interface)) + dflt = IF_DEF_PARAMS (ifp)->passive_interface; + else + dflt = ospf->passive_interface_default; + + if (value != dflt) + SET_IF_PARAM (params, passive_interface); + else + UNSET_IF_PARAM (params, passive_interface); + + ospf_free_if_params (ifp, addr); + ospf_if_update_params (ifp, addr); + } +} + +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); + else + UNSET_IF_PARAM (params, passive_interface); + } +} + +/* 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", + "Suppress routing updates on an interface\n" + "Interface's name\n") +{ + struct interface *ifp; + struct in_addr addr; + int ret; + struct ospf_if_params *params; + struct route_node *rn; + struct ospf *ospf = vty->index; + + if (argc == 0) + { + ospf_passive_interface_default (ospf, OSPF_IF_PASSIVE); + return CMD_SUCCESS; + } + + ifp = if_get_by_name (argv[0]); + + params = IF_DEF_PARAMS (ifp); + + if (argc == 2) + { + ret = inet_aton(argv[1], &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); + ospf_if_update_params (ifp, addr); + ospf_passive_interface_update_addr (ospf, ifp, params, + OSPF_IF_PASSIVE, addr); + } + + 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 + * work to determine this set, so we do this for every interface. + * This is safe and reasonable because ospf_if_set_multicast uses a + * record of joined groups to avoid systems calls if the desired + * memberships match the current memership. + */ + + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next (rn)) + { + struct ospf_interface *oi = rn->info; + + if (oi && (OSPF_IF_PARAM(oi, passive_interface) == OSPF_IF_PASSIVE)) + ospf_if_set_multicast(oi); + } + /* + * XXX It is not clear what state transitions the interface needs to + * undergo when going from active to passive. Fixing this will + * require precise identification of interfaces having such a + * transition. + */ + + return CMD_SUCCESS; +} + +ALIAS (ospf_passive_interface, + ospf_passive_interface_cmd, + "passive-interface IFNAME", + "Suppress routing updates on an interface\n" + "Interface's name\n") + +ALIAS (ospf_passive_interface, + ospf_passive_interface_default_cmd, + "passive-interface default", + "Suppress routing updates on an interface\n" + "Suppress routing updates on interfaces by default\n") + +DEFUN (no_ospf_passive_interface, + no_ospf_passive_interface_addr_cmd, + "no passive-interface IFNAME A.B.C.D", + NO_STR + "Allow routing updates on an interface\n" + "Interface's name\n") +{ + struct interface *ifp; + struct in_addr addr; + struct ospf_if_params *params; + int ret; + struct route_node *rn; + struct ospf *ospf = vty->index; + + if (argc == 0) + { + ospf_passive_interface_default (ospf, OSPF_IF_ACTIVE); + return CMD_SUCCESS; + } + + ifp = if_get_by_name (argv[0]); + + params = IF_DEF_PARAMS (ifp); + + if (argc == 2) + { + ret = inet_aton(argv[1], &addr); + if (!ret) + { + vty_out (vty, "Please specify interface address by A.B.C.D%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + 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, 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. + * This is safe and reasonable because ospf_if_set_multicast uses a + * record of joined groups to avoid systems calls if the desired + * memberships match the current memership. + */ + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next (rn)) + { + struct ospf_interface *oi = rn->info; + + if (oi && (OSPF_IF_PARAM(oi, passive_interface) == OSPF_IF_ACTIVE)) + ospf_if_set_multicast(oi); + } + + return CMD_SUCCESS; +} + +ALIAS (no_ospf_passive_interface, + no_ospf_passive_interface_cmd, + "no passive-interface IFNAME", + NO_STR + "Allow routing updates on an interface\n" + "Interface's name\n") + +ALIAS (no_ospf_passive_interface, + no_ospf_passive_interface_default_cmd, + "no passive-interface default", + NO_STR + "Allow routing updates on an interface\n" + "Allow routing updates on interfaces by default\n") + +DEFUN (ospf_network_area, + ospf_network_area_cmd, + "network A.B.C.D/M area (A.B.C.D|<0-4294967295>)", + "Enable routing on an IP network\n" + "OSPF network prefix\n" + "Set the OSPF area ID\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n") +{ + struct ospf *ospf = vty->index; + struct prefix_ipv4 p; + struct in_addr area_id; + int ret, format; + + /* Get network prefix and Area ID. */ + VTY_GET_IPV4_PREFIX ("network prefix", p, argv[0]); + VTY_GET_OSPF_AREA_ID (area_id, format, argv[1]); + + ret = ospf_network_set (ospf, &p, area_id); + if (ret == 0) + { + vty_out (vty, "There is already same network statement.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_network_area, + no_ospf_network_area_cmd, + "no network A.B.C.D/M area (A.B.C.D|<0-4294967295>)", + NO_STR + "Enable routing on an IP network\n" + "OSPF network prefix\n" + "Set the OSPF area ID\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n") +{ + struct ospf *ospf = (struct ospf *) vty->index; + struct prefix_ipv4 p; + struct in_addr area_id; + int ret, format; + + /* Get network prefix and Area ID. */ + VTY_GET_IPV4_PREFIX ("network prefix", p, argv[0]); + VTY_GET_OSPF_AREA_ID (area_id, format, argv[1]); + + ret = ospf_network_unset (ospf, &p, area_id); + if (ret == 0) + { + vty_out (vty, "Can't find specified network area configuration.%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + + +DEFUN (ospf_area_range, + ospf_area_range_cmd, + "area (A.B.C.D|<0-4294967295>) range A.B.C.D/M", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Summarize routes matching address/mask (border routers only)\n" + "Area range prefix\n") +{ + struct ospf *ospf = vty->index; + struct prefix_ipv4 p; + struct in_addr area_id; + int format; + u_int32_t cost; + + VTY_GET_OSPF_AREA_ID (area_id, format, argv[0]); + VTY_GET_IPV4_PREFIX ("area range", p, argv[1]); + + ospf_area_range_set (ospf, area_id, &p, OSPF_AREA_RANGE_ADVERTISE); + if (argc > 2) + { + VTY_GET_INTEGER ("range cost", cost, argv[2]); + ospf_area_range_cost_set (ospf, area_id, &p, cost); + } + + return CMD_SUCCESS; +} + +ALIAS (ospf_area_range, + ospf_area_range_advertise_cmd, + "area (A.B.C.D|<0-4294967295>) range A.B.C.D/M advertise", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "OSPF area range for route advertise (default)\n" + "Area range prefix\n" + "Advertise this range (default)\n") + +ALIAS (ospf_area_range, + ospf_area_range_cost_cmd, + "area (A.B.C.D|<0-4294967295>) range A.B.C.D/M cost <0-16777215>", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Summarize routes matching address/mask (border routers only)\n" + "Area range prefix\n" + "User specified metric for this range\n" + "Advertised metric for this range\n") + +ALIAS (ospf_area_range, + ospf_area_range_advertise_cost_cmd, + "area (A.B.C.D|<0-4294967295>) range A.B.C.D/M advertise cost <0-16777215>", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Summarize routes matching address/mask (border routers only)\n" + "Area range prefix\n" + "Advertise this range (default)\n" + "User specified metric for this range\n" + "Advertised metric for this range\n") + +DEFUN (ospf_area_range_not_advertise, + ospf_area_range_not_advertise_cmd, + "area (A.B.C.D|<0-4294967295>) range A.B.C.D/M not-advertise", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Summarize routes matching address/mask (border routers only)\n" + "Area range prefix\n" + "DoNotAdvertise this range\n") +{ + struct ospf *ospf = vty->index; + struct prefix_ipv4 p; + struct in_addr area_id; + int format; + + VTY_GET_OSPF_AREA_ID (area_id, format, argv[0]); + VTY_GET_IPV4_PREFIX ("area range", p, argv[1]); + + ospf_area_range_set (ospf, area_id, &p, 0); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_area_range, + no_ospf_area_range_cmd, + "no area (A.B.C.D|<0-4294967295>) range A.B.C.D/M", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Summarize routes matching address/mask (border routers only)\n" + "Area range prefix\n") +{ + struct ospf *ospf = vty->index; + struct prefix_ipv4 p; + struct in_addr area_id; + int format; + + VTY_GET_OSPF_AREA_ID (area_id, format, argv[0]); + VTY_GET_IPV4_PREFIX ("area range", p, argv[1]); + + ospf_area_range_unset (ospf, area_id, &p); + + return CMD_SUCCESS; +} + +ALIAS (no_ospf_area_range, + no_ospf_area_range_advertise_cmd, + "no area (A.B.C.D|<0-4294967295>) range A.B.C.D/M (advertise|not-advertise)", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Summarize routes matching address/mask (border routers only)\n" + "Area range prefix\n" + "Advertise this range (default)\n" + "DoNotAdvertise this range\n") + +ALIAS (no_ospf_area_range, + no_ospf_area_range_cost_cmd, + "no area (A.B.C.D|<0-4294967295>) range A.B.C.D/M cost <0-16777215>", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Summarize routes matching address/mask (border routers only)\n" + "Area range prefix\n" + "User specified metric for this range\n" + "Advertised metric for this range\n") + +ALIAS (no_ospf_area_range, + no_ospf_area_range_advertise_cost_cmd, + "no area (A.B.C.D|<0-4294967295>) range A.B.C.D/M advertise cost <0-16777215>", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Summarize routes matching address/mask (border routers only)\n" + "Area range prefix\n" + "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", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Summarize routes matching address/mask (border routers only)\n" + "Area range prefix\n" + "Announce area range as another prefix\n" + "Network prefix to be announced instead of range\n") +{ + struct ospf *ospf = vty->index; + struct prefix_ipv4 p, s; + struct in_addr area_id; + int format; + + VTY_GET_OSPF_AREA_ID (area_id, format, argv[0]); + VTY_GET_IPV4_PREFIX ("area range", p, argv[1]); + VTY_GET_IPV4_PREFIX ("substituted network prefix", s, argv[2]); + + ospf_area_range_substitute_set (ospf, area_id, &p, &s); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_area_range_substitute, + no_ospf_area_range_substitute_cmd, + "no area (A.B.C.D|<0-4294967295>) range A.B.C.D/M substitute A.B.C.D/M", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Summarize routes matching address/mask (border routers only)\n" + "Area range prefix\n" + "Announce area range as another prefix\n" + "Network prefix to be announced instead of range\n") +{ + struct ospf *ospf = vty->index; + struct prefix_ipv4 p, s; + struct in_addr area_id; + int format; + + VTY_GET_OSPF_AREA_ID (area_id, format, argv[0]); + VTY_GET_IPV4_PREFIX ("area range", p, argv[1]); + VTY_GET_IPV4_PREFIX ("substituted network prefix", s, argv[2]); + + ospf_area_range_substitute_unset (ospf, area_id, &p); + + return CMD_SUCCESS; +} + + +/* Command Handler Logic in VLink stuff is delicate!! + + ALTER AT YOUR OWN RISK!!!! + + Various dummy values are used to represent 'NoChange' state for + VLink configuration NOT being changed by a VLink command, and + special syntax is used within the command strings so that the + typed in command verbs can be seen in the configuration command + bacckend handler. This is to drastically reduce the verbeage + required to coe up with a reasonably compatible Cisco VLink command + + - Matthew Grant + Wed, 21 Feb 2001 15:13:52 +1300 + */ + + +/* Configuration data for virtual links + */ +struct ospf_vl_config_data { + struct vty *vty; /* vty stuff */ + struct in_addr area_id; /* area ID from command line */ + int format; /* command line area ID format */ + struct in_addr vl_peer; /* command line vl_peer */ + int auth_type; /* Authehntication type, if given */ + char *auth_key; /* simple password if present */ + int crypto_key_id; /* Cryptographic key ID */ + char *md5_key; /* MD5 authentication key */ + int hello_interval; /* Obvious what these are... */ + int retransmit_interval; + int transmit_delay; + int dead_interval; +}; + +static void +ospf_vl_config_data_init (struct ospf_vl_config_data *vl_config, + struct vty *vty) +{ + memset (vl_config, 0, sizeof (struct ospf_vl_config_data)); + vl_config->auth_type = OSPF_AUTH_CMD_NOTSEEN; + vl_config->vty = vty; +} + +static struct ospf_vl_data * +ospf_find_vl_data (struct ospf *ospf, struct ospf_vl_config_data *vl_config) +{ + struct ospf_area *area; + struct ospf_vl_data *vl_data; + struct vty *vty; + struct in_addr area_id; + + vty = vl_config->vty; + area_id = vl_config->area_id; + + if (area_id.s_addr == OSPF_AREA_BACKBONE) + { + vty_out (vty, + "Configuring VLs over the backbone is not allowed%s", + VTY_NEWLINE); + return NULL; + } + area = ospf_area_get (ospf, area_id, vl_config->format); + + if (area->external_routing != OSPF_AREA_DEFAULT) + { + if (vl_config->format == OSPF_AREA_ID_FORMAT_ADDRESS) + vty_out (vty, "Area %s is %s%s", + inet_ntoa (area_id), + area->external_routing == OSPF_AREA_NSSA?"nssa":"stub", + VTY_NEWLINE); + else + vty_out (vty, "Area %ld is %s%s", + (u_long)ntohl (area_id.s_addr), + area->external_routing == OSPF_AREA_NSSA?"nssa":"stub", + VTY_NEWLINE); + return NULL; + } + + if ((vl_data = ospf_vl_lookup (ospf, area, vl_config->vl_peer)) == NULL) + { + vl_data = ospf_vl_data_new (area, vl_config->vl_peer); + if (vl_data->vl_oi == NULL) + { + vl_data->vl_oi = ospf_vl_new (ospf, vl_data); + ospf_vl_add (ospf, vl_data); + ospf_spf_calculate_schedule (ospf, SPF_FLAG_CONFIG_CHANGE); + } + } + return vl_data; +} + + +static int +ospf_vl_set_security (struct ospf_vl_data *vl_data, + struct ospf_vl_config_data *vl_config) +{ + struct crypt_key *ck; + struct vty *vty; + struct interface *ifp = vl_data->vl_oi->ifp; + + vty = vl_config->vty; + + if (vl_config->auth_type != OSPF_AUTH_CMD_NOTSEEN) + { + SET_IF_PARAM (IF_DEF_PARAMS (ifp), auth_type); + IF_DEF_PARAMS (ifp)->auth_type = vl_config->auth_type; + } + + if (vl_config->auth_key) + { + memset(IF_DEF_PARAMS (ifp)->auth_simple, 0, OSPF_AUTH_SIMPLE_SIZE+1); + strncpy ((char *) IF_DEF_PARAMS (ifp)->auth_simple, vl_config->auth_key, + OSPF_AUTH_SIMPLE_SIZE); + } + else if (vl_config->md5_key) + { + if (ospf_crypt_key_lookup (IF_DEF_PARAMS (ifp)->auth_crypt, vl_config->crypto_key_id) + != NULL) + { + vty_out (vty, "OSPF: Key %d already exists%s", + vl_config->crypto_key_id, VTY_NEWLINE); + return CMD_WARNING; + } + ck = ospf_crypt_key_new (); + ck->key_id = vl_config->crypto_key_id; + memset(ck->auth_key, 0, OSPF_AUTH_MD5_SIZE+1); + strncpy ((char *) ck->auth_key, vl_config->md5_key, OSPF_AUTH_MD5_SIZE); + + ospf_crypt_key_add (IF_DEF_PARAMS (ifp)->auth_crypt, ck); + } + else if (vl_config->crypto_key_id != 0) + { + /* Delete a key */ + + if (ospf_crypt_key_lookup (IF_DEF_PARAMS (ifp)->auth_crypt, + vl_config->crypto_key_id) == NULL) + { + vty_out (vty, "OSPF: Key %d does not exist%s", + vl_config->crypto_key_id, VTY_NEWLINE); + return CMD_WARNING; + } + + ospf_crypt_key_delete (IF_DEF_PARAMS (ifp)->auth_crypt, vl_config->crypto_key_id); + + } + + return CMD_SUCCESS; +} + +static int +ospf_vl_set_timers (struct ospf_vl_data *vl_data, + struct ospf_vl_config_data *vl_config) +{ + 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) + { + SET_IF_PARAM (IF_DEF_PARAMS (ifp), v_hello); + IF_DEF_PARAMS (ifp)->v_hello = vl_config->hello_interval; + } + + if (vl_config->dead_interval) + { + SET_IF_PARAM (IF_DEF_PARAMS (ifp), v_wait); + IF_DEF_PARAMS (ifp)->v_wait = vl_config->dead_interval; + } + + if (vl_config->retransmit_interval) + { + SET_IF_PARAM (IF_DEF_PARAMS (ifp), retransmit_interval); + IF_DEF_PARAMS (ifp)->retransmit_interval = vl_config->retransmit_interval; + } + + if (vl_config->transmit_delay) + { + SET_IF_PARAM (IF_DEF_PARAMS (ifp), transmit_delay); + IF_DEF_PARAMS (ifp)->transmit_delay = vl_config->transmit_delay; + } + + return CMD_SUCCESS; +} + + + +/* The business end of all of the above */ +static int +ospf_vl_set (struct ospf *ospf, struct ospf_vl_config_data *vl_config) +{ + struct ospf_vl_data *vl_data; + int ret; + + vl_data = ospf_find_vl_data (ospf, vl_config); + if (!vl_data) + return CMD_WARNING; + + /* Process this one first as it can have a fatal result, which can + only logically occur if the virtual link exists already + Thus a command error does not result in a change to the + running configuration such as unexpectedly altered timer + values etc.*/ + ret = ospf_vl_set_security (vl_data, vl_config); + if (ret != CMD_SUCCESS) + return ret; + + /* Set any time based parameters, these area already range checked */ + + ret = ospf_vl_set_timers (vl_data, vl_config); + if (ret != CMD_SUCCESS) + return ret; + + return CMD_SUCCESS; + +} + +/* This stuff exists to make specifying all the alias commands A LOT simpler + */ +#define VLINK_HELPSTR_IPADDR \ + "OSPF area parameters\n" \ + "OSPF area ID in IP address format\n" \ + "OSPF area ID as a decimal value\n" \ + "Configure a virtual link\n" \ + "Router ID of the remote ABR\n" + +#define VLINK_HELPSTR_AUTHTYPE_SIMPLE \ + "Enable authentication on this virtual link\n" \ + "dummy string \n" + +#define VLINK_HELPSTR_AUTHTYPE_ALL \ + VLINK_HELPSTR_AUTHTYPE_SIMPLE \ + "Use null authentication\n" \ + "Use message-digest authentication\n" + +#define VLINK_HELPSTR_TIME_PARAM_NOSECS \ + "Time between HELLO packets\n" \ + "Time between retransmitting lost link state advertisements\n" \ + "Link state transmit delay\n" \ + "Interval after which a neighbor is declared dead\n" + +#define VLINK_HELPSTR_TIME_PARAM \ + VLINK_HELPSTR_TIME_PARAM_NOSECS \ + "Seconds\n" + +#define VLINK_HELPSTR_AUTH_SIMPLE \ + "Authentication password (key)\n" \ + "The OSPF password (key)" + +#define VLINK_HELPSTR_AUTH_MD5 \ + "Message digest authentication password (key)\n" \ + "dummy string \n" \ + "Key ID\n" \ + "Use MD5 algorithm\n" \ + "The OSPF password (key)" + +DEFUN (ospf_area_vlink, + ospf_area_vlink_cmd, + "area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D", + VLINK_HELPSTR_IPADDR) +{ + struct ospf *ospf = vty->index; + struct ospf_vl_config_data vl_config; + char auth_key[OSPF_AUTH_SIMPLE_SIZE+1]; + char md5_key[OSPF_AUTH_MD5_SIZE+1]; + int i; + int ret; + + ospf_vl_config_data_init(&vl_config, vty); + + /* Read off first 2 parameters and check them */ + ret = ospf_str2area_id (argv[0], &vl_config.area_id, &vl_config.format); + if (ret < 0) + { + vty_out (vty, "OSPF area ID is invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ret = inet_aton (argv[1], &vl_config.vl_peer); + if (! ret) + { + vty_out (vty, "Please specify valid Router ID as a.b.c.d%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + if (argc <=2) + { + /* Thats all folks! - BUGS B. strikes again!!!*/ + + return ospf_vl_set (ospf, &vl_config); + } + + /* Deal with other parameters */ + for (i=2; i < argc; i++) + { + + /* vty_out (vty, "argv[%d] - %s%s", i, argv[i], VTY_NEWLINE); */ + + switch (argv[i][0]) + { + + case 'a': + if (i > 2 || strncmp (argv[i], "authentication-", 15) == 0) + { + /* authentication-key - this option can occur anywhere on + command line. At start of command line + must check for authentication option. */ + memset (auth_key, 0, OSPF_AUTH_SIMPLE_SIZE + 1); + strncpy (auth_key, argv[i+1], OSPF_AUTH_SIMPLE_SIZE); + vl_config.auth_key = auth_key; + i++; + } + else if (strncmp (argv[i], "authentication", 14) == 0) + { + /* authentication - this option can only occur at start + of command line */ + vl_config.auth_type = OSPF_AUTH_SIMPLE; + if ((i+1) < argc) + { + if (strncmp (argv[i+1], "n", 1) == 0) + { + /* "authentication null" */ + vl_config.auth_type = OSPF_AUTH_NULL; + i++; + } + else if (strncmp (argv[i+1], "m", 1) == 0 + && strcmp (argv[i+1], "message-digest-") != 0) + { + /* "authentication message-digest" */ + vl_config.auth_type = OSPF_AUTH_CRYPTOGRAPHIC; + i++; + } + } + } + break; + + case 'm': + /* message-digest-key */ + i++; + vl_config.crypto_key_id = strtol (argv[i], NULL, 10); + if (vl_config.crypto_key_id < 0) + return CMD_WARNING; + i++; + memset(md5_key, 0, OSPF_AUTH_MD5_SIZE+1); + strncpy (md5_key, argv[i], OSPF_AUTH_MD5_SIZE); + vl_config.md5_key = md5_key; + break; + + case 'h': + /* Hello interval */ + i++; + vl_config.hello_interval = strtol (argv[i], NULL, 10); + if (vl_config.hello_interval < 0) + return CMD_WARNING; + break; + + case 'r': + /* Retransmit Interval */ + i++; + vl_config.retransmit_interval = strtol (argv[i], NULL, 10); + if (vl_config.retransmit_interval < 0) + return CMD_WARNING; + break; + + case 't': + /* Transmit Delay */ + i++; + vl_config.transmit_delay = strtol (argv[i], NULL, 10); + if (vl_config.transmit_delay < 0) + return CMD_WARNING; + break; + + case 'd': + /* Dead Interval */ + i++; + vl_config.dead_interval = strtol (argv[i], NULL, 10); + if (vl_config.dead_interval < 0) + return CMD_WARNING; + break; + } + } + + + /* Action configuration */ + + return ospf_vl_set (ospf, &vl_config); + +} + +DEFUN (no_ospf_area_vlink, + no_ospf_area_vlink_cmd, + "no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D", + NO_STR + VLINK_HELPSTR_IPADDR) +{ + struct ospf *ospf = vty->index; + struct ospf_area *area; + struct ospf_vl_config_data vl_config; + struct ospf_vl_data *vl_data = NULL; + char auth_key[OSPF_AUTH_SIMPLE_SIZE+1]; + int i; + int ret, format; + + ospf_vl_config_data_init(&vl_config, vty); + + ret = ospf_str2area_id (argv[0], &vl_config.area_id, &format); + if (ret < 0) + { + vty_out (vty, "OSPF area ID is invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + + area = ospf_area_lookup_by_area_id (ospf, vl_config.area_id); + if (!area) + { + vty_out (vty, "Area does not exist%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ret = inet_aton (argv[1], &vl_config.vl_peer); + if (! ret) + { + vty_out (vty, "Please specify valid Router ID as a.b.c.d%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + if (argc <=2) + { + /* Basic VLink no command */ + /* Thats all folks! - BUGS B. strikes again!!!*/ + if ((vl_data = ospf_vl_lookup (ospf, area, vl_config.vl_peer))) + ospf_vl_delete (ospf, vl_data); + + ospf_area_check_free (ospf, vl_config.area_id); + + return CMD_SUCCESS; + } + + /* If we are down here, we are reseting parameters */ + + /* Deal with other parameters */ + for (i=2; i < argc; i++) + { + /* vty_out (vty, "argv[%d] - %s%s", i, argv[i], VTY_NEWLINE); */ + + switch (argv[i][0]) + { + + case 'a': + if (i > 2 || strncmp (argv[i], "authentication-", 15) == 0) + { + /* authentication-key - this option can occur anywhere on + command line. At start of command line + must check for authentication option. */ + memset (auth_key, 0, OSPF_AUTH_SIMPLE_SIZE + 1); + vl_config.auth_key = auth_key; + } + else if (strncmp (argv[i], "authentication", 14) == 0) + { + /* authentication - this option can only occur at start + of command line */ + vl_config.auth_type = OSPF_AUTH_NOTSET; + } + break; + + case 'm': + /* message-digest-key */ + /* Delete one key */ + i++; + vl_config.crypto_key_id = strtol (argv[i], NULL, 10); + if (vl_config.crypto_key_id < 0) + return CMD_WARNING; + vl_config.md5_key = NULL; + break; + + case 'h': + /* Hello interval */ + vl_config.hello_interval = OSPF_HELLO_INTERVAL_DEFAULT; + break; + + case 'r': + /* Retransmit Interval */ + vl_config.retransmit_interval = OSPF_RETRANSMIT_INTERVAL_DEFAULT; + break; + + case 't': + /* Transmit Delay */ + vl_config.transmit_delay = OSPF_TRANSMIT_DELAY_DEFAULT; + break; + + case 'd': + /* Dead Interval */ + i++; + vl_config.dead_interval = OSPF_ROUTER_DEAD_INTERVAL_DEFAULT; + break; + } + } + + + /* Action configuration */ + + return ospf_vl_set (ospf, &vl_config); +} + +ALIAS (ospf_area_vlink, + ospf_area_vlink_param1_cmd, + "area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D " + "(hello-interval|retransmit-interval|transmit-delay|dead-interval) <1-65535>", + VLINK_HELPSTR_IPADDR + VLINK_HELPSTR_TIME_PARAM) + +ALIAS (no_ospf_area_vlink, + no_ospf_area_vlink_param1_cmd, + "no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D " + "(hello-interval|retransmit-interval|transmit-delay|dead-interval)", + NO_STR + VLINK_HELPSTR_IPADDR + VLINK_HELPSTR_TIME_PARAM) + +ALIAS (ospf_area_vlink, + ospf_area_vlink_param2_cmd, + "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>", + VLINK_HELPSTR_IPADDR + VLINK_HELPSTR_TIME_PARAM + VLINK_HELPSTR_TIME_PARAM) + +ALIAS (no_ospf_area_vlink, + no_ospf_area_vlink_param2_cmd, + "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)", + NO_STR + VLINK_HELPSTR_IPADDR + VLINK_HELPSTR_TIME_PARAM + VLINK_HELPSTR_TIME_PARAM) + +ALIAS (ospf_area_vlink, + ospf_area_vlink_param3_cmd, + "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>", + VLINK_HELPSTR_IPADDR + VLINK_HELPSTR_TIME_PARAM + VLINK_HELPSTR_TIME_PARAM + VLINK_HELPSTR_TIME_PARAM) + +ALIAS (no_ospf_area_vlink, + no_ospf_area_vlink_param3_cmd, + "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)", + NO_STR + VLINK_HELPSTR_IPADDR + VLINK_HELPSTR_TIME_PARAM + VLINK_HELPSTR_TIME_PARAM + VLINK_HELPSTR_TIME_PARAM) + +ALIAS (ospf_area_vlink, + ospf_area_vlink_param4_cmd, + "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>", + VLINK_HELPSTR_IPADDR + VLINK_HELPSTR_TIME_PARAM + VLINK_HELPSTR_TIME_PARAM + VLINK_HELPSTR_TIME_PARAM + VLINK_HELPSTR_TIME_PARAM) + +ALIAS (no_ospf_area_vlink, + no_ospf_area_vlink_param4_cmd, + "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)", + NO_STR + VLINK_HELPSTR_IPADDR + VLINK_HELPSTR_TIME_PARAM + VLINK_HELPSTR_TIME_PARAM + VLINK_HELPSTR_TIME_PARAM + VLINK_HELPSTR_TIME_PARAM) + +ALIAS (ospf_area_vlink, + ospf_area_vlink_authtype_args_cmd, + "area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D " + "(authentication|) (message-digest|null)", + VLINK_HELPSTR_IPADDR + VLINK_HELPSTR_AUTHTYPE_ALL) + +ALIAS (ospf_area_vlink, + ospf_area_vlink_authtype_cmd, + "area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D " + "(authentication|)", + VLINK_HELPSTR_IPADDR + VLINK_HELPSTR_AUTHTYPE_SIMPLE) + +ALIAS (no_ospf_area_vlink, + no_ospf_area_vlink_authtype_cmd, + "no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D " + "(authentication|)", + NO_STR + VLINK_HELPSTR_IPADDR + VLINK_HELPSTR_AUTHTYPE_SIMPLE) + +ALIAS (ospf_area_vlink, + ospf_area_vlink_md5_cmd, + "area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D " + "(message-digest-key|) <1-255> md5 KEY", + VLINK_HELPSTR_IPADDR + VLINK_HELPSTR_AUTH_MD5) + +ALIAS (no_ospf_area_vlink, + no_ospf_area_vlink_md5_cmd, + "no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D " + "(message-digest-key|) <1-255>", + NO_STR + VLINK_HELPSTR_IPADDR + VLINK_HELPSTR_AUTH_MD5) + +ALIAS (ospf_area_vlink, + ospf_area_vlink_authkey_cmd, + "area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D " + "(authentication-key|) AUTH_KEY", + VLINK_HELPSTR_IPADDR + VLINK_HELPSTR_AUTH_SIMPLE) + +ALIAS (no_ospf_area_vlink, + no_ospf_area_vlink_authkey_cmd, + "no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D " + "(authentication-key|)", + NO_STR + VLINK_HELPSTR_IPADDR + VLINK_HELPSTR_AUTH_SIMPLE) + +ALIAS (ospf_area_vlink, + ospf_area_vlink_authtype_args_authkey_cmd, + "area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D " + "(authentication|) (message-digest|null) " + "(authentication-key|) AUTH_KEY", + VLINK_HELPSTR_IPADDR + VLINK_HELPSTR_AUTHTYPE_ALL + VLINK_HELPSTR_AUTH_SIMPLE) + +ALIAS (ospf_area_vlink, + ospf_area_vlink_authtype_authkey_cmd, + "area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D " + "(authentication|) " + "(authentication-key|) AUTH_KEY", + VLINK_HELPSTR_IPADDR + VLINK_HELPSTR_AUTHTYPE_SIMPLE + VLINK_HELPSTR_AUTH_SIMPLE) + +ALIAS (no_ospf_area_vlink, + no_ospf_area_vlink_authtype_authkey_cmd, + "no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D " + "(authentication|) " + "(authentication-key|)", + NO_STR + VLINK_HELPSTR_IPADDR + VLINK_HELPSTR_AUTHTYPE_SIMPLE + VLINK_HELPSTR_AUTH_SIMPLE) + +ALIAS (ospf_area_vlink, + ospf_area_vlink_authtype_args_md5_cmd, + "area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D " + "(authentication|) (message-digest|null) " + "(message-digest-key|) <1-255> md5 KEY", + VLINK_HELPSTR_IPADDR + VLINK_HELPSTR_AUTHTYPE_ALL + VLINK_HELPSTR_AUTH_MD5) + +ALIAS (ospf_area_vlink, + ospf_area_vlink_authtype_md5_cmd, + "area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D " + "(authentication|) " + "(message-digest-key|) <1-255> md5 KEY", + VLINK_HELPSTR_IPADDR + VLINK_HELPSTR_AUTHTYPE_SIMPLE + VLINK_HELPSTR_AUTH_MD5) + +ALIAS (no_ospf_area_vlink, + no_ospf_area_vlink_authtype_md5_cmd, + "no area (A.B.C.D|<0-4294967295>) virtual-link A.B.C.D " + "(authentication|) " + "(message-digest-key|)", + NO_STR + VLINK_HELPSTR_IPADDR + 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)", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Configure the area's shortcutting mode\n" + "Set default shortcutting behavior\n" + "Enable shortcutting through the area\n" + "Disable shortcutting through the area\n") +{ + struct ospf *ospf = vty->index; + struct ospf_area *area; + struct in_addr area_id; + int mode; + int format; + + VTY_GET_OSPF_AREA_ID_NO_BB ("shortcut", area_id, format, argv[0]); + + area = ospf_area_get (ospf, area_id, format); + + if (strncmp (argv[1], "de", 2) == 0) + mode = OSPF_SHORTCUT_DEFAULT; + else if (strncmp (argv[1], "di", 2) == 0) + mode = OSPF_SHORTCUT_DISABLE; + else if (strncmp (argv[1], "e", 1) == 0) + mode = OSPF_SHORTCUT_ENABLE; + else + return CMD_WARNING; + + ospf_area_shortcut_set (ospf, area, mode); + + if (ospf->abr_type != OSPF_ABR_SHORTCUT) + vty_out (vty, "Shortcut area setting will take effect " + "only when the router is configured as Shortcut ABR%s", + VTY_NEWLINE); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_area_shortcut, + no_ospf_area_shortcut_cmd, + "no area (A.B.C.D|<0-4294967295>) shortcut (enable|disable)", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Deconfigure the area's shortcutting mode\n" + "Deconfigure enabled shortcutting through the area\n" + "Deconfigure disabled shortcutting through the area\n") +{ + struct ospf *ospf = vty->index; + struct ospf_area *area; + struct in_addr area_id; + int format; + + VTY_GET_OSPF_AREA_ID_NO_BB ("shortcut", area_id, format, argv[0]); + + area = ospf_area_lookup_by_area_id (ospf, area_id); + if (!area) + return CMD_SUCCESS; + + ospf_area_shortcut_unset (ospf, area); + + return CMD_SUCCESS; +} + + +DEFUN (ospf_area_stub, + ospf_area_stub_cmd, + "area (A.B.C.D|<0-4294967295>) stub", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Configure OSPF area as stub\n") +{ + struct ospf *ospf = vty->index; + struct in_addr area_id; + int ret, format; + + VTY_GET_OSPF_AREA_ID_NO_BB ("stub", area_id, format, argv[0]); + + ret = ospf_area_stub_set (ospf, area_id); + if (ret == 0) + { + vty_out (vty, "First deconfigure all virtual link through this area%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + ospf_area_no_summary_unset (ospf, area_id); + + return CMD_SUCCESS; +} + +DEFUN (ospf_area_stub_no_summary, + ospf_area_stub_no_summary_cmd, + "area (A.B.C.D|<0-4294967295>) stub no-summary", + "OSPF stub parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Configure OSPF area as stub\n" + "Do not inject inter-area routes into stub\n") +{ + struct ospf *ospf = vty->index; + struct in_addr area_id; + int ret, format; + + VTY_GET_OSPF_AREA_ID_NO_BB ("stub", area_id, format, argv[0]); + + ret = ospf_area_stub_set (ospf, area_id); + if (ret == 0) + { + vty_out (vty, "%% Area cannot be stub as it contains a virtual link%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + ospf_area_no_summary_set (ospf, area_id); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_area_stub, + no_ospf_area_stub_cmd, + "no area (A.B.C.D|<0-4294967295>) stub", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Configure OSPF area as stub\n") +{ + struct ospf *ospf = vty->index; + struct in_addr area_id; + int format; + + VTY_GET_OSPF_AREA_ID_NO_BB ("stub", area_id, format, argv[0]); + + ospf_area_stub_unset (ospf, area_id); + ospf_area_no_summary_unset (ospf, area_id); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_area_stub_no_summary, + no_ospf_area_stub_no_summary_cmd, + "no area (A.B.C.D|<0-4294967295>) stub no-summary", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Configure OSPF area as stub\n" + "Do not inject inter-area routes into area\n") +{ + struct ospf *ospf = vty->index; + struct in_addr area_id; + int format; + + VTY_GET_OSPF_AREA_ID_NO_BB ("stub", area_id, format, argv[0]); + ospf_area_no_summary_unset (ospf, area_id); + + return CMD_SUCCESS; +} + +static int +ospf_area_nssa_cmd_handler (struct vty *vty, int argc, const char *argv[], + int nosum) +{ + struct ospf *ospf = vty->index; + struct in_addr area_id; + int ret, format; + + VTY_GET_OSPF_AREA_ID_NO_BB ("NSSA", area_id, format, argv[0]); + + ret = ospf_area_nssa_set (ospf, area_id); + if (ret == 0) + { + vty_out (vty, "%% Area cannot be nssa as it contains a virtual link%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + if (argc > 1) + { + if (strncmp (argv[1], "translate-c", 11) == 0) + ospf_area_nssa_translator_role_set (ospf, area_id, + OSPF_NSSA_ROLE_CANDIDATE); + else if (strncmp (argv[1], "translate-n", 11) == 0) + ospf_area_nssa_translator_role_set (ospf, area_id, + OSPF_NSSA_ROLE_NEVER); + else if (strncmp (argv[1], "translate-a", 11) == 0) + ospf_area_nssa_translator_role_set (ospf, area_id, + OSPF_NSSA_ROLE_ALWAYS); + } + else + { + ospf_area_nssa_translator_role_set (ospf, area_id, + OSPF_NSSA_ROLE_CANDIDATE); + } + + if (nosum) + ospf_area_no_summary_set (ospf, area_id); + else + ospf_area_no_summary_unset (ospf, area_id); + + ospf_schedule_abr_task (ospf); + + return CMD_SUCCESS; +} + +DEFUN (ospf_area_nssa_translate_no_summary, + ospf_area_nssa_translate_no_summary_cmd, + "area (A.B.C.D|<0-4294967295>) nssa (translate-candidate|translate-never|translate-always) no-summary", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Configure OSPF area as nssa\n" + "Configure NSSA-ABR for translate election (default)\n" + "Configure NSSA-ABR to never translate\n" + "Configure NSSA-ABR to always translate\n" + "Do not inject inter-area routes into nssa\n") +{ + return ospf_area_nssa_cmd_handler (vty, argc, argv, 1); +} + +DEFUN (ospf_area_nssa_translate, + ospf_area_nssa_translate_cmd, + "area (A.B.C.D|<0-4294967295>) nssa (translate-candidate|translate-never|translate-always)", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Configure OSPF area as nssa\n" + "Configure NSSA-ABR for translate election (default)\n" + "Configure NSSA-ABR to never translate\n" + "Configure NSSA-ABR to always translate\n") +{ + return ospf_area_nssa_cmd_handler (vty, argc, argv, 0); +} + +DEFUN (ospf_area_nssa, + ospf_area_nssa_cmd, + "area (A.B.C.D|<0-4294967295>) nssa", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Configure OSPF area as nssa\n") +{ + return ospf_area_nssa_cmd_handler (vty, argc, argv, 0); +} + +DEFUN (ospf_area_nssa_no_summary, + ospf_area_nssa_no_summary_cmd, + "area (A.B.C.D|<0-4294967295>) nssa no-summary", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Configure OSPF area as nssa\n" + "Do not inject inter-area routes into nssa\n") +{ + return ospf_area_nssa_cmd_handler (vty, argc, argv, 1); +} + +DEFUN (no_ospf_area_nssa, + no_ospf_area_nssa_cmd, + "no area (A.B.C.D|<0-4294967295>) nssa", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Configure OSPF area as nssa\n") +{ + struct ospf *ospf = vty->index; + struct in_addr area_id; + int format; + + VTY_GET_OSPF_AREA_ID_NO_BB ("NSSA", area_id, format, argv[0]); + + ospf_area_nssa_unset (ospf, area_id); + ospf_area_no_summary_unset (ospf, area_id); + + ospf_schedule_abr_task (ospf); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_area_nssa_no_summary, + no_ospf_area_nssa_no_summary_cmd, + "no area (A.B.C.D|<0-4294967295>) nssa no-summary", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Configure OSPF area as nssa\n" + "Do not inject inter-area routes into nssa\n") +{ + struct ospf *ospf = vty->index; + struct in_addr area_id; + int format; + + VTY_GET_OSPF_AREA_ID_NO_BB ("NSSA", area_id, format, argv[0]); + ospf_area_no_summary_unset (ospf, area_id); + + return CMD_SUCCESS; +} + +DEFUN (ospf_area_default_cost, + ospf_area_default_cost_cmd, + "area (A.B.C.D|<0-4294967295>) default-cost <0-16777215>", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Set the summary-default cost of a NSSA or stub area\n" + "Stub's advertised default summary cost\n") +{ + 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); + + area = ospf_area_get (ospf, area_id, format); + + if (area->external_routing == OSPF_AREA_DEFAULT) + { + vty_out (vty, "The area is neither stub, nor NSSA%s", VTY_NEWLINE); + return CMD_WARNING; + } + + area->default_cost = cost; + + p.family = AF_INET; + p.prefix.s_addr = OSPF_DEFAULT_DESTINATION; + p.prefixlen = 0; + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_stub_defaults(): " + "announcing 0.0.0.0/0 to area %s", + inet_ntoa (area->area_id)); + ospf_abr_announce_network_to_area (&p, area->default_cost, area); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_area_default_cost, + no_ospf_area_default_cost_cmd, + "no area (A.B.C.D|<0-4294967295>) default-cost <0-16777215>", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Set the summary-default cost of a NSSA or stub area\n" + "Stub's advertised default summary cost\n") +{ + struct ospf *ospf = vty->index; + struct ospf_area *area; + struct in_addr area_id; + int format; + struct prefix_ipv4 p; + + VTY_GET_OSPF_AREA_ID_NO_BB ("default-cost", area_id, format, argv[0]); + 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) + return CMD_SUCCESS; + + if (area->external_routing == OSPF_AREA_DEFAULT) + { + vty_out (vty, "The area is neither stub, nor NSSA%s", VTY_NEWLINE); + return CMD_WARNING; + } + + area->default_cost = 1; + + p.family = AF_INET; + p.prefix.s_addr = OSPF_DEFAULT_DESTINATION; + p.prefixlen = 0; + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_announce_stub_defaults(): " + "announcing 0.0.0.0/0 to area %s", + inet_ntoa (area->area_id)); + ospf_abr_announce_network_to_area (&p, area->default_cost, area); + + + ospf_area_check_free (ospf, area_id); + + return CMD_SUCCESS; +} + +DEFUN (ospf_area_export_list, + ospf_area_export_list_cmd, + "area (A.B.C.D|<0-4294967295>) export-list NAME", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Set the filter for networks announced to other areas\n" + "Name of the access-list\n") +{ + struct ospf *ospf = vty->index; + struct ospf_area *area; + struct in_addr area_id; + int format; + + VTY_GET_OSPF_AREA_ID (area_id, format, argv[0]); + + area = ospf_area_get (ospf, area_id, format); + ospf_area_export_list_set (ospf, area, argv[1]); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_area_export_list, + no_ospf_area_export_list_cmd, + "no area (A.B.C.D|<0-4294967295>) export-list NAME", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Unset the filter for networks announced to other areas\n" + "Name of the access-list\n") +{ + struct ospf *ospf = vty->index; + struct ospf_area *area; + struct in_addr area_id; + int format; + + VTY_GET_OSPF_AREA_ID (area_id, format, argv[0]); + + area = ospf_area_lookup_by_area_id (ospf, area_id); + if (area == NULL) + return CMD_SUCCESS; + + ospf_area_export_list_unset (ospf, area); + + return CMD_SUCCESS; +} + + +DEFUN (ospf_area_import_list, + ospf_area_import_list_cmd, + "area (A.B.C.D|<0-4294967295>) import-list NAME", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Set the filter for networks from other areas announced to the specified one\n" + "Name of the access-list\n") +{ + struct ospf *ospf = vty->index; + struct ospf_area *area; + struct in_addr area_id; + int format; + + VTY_GET_OSPF_AREA_ID (area_id, format, argv[0]); + + area = ospf_area_get (ospf, area_id, format); + ospf_area_import_list_set (ospf, area, argv[1]); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_area_import_list, + no_ospf_area_import_list_cmd, + "no area (A.B.C.D|<0-4294967295>) import-list NAME", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Unset the filter for networks announced to other areas\n" + "Name of the access-list\n") +{ + struct ospf *ospf = vty->index; + struct ospf_area *area; + struct in_addr area_id; + int format; + + VTY_GET_OSPF_AREA_ID (area_id, format, argv[0]); + + area = ospf_area_lookup_by_area_id (ospf, area_id); + if (area == NULL) + return CMD_SUCCESS; + + ospf_area_import_list_unset (ospf, area); + + return CMD_SUCCESS; +} + +DEFUN (ospf_area_filter_list, + ospf_area_filter_list_cmd, + "area (A.B.C.D|<0-4294967295>) filter-list prefix WORD (in|out)", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Filter networks between OSPF areas\n" + "Filter prefixes between OSPF areas\n" + "Name of an IP prefix-list\n" + "Filter networks sent to this area\n" + "Filter networks sent from this area\n") +{ + 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]); + + area = ospf_area_get (ospf, area_id, format); + plist = prefix_list_lookup (AFI_IP, argv[1]); + if (strncmp (argv[2], "in", 2) == 0) + { + PREFIX_LIST_IN (area) = plist; + if (PREFIX_NAME_IN (area)) + free (PREFIX_NAME_IN (area)); + + PREFIX_NAME_IN (area) = strdup (argv[1]); + ospf_schedule_abr_task (ospf); + } + else + { + PREFIX_LIST_OUT (area) = plist; + if (PREFIX_NAME_OUT (area)) + free (PREFIX_NAME_OUT (area)); + + PREFIX_NAME_OUT (area) = strdup (argv[1]); + ospf_schedule_abr_task (ospf); + } + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_area_filter_list, + no_ospf_area_filter_list_cmd, + "no area (A.B.C.D|<0-4294967295>) filter-list prefix WORD (in|out)", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Filter networks between OSPF areas\n" + "Filter prefixes between OSPF areas\n" + "Name of an IP prefix-list\n" + "Filter networks sent to this area\n" + "Filter networks sent from this area\n") +{ + struct ospf *ospf = vty->index; + struct ospf_area *area; + struct in_addr area_id; + int format; + + VTY_GET_OSPF_AREA_ID (area_id, format, argv[0]); + + if ((area = ospf_area_lookup_by_area_id (ospf, area_id)) == NULL) + return CMD_SUCCESS; + + if (strncmp (argv[2], "in", 2) == 0) + { + if (PREFIX_NAME_IN (area)) + if (strcmp (PREFIX_NAME_IN (area), argv[1]) != 0) + return CMD_SUCCESS; + + PREFIX_LIST_IN (area) = NULL; + if (PREFIX_NAME_IN (area)) + free (PREFIX_NAME_IN (area)); + + PREFIX_NAME_IN (area) = NULL; + + ospf_schedule_abr_task (ospf); + } + else + { + if (PREFIX_NAME_OUT (area)) + if (strcmp (PREFIX_NAME_OUT (area), argv[1]) != 0) + return CMD_SUCCESS; + + PREFIX_LIST_OUT (area) = NULL; + if (PREFIX_NAME_OUT (area)) + free (PREFIX_NAME_OUT (area)); + + PREFIX_NAME_OUT (area) = NULL; + + ospf_schedule_abr_task (ospf); + } + + 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") +{ + struct ospf *ospf = vty->index; + struct ospf_area *area; + struct in_addr area_id; + int format; + + VTY_GET_OSPF_AREA_ID (area_id, format, argv[0]); + + area = ospf_area_get (ospf, area_id, format); + area->auth_type = OSPF_AUTH_CRYPTOGRAPHIC; + + return CMD_SUCCESS; +} + +DEFUN (ospf_area_authentication, + ospf_area_authentication_cmd, + "area (A.B.C.D|<0-4294967295>) authentication", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Enable authentication\n") +{ + struct ospf *ospf = vty->index; + struct ospf_area *area; + struct in_addr area_id; + int format; + + VTY_GET_OSPF_AREA_ID (area_id, format, argv[0]); + + area = ospf_area_get (ospf, area_id, format); + area->auth_type = OSPF_AUTH_SIMPLE; + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_area_authentication, + no_ospf_area_authentication_cmd, + "no area (A.B.C.D|<0-4294967295>) authentication", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Enable authentication\n") +{ + struct ospf *ospf = vty->index; + struct ospf_area *area; + struct in_addr area_id; + int format; + + VTY_GET_OSPF_AREA_ID (area_id, format, argv[0]); + + area = ospf_area_lookup_by_area_id (ospf, area_id); + if (area == NULL) + return CMD_SUCCESS; + + area->auth_type = OSPF_AUTH_NULL; + + ospf_area_check_free (ospf, area_id); + + return CMD_SUCCESS; +} + + +DEFUN (ospf_abr_type, + ospf_abr_type_cmd, + "ospf abr-type (cisco|ibm|shortcut|standard)", + "OSPF specific commands\n" + "Set OSPF ABR type\n" + "Alternative ABR, cisco implementation\n" + "Alternative ABR, IBM implementation\n" + "Shortcut ABR\n" + "Standard behavior (RFC2328)\n") +{ + struct ospf *ospf = vty->index; + u_char abr_type = OSPF_ABR_UNKNOWN; + + if (strncmp (argv[0], "c", 1) == 0) + abr_type = OSPF_ABR_CISCO; + else if (strncmp (argv[0], "i", 1) == 0) + abr_type = OSPF_ABR_IBM; + else if (strncmp (argv[0], "sh", 2) == 0) + abr_type = OSPF_ABR_SHORTCUT; + else if (strncmp (argv[0], "st", 2) == 0) + abr_type = OSPF_ABR_STAND; + else + return CMD_WARNING; + + /* If ABR type value is changed, schedule ABR task. */ + if (ospf->abr_type != abr_type) + { + ospf->abr_type = abr_type; + ospf_schedule_abr_task (ospf); + } + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_abr_type, + no_ospf_abr_type_cmd, + "no ospf abr-type (cisco|ibm|shortcut|standard)", + NO_STR + "OSPF specific commands\n" + "Set OSPF ABR type\n" + "Alternative ABR, cisco implementation\n" + "Alternative ABR, IBM implementation\n" + "Shortcut ABR\n") +{ + struct ospf *ospf = vty->index; + u_char abr_type = OSPF_ABR_UNKNOWN; + + if (strncmp (argv[0], "c", 1) == 0) + abr_type = OSPF_ABR_CISCO; + else if (strncmp (argv[0], "i", 1) == 0) + abr_type = OSPF_ABR_IBM; + else if (strncmp (argv[0], "sh", 2) == 0) + abr_type = OSPF_ABR_SHORTCUT; + else if (strncmp (argv[0], "st", 2) == 0) + abr_type = OSPF_ABR_STAND; + else + return CMD_WARNING; + + /* If ABR type value is changed, schedule ABR task. */ + if (ospf->abr_type == abr_type) + { + ospf->abr_type = OSPF_ABR_DEFAULT; + ospf_schedule_abr_task (ospf); + } + + return CMD_SUCCESS; +} + +DEFUN (ospf_log_adjacency_changes, + ospf_log_adjacency_changes_cmd, + "log-adjacency-changes", + "Log changes in adjacency state\n") +{ + struct ospf *ospf = vty->index; + + SET_FLAG(ospf->config, OSPF_LOG_ADJACENCY_CHANGES); + return CMD_SUCCESS; +} + +DEFUN (ospf_log_adjacency_changes_detail, + ospf_log_adjacency_changes_detail_cmd, + "log-adjacency-changes detail", + "Log changes in adjacency state\n" + "Log all state changes\n") +{ + struct ospf *ospf = vty->index; + + SET_FLAG(ospf->config, OSPF_LOG_ADJACENCY_CHANGES); + SET_FLAG(ospf->config, OSPF_LOG_ADJACENCY_DETAIL); + return CMD_SUCCESS; +} + +DEFUN (no_ospf_log_adjacency_changes, + no_ospf_log_adjacency_changes_cmd, + "no log-adjacency-changes", + NO_STR + "Log changes in adjacency state\n") +{ + struct ospf *ospf = vty->index; + + UNSET_FLAG(ospf->config, OSPF_LOG_ADJACENCY_DETAIL); + UNSET_FLAG(ospf->config, OSPF_LOG_ADJACENCY_CHANGES); + return CMD_SUCCESS; +} + +DEFUN (no_ospf_log_adjacency_changes_detail, + no_ospf_log_adjacency_changes_detail_cmd, + "no log-adjacency-changes detail", + NO_STR + "Log changes in adjacency state\n" + "Log all state changes\n") +{ + struct ospf *ospf = vty->index; + + UNSET_FLAG(ospf->config, OSPF_LOG_ADJACENCY_DETAIL); + return CMD_SUCCESS; +} + +DEFUN (ospf_compatible_rfc1583, + ospf_compatible_rfc1583_cmd, + "compatible rfc1583", + "OSPF compatibility list\n" + "compatible with RFC 1583\n") +{ + struct ospf *ospf = vty->index; + + if (!CHECK_FLAG (ospf->config, OSPF_RFC1583_COMPATIBLE)) + { + SET_FLAG (ospf->config, OSPF_RFC1583_COMPATIBLE); + ospf_spf_calculate_schedule (ospf, SPF_FLAG_CONFIG_CHANGE); + } + return CMD_SUCCESS; +} + +DEFUN (no_ospf_compatible_rfc1583, + no_ospf_compatible_rfc1583_cmd, + "no compatible rfc1583", + NO_STR + "OSPF compatibility list\n" + "compatible with RFC 1583\n") +{ + struct ospf *ospf = vty->index; + + if (CHECK_FLAG (ospf->config, OSPF_RFC1583_COMPATIBLE)) + { + UNSET_FLAG (ospf->config, OSPF_RFC1583_COMPATIBLE); + ospf_spf_calculate_schedule (ospf, SPF_FLAG_CONFIG_CHANGE); + } + return CMD_SUCCESS; +} + +ALIAS (ospf_compatible_rfc1583, + ospf_rfc1583_flag_cmd, + "ospf rfc1583compatibility", + "OSPF specific commands\n" + "Enable the RFC1583Compatibility flag\n") + +ALIAS (no_ospf_compatible_rfc1583, + no_ospf_rfc1583_flag_cmd, + "no ospf rfc1583compatibility", + 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, + unsigned int max) +{ + struct ospf *ospf = vty->index; + + ospf->spf_delay = delay; + ospf->spf_holdtime = hold; + ospf->spf_max_holdtime = max; + + 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>", + "Adjust routing timers\n" + "Throttling adaptive timer\n" + "OSPF 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 ospf_timers_spf_set (vty, delay, hold, max); +} + +DEFUN_DEPRECATED (ospf_timers_spf, + ospf_timers_spf_cmd, + "timers spf <0-4294967295> <0-4294967295>", + "Adjust routing timers\n" + "OSPF SPF timers\n" + "Delay (s) between receiving a change to SPF calculation\n" + "Hold time (s) between consecutive SPF calculations\n") +{ + unsigned int delay, hold; + + if (argc != 2) + { + vty_out (vty, "Insufficient number of arguments%s", VTY_NEWLINE); + return CMD_WARNING; + } + + VTY_GET_INTEGER ("SPF delay timer", delay, argv[0]); + VTY_GET_INTEGER ("SPF hold timer", hold, argv[1]); + + /* truncate down the second values if they're greater than 600000ms */ + if (delay > (600000 / 1000)) + delay = 600000; + else if (delay == 0) + /* 0s delay was probably specified because of lack of ms resolution */ + delay = OSPF_SPF_DELAY_DEFAULT; + if (hold > (600000 / 1000)) + hold = 600000; + + return ospf_timers_spf_set (vty, delay * 1000, hold * 1000, hold * 1000); +} + +DEFUN (no_ospf_timers_throttle_spf, + no_ospf_timers_throttle_spf_cmd, + "no timers throttle spf", + NO_STR + "Adjust routing timers\n" + "Throttling adaptive timer\n" + "OSPF SPF timers\n") +{ + return ospf_timers_spf_set (vty, + OSPF_SPF_DELAY_DEFAULT, + OSPF_SPF_HOLDTIME_DEFAULT, + OSPF_SPF_MAX_HOLDTIME_DEFAULT); +} + +ALIAS_DEPRECATED (no_ospf_timers_throttle_spf, + no_ospf_timers_spf_cmd, + "no timers spf", + NO_STR + "Adjust routing timers\n" + "OSPF SPF timers\n") + +DEFUN (ospf_neighbor, + ospf_neighbor_cmd, + "neighbor A.B.C.D", + NEIGHBOR_STR + "Neighbor IP address\n") +{ + struct ospf *ospf = vty->index; + struct in_addr nbr_addr; + unsigned int priority = OSPF_NEIGHBOR_PRIORITY_DEFAULT; + unsigned int interval = OSPF_POLL_INTERVAL_DEFAULT; + + VTY_GET_IPV4_ADDRESS ("neighbor address", nbr_addr, argv[0]); + + if (argc > 1) + VTY_GET_INTEGER_RANGE ("neighbor priority", priority, argv[1], 0, 255); + + if (argc > 2) + VTY_GET_INTEGER_RANGE ("poll interval", interval, argv[2], 1, 65535); + + ospf_nbr_nbma_set (ospf, nbr_addr); + if (argc > 1) + ospf_nbr_nbma_priority_set (ospf, nbr_addr, priority); + if (argc > 2) + ospf_nbr_nbma_poll_interval_set (ospf, nbr_addr, interval); + + return CMD_SUCCESS; +} + +ALIAS (ospf_neighbor, + ospf_neighbor_priority_poll_interval_cmd, + "neighbor A.B.C.D priority <0-255> poll-interval <1-65535>", + NEIGHBOR_STR + "Neighbor IP address\n" + "Neighbor Priority\n" + "Priority\n" + "Dead Neighbor Polling interval\n" + "Seconds\n") + +ALIAS (ospf_neighbor, + ospf_neighbor_priority_cmd, + "neighbor A.B.C.D priority <0-255>", + NEIGHBOR_STR + "Neighbor IP address\n" + "Neighbor Priority\n" + "Seconds\n") + +DEFUN (ospf_neighbor_poll_interval, + ospf_neighbor_poll_interval_cmd, + "neighbor A.B.C.D poll-interval <1-65535>", + NEIGHBOR_STR + "Neighbor IP address\n" + "Dead Neighbor Polling interval\n" + "Seconds\n") +{ + struct ospf *ospf = vty->index; + struct in_addr nbr_addr; + unsigned int priority = OSPF_NEIGHBOR_PRIORITY_DEFAULT; + unsigned int interval = OSPF_POLL_INTERVAL_DEFAULT; + + VTY_GET_IPV4_ADDRESS ("neighbor address", nbr_addr, argv[0]); + + if (argc > 1) + VTY_GET_INTEGER_RANGE ("poll interval", interval, argv[1], 1, 65535); + + if (argc > 2) + VTY_GET_INTEGER_RANGE ("neighbor priority", priority, argv[2], 0, 255); + + ospf_nbr_nbma_set (ospf, nbr_addr); + if (argc > 1) + ospf_nbr_nbma_poll_interval_set (ospf, nbr_addr, interval); + if (argc > 2) + ospf_nbr_nbma_priority_set (ospf, nbr_addr, priority); + + return CMD_SUCCESS; +} + +ALIAS (ospf_neighbor_poll_interval, + ospf_neighbor_poll_interval_priority_cmd, + "neighbor A.B.C.D poll-interval <1-65535> priority <0-255>", + NEIGHBOR_STR + "Neighbor address\n" + "OSPF dead-router polling interval\n" + "Seconds\n" + "OSPF priority of non-broadcast neighbor\n" + "Priority\n") + +DEFUN (no_ospf_neighbor, + no_ospf_neighbor_cmd, + "no neighbor A.B.C.D", + NO_STR + NEIGHBOR_STR + "Neighbor IP address\n") +{ + struct ospf *ospf = vty->index; + struct in_addr nbr_addr; + + VTY_GET_IPV4_ADDRESS ("neighbor address", nbr_addr, argv[0]); + + (void)ospf_nbr_nbma_unset (ospf, nbr_addr); + + return CMD_SUCCESS; +} + +ALIAS (no_ospf_neighbor, + no_ospf_neighbor_priority_cmd, + "no neighbor A.B.C.D priority <0-255>", + NO_STR + NEIGHBOR_STR + "Neighbor IP address\n" + "Neighbor Priority\n" + "Priority\n") + +ALIAS (no_ospf_neighbor, + no_ospf_neighbor_poll_interval_cmd, + "no neighbor A.B.C.D poll-interval <1-65535>", + NO_STR + NEIGHBOR_STR + "Neighbor IP address\n" + "Dead Neighbor Polling interval\n" + "Seconds\n") + +ALIAS (no_ospf_neighbor, + no_ospf_neighbor_priority_pollinterval_cmd, + "no neighbor A.B.C.D priority <0-255> poll-interval <1-65535>", + NO_STR + NEIGHBOR_STR + "Neighbor IP address\n" + "Neighbor Priority\n" + "Priority\n" + "Dead Neighbor Polling interval\n" + "Seconds\n") + + +DEFUN (ospf_refresh_timer, ospf_refresh_timer_cmd, + "refresh timer <10-1800>", + "Adjust refresh parameters\n" + "Set refresh timer\n" + "Timer value in seconds\n") +{ + struct ospf *ospf = vty->index; + unsigned int interval; + + VTY_GET_INTEGER_RANGE ("refresh timer", interval, argv[0], 10, 1800); + interval = (interval / 10) * 10; + + ospf_timers_refresh_set (ospf, interval); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_refresh_timer, no_ospf_refresh_timer_val_cmd, + "no refresh timer <10-1800>", + "Adjust refresh parameters\n" + "Unset refresh timer\n" + "Timer value in seconds\n") +{ + struct ospf *ospf = vty->index; + unsigned int interval; + + if (argc == 1) + { + VTY_GET_INTEGER_RANGE ("refresh timer", interval, argv[0], 10, 1800); + + if (ospf->lsa_refresh_interval != interval || + interval == OSPF_LSA_REFRESH_INTERVAL_DEFAULT) + return CMD_SUCCESS; + } + + ospf_timers_refresh_unset (ospf); + + return CMD_SUCCESS; +} + +ALIAS (no_ospf_refresh_timer, + no_ospf_refresh_timer_cmd, + "no refresh timer", + "Adjust refresh parameters\n" + "Unset refresh timer\n") + +DEFUN (ospf_auto_cost_reference_bandwidth, + ospf_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 ospf *ospf = vty->index; + u_int32_t refbw; + struct listnode *node; + struct interface *ifp; + + refbw = strtol (argv[0], NULL, 10); + if (refbw < 1 || refbw > 4294967) + { + vty_out (vty, "reference-bandwidth value is invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* If reference bandwidth is changed. */ + if ((refbw * 1000) == ospf->ref_bandwidth) + return CMD_SUCCESS; + + ospf->ref_bandwidth = refbw * 1000; + for (ALL_LIST_ELEMENTS_RO (om->iflist, node, ifp)) + ospf_if_recalculate_output_cost (ifp); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_auto_cost_reference_bandwidth, + no_ospf_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 ospf *ospf = vty->index; + struct listnode *node, *nnode; + struct interface *ifp; + + if (ospf->ref_bandwidth == OSPF_DEFAULT_REF_BANDWIDTH) + return CMD_SUCCESS; + + ospf->ref_bandwidth = OSPF_DEFAULT_REF_BANDWIDTH; + vty_out (vty, "%% OSPF: Reference bandwidth is changed.%s", VTY_NEWLINE); + vty_out (vty, " Please ensure reference bandwidth is consistent across all routers%s", VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS (om->iflist, node, nnode, ifp)) + ospf_if_recalculate_output_cost (ifp); + + return CMD_SUCCESS; +} + +const char *ospf_abr_type_descr_str[] = +{ + "Unknown", + "Standard (RFC2328)", + "Alternative IBM", + "Alternative Cisco", + "Alternative Shortcut" +}; + +const char *ospf_shortcut_mode_descr_str[] = +{ + "Default", + "Enabled", + "Disabled" +}; + + + +static void +show_ip_ospf_area (struct vty *vty, struct ospf_area *area) +{ + /* Show Area ID. */ + vty_out (vty, " Area ID: %s", inet_ntoa (area->area_id)); + + /* Show Area type/mode. */ + if (OSPF_IS_AREA_BACKBONE (area)) + vty_out (vty, " (Backbone)%s", VTY_NEWLINE); + else + { + if (area->external_routing == OSPF_AREA_STUB) + vty_out (vty, " (Stub%s%s)", + area->no_summary ? ", no summary" : "", + area->shortcut_configured ? "; " : ""); + + else if (area->external_routing == OSPF_AREA_NSSA) + vty_out (vty, " (NSSA%s%s)", + area->no_summary ? ", no summary" : "", + area->shortcut_configured ? "; " : ""); + + vty_out (vty, "%s", VTY_NEWLINE); + vty_out (vty, " Shortcutting mode: %s", + ospf_shortcut_mode_descr_str[area->shortcut_configured]); + vty_out (vty, ", S-bit consensus: %s%s", + area->shortcut_capability ? "ok" : "no", VTY_NEWLINE); + } + + /* Show number of interfaces. */ + vty_out (vty, " Number of interfaces in this area: Total: %d, " + "Active: %d%s", listcount (area->oiflist), + area->act_ints, VTY_NEWLINE); + + if (area->external_routing == OSPF_AREA_NSSA) + { + vty_out (vty, " It is an NSSA configuration. %s Elected NSSA/ABR performs type-7/type-5 LSA translation. %s", VTY_NEWLINE, VTY_NEWLINE); + if (! IS_OSPF_ABR (area->ospf)) + vty_out (vty, " It is not ABR, therefore not Translator. %s", + VTY_NEWLINE); + else if (area->NSSATranslatorState) + { + vty_out (vty, " We are an ABR and "); + if (area->NSSATranslatorRole == OSPF_NSSA_ROLE_CANDIDATE) + vty_out (vty, "the NSSA Elected Translator. %s", + VTY_NEWLINE); + else if (area->NSSATranslatorRole == OSPF_NSSA_ROLE_ALWAYS) + vty_out (vty, "always an NSSA Translator. %s", + VTY_NEWLINE); + } + else + { + vty_out (vty, " We are an ABR, but "); + if (area->NSSATranslatorRole == OSPF_NSSA_ROLE_CANDIDATE) + vty_out (vty, "not the NSSA Elected Translator. %s", + VTY_NEWLINE); + else + vty_out (vty, "never an NSSA Translator. %s", + VTY_NEWLINE); + } + } + /* Stub-router state for this area */ + if (CHECK_FLAG (area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED)) + { + char timebuf[OSPF_TIME_DUMP_SIZE]; + vty_out (vty, " Originating stub / maximum-distance Router-LSA%s", + VTY_NEWLINE); + if (CHECK_FLAG(area->stub_router_state, OSPF_AREA_ADMIN_STUB_ROUTED)) + vty_out (vty, " Administratively activated (indefinitely)%s", + VTY_NEWLINE); + if (area->t_stub_router) + vty_out (vty, " Active from startup, %s remaining%s", + ospf_timer_dump (area->t_stub_router, timebuf, + sizeof(timebuf)), VTY_NEWLINE); + } + + /* Show number of fully adjacent neighbors. */ + vty_out (vty, " Number of fully adjacent neighbors in this area:" + " %d%s", area->full_nbrs, VTY_NEWLINE); + + /* Show authentication type. */ + vty_out (vty, " Area has "); + if (area->auth_type == OSPF_AUTH_NULL) + vty_out (vty, "no authentication%s", VTY_NEWLINE); + else if (area->auth_type == OSPF_AUTH_SIMPLE) + vty_out (vty, "simple password authentication%s", VTY_NEWLINE); + else if (area->auth_type == OSPF_AUTH_CRYPTOGRAPHIC) + vty_out (vty, "message digest authentication%s", VTY_NEWLINE); + + if (!OSPF_IS_AREA_BACKBONE (area)) + vty_out (vty, " Number of full virtual adjacencies going through" + " this area: %d%s", area->full_vls, VTY_NEWLINE); + + /* Show SPF calculation times. */ + vty_out (vty, " SPF algorithm executed %d times%s", + area->spf_calculation, VTY_NEWLINE); + + /* Show number of LSA. */ + vty_out (vty, " Number of LSA %ld%s", area->lsdb->total, VTY_NEWLINE); + vty_out (vty, " Number of router LSA %ld. Checksum Sum 0x%08x%s", + ospf_lsdb_count (area->lsdb, OSPF_ROUTER_LSA), + ospf_lsdb_checksum (area->lsdb, OSPF_ROUTER_LSA), VTY_NEWLINE); + vty_out (vty, " Number of network LSA %ld. Checksum Sum 0x%08x%s", + ospf_lsdb_count (area->lsdb, OSPF_NETWORK_LSA), + ospf_lsdb_checksum (area->lsdb, OSPF_NETWORK_LSA), VTY_NEWLINE); + vty_out (vty, " Number of summary LSA %ld. Checksum Sum 0x%08x%s", + ospf_lsdb_count (area->lsdb, OSPF_SUMMARY_LSA), + ospf_lsdb_checksum (area->lsdb, OSPF_SUMMARY_LSA), VTY_NEWLINE); + vty_out (vty, " Number of ASBR summary LSA %ld. Checksum Sum 0x%08x%s", + ospf_lsdb_count (area->lsdb, OSPF_ASBR_SUMMARY_LSA), + ospf_lsdb_checksum (area->lsdb, OSPF_ASBR_SUMMARY_LSA), VTY_NEWLINE); + 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); + 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); + vty_out (vty, "%s", VTY_NEWLINE); +} + +DEFUN (show_ip_ospf, + show_ip_ospf_cmd, + "show ip ospf", + SHOW_STR + IP_STR + "OSPF information\n") +{ + struct listnode *node, *nnode; + struct ospf_area * area; + struct ospf *ospf; + struct timeval result; + char timebuf[OSPF_TIME_DUMP_SIZE]; + + /* Check OSPF is enable. */ + ospf = ospf_lookup (); + if (ospf == NULL) + { + vty_out (vty, " OSPF Routing Process not enabled%s", VTY_NEWLINE); + return CMD_SUCCESS; + } + + /* Show Router ID. */ + vty_out (vty, " OSPF Routing Process, Router ID: %s%s", + inet_ntoa (ospf->router_id), + VTY_NEWLINE); + + /* Graceful shutdown */ + if (ospf->t_deferred_shutdown) + vty_out (vty, " Deferred shutdown in progress, %s remaining%s", + ospf_timer_dump (ospf->t_deferred_shutdown, + timebuf, sizeof (timebuf)), VTY_NEWLINE); + /* Show capability. */ + vty_out (vty, " Supports only single TOS (TOS0) routes%s", VTY_NEWLINE); + vty_out (vty, " This implementation conforms to RFC2328%s", VTY_NEWLINE); + vty_out (vty, " RFC1583Compatibility flag is %s%s", + CHECK_FLAG (ospf->config, OSPF_RFC1583_COMPATIBLE) ? + "enabled" : "disabled", VTY_NEWLINE); + vty_out (vty, " OpaqueCapability flag is %s%s", + CHECK_FLAG (ospf->config, OSPF_OPAQUE_CAPABLE) ? + "enabled" : "disabled", + VTY_NEWLINE); + + /* Show stub-router configuration */ + if (ospf->stub_router_startup_time != OSPF_STUB_ROUTER_UNCONFIGURED + || ospf->stub_router_shutdown_time != OSPF_STUB_ROUTER_UNCONFIGURED) + { + vty_out (vty, " Stub router advertisement is configured%s", + VTY_NEWLINE); + if (ospf->stub_router_startup_time != OSPF_STUB_ROUTER_UNCONFIGURED) + vty_out (vty, " Enabled for %us after start-up%s", + ospf->stub_router_startup_time, VTY_NEWLINE); + if (ospf->stub_router_shutdown_time != OSPF_STUB_ROUTER_UNCONFIGURED) + vty_out (vty, " Enabled for %us prior to full shutdown%s", + ospf->stub_router_shutdown_time, VTY_NEWLINE); + } + + /* Show SPF timers. */ + vty_out (vty, " Initial SPF scheduling delay %d millisec(s)%s" + " Minimum hold time between consecutive SPFs %d millisec(s)%s" + " Maximum hold time between consecutive SPFs %d millisec(s)%s" + " Hold time multiplier is currently %d%s", + ospf->spf_delay, VTY_NEWLINE, + ospf->spf_holdtime, VTY_NEWLINE, + ospf->spf_max_holdtime, VTY_NEWLINE, + ospf->spf_hold_multiplier, VTY_NEWLINE); + vty_out (vty, " SPF algorithm "); + if (ospf->ts_spf.tv_sec || ospf->ts_spf.tv_usec) + { + result = tv_sub (recent_relative_time (), ospf->ts_spf); + 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); + vty_out (vty, " SPF timer %s%s%s", + (ospf->t_spf_calc ? "due in " : "is "), + ospf_timer_dump (ospf->t_spf_calc, timebuf, sizeof (timebuf)), + VTY_NEWLINE); + + /* Show refresh parameters. */ + vty_out (vty, " Refresh timer %d secs%s", + ospf->lsa_refresh_interval, VTY_NEWLINE); + + /* Show ABR/ASBR flags. */ + if (CHECK_FLAG (ospf->flags, OSPF_FLAG_ABR)) + vty_out (vty, " This router is an ABR, ABR type is: %s%s", + ospf_abr_type_descr_str[ospf->abr_type], VTY_NEWLINE); + + if (CHECK_FLAG (ospf->flags, OSPF_FLAG_ASBR)) + vty_out (vty, " This router is an ASBR " + "(injecting external routing information)%s", VTY_NEWLINE); + + /* Show Number of AS-external-LSAs. */ + 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); + 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); + /* Show number of areas attached. */ + vty_out (vty, " Number of areas attached to this router: %d%s", + listcount (ospf->areas), VTY_NEWLINE); + + if (CHECK_FLAG(ospf->config, OSPF_LOG_ADJACENCY_CHANGES)) + { + if (CHECK_FLAG(ospf->config, OSPF_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); + + /* Show each area status. */ + for (ALL_LIST_ELEMENTS (ospf->areas, node, nnode, area)) + show_ip_ospf_area (vty, area); + + return CMD_SUCCESS; +} + + +static void +show_ip_ospf_interface_sub (struct vty *vty, struct ospf *ospf, + struct interface *ifp) +{ + int is_up; + struct ospf_neighbor *nbr; + struct route_node *rn; + + /* Is interface up? */ + 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, BW %u Kbit %s%s", + ifp->ifindex, ifp->mtu, ifp->bandwidth, if_flag_dump(ifp->flags), + VTY_NEWLINE); + + /* Is interface OSPF enabled? */ + if (ospf_oi_count(ifp) == 0) + { + vty_out (vty, " OSPF not enabled on this interface%s", VTY_NEWLINE); + return; + } + else if (!is_up) + { + vty_out (vty, " OSPF is enabled, but not running on this interface%s", + VTY_NEWLINE); + return; + } + + for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn)) + { + struct ospf_interface *oi = rn->info; + + if (oi == NULL) + continue; + + /* Show OSPF interface information. */ + vty_out (vty, " Internet Address %s/%d,", + inet_ntoa (oi->address->u.prefix4), oi->address->prefixlen); + + if (oi->connected->destination || oi->type == OSPF_IFTYPE_VIRTUALLINK) + { + struct in_addr *dest; + const char *dstr; + + if (CONNECTED_PEER(oi->connected) + || oi->type == OSPF_IFTYPE_VIRTUALLINK) + dstr = "Peer"; + else + dstr = "Broadcast"; + + /* For Vlinks, showing the peer address is probably more + * informative than the local interface that is being used + */ + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) + dest = &oi->vl_data->peer_addr; + else + dest = &oi->connected->destination->u.prefix4; + + vty_out (vty, " %s %s,", dstr, inet_ntoa (*dest)); + } + + vty_out (vty, " Area %s%s", ospf_area_desc_string (oi->area), + VTY_NEWLINE); + + vty_out (vty, " MTU mismatch detection:%s%s", + OSPF_IF_PARAM(oi, mtu_ignore) ? "disabled" : "enabled", VTY_NEWLINE); + + vty_out (vty, " Router ID %s, Network Type %s, Cost: %d%s", + inet_ntoa (ospf->router_id), ospf_network_type_str[oi->type], + oi->output_cost, VTY_NEWLINE); + + vty_out (vty, " Transmit Delay is %d sec, State %s, Priority %d%s", + OSPF_IF_PARAM (oi,transmit_delay), LOOKUP (ospf_ism_state_msg, oi->state), + PRIORITY (oi), VTY_NEWLINE); + + /* Show DR information. */ + if (DR (oi).s_addr == 0) + vty_out (vty, " No designated router on this network%s", VTY_NEWLINE); + else + { + nbr = ospf_nbr_lookup_by_addr (oi->nbrs, &DR (oi)); + if (nbr == NULL) + vty_out (vty, " No designated router on this network%s", VTY_NEWLINE); + else + { + vty_out (vty, " Designated Router (ID) %s,", + inet_ntoa (nbr->router_id)); + vty_out (vty, " Interface Address %s%s", + inet_ntoa (nbr->address.u.prefix4), VTY_NEWLINE); + } + } + + /* Show BDR information. */ + if (BDR (oi).s_addr == 0) + vty_out (vty, " No backup designated router on this network%s", + VTY_NEWLINE); + else + { + nbr = ospf_nbr_lookup_by_addr (oi->nbrs, &BDR (oi)); + if (nbr == NULL) + vty_out (vty, " No backup designated router on this network%s", + VTY_NEWLINE); + else + { + vty_out (vty, " Backup Designated Router (ID) %s,", + inet_ntoa (nbr->router_id)); + vty_out (vty, " Interface Address %s%s", + inet_ntoa (nbr->address.u.prefix4), VTY_NEWLINE); + } + } + + /* Next network-LSA sequence number we'll use, if we're elected DR */ + if (oi->params && ntohl (oi->params->network_lsa_seqnum) + != OSPF_INITIAL_SEQUENCE_NUMBER) + vty_out (vty, " Saved Network-LSA sequence number 0x%x%s", + ntohl (oi->params->network_lsa_seqnum), VTY_NEWLINE); + + vty_out (vty, " Multicast group memberships:"); + if (OI_MEMBER_CHECK(oi, MEMBER_ALLROUTERS) + || OI_MEMBER_CHECK(oi, MEMBER_DROUTERS)) + { + if (OI_MEMBER_CHECK(oi, MEMBER_ALLROUTERS)) + vty_out (vty, " OSPFAllRouters"); + if (OI_MEMBER_CHECK(oi, MEMBER_DROUTERS)) + vty_out (vty, " OSPFDesignatedRouters"); + } + else + vty_out (vty, " "); + vty_out (vty, "%s", VTY_NEWLINE); + + vty_out (vty, " Timer intervals configured,"); + vty_out (vty, " Hello "); + if (OSPF_IF_PARAM (oi, fast_hello) == 0) + vty_out (vty, "%ds,", OSPF_IF_PARAM (oi, v_hello)); + else + vty_out (vty, "%dms,", 1000 / OSPF_IF_PARAM (oi, fast_hello)); + vty_out (vty, " Dead %ds, Wait %ds, Retransmit %d%s", + OSPF_IF_PARAM (oi, v_wait), + OSPF_IF_PARAM (oi, v_wait), + OSPF_IF_PARAM (oi, retransmit_interval), + VTY_NEWLINE); + + if (OSPF_IF_PASSIVE_STATUS (oi) == OSPF_IF_ACTIVE) + { + char timebuf[OSPF_TIME_DUMP_SIZE]; + vty_out (vty, " Hello due in %s%s", + ospf_timer_dump (oi->t_hello, timebuf, sizeof(timebuf)), + VTY_NEWLINE); + } + else /* passive-interface is set */ + vty_out (vty, " No Hellos (Passive interface)%s", VTY_NEWLINE); + + vty_out (vty, " Neighbor Count is %d, Adjacent neighbor count is %d%s", + ospf_nbr_count (oi, 0), ospf_nbr_count (oi, NSM_Full), + VTY_NEWLINE); + } +} + +DEFUN (show_ip_ospf_interface, + show_ip_ospf_interface_cmd, + "show ip ospf interface [INTERFACE]", + SHOW_STR + IP_STR + "OSPF information\n" + "Interface information\n" + "Interface name\n") +{ + struct interface *ifp; + struct ospf *ospf; + struct listnode *node; + + ospf = ospf_lookup (); + if (ospf == NULL) + { + vty_out (vty, "OSPF Routing Process not enabled%s", VTY_NEWLINE); + return CMD_SUCCESS; + } + + /* Show All Interfaces. */ + if (argc == 0) + for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) + show_ip_ospf_interface_sub (vty, ospf, 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_ip_ospf_interface_sub (vty, ospf, ifp); + } + + return CMD_SUCCESS; +} + +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_NEWLINE, + "Neighbor ID", "Pri", "State", "Dead Time", + "Address", "Interface", "RXmtL", "RqstL", "DBsmL", + VTY_NEWLINE); +} + +static void +show_ip_ospf_neighbor_sub (struct vty *vty, struct ospf_interface *oi) +{ + struct route_node *rn; + struct ospf_neighbor *nbr; + char msgbuf[16]; + char timebuf[OSPF_TIME_DUMP_SIZE]; + + for (rn = route_top (oi->nbrs); rn; rn = route_next (rn)) + if ((nbr = rn->info)) + /* Do not show myself. */ + if (nbr != oi->nbr_self) + /* Down state is not shown. */ + if (nbr->state != NSM_Down) + { + ospf_nbr_state_message (nbr, msgbuf, 16); + + if (nbr->state == NSM_Attempt && nbr->router_id.s_addr == 0) + vty_out (vty, "%-15s %3d %-15s ", + "-", nbr->priority, + msgbuf); + else + vty_out (vty, "%-15s %3d %-15s ", + inet_ntoa (nbr->router_id), nbr->priority, + msgbuf); + + vty_out (vty, "%9s ", + ospf_timer_dump (nbr->t_inactivity, timebuf, + sizeof(timebuf))); + + vty_out (vty, "%-15s ", inet_ntoa (nbr->src)); + vty_out (vty, "%-20s %5ld %5ld %5d%s", + IF_NAME (oi), ospf_ls_retransmit_count (nbr), + ospf_ls_request_count (nbr), ospf_db_summary_count (nbr), + VTY_NEWLINE); + } +} + +DEFUN (show_ip_ospf_neighbor, + show_ip_ospf_neighbor_cmd, + "show ip ospf neighbor", + SHOW_STR + IP_STR + "OSPF information\n" + "Neighbor list\n") +{ + struct ospf *ospf; + struct ospf_interface *oi; + struct listnode *node; + + ospf = ospf_lookup (); + if (ospf == NULL) + { + vty_out (vty, " OSPF Routing Process not enabled%s", VTY_NEWLINE); + return CMD_SUCCESS; + } + + show_ip_ospf_neighbour_header (vty); + + for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi)) + show_ip_ospf_neighbor_sub (vty, oi); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_ospf_neighbor_all, + show_ip_ospf_neighbor_all_cmd, + "show ip ospf neighbor all", + SHOW_STR + IP_STR + "OSPF information\n" + "Neighbor list\n" + "include down status neighbor\n") +{ + struct ospf *ospf = ospf_lookup (); + struct listnode *node; + struct ospf_interface *oi; + + if (ospf == NULL) + { + vty_out (vty, " OSPF Routing Process not enabled%s", VTY_NEWLINE); + return CMD_SUCCESS; + } + + show_ip_ospf_neighbour_header (vty); + + for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi)) + { + struct listnode *nbr_node; + struct ospf_nbr_nbma *nbr_nbma; + + show_ip_ospf_neighbor_sub (vty, oi); + + /* print Down neighbor status */ + for (ALL_LIST_ELEMENTS_RO (oi->nbr_nbma, nbr_node, nbr_nbma)) + { + if (nbr_nbma->nbr == NULL + || nbr_nbma->nbr->state == NSM_Down) + { + vty_out (vty, "%-15s %3d %-15s %9s ", + "-", nbr_nbma->priority, "Down", "-"); + vty_out (vty, "%-15s %-20s %5d %5d %5d%s", + inet_ntoa (nbr_nbma->addr), IF_NAME (oi), + 0, 0, 0, VTY_NEWLINE); + } + } + } + + return CMD_SUCCESS; +} + +DEFUN (show_ip_ospf_neighbor_int, + show_ip_ospf_neighbor_int_cmd, + "show ip ospf neighbor IFNAME", + SHOW_STR + IP_STR + "OSPF information\n" + "Neighbor list\n" + "Interface name\n") +{ + struct ospf *ospf; + struct interface *ifp; + struct route_node *rn; + + ifp = if_lookup_by_name (argv[0]); + if (!ifp) + { + vty_out (vty, "No such interface.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ospf = ospf_lookup (); + if (ospf == NULL) + { + vty_out (vty, " OSPF Routing Process not enabled%s", VTY_NEWLINE); + return CMD_SUCCESS; + } + + show_ip_ospf_neighbour_header (vty); + + for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn)) + { + struct ospf_interface *oi = rn->info; + + if (oi == NULL) + continue; + + show_ip_ospf_neighbor_sub (vty, oi); + } + + return CMD_SUCCESS; +} + +static void +show_ip_ospf_nbr_nbma_detail_sub (struct vty *vty, struct ospf_interface *oi, + struct ospf_nbr_nbma *nbr_nbma) +{ + char timebuf[OSPF_TIME_DUMP_SIZE]; + + /* Show neighbor ID. */ + vty_out (vty, " Neighbor %s,", "-"); + + /* Show interface address. */ + vty_out (vty, " interface address %s%s", + inet_ntoa (nbr_nbma->addr), VTY_NEWLINE); + /* Show Area ID. */ + vty_out (vty, " In the area %s via interface %s%s", + ospf_area_desc_string (oi->area), IF_NAME (oi), VTY_NEWLINE); + /* Show neighbor priority and state. */ + vty_out (vty, " Neighbor priority is %d, State is %s,", + nbr_nbma->priority, "Down"); + /* Show state changes. */ + vty_out (vty, " %d state changes%s", nbr_nbma->state_change, VTY_NEWLINE); + + /* Show PollInterval */ + vty_out (vty, " Poll interval %d%s", nbr_nbma->v_poll, VTY_NEWLINE); + + /* Show poll-interval timer. */ + vty_out (vty, " Poll timer due in %s%s", + ospf_timer_dump (nbr_nbma->t_poll, timebuf, sizeof(timebuf)), + VTY_NEWLINE); + + /* Show poll-interval timer thread. */ + vty_out (vty, " Thread Poll Timer %s%s", + nbr_nbma->t_poll != NULL ? "on" : "off", VTY_NEWLINE); +} + +static void +show_ip_ospf_neighbor_detail_sub (struct vty *vty, struct ospf_interface *oi, + struct ospf_neighbor *nbr) +{ + char timebuf[OSPF_TIME_DUMP_SIZE]; + + /* Show neighbor ID. */ + if (nbr->state == NSM_Attempt && nbr->router_id.s_addr == 0) + vty_out (vty, " Neighbor %s,", "-"); + else + vty_out (vty, " Neighbor %s,", inet_ntoa (nbr->router_id)); + + /* Show interface address. */ + vty_out (vty, " interface address %s%s", + inet_ntoa (nbr->address.u.prefix4), VTY_NEWLINE); + /* Show Area ID. */ + vty_out (vty, " In the area %s via interface %s%s", + ospf_area_desc_string (oi->area), oi->ifp->name, VTY_NEWLINE); + /* Show neighbor priority and state. */ + vty_out (vty, " Neighbor priority is %d, State is %s,", + nbr->priority, LOOKUP (ospf_nsm_state_msg, nbr->state)); + /* Show state changes. */ + vty_out (vty, " %d state changes%s", nbr->state_change, VTY_NEWLINE); + if (nbr->ts_last_progress.tv_sec || nbr->ts_last_progress.tv_usec) + { + struct timeval res + = tv_sub (recent_relative_time (), nbr->ts_last_progress); + vty_out (vty, " Most recent state change statistics:%s", + VTY_NEWLINE); + vty_out (vty, " Progressive change %s ago%s", + ospf_timeval_dump (&res, timebuf, sizeof(timebuf)), + VTY_NEWLINE); + } + if (nbr->ts_last_regress.tv_sec || nbr->ts_last_regress.tv_usec) + { + struct timeval res + = tv_sub (recent_relative_time (), nbr->ts_last_regress); + vty_out (vty, " Regressive change %s ago, due to %s%s", + ospf_timeval_dump (&res, timebuf, sizeof(timebuf)), + (nbr->last_regress_str ? nbr->last_regress_str : "??"), + VTY_NEWLINE); + } + /* Show Designated Rotuer ID. */ + vty_out (vty, " DR is %s,", inet_ntoa (nbr->d_router)); + /* Show Backup Designated Rotuer ID. */ + vty_out (vty, " BDR is %s%s", inet_ntoa (nbr->bd_router), VTY_NEWLINE); + /* Show options. */ + vty_out (vty, " Options %d %s%s", nbr->options, + ospf_options_dump (nbr->options), VTY_NEWLINE); + /* Show Router Dead interval timer. */ + vty_out (vty, " Dead timer due in %s%s", + ospf_timer_dump (nbr->t_inactivity, timebuf, sizeof (timebuf)), + VTY_NEWLINE); + /* Show Database Summary list. */ + vty_out (vty, " Database Summary List %d%s", + ospf_db_summary_count (nbr), VTY_NEWLINE); + /* Show Link State Request list. */ + vty_out (vty, " Link State Request List %ld%s", + ospf_ls_request_count (nbr), VTY_NEWLINE); + /* Show Link State Retransmission list. */ + vty_out (vty, " Link State Retransmission List %ld%s", + ospf_ls_retransmit_count (nbr), VTY_NEWLINE); + /* Show inactivity timer thread. */ + vty_out (vty, " Thread Inactivity Timer %s%s", + nbr->t_inactivity != NULL ? "on" : "off", VTY_NEWLINE); + /* Show Database Description retransmission thread. */ + vty_out (vty, " Thread Database Description Retransmision %s%s", + nbr->t_db_desc != NULL ? "on" : "off", VTY_NEWLINE); + /* Show Link State Request Retransmission thread. */ + vty_out (vty, " Thread Link State Request Retransmission %s%s", + nbr->t_ls_req != NULL ? "on" : "off", VTY_NEWLINE); + /* Show Link State Update Retransmission thread. */ + vty_out (vty, " Thread Link State Update Retransmission %s%s%s", + nbr->t_ls_upd != NULL ? "on" : "off", VTY_NEWLINE, VTY_NEWLINE); +} + +DEFUN (show_ip_ospf_neighbor_id, + show_ip_ospf_neighbor_id_cmd, + "show ip ospf neighbor A.B.C.D", + SHOW_STR + IP_STR + "OSPF information\n" + "Neighbor list\n" + "Neighbor ID\n") +{ + struct ospf *ospf; + struct listnode *node; + struct ospf_neighbor *nbr; + struct ospf_interface *oi; + struct in_addr router_id; + int ret; + + ret = inet_aton (argv[0], &router_id); + if (!ret) + { + vty_out (vty, "Please specify Neighbor ID by A.B.C.D%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ospf = ospf_lookup (); + if (ospf == NULL) + { + vty_out (vty, " OSPF Routing Process not enabled%s", VTY_NEWLINE); + return CMD_SUCCESS; + } + + for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi)) + if ((nbr = ospf_nbr_lookup_by_routerid (oi->nbrs, &router_id))) + show_ip_ospf_neighbor_detail_sub (vty, oi, nbr); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_ospf_neighbor_detail, + show_ip_ospf_neighbor_detail_cmd, + "show ip ospf neighbor detail", + SHOW_STR + IP_STR + "OSPF information\n" + "Neighbor list\n" + "detail of all neighbors\n") +{ + struct ospf *ospf; + struct ospf_interface *oi; + struct listnode *node; + + ospf = ospf_lookup (); + if (ospf == NULL) + { + vty_out (vty, " OSPF Routing Process not enabled%s", VTY_NEWLINE); + return CMD_SUCCESS; + } + + for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi)) + { + struct route_node *rn; + struct ospf_neighbor *nbr; + + for (rn = route_top (oi->nbrs); rn; rn = route_next (rn)) + if ((nbr = rn->info)) + if (nbr != oi->nbr_self) + if (nbr->state != NSM_Down) + show_ip_ospf_neighbor_detail_sub (vty, oi, nbr); + } + + return CMD_SUCCESS; +} + +DEFUN (show_ip_ospf_neighbor_detail_all, + show_ip_ospf_neighbor_detail_all_cmd, + "show ip ospf neighbor detail all", + SHOW_STR + IP_STR + "OSPF information\n" + "Neighbor list\n" + "detail of all neighbors\n" + "include down status neighbor\n") +{ + struct ospf *ospf; + struct listnode *node; + struct ospf_interface *oi; + + ospf = ospf_lookup (); + if (ospf == NULL) + { + vty_out (vty, " OSPF Routing Process not enabled%s", VTY_NEWLINE); + return CMD_SUCCESS; + } + + for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi)) + { + struct route_node *rn; + struct ospf_neighbor *nbr; + struct ospf_nbr_nbma *nbr_nbma; + + for (rn = route_top (oi->nbrs); rn; rn = route_next (rn)) + if ((nbr = rn->info)) + if (nbr != oi->nbr_self) + if (oi->type == OSPF_IFTYPE_NBMA && nbr->state != NSM_Down) + show_ip_ospf_neighbor_detail_sub (vty, oi, rn->info); + + if (oi->type == OSPF_IFTYPE_NBMA) + { + struct listnode *nd; + + for (ALL_LIST_ELEMENTS_RO (oi->nbr_nbma, nd, nbr_nbma)) + if (nbr_nbma->nbr == NULL + || nbr_nbma->nbr->state == NSM_Down) + show_ip_ospf_nbr_nbma_detail_sub (vty, oi, nbr_nbma); + } + } + + return CMD_SUCCESS; +} + +DEFUN (show_ip_ospf_neighbor_int_detail, + show_ip_ospf_neighbor_int_detail_cmd, + "show ip ospf neighbor IFNAME detail", + SHOW_STR + IP_STR + "OSPF information\n" + "Neighbor list\n" + "Interface name\n" + "detail of all neighbors") +{ + struct ospf *ospf; + struct ospf_interface *oi; + struct interface *ifp; + struct route_node *rn, *nrn; + struct ospf_neighbor *nbr; + + ifp = if_lookup_by_name (argv[0]); + if (!ifp) + { + vty_out (vty, "No such interface.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ospf = ospf_lookup (); + if (ospf == NULL) + { + vty_out (vty, " OSPF Routing Process not enabled%s", VTY_NEWLINE); + return CMD_SUCCESS; + } + + + for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn)) + if ((oi = rn->info)) + for (nrn = route_top (oi->nbrs); nrn; nrn = route_next (nrn)) + if ((nbr = nrn->info)) + if (nbr != oi->nbr_self) + if (nbr->state != NSM_Down) + show_ip_ospf_neighbor_detail_sub (vty, oi, nbr); + + return CMD_SUCCESS; +} + + +/* Show functions */ +static int +show_lsa_summary (struct vty *vty, struct ospf_lsa *lsa, int self) +{ + struct router_lsa *rl; + struct summary_lsa *sl; + struct as_external_lsa *asel; + struct prefix_ipv4 p; + + if (lsa != NULL) + /* If self option is set, check LSA self flag. */ + if (self == 0 || IS_LSA_SELF (lsa)) + { + /* LSA common part show. */ + vty_out (vty, "%-15s ", inet_ntoa (lsa->data->id)); + vty_out (vty, "%-15s %4d 0x%08lx 0x%04x", + inet_ntoa (lsa->data->adv_router), LS_AGE (lsa), + (u_long)ntohl (lsa->data->ls_seqnum), ntohs (lsa->data->checksum)); + /* LSA specific part show. */ + switch (lsa->data->type) + { + case OSPF_ROUTER_LSA: + rl = (struct router_lsa *) lsa->data; + vty_out (vty, " %-d", ntohs (rl->links)); + break; + case OSPF_SUMMARY_LSA: + sl = (struct summary_lsa *) lsa->data; + + p.family = AF_INET; + p.prefix = sl->header.id; + p.prefixlen = ip_masklen (sl->mask); + apply_mask_ipv4 (&p); + + vty_out (vty, " %s/%d", inet_ntoa (p.prefix), p.prefixlen); + break; + case OSPF_AS_EXTERNAL_LSA: + case OSPF_AS_NSSA_LSA: + asel = (struct as_external_lsa *) lsa->data; + + p.family = AF_INET; + p.prefix = asel->header.id; + p.prefixlen = ip_masklen (asel->mask); + apply_mask_ipv4 (&p); + + vty_out (vty, " %s %s/%d [0x%lx]", + IS_EXTERNAL_METRIC (asel->e[0].tos) ? "E2" : "E1", + inet_ntoa (p.prefix), p.prefixlen, + (u_long)ntohl (asel->e[0].route_tag)); + break; + case OSPF_NETWORK_LSA: + case OSPF_ASBR_SUMMARY_LSA: + case OSPF_OPAQUE_LINK_LSA: + case OSPF_OPAQUE_AREA_LSA: + case OSPF_OPAQUE_AS_LSA: + default: + break; + } + vty_out (vty, VTY_NEWLINE); + } + + return 0; +} + +static const char *show_database_desc[] = +{ + "unknown", + "Router Link States", + "Net Link States", + "Summary Link States", + "ASBR-Summary Link States", + "AS External Link States", + "Group Membership LSA", + "NSSA-external Link States", + "Type-8 LSA", + "Link-Local Opaque-LSA", + "Area-Local Opaque-LSA", + "AS-external Opaque-LSA", +}; + +static const char *show_database_header[] = +{ + "", + "Link ID ADV Router Age Seq# CkSum Link count", + "Link ID ADV Router Age Seq# CkSum", + "Link ID ADV Router Age Seq# CkSum Route", + "Link ID ADV Router Age Seq# CkSum", + "Link ID ADV Router Age Seq# CkSum Route", + " --- header for Group Member ----", + "Link ID ADV Router Age Seq# CkSum Route", + " --- 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", +}; + +static void +show_ip_ospf_database_header (struct vty *vty, struct ospf_lsa *lsa) +{ + struct router_lsa *rlsa = (struct router_lsa*) lsa->data; + + vty_out (vty, " LS age: %d%s", LS_AGE (lsa), VTY_NEWLINE); + vty_out (vty, " Options: 0x%-2x : %s%s", + lsa->data->options, + ospf_options_dump(lsa->data->options), + VTY_NEWLINE); + vty_out (vty, " LS Flags: 0x%-2x %s%s", + lsa->flags, + ((lsa->flags & OSPF_LSA_LOCAL_XLT) ? "(Translated from Type-7)" : ""), + VTY_NEWLINE); + + if (lsa->data->type == OSPF_ROUTER_LSA) + { + vty_out (vty, " Flags: 0x%x" , rlsa->flags); + + if (rlsa->flags) + vty_out (vty, " :%s%s%s%s", + IS_ROUTER_LSA_BORDER (rlsa) ? " ABR" : "", + IS_ROUTER_LSA_EXTERNAL (rlsa) ? " ASBR" : "", + IS_ROUTER_LSA_VIRTUAL (rlsa) ? " VL-endpoint" : "", + IS_ROUTER_LSA_SHORTCUT (rlsa) ? " Shortcut" : ""); + + vty_out (vty, "%s", VTY_NEWLINE); + } + vty_out (vty, " LS Type: %s%s", + LOOKUP (ospf_lsa_type_msg, lsa->data->type), VTY_NEWLINE); + vty_out (vty, " Link State ID: %s %s%s", inet_ntoa (lsa->data->id), + LOOKUP (ospf_link_state_id_type_msg, lsa->data->type), VTY_NEWLINE); + vty_out (vty, " Advertising Router: %s%s", + inet_ntoa (lsa->data->adv_router), VTY_NEWLINE); + vty_out (vty, " LS Seq Number: %08lx%s", (u_long)ntohl (lsa->data->ls_seqnum), + VTY_NEWLINE); + vty_out (vty, " Checksum: 0x%04x%s", ntohs (lsa->data->checksum), + VTY_NEWLINE); + vty_out (vty, " Length: %d%s", ntohs (lsa->data->length), VTY_NEWLINE); +} + +const char *link_type_desc[] = +{ + "(null)", + "another Router (point-to-point)", + "a Transit Network", + "Stub Network", + "a Virtual Link", +}; + +const char *link_id_desc[] = +{ + "(null)", + "Neighboring Router ID", + "Designated Router address", + "Net", + "Neighboring Router ID", +}; + +const char *link_data_desc[] = +{ + "(null)", + "Router Interface address", + "Router Interface address", + "Network Mask", + "Router Interface address", +}; + +/* Show router-LSA each Link information. */ +static void +show_ip_ospf_database_router_links (struct vty *vty, + struct router_lsa *rl) +{ + int len, type; + unsigned int i; + + len = ntohs (rl->header.length) - 4; + for (i = 0; i < ntohs (rl->links) && len > 0; len -= 12, i++) + { + type = rl->link[i].type; + + vty_out (vty, " Link connected to: %s%s", + link_type_desc[type], VTY_NEWLINE); + vty_out (vty, " (Link ID) %s: %s%s", link_id_desc[type], + inet_ntoa (rl->link[i].link_id), VTY_NEWLINE); + vty_out (vty, " (Link Data) %s: %s%s", link_data_desc[type], + inet_ntoa (rl->link[i].link_data), VTY_NEWLINE); + vty_out (vty, " Number of TOS metrics: 0%s", VTY_NEWLINE); + vty_out (vty, " TOS 0 Metric: %d%s", + ntohs (rl->link[i].metric), VTY_NEWLINE); + vty_out (vty, "%s", VTY_NEWLINE); + } +} + +/* Show router-LSA detail information. */ +static int +show_router_lsa_detail (struct vty *vty, struct ospf_lsa *lsa) +{ + if (lsa != NULL) + { + struct router_lsa *rl = (struct router_lsa *) lsa->data; + + show_ip_ospf_database_header (vty, lsa); + + vty_out (vty, " Number of Links: %d%s%s", ntohs (rl->links), + VTY_NEWLINE, VTY_NEWLINE); + + show_ip_ospf_database_router_links (vty, rl); + vty_out (vty, "%s", VTY_NEWLINE); + } + + return 0; +} + +/* Show network-LSA detail information. */ +static int +show_network_lsa_detail (struct vty *vty, struct ospf_lsa *lsa) +{ + int length, i; + + if (lsa != NULL) + { + struct network_lsa *nl = (struct network_lsa *) lsa->data; + + show_ip_ospf_database_header (vty, lsa); + + vty_out (vty, " Network Mask: /%d%s", + ip_masklen (nl->mask), VTY_NEWLINE); + + length = ntohs (lsa->data->length) - OSPF_LSA_HEADER_SIZE - 4; + + for (i = 0; length > 0; i++, length -= 4) + vty_out (vty, " Attached Router: %s%s", + inet_ntoa (nl->routers[i]), VTY_NEWLINE); + + vty_out (vty, "%s", VTY_NEWLINE); + } + + return 0; +} + +/* Show summary-LSA detail information. */ +static int +show_summary_lsa_detail (struct vty *vty, struct ospf_lsa *lsa) +{ + if (lsa != NULL) + { + struct summary_lsa *sl = (struct summary_lsa *) lsa->data; + + show_ip_ospf_database_header (vty, lsa); + + vty_out (vty, " Network Mask: /%d%s", ip_masklen (sl->mask), + VTY_NEWLINE); + vty_out (vty, " TOS: 0 Metric: %d%s", GET_METRIC (sl->metric), + VTY_NEWLINE); + vty_out (vty, "%s", VTY_NEWLINE); + } + + return 0; +} + +/* Show summary-ASBR-LSA detail information. */ +static int +show_summary_asbr_lsa_detail (struct vty *vty, struct ospf_lsa *lsa) +{ + if (lsa != NULL) + { + struct summary_lsa *sl = (struct summary_lsa *) lsa->data; + + show_ip_ospf_database_header (vty, lsa); + + vty_out (vty, " Network Mask: /%d%s", + ip_masklen (sl->mask), VTY_NEWLINE); + vty_out (vty, " TOS: 0 Metric: %d%s", GET_METRIC (sl->metric), + VTY_NEWLINE); + vty_out (vty, "%s", VTY_NEWLINE); + } + + return 0; +} + +/* Show AS-external-LSA detail information. */ +static int +show_as_external_lsa_detail (struct vty *vty, struct ospf_lsa *lsa) +{ + if (lsa != NULL) + { + struct as_external_lsa *al = (struct as_external_lsa *) lsa->data; + + show_ip_ospf_database_header (vty, lsa); + + vty_out (vty, " Network Mask: /%d%s", + ip_masklen (al->mask), VTY_NEWLINE); + vty_out (vty, " Metric Type: %s%s", + IS_EXTERNAL_METRIC (al->e[0].tos) ? + "2 (Larger than any link state path)" : "1", VTY_NEWLINE); + vty_out (vty, " TOS: 0%s", VTY_NEWLINE); + vty_out (vty, " Metric: %d%s", + GET_METRIC (al->e[0].metric), VTY_NEWLINE); + vty_out (vty, " Forward Address: %s%s", + inet_ntoa (al->e[0].fwd_addr), 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; +} + +#if 0 +static int +show_as_external_lsa_stdvty (struct ospf_lsa *lsa) +{ + struct as_external_lsa *al = (struct as_external_lsa *) lsa->data; + + /* show_ip_ospf_database_header (vty, lsa); */ + + zlog_debug( " Network Mask: /%d%s", + ip_masklen (al->mask), "\n"); + zlog_debug( " Metric Type: %s%s", + IS_EXTERNAL_METRIC (al->e[0].tos) ? + "2 (Larger than any link state path)" : "1", "\n"); + zlog_debug( " TOS: 0%s", "\n"); + zlog_debug( " Metric: %d%s", + GET_METRIC (al->e[0].metric), "\n"); + zlog_debug( " Forward Address: %s%s", + inet_ntoa (al->e[0].fwd_addr), "\n"); + + zlog_debug( " External Route Tag: %lu%s%s", + (u_long)ntohl (al->e[0].route_tag), "\n", "\n"); + + return 0; +} +#endif + +/* Show AS-NSSA-LSA detail information. */ +static int +show_as_nssa_lsa_detail (struct vty *vty, struct ospf_lsa *lsa) +{ + if (lsa != NULL) + { + struct as_external_lsa *al = (struct as_external_lsa *) lsa->data; + + show_ip_ospf_database_header (vty, lsa); + + vty_out (vty, " Network Mask: /%d%s", + ip_masklen (al->mask), VTY_NEWLINE); + vty_out (vty, " Metric Type: %s%s", + IS_EXTERNAL_METRIC (al->e[0].tos) ? + "2 (Larger than any link state path)" : "1", VTY_NEWLINE); + vty_out (vty, " TOS: 0%s", VTY_NEWLINE); + vty_out (vty, " Metric: %d%s", + GET_METRIC (al->e[0].metric), VTY_NEWLINE); + vty_out (vty, " NSSA: Forward Address: %s%s", + inet_ntoa (al->e[0].fwd_addr), 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; +} + +static int +show_func_dummy (struct vty *vty, struct ospf_lsa *lsa) +{ + return 0; +} + +static int +show_opaque_lsa_detail (struct vty *vty, struct ospf_lsa *lsa) +{ + if (lsa != NULL) + { + show_ip_ospf_database_header (vty, lsa); + show_opaque_info_detail (vty, lsa); + + vty_out (vty, "%s", VTY_NEWLINE); + } + return 0; +} + +int (*show_function[])(struct vty *, struct ospf_lsa *) = +{ + NULL, + show_router_lsa_detail, + show_network_lsa_detail, + show_summary_lsa_detail, + show_summary_asbr_lsa_detail, + show_as_external_lsa_detail, + show_func_dummy, + show_as_nssa_lsa_detail, /* almost same as external */ + NULL, /* type-8 */ + show_opaque_lsa_detail, + show_opaque_lsa_detail, + show_opaque_lsa_detail, +}; + +static void +show_lsa_prefix_set (struct vty *vty, struct prefix_ls *lp, struct in_addr *id, + struct in_addr *adv_router) +{ + memset (lp, 0, sizeof (struct prefix_ls)); + lp->family = 0; + if (id == NULL) + lp->prefixlen = 0; + else if (adv_router == NULL) + { + lp->prefixlen = 32; + lp->id = *id; + } + else + { + lp->prefixlen = 64; + lp->id = *id; + lp->adv_router = *adv_router; + } +} + +static void +show_lsa_detail_proc (struct vty *vty, struct route_table *rt, + struct in_addr *id, struct in_addr *adv_router) +{ + struct prefix_ls lp; + struct route_node *rn, *start; + struct ospf_lsa *lsa; + + show_lsa_prefix_set (vty, &lp, id, adv_router); + start = route_node_get (rt, (struct prefix *) &lp); + if (start) + { + route_lock_node (start); + for (rn = start; rn; rn = route_next_until (rn, start)) + if ((lsa = rn->info)) + { + if (show_function[lsa->data->type] != NULL) + show_function[lsa->data->type] (vty, lsa); + } + route_unlock_node (start); + } +} + +/* Show detail LSA information + -- if id is NULL then show all LSAs. */ +static void +show_lsa_detail (struct vty *vty, struct ospf *ospf, int type, + struct in_addr *id, struct in_addr *adv_router) +{ + struct listnode *node; + struct ospf_area *area; + + switch (type) + { + case OSPF_AS_EXTERNAL_LSA: + case OSPF_OPAQUE_AS_LSA: + vty_out (vty, " %s %s%s", + show_database_desc[type], + VTY_NEWLINE, VTY_NEWLINE); + show_lsa_detail_proc (vty, AS_LSDB (ospf, type), id, adv_router); + break; + default: + for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area)) + { + vty_out (vty, "%s %s (Area %s)%s%s", + VTY_NEWLINE, show_database_desc[type], + ospf_area_desc_string (area), VTY_NEWLINE, VTY_NEWLINE); + show_lsa_detail_proc (vty, AREA_LSDB (area, type), id, adv_router); + } + break; + } +} + +static void +show_lsa_detail_adv_router_proc (struct vty *vty, struct route_table *rt, + struct in_addr *adv_router) +{ + struct route_node *rn; + struct ospf_lsa *lsa; + + for (rn = route_top (rt); rn; rn = route_next (rn)) + if ((lsa = rn->info)) + if (IPV4_ADDR_SAME (adv_router, &lsa->data->adv_router)) + { + if (CHECK_FLAG (lsa->flags, OSPF_LSA_LOCAL_XLT)) + continue; + if (show_function[lsa->data->type] != NULL) + show_function[lsa->data->type] (vty, lsa); + } +} + +/* Show detail LSA information. */ +static void +show_lsa_detail_adv_router (struct vty *vty, struct ospf *ospf, int type, + struct in_addr *adv_router) +{ + struct listnode *node; + struct ospf_area *area; + + switch (type) + { + case OSPF_AS_EXTERNAL_LSA: + case OSPF_OPAQUE_AS_LSA: + vty_out (vty, " %s %s%s", + show_database_desc[type], + VTY_NEWLINE, VTY_NEWLINE); + show_lsa_detail_adv_router_proc (vty, AS_LSDB (ospf, type), + adv_router); + break; + default: + for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area)) + { + vty_out (vty, "%s %s (Area %s)%s%s", + VTY_NEWLINE, show_database_desc[type], + ospf_area_desc_string (area), VTY_NEWLINE, VTY_NEWLINE); + show_lsa_detail_adv_router_proc (vty, AREA_LSDB (area, type), + adv_router); + } + break; + } +} + +static void +show_ip_ospf_database_summary (struct vty *vty, struct ospf *ospf, int self) +{ + struct ospf_lsa *lsa; + struct route_node *rn; + struct ospf_area *area; + struct listnode *node; + int type; + + for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area)) + { + for (type = OSPF_MIN_LSA; type < OSPF_MAX_LSA; type++) + { + switch (type) + { + case OSPF_AS_EXTERNAL_LSA: + case OSPF_OPAQUE_AS_LSA: + continue; + default: + break; + } + if (ospf_lsdb_count_self (area->lsdb, type) > 0 || + (!self && ospf_lsdb_count (area->lsdb, type) > 0)) + { + vty_out (vty, " %s (Area %s)%s%s", + show_database_desc[type], + ospf_area_desc_string (area), + VTY_NEWLINE, VTY_NEWLINE); + vty_out (vty, "%s%s", show_database_header[type], VTY_NEWLINE); + + LSDB_LOOP (AREA_LSDB (area, type), rn, lsa) + show_lsa_summary (vty, lsa, self); + + vty_out (vty, "%s", VTY_NEWLINE); + } + } + } + + for (type = OSPF_MIN_LSA; type < OSPF_MAX_LSA; type++) + { + switch (type) + { + case OSPF_AS_EXTERNAL_LSA: + case OSPF_OPAQUE_AS_LSA: + break; + default: + continue; + } + if (ospf_lsdb_count_self (ospf->lsdb, type) || + (!self && ospf_lsdb_count (ospf->lsdb, type))) + { + vty_out (vty, " %s%s%s", + show_database_desc[type], + VTY_NEWLINE, VTY_NEWLINE); + vty_out (vty, "%s%s", show_database_header[type], + VTY_NEWLINE); + + LSDB_LOOP (AS_LSDB (ospf, type), rn, lsa) + show_lsa_summary (vty, lsa, self); + + vty_out (vty, "%s", VTY_NEWLINE); + } + } + + vty_out (vty, "%s", VTY_NEWLINE); +} + +static void +show_ip_ospf_database_maxage (struct vty *vty, struct ospf *ospf) +{ + struct route_node *rn; + + vty_out (vty, "%s MaxAge Link States:%s%s", + VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + + for (rn = route_top (ospf->maxage_lsa); rn; rn = route_next (rn)) + { + 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" + +#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" + +#define OSPF_LSA_TYPES_CMD_STR \ + "asbr-summary|external|network|router|summary" \ + OSPF_LSA_TYPE_NSSA_CMD_STR \ + OSPF_LSA_TYPE_OPAQUE_CMD_STR + +#define OSPF_LSA_TYPES_DESC \ + "ASBR summary link states\n" \ + "External link states\n" \ + "Network link states\n" \ + "Router link states\n" \ + "Network summary link states\n" \ + OSPF_LSA_TYPE_NSSA_DESC \ + OSPF_LSA_TYPE_OPAQUE_LINK_DESC \ + OSPF_LSA_TYPE_OPAQUE_AREA_DESC \ + OSPF_LSA_TYPE_OPAQUE_AS_DESC + +DEFUN (show_ip_ospf_database, + show_ip_ospf_database_cmd, + "show ip ospf database", + SHOW_STR + IP_STR + "OSPF information\n" + "Database summary\n") +{ + struct ospf *ospf; + int type, ret; + struct in_addr id, adv_router; + + ospf = ospf_lookup (); + if (ospf == NULL) + { + vty_out (vty, " OSPF Routing Process not enabled%s", VTY_NEWLINE); + return CMD_SUCCESS; + } + + vty_out (vty, "%s OSPF Router with ID (%s)%s%s", VTY_NEWLINE, + inet_ntoa (ospf->router_id), VTY_NEWLINE, VTY_NEWLINE); + + /* Show all LSA. */ + if (argc == 0) + { + show_ip_ospf_database_summary (vty, ospf, 0); + return CMD_SUCCESS; + } + + /* Set database type to show. */ + if (strncmp (argv[0], "r", 1) == 0) + type = OSPF_ROUTER_LSA; + else if (strncmp (argv[0], "ne", 2) == 0) + type = OSPF_NETWORK_LSA; + else if (strncmp (argv[0], "ns", 2) == 0) + type = OSPF_AS_NSSA_LSA; + else if (strncmp (argv[0], "su", 2) == 0) + type = OSPF_SUMMARY_LSA; + else if (strncmp (argv[0], "a", 1) == 0) + type = OSPF_ASBR_SUMMARY_LSA; + else if (strncmp (argv[0], "e", 1) == 0) + type = OSPF_AS_EXTERNAL_LSA; + else if (strncmp (argv[0], "se", 2) == 0) + { + show_ip_ospf_database_summary (vty, ospf, 1); + return CMD_SUCCESS; + } + else if (strncmp (argv[0], "m", 1) == 0) + { + show_ip_ospf_database_maxage (vty, ospf); + return CMD_SUCCESS; + } + 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; + else + return CMD_WARNING; + + /* `show ip ospf database LSA'. */ + if (argc == 1) + show_lsa_detail (vty, ospf, type, NULL, NULL); + else if (argc >= 2) + { + ret = inet_aton (argv[1], &id); + if (!ret) + return CMD_WARNING; + + /* `show ip ospf database LSA ID'. */ + if (argc == 2) + show_lsa_detail (vty, ospf, type, &id, NULL); + /* `show ip ospf database LSA ID adv-router ADV_ROUTER'. */ + else if (argc == 3) + { + if (strncmp (argv[2], "s", 1) == 0) + adv_router = ospf->router_id; + else + { + ret = inet_aton (argv[2], &adv_router); + if (!ret) + return CMD_WARNING; + } + show_lsa_detail (vty, ospf, type, &id, &adv_router); + } + } + + return CMD_SUCCESS; +} + +ALIAS (show_ip_ospf_database, + show_ip_ospf_database_type_cmd, + "show ip ospf database (" OSPF_LSA_TYPES_CMD_STR "|max-age|self-originate)", + SHOW_STR + IP_STR + "OSPF information\n" + "Database summary\n" + OSPF_LSA_TYPES_DESC + "LSAs in MaxAge list\n" + "Self-originated link states\n") + +ALIAS (show_ip_ospf_database, + show_ip_ospf_database_type_id_cmd, + "show ip ospf database (" OSPF_LSA_TYPES_CMD_STR ") A.B.C.D", + SHOW_STR + IP_STR + "OSPF information\n" + "Database summary\n" + OSPF_LSA_TYPES_DESC + "Link State ID (as an IP address)\n") + +ALIAS (show_ip_ospf_database, + show_ip_ospf_database_type_id_adv_router_cmd, + "show ip ospf database (" OSPF_LSA_TYPES_CMD_STR ") A.B.C.D adv-router A.B.C.D", + SHOW_STR + IP_STR + "OSPF information\n" + "Database summary\n" + OSPF_LSA_TYPES_DESC + "Link State ID (as an IP address)\n" + "Advertising Router link states\n" + "Advertising Router (as an IP address)\n") + +ALIAS (show_ip_ospf_database, + show_ip_ospf_database_type_id_self_cmd, + "show ip ospf database (" OSPF_LSA_TYPES_CMD_STR ") A.B.C.D (self-originate|)", + SHOW_STR + IP_STR + "OSPF information\n" + "Database summary\n" + OSPF_LSA_TYPES_DESC + "Link State ID (as an IP address)\n" + "Self-originated link states\n" + "\n") + +DEFUN (show_ip_ospf_database_type_adv_router, + show_ip_ospf_database_type_adv_router_cmd, + "show ip ospf database (" OSPF_LSA_TYPES_CMD_STR ") adv-router A.B.C.D", + SHOW_STR + IP_STR + "OSPF information\n" + "Database summary\n" + OSPF_LSA_TYPES_DESC + "Advertising Router link states\n" + "Advertising Router (as an IP address)\n") +{ + struct ospf *ospf; + int type, ret; + struct in_addr adv_router; + + ospf = ospf_lookup (); + if (ospf == NULL) + { + vty_out (vty, " OSPF Routing Process not enabled%s", VTY_NEWLINE); + return CMD_SUCCESS; + } + + vty_out (vty, "%s OSPF Router with ID (%s)%s%s", VTY_NEWLINE, + inet_ntoa (ospf->router_id), VTY_NEWLINE, VTY_NEWLINE); + + if (argc != 2) + return CMD_WARNING; + + /* Set database type to show. */ + if (strncmp (argv[0], "r", 1) == 0) + type = OSPF_ROUTER_LSA; + else if (strncmp (argv[0], "ne", 2) == 0) + type = OSPF_NETWORK_LSA; + else if (strncmp (argv[0], "ns", 2) == 0) + type = OSPF_AS_NSSA_LSA; + else if (strncmp (argv[0], "s", 1) == 0) + type = OSPF_SUMMARY_LSA; + else if (strncmp (argv[0], "a", 1) == 0) + type = OSPF_ASBR_SUMMARY_LSA; + else if (strncmp (argv[0], "e", 1) == 0) + type = OSPF_AS_EXTERNAL_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; + else + return CMD_WARNING; + + /* `show ip ospf database LSA adv-router ADV_ROUTER'. */ + if (strncmp (argv[1], "s", 1) == 0) + adv_router = ospf->router_id; + else + { + ret = inet_aton (argv[1], &adv_router); + if (!ret) + return CMD_WARNING; + } + + show_lsa_detail_adv_router (vty, ospf, type, &adv_router); + + return CMD_SUCCESS; +} + +ALIAS (show_ip_ospf_database_type_adv_router, + show_ip_ospf_database_type_self_cmd, + "show ip ospf database (" OSPF_LSA_TYPES_CMD_STR ") (self-originate|)", + SHOW_STR + IP_STR + "OSPF information\n" + "Database summary\n" + 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", + "IP Information\n" + "OSPF interface commands\n" + "Enable authentication on this interface\n" + "Use null authentication\n" + "Use message-digest authentication\n" + "Address of interface") +{ + struct interface *ifp; + struct in_addr addr; + int ret; + struct ospf_if_params *params; + + ifp = vty->index; + params = IF_DEF_PARAMS (ifp); + + if (argc == 2) + { + ret = inet_aton(argv[1], &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); + ospf_if_update_params (ifp, addr); + } + + /* Handle null authentication */ + if ( argv[0][0] == 'n' ) + { + SET_IF_PARAM (params, auth_type); + params->auth_type = OSPF_AUTH_NULL; + return CMD_SUCCESS; + } + + /* Handle message-digest authentication */ + if ( argv[0][0] == 'm' ) + { + SET_IF_PARAM (params, auth_type); + params->auth_type = OSPF_AUTH_CRYPTOGRAPHIC; + return CMD_SUCCESS; + } + + vty_out (vty, "You shouldn't get here!%s", VTY_NEWLINE); + return CMD_WARNING; +} + +ALIAS (ip_ospf_authentication_args, + ip_ospf_authentication_args_cmd, + "ip ospf authentication (null|message-digest)", + "IP Information\n" + "OSPF interface commands\n" + "Enable authentication on this interface\n" + "Use null authentication\n" + "Use message-digest authentication\n") + +DEFUN (ip_ospf_authentication, + ip_ospf_authentication_addr_cmd, + "ip ospf authentication A.B.C.D", + "IP Information\n" + "OSPF interface commands\n" + "Enable authentication on this interface\n" + "Address of interface") +{ + struct interface *ifp; + struct in_addr addr; + int ret; + struct ospf_if_params *params; + + ifp = vty->index; + params = IF_DEF_PARAMS (ifp); + + if (argc == 1) + { + ret = inet_aton(argv[0], &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); + ospf_if_update_params (ifp, addr); + } + + SET_IF_PARAM (params, auth_type); + params->auth_type = OSPF_AUTH_SIMPLE; + + return CMD_SUCCESS; +} + +ALIAS (ip_ospf_authentication, + ip_ospf_authentication_cmd, + "ip ospf authentication", + "IP Information\n" + "OSPF interface commands\n" + "Enable authentication on this interface\n") + +DEFUN (no_ip_ospf_authentication, + no_ip_ospf_authentication_addr_cmd, + "no ip ospf authentication A.B.C.D", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Enable authentication on this interface\n" + "Address of interface") +{ + struct interface *ifp; + struct in_addr addr; + int ret; + struct ospf_if_params *params; + + ifp = vty->index; + params = IF_DEF_PARAMS (ifp); + + if (argc == 1) + { + ret = inet_aton(argv[0], &addr); + if (!ret) + { + vty_out (vty, "Please specify interface address by A.B.C.D%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + params = ospf_lookup_if_params (ifp, addr); + if (params == NULL) + return CMD_SUCCESS; + } + + params->auth_type = OSPF_AUTH_NOTSET; + UNSET_IF_PARAM (params, auth_type); + + if (params != IF_DEF_PARAMS (ifp)) + { + ospf_free_if_params (ifp, addr); + ospf_if_update_params (ifp, addr); + } + + return CMD_SUCCESS; +} + +ALIAS (no_ip_ospf_authentication, + no_ip_ospf_authentication_cmd, + "no ip ospf authentication", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Enable authentication on this interface\n") + +DEFUN (ip_ospf_authentication_key, + ip_ospf_authentication_key_addr_cmd, + "ip ospf authentication-key AUTH_KEY A.B.C.D", + "IP Information\n" + "OSPF interface commands\n" + "Authentication password (key)\n" + "The OSPF password (key)\n" + "Address of interface") +{ + struct interface *ifp; + struct in_addr addr; + int ret; + struct ospf_if_params *params; + + ifp = vty->index; + params = IF_DEF_PARAMS (ifp); + + if (argc == 2) + { + ret = inet_aton(argv[1], &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); + ospf_if_update_params (ifp, addr); + } + + + memset (params->auth_simple, 0, OSPF_AUTH_SIMPLE_SIZE + 1); + strncpy ((char *) params->auth_simple, argv[0], OSPF_AUTH_SIMPLE_SIZE); + SET_IF_PARAM (params, auth_simple); + + return CMD_SUCCESS; +} + +ALIAS (ip_ospf_authentication_key, + ip_ospf_authentication_key_cmd, + "ip ospf authentication-key AUTH_KEY", + "IP Information\n" + "OSPF interface commands\n" + "Authentication password (key)\n" + "The OSPF password (key)") + +ALIAS (ip_ospf_authentication_key, + ospf_authentication_key_cmd, + "ospf authentication-key AUTH_KEY", + "OSPF interface commands\n" + "Authentication password (key)\n" + "The OSPF password (key)") + +DEFUN (no_ip_ospf_authentication_key, + no_ip_ospf_authentication_key_addr_cmd, + "no ip ospf authentication-key A.B.C.D", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Authentication password (key)\n" + "Address of interface") +{ + struct interface *ifp; + struct in_addr addr; + int ret; + struct ospf_if_params *params; + + ifp = vty->index; + params = IF_DEF_PARAMS (ifp); + + if (argc == 1) + { + ret = inet_aton(argv[0], &addr); + if (!ret) + { + vty_out (vty, "Please specify interface address by A.B.C.D%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + params = ospf_lookup_if_params (ifp, addr); + if (params == NULL) + return CMD_SUCCESS; + } + + memset (params->auth_simple, 0, OSPF_AUTH_SIMPLE_SIZE); + UNSET_IF_PARAM (params, auth_simple); + + if (params != IF_DEF_PARAMS (ifp)) + { + ospf_free_if_params (ifp, addr); + ospf_if_update_params (ifp, addr); + } + + return CMD_SUCCESS; +} + +ALIAS (no_ip_ospf_authentication_key, + no_ip_ospf_authentication_key_cmd, + "no ip ospf authentication-key", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Authentication password (key)\n") + +ALIAS (no_ip_ospf_authentication_key, + no_ospf_authentication_key_cmd, + "no ospf authentication-key", + NO_STR + "OSPF interface commands\n" + "Authentication password (key)\n") + +DEFUN (ip_ospf_message_digest_key, + ip_ospf_message_digest_key_addr_cmd, + "ip ospf message-digest-key <1-255> md5 KEY A.B.C.D", + "IP Information\n" + "OSPF interface commands\n" + "Message digest authentication password (key)\n" + "Key ID\n" + "Use MD5 algorithm\n" + "The OSPF password (key)" + "Address of interface") +{ + struct interface *ifp; + struct crypt_key *ck; + u_char key_id; + struct in_addr addr; + int ret; + struct ospf_if_params *params; + + ifp = vty->index; + params = IF_DEF_PARAMS (ifp); + + if (argc == 3) + { + ret = inet_aton(argv[2], &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); + ospf_if_update_params (ifp, addr); + } + + key_id = strtol (argv[0], NULL, 10); + if (ospf_crypt_key_lookup (params->auth_crypt, key_id) != NULL) + { + vty_out (vty, "OSPF: Key %d already exists%s", key_id, VTY_NEWLINE); + return CMD_WARNING; + } + + ck = ospf_crypt_key_new (); + ck->key_id = (u_char) key_id; + memset (ck->auth_key, 0, OSPF_AUTH_MD5_SIZE+1); + strncpy ((char *) ck->auth_key, argv[1], OSPF_AUTH_MD5_SIZE); + + ospf_crypt_key_add (params->auth_crypt, ck); + SET_IF_PARAM (params, auth_crypt); + + return CMD_SUCCESS; +} + +ALIAS (ip_ospf_message_digest_key, + ip_ospf_message_digest_key_cmd, + "ip ospf message-digest-key <1-255> md5 KEY", + "IP Information\n" + "OSPF interface commands\n" + "Message digest authentication password (key)\n" + "Key ID\n" + "Use MD5 algorithm\n" + "The OSPF password (key)") + +ALIAS (ip_ospf_message_digest_key, + ospf_message_digest_key_cmd, + "ospf message-digest-key <1-255> md5 KEY", + "OSPF interface commands\n" + "Message digest authentication password (key)\n" + "Key ID\n" + "Use MD5 algorithm\n" + "The OSPF password (key)") + +DEFUN (no_ip_ospf_message_digest_key, + no_ip_ospf_message_digest_key_addr_cmd, + "no ip ospf message-digest-key <1-255> A.B.C.D", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Message digest authentication password (key)\n" + "Key ID\n" + "Address of interface") +{ + struct interface *ifp; + struct crypt_key *ck; + int key_id; + struct in_addr addr; + int ret; + struct ospf_if_params *params; + + ifp = vty->index; + params = IF_DEF_PARAMS (ifp); + + if (argc == 2) + { + ret = inet_aton(argv[1], &addr); + if (!ret) + { + vty_out (vty, "Please specify interface address by A.B.C.D%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + params = ospf_lookup_if_params (ifp, addr); + if (params == NULL) + return CMD_SUCCESS; + } + + key_id = strtol (argv[0], NULL, 10); + ck = ospf_crypt_key_lookup (params->auth_crypt, key_id); + if (ck == NULL) + { + vty_out (vty, "OSPF: Key %d does not exist%s", key_id, VTY_NEWLINE); + return CMD_WARNING; + } + + ospf_crypt_key_delete (params->auth_crypt, key_id); + + if (params != IF_DEF_PARAMS (ifp)) + { + ospf_free_if_params (ifp, addr); + ospf_if_update_params (ifp, addr); + } + + return CMD_SUCCESS; +} + +ALIAS (no_ip_ospf_message_digest_key, + no_ip_ospf_message_digest_key_cmd, + "no ip ospf message-digest-key <1-255>", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Message digest authentication password (key)\n" + "Key ID\n") + +ALIAS (no_ip_ospf_message_digest_key, + no_ospf_message_digest_key_cmd, + "no ospf message-digest-key <1-255>", + NO_STR + "OSPF interface commands\n" + "Message digest authentication password (key)\n" + "Key ID\n") + +DEFUN (ip_ospf_cost, + ip_ospf_cost_u32_inet4_cmd, + "ip ospf cost <1-65535> A.B.C.D", + "IP Information\n" + "OSPF interface commands\n" + "Interface cost\n" + "Cost\n" + "Address of interface") +{ + struct interface *ifp = vty->index; + u_int32_t cost; + struct in_addr addr; + int ret; + struct ospf_if_params *params; + + params = IF_DEF_PARAMS (ifp); + + cost = strtol (argv[0], NULL, 10); + + /* cost range is <1-65535>. */ + if (cost < 1 || cost > 65535) + { + vty_out (vty, "Interface output cost is invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (argc == 2) + { + ret = inet_aton(argv[1], &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); + ospf_if_update_params (ifp, addr); + } + + SET_IF_PARAM (params, output_cost_cmd); + params->output_cost_cmd = cost; + + ospf_if_recalculate_output_cost (ifp); + + return CMD_SUCCESS; +} + +ALIAS (ip_ospf_cost, + ip_ospf_cost_u32_cmd, + "ip ospf cost <1-65535>", + "IP Information\n" + "OSPF interface commands\n" + "Interface cost\n" + "Cost") + +ALIAS (ip_ospf_cost, + ospf_cost_u32_cmd, + "ospf cost <1-65535>", + "OSPF interface commands\n" + "Interface cost\n" + "Cost") + +ALIAS (ip_ospf_cost, + ospf_cost_u32_inet4_cmd, + "ospf cost <1-65535> A.B.C.D", + "OSPF interface commands\n" + "Interface cost\n" + "Cost\n" + "Address of interface") + +DEFUN (no_ip_ospf_cost, + no_ip_ospf_cost_inet4_cmd, + "no ip ospf cost A.B.C.D", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Interface cost\n" + "Address of interface") +{ + struct interface *ifp = vty->index; + struct in_addr addr; + int ret; + struct ospf_if_params *params; + + ifp = vty->index; + params = IF_DEF_PARAMS (ifp); + + if (argc == 1) + { + ret = inet_aton(argv[0], &addr); + if (!ret) + { + vty_out (vty, "Please specify interface address by A.B.C.D%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + params = ospf_lookup_if_params (ifp, addr); + if (params == NULL) + return CMD_SUCCESS; + } + + UNSET_IF_PARAM (params, output_cost_cmd); + + if (params != IF_DEF_PARAMS (ifp)) + { + ospf_free_if_params (ifp, addr); + ospf_if_update_params (ifp, addr); + } + + ospf_if_recalculate_output_cost (ifp); + + return CMD_SUCCESS; +} + +ALIAS (no_ip_ospf_cost, + no_ip_ospf_cost_cmd, + "no ip ospf cost", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Interface cost\n") + +ALIAS (no_ip_ospf_cost, + no_ospf_cost_cmd, + "no ospf cost", + NO_STR + "OSPF interface commands\n" + "Interface cost\n") + +ALIAS (no_ip_ospf_cost, + no_ospf_cost_inet4_cmd, + "no ospf cost A.B.C.D", + NO_STR + "OSPF interface commands\n" + "Interface cost\n" + "Address of interface") + +DEFUN (no_ip_ospf_cost2, + no_ip_ospf_cost_u32_cmd, + "no ip ospf cost <1-65535>", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Interface cost\n" + "Cost") +{ + struct interface *ifp = vty->index; + struct in_addr addr; + u_int32_t cost; + int ret; + struct ospf_if_params *params; + + ifp = vty->index; + params = IF_DEF_PARAMS (ifp); + + /* According to the semantics we are mimicking "no ip ospf cost N" is + * always treated as "no ip ospf cost" regardless of the actual value + * of N already configured for the interface. Thus the first argument + * is always checked to be a number, but is ignored after that. + */ + cost = strtol (argv[0], NULL, 10); + if (cost < 1 || cost > 65535) + { + vty_out (vty, "Interface output cost is invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (argc == 2) + { + ret = inet_aton(argv[1], &addr); + if (!ret) + { + vty_out (vty, "Please specify interface address by A.B.C.D%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + params = ospf_lookup_if_params (ifp, addr); + if (params == NULL) + return CMD_SUCCESS; + } + + UNSET_IF_PARAM (params, output_cost_cmd); + + if (params != IF_DEF_PARAMS (ifp)) + { + ospf_free_if_params (ifp, addr); + ospf_if_update_params (ifp, addr); + } + + ospf_if_recalculate_output_cost (ifp); + + return CMD_SUCCESS; +} + +ALIAS (no_ip_ospf_cost2, + no_ospf_cost_u32_cmd, + "no ospf cost <1-65535>", + NO_STR + "OSPF interface commands\n" + "Interface cost\n" + "Cost") + +ALIAS (no_ip_ospf_cost2, + no_ip_ospf_cost_u32_inet4_cmd, + "no ip ospf cost <1-65535> A.B.C.D", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Interface cost\n" + "Cost\n" + "Address of interface") + +ALIAS (no_ip_ospf_cost2, + no_ospf_cost_u32_inet4_cmd, + "no ospf cost <1-65535> A.B.C.D", + NO_STR + "OSPF interface commands\n" + "Interface cost\n" + "Cost\n" + "Address of interface") + +static void +ospf_nbr_timer_update (struct ospf_interface *oi) +{ + struct route_node *rn; + struct ospf_neighbor *nbr; + + for (rn = route_top (oi->nbrs); rn; rn = route_next (rn)) + if ((nbr = rn->info)) + { + nbr->v_inactivity = OSPF_IF_PARAM (oi, v_wait); + nbr->v_db_desc = OSPF_IF_PARAM (oi, retransmit_interval); + nbr->v_ls_req = OSPF_IF_PARAM (oi, retransmit_interval); + nbr->v_ls_upd = OSPF_IF_PARAM (oi, retransmit_interval); + } +} + +static int +ospf_vty_dead_interval_set (struct vty *vty, const char *interval_str, + const char *nbr_str, + const char *fast_hello_str) +{ + struct interface *ifp = vty->index; + u_int32_t seconds; + u_char hellomult; + struct in_addr addr; + int ret; + struct ospf_if_params *params; + struct ospf_interface *oi; + struct route_node *rn; + + params = IF_DEF_PARAMS (ifp); + + if (nbr_str) + { + ret = inet_aton(nbr_str, &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); + ospf_if_update_params (ifp, addr); + } + + if (interval_str) + { + VTY_GET_INTEGER_RANGE ("Router Dead Interval", seconds, interval_str, + 1, 65535); + + /* reset fast_hello too, just to be sure */ + UNSET_IF_PARAM (params, fast_hello); + params->fast_hello = OSPF_FAST_HELLO_DEFAULT; + } + else if (fast_hello_str) + { + VTY_GET_INTEGER_RANGE ("Hello Multiplier", hellomult, fast_hello_str, + 1, 10); + /* 1s dead-interval with sub-second hellos desired */ + seconds = OSPF_ROUTER_DEAD_INTERVAL_MINIMAL; + SET_IF_PARAM (params, fast_hello); + params->fast_hello = hellomult; + } + else + { + vty_out (vty, "Please specify dead-interval or hello-multiplier%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + SET_IF_PARAM (params, v_wait); + params->v_wait = seconds; + + /* Update timer values in neighbor structure. */ + if (nbr_str) + { + struct ospf *ospf; + if ((ospf = ospf_lookup())) + { + oi = ospf_if_lookup_by_local_addr (ospf, ifp, addr); + if (oi) + ospf_nbr_timer_update (oi); + } + } + else + { + for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn)) + if ((oi = rn->info)) + ospf_nbr_timer_update (oi); + } + + return CMD_SUCCESS; +} + + +DEFUN (ip_ospf_dead_interval, + ip_ospf_dead_interval_addr_cmd, + "ip ospf dead-interval <1-65535> A.B.C.D", + "IP Information\n" + "OSPF interface commands\n" + "Interval after which a neighbor is declared dead\n" + "Seconds\n" + "Address of interface\n") +{ + if (argc == 2) + return ospf_vty_dead_interval_set (vty, argv[0], argv[1], NULL); + else + return ospf_vty_dead_interval_set (vty, argv[0], NULL, NULL); +} + +ALIAS (ip_ospf_dead_interval, + ip_ospf_dead_interval_cmd, + "ip ospf dead-interval <1-65535>", + "IP Information\n" + "OSPF interface commands\n" + "Interval after which a neighbor is declared dead\n" + "Seconds\n") + +ALIAS (ip_ospf_dead_interval, + ospf_dead_interval_cmd, + "ospf dead-interval <1-65535>", + "OSPF interface commands\n" + "Interval after which a neighbor is declared dead\n" + "Seconds\n") + +DEFUN (ip_ospf_dead_interval_minimal, + ip_ospf_dead_interval_minimal_addr_cmd, + "ip ospf dead-interval minimal hello-multiplier <1-10> A.B.C.D", + "IP Information\n" + "OSPF interface commands\n" + "Interval after which a neighbor is declared dead\n" + "Minimal 1s dead-interval with fast sub-second hellos\n" + "Hello multiplier factor\n" + "Number of Hellos to send each second\n" + "Address of interface\n") +{ + if (argc == 2) + return ospf_vty_dead_interval_set (vty, NULL, argv[1], argv[0]); + else + return ospf_vty_dead_interval_set (vty, NULL, NULL, argv[0]); +} + +ALIAS (ip_ospf_dead_interval_minimal, + ip_ospf_dead_interval_minimal_cmd, + "ip ospf dead-interval minimal hello-multiplier <1-10>", + "IP Information\n" + "OSPF interface commands\n" + "Interval after which a neighbor is declared dead\n" + "Minimal 1s dead-interval with fast sub-second hellos\n" + "Hello multiplier factor\n" + "Number of Hellos to send each second\n") + +DEFUN (no_ip_ospf_dead_interval, + no_ip_ospf_dead_interval_addr_cmd, + "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; + struct in_addr addr; + int ret; + struct ospf_if_params *params; + struct ospf_interface *oi; + struct route_node *rn; + + ifp = vty->index; + params = IF_DEF_PARAMS (ifp); + + if (argc == 2) + { + ret = inet_aton(argv[1], &addr); + if (!ret) + { + vty_out (vty, "Please specify interface address by A.B.C.D%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + params = ospf_lookup_if_params (ifp, addr); + if (params == NULL) + return CMD_SUCCESS; + } + + UNSET_IF_PARAM (params, v_wait); + params->v_wait = OSPF_ROUTER_DEAD_INTERVAL_DEFAULT; + + UNSET_IF_PARAM (params, fast_hello); + params->fast_hello = OSPF_FAST_HELLO_DEFAULT; + + if (params != IF_DEF_PARAMS (ifp)) + { + ospf_free_if_params (ifp, addr); + ospf_if_update_params (ifp, addr); + } + + /* Update timer values in neighbor structure. */ + if (argc == 1) + { + struct ospf *ospf; + + if ((ospf = ospf_lookup())) + { + oi = ospf_if_lookup_by_local_addr (ospf, ifp, addr); + if (oi) + ospf_nbr_timer_update (oi); + } + } + else + { + for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn)) + if ((oi = rn->info)) + ospf_nbr_timer_update (oi); + } + + 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", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Interval after which a neighbor is declared dead\n") + +ALIAS (no_ip_ospf_dead_interval, + no_ospf_dead_interval_cmd, + "no ospf dead-interval", + NO_STR + "OSPF interface commands\n" + "Interval after which a neighbor is declared dead\n") + +DEFUN (ip_ospf_hello_interval, + ip_ospf_hello_interval_addr_cmd, + "ip ospf hello-interval <1-65535> A.B.C.D", + "IP Information\n" + "OSPF interface commands\n" + "Time between HELLO packets\n" + "Seconds\n" + "Address of interface") +{ + struct interface *ifp = vty->index; + u_int32_t seconds; + struct in_addr addr; + int ret; + struct ospf_if_params *params; + + params = IF_DEF_PARAMS (ifp); + + seconds = strtol (argv[0], NULL, 10); + + /* HelloInterval range is <1-65535>. */ + if (seconds < 1 || seconds > 65535) + { + vty_out (vty, "Hello Interval is invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (argc == 2) + { + ret = inet_aton(argv[1], &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); + ospf_if_update_params (ifp, addr); + } + + SET_IF_PARAM (params, v_hello); + params->v_hello = seconds; + + return CMD_SUCCESS; +} + +ALIAS (ip_ospf_hello_interval, + ip_ospf_hello_interval_cmd, + "ip ospf hello-interval <1-65535>", + "IP Information\n" + "OSPF interface commands\n" + "Time between HELLO packets\n" + "Seconds\n") + +ALIAS (ip_ospf_hello_interval, + ospf_hello_interval_cmd, + "ospf hello-interval <1-65535>", + "OSPF interface commands\n" + "Time between HELLO packets\n" + "Seconds\n") + +DEFUN (no_ip_ospf_hello_interval, + no_ip_ospf_hello_interval_addr_cmd, + "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; + struct in_addr addr; + int ret; + struct ospf_if_params *params; + + ifp = vty->index; + params = IF_DEF_PARAMS (ifp); + + if (argc == 2) + { + ret = inet_aton(argv[1], &addr); + if (!ret) + { + vty_out (vty, "Please specify interface address by A.B.C.D%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + params = ospf_lookup_if_params (ifp, addr); + if (params == NULL) + return CMD_SUCCESS; + } + + UNSET_IF_PARAM (params, v_hello); + params->v_hello = OSPF_HELLO_INTERVAL_DEFAULT; + + if (params != IF_DEF_PARAMS (ifp)) + { + ospf_free_if_params (ifp, addr); + ospf_if_update_params (ifp, addr); + } + + 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", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Time between HELLO packets\n") + +ALIAS (no_ip_ospf_hello_interval, + no_ospf_hello_interval_cmd, + "no ospf hello-interval", + NO_STR + "OSPF interface commands\n" + "Time between HELLO packets\n") + +DEFUN (ip_ospf_network, + ip_ospf_network_cmd, + "ip ospf network (broadcast|non-broadcast|point-to-multipoint|point-to-point)", + "IP Information\n" + "OSPF interface commands\n" + "Network type\n" + "Specify OSPF broadcast multi-access network\n" + "Specify OSPF NBMA network\n" + "Specify OSPF point-to-multipoint network\n" + "Specify OSPF point-to-point network\n") +{ + struct interface *ifp = vty->index; + int old_type = IF_DEF_PARAMS (ifp)->type; + + 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) + IF_DEF_PARAMS (ifp)->type = OSPF_IFTYPE_NBMA; + else if (strncmp (argv[0], "point-to-m", 10) == 0) + IF_DEF_PARAMS (ifp)->type = OSPF_IFTYPE_POINTOMULTIPOINT; + else if (strncmp (argv[0], "point-to-p", 10) == 0) + IF_DEF_PARAMS (ifp)->type = OSPF_IFTYPE_POINTOPOINT; + + if (IF_DEF_PARAMS (ifp)->type == old_type) + return CMD_SUCCESS; + + SET_IF_PARAM (IF_DEF_PARAMS (ifp), type); + ospf_if_reset_type (ifp, IF_DEF_PARAMS (ifp)->type); + return CMD_SUCCESS; +} + +ALIAS (ip_ospf_network, + ospf_network_cmd, + "ospf network (broadcast|non-broadcast|point-to-multipoint|point-to-point)", + "OSPF interface commands\n" + "Network type\n" + "Specify OSPF broadcast multi-access network\n" + "Specify OSPF NBMA network\n" + "Specify OSPF point-to-multipoint network\n" + "Specify OSPF point-to-point network\n") + +DEFUN (no_ip_ospf_network, + no_ip_ospf_network_cmd, + "no ip ospf network", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Network type\n") +{ + struct interface *ifp = vty->index; + int old_type = IF_DEF_PARAMS (ifp)->type; + + IF_DEF_PARAMS (ifp)->type = ospf_default_iftype(ifp); + + if (IF_DEF_PARAMS (ifp)->type == old_type) + return CMD_SUCCESS; + + ospf_if_reset_type (ifp, IF_DEF_PARAMS (ifp)->type); + + return CMD_SUCCESS; +} + +ALIAS (no_ip_ospf_network, + no_ospf_network_cmd, + "no ospf network", + NO_STR + "OSPF interface commands\n" + "Network type\n") + +DEFUN (ip_ospf_priority, + ip_ospf_priority_addr_cmd, + "ip ospf priority <0-255> A.B.C.D", + "IP Information\n" + "OSPF interface commands\n" + "Router priority\n" + "Priority\n" + "Address of interface") +{ + struct interface *ifp = vty->index; + long priority; + struct route_node *rn; + struct in_addr addr; + int ret; + struct ospf_if_params *params; + + params = IF_DEF_PARAMS (ifp); + + priority = strtol (argv[0], NULL, 10); + + /* Router Priority range is <0-255>. */ + if (priority < 0 || priority > 255) + { + vty_out (vty, "Router Priority is invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (argc == 2) + { + ret = inet_aton(argv[1], &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); + ospf_if_update_params (ifp, addr); + } + + SET_IF_PARAM (params, priority); + params->priority = priority; + + for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn)) + { + struct ospf_interface *oi = rn->info; + + if (!oi) + continue; + + + if (PRIORITY (oi) != OSPF_IF_PARAM (oi, priority)) + { + PRIORITY (oi) = OSPF_IF_PARAM (oi, priority); + OSPF_ISM_EVENT_SCHEDULE (oi, ISM_NeighborChange); + } + } + + return CMD_SUCCESS; +} + +ALIAS (ip_ospf_priority, + ip_ospf_priority_cmd, + "ip ospf priority <0-255>", + "IP Information\n" + "OSPF interface commands\n" + "Router priority\n" + "Priority\n") + +ALIAS (ip_ospf_priority, + ospf_priority_cmd, + "ospf priority <0-255>", + "OSPF interface commands\n" + "Router priority\n" + "Priority\n") + +DEFUN (no_ip_ospf_priority, + no_ip_ospf_priority_addr_cmd, + "no ip ospf priority A.B.C.D", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Router priority\n" + "Address of interface") +{ + struct interface *ifp = vty->index; + struct route_node *rn; + struct in_addr addr; + int ret; + struct ospf_if_params *params; + + ifp = vty->index; + params = IF_DEF_PARAMS (ifp); + + if (argc == 1) + { + ret = inet_aton(argv[0], &addr); + if (!ret) + { + vty_out (vty, "Please specify interface address by A.B.C.D%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + params = ospf_lookup_if_params (ifp, addr); + if (params == NULL) + return CMD_SUCCESS; + } + + UNSET_IF_PARAM (params, priority); + params->priority = OSPF_ROUTER_PRIORITY_DEFAULT; + + if (params != IF_DEF_PARAMS (ifp)) + { + ospf_free_if_params (ifp, addr); + ospf_if_update_params (ifp, addr); + } + + for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn)) + { + struct ospf_interface *oi = rn->info; + + if (!oi) + continue; + + + if (PRIORITY (oi) != OSPF_IF_PARAM (oi, priority)) + { + PRIORITY (oi) = OSPF_IF_PARAM (oi, priority); + OSPF_ISM_EVENT_SCHEDULE (oi, ISM_NeighborChange); + } + } + + return CMD_SUCCESS; +} + +ALIAS (no_ip_ospf_priority, + no_ip_ospf_priority_cmd, + "no ip ospf priority", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Router priority\n") + +ALIAS (no_ip_ospf_priority, + no_ospf_priority_cmd, + "no ospf priority", + NO_STR + "OSPF interface commands\n" + "Router priority\n") + +DEFUN (ip_ospf_retransmit_interval, + ip_ospf_retransmit_interval_addr_cmd, + "ip ospf retransmit-interval <3-65535> A.B.C.D", + "IP Information\n" + "OSPF interface commands\n" + "Time between retransmitting lost link state advertisements\n" + "Seconds\n" + "Address of interface") +{ + struct interface *ifp = vty->index; + u_int32_t seconds; + struct in_addr addr; + int ret; + struct ospf_if_params *params; + + params = IF_DEF_PARAMS (ifp); + seconds = strtol (argv[0], NULL, 10); + + /* Retransmit Interval range is <3-65535>. */ + if (seconds < 3 || seconds > 65535) + { + vty_out (vty, "Retransmit Interval is invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + + + if (argc == 2) + { + ret = inet_aton(argv[1], &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); + ospf_if_update_params (ifp, addr); + } + + SET_IF_PARAM (params, retransmit_interval); + params->retransmit_interval = seconds; + + return CMD_SUCCESS; +} + +ALIAS (ip_ospf_retransmit_interval, + ip_ospf_retransmit_interval_cmd, + "ip ospf retransmit-interval <3-65535>", + "IP Information\n" + "OSPF interface commands\n" + "Time between retransmitting lost link state advertisements\n" + "Seconds\n") + +ALIAS (ip_ospf_retransmit_interval, + ospf_retransmit_interval_cmd, + "ospf retransmit-interval <3-65535>", + "OSPF interface commands\n" + "Time between retransmitting lost link state advertisements\n" + "Seconds\n") + +DEFUN (no_ip_ospf_retransmit_interval, + no_ip_ospf_retransmit_interval_addr_cmd, + "no ip ospf retransmit-interval A.B.C.D", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Time between retransmitting lost link state advertisements\n" + "Address of interface") +{ + struct interface *ifp = vty->index; + struct in_addr addr; + int ret; + struct ospf_if_params *params; + + ifp = vty->index; + params = IF_DEF_PARAMS (ifp); + + if (argc == 1) + { + ret = inet_aton(argv[0], &addr); + if (!ret) + { + vty_out (vty, "Please specify interface address by A.B.C.D%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + params = ospf_lookup_if_params (ifp, addr); + if (params == NULL) + return CMD_SUCCESS; + } + + UNSET_IF_PARAM (params, retransmit_interval); + params->retransmit_interval = OSPF_RETRANSMIT_INTERVAL_DEFAULT; + + if (params != IF_DEF_PARAMS (ifp)) + { + ospf_free_if_params (ifp, addr); + ospf_if_update_params (ifp, addr); + } + + return CMD_SUCCESS; +} + +ALIAS (no_ip_ospf_retransmit_interval, + no_ip_ospf_retransmit_interval_cmd, + "no ip ospf retransmit-interval", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Time between retransmitting lost link state advertisements\n") + +ALIAS (no_ip_ospf_retransmit_interval, + no_ospf_retransmit_interval_cmd, + "no ospf retransmit-interval", + NO_STR + "OSPF interface commands\n" + "Time between retransmitting lost link state advertisements\n") + +DEFUN (ip_ospf_transmit_delay, + ip_ospf_transmit_delay_addr_cmd, + "ip ospf transmit-delay <1-65535> A.B.C.D", + "IP Information\n" + "OSPF interface commands\n" + "Link state transmit delay\n" + "Seconds\n" + "Address of interface") +{ + struct interface *ifp = vty->index; + u_int32_t seconds; + struct in_addr addr; + int ret; + struct ospf_if_params *params; + + params = IF_DEF_PARAMS (ifp); + seconds = strtol (argv[0], NULL, 10); + + /* Transmit Delay range is <1-65535>. */ + if (seconds < 1 || seconds > 65535) + { + vty_out (vty, "Transmit Delay is invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (argc == 2) + { + ret = inet_aton(argv[1], &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); + ospf_if_update_params (ifp, addr); + } + + SET_IF_PARAM (params, transmit_delay); + params->transmit_delay = seconds; + + return CMD_SUCCESS; +} + +ALIAS (ip_ospf_transmit_delay, + ip_ospf_transmit_delay_cmd, + "ip ospf transmit-delay <1-65535>", + "IP Information\n" + "OSPF interface commands\n" + "Link state transmit delay\n" + "Seconds\n") + +ALIAS (ip_ospf_transmit_delay, + ospf_transmit_delay_cmd, + "ospf transmit-delay <1-65535>", + "OSPF interface commands\n" + "Link state transmit delay\n" + "Seconds\n") + +DEFUN (no_ip_ospf_transmit_delay, + no_ip_ospf_transmit_delay_addr_cmd, + "no ip ospf transmit-delay A.B.C.D", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Link state transmit delay\n" + "Address of interface") +{ + struct interface *ifp = vty->index; + struct in_addr addr; + int ret; + struct ospf_if_params *params; + + ifp = vty->index; + params = IF_DEF_PARAMS (ifp); + + if (argc == 1) + { + ret = inet_aton(argv[0], &addr); + if (!ret) + { + vty_out (vty, "Please specify interface address by A.B.C.D%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + params = ospf_lookup_if_params (ifp, addr); + if (params == NULL) + return CMD_SUCCESS; + } + + UNSET_IF_PARAM (params, transmit_delay); + params->transmit_delay = OSPF_TRANSMIT_DELAY_DEFAULT; + + if (params != IF_DEF_PARAMS (ifp)) + { + ospf_free_if_params (ifp, addr); + ospf_if_update_params (ifp, addr); + } + + return CMD_SUCCESS; +} + +ALIAS (no_ip_ospf_transmit_delay, + no_ip_ospf_transmit_delay_cmd, + "no ip ospf transmit-delay", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Link state transmit delay\n") + +ALIAS (no_ip_ospf_transmit_delay, + no_ospf_transmit_delay_cmd, + "no ospf transmit-delay", + NO_STR + "OSPF interface commands\n" + "Link state transmit delay\n") + +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" + "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" + "Route map reference\n" + "Pointer to route-map entries\n") +{ + struct ospf *ospf = vty->index; + int source; + 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 (argv[1] != NULL) + if (!str2metric (argv[1], &metric)) + return CMD_WARNING; + + /* Get metric type. */ + if (argv[2] != NULL) + if (!str2metric_type (argv[2], &type)) + return CMD_WARNING; + + 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); +} + +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); +} + +DEFUN (ospf_distribute_list_out, + ospf_distribute_list_out_cmd, + "distribute-list WORD out " QUAGGA_REDIST_STR_OSPFD, + "Filter networks in routing updates\n" + "Access-list name\n" + OUT_STR + QUAGGA_REDIST_HELP_STR_OSPFD) +{ + struct ospf *ospf = vty->index; + int source; + + /* Get distribute source. */ + source = proto_redistnum(AFI_IP, argv[1]); + if (source < 0 || source == ZEBRA_ROUTE_OSPF) + return CMD_WARNING; + + return ospf_distribute_list_out_set (ospf, source, argv[0]); +} + +DEFUN (no_ospf_distribute_list_out, + no_ospf_distribute_list_out_cmd, + "no distribute-list WORD out " QUAGGA_REDIST_STR_OSPFD, + NO_STR + "Filter networks in routing updates\n" + "Access-list name\n" + OUT_STR + QUAGGA_REDIST_HELP_STR_OSPFD) +{ + struct ospf *ospf = vty->index; + int source; + + source = proto_redistnum(AFI_IP, argv[1]); + 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, + ospf_default_information_originate_cmd, + "default-information originate " + "{always|metric <0-16777214>|metric-type (1|2)|route-map WORD}", + "Control distribution of default information\n" + "Distribute a default route\n" + "Always advertise default route\n" + "OSPF default metric\n" + "OSPF metric\n" + "OSPF metric type for default routes\n" + "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 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 (argv[2] != NULL) + if (!str2metric_type (argv[2], &type)) + return CMD_WARNING; + + 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, + type, metric); +} + +DEFUN (no_ospf_default_information_originate, + no_ospf_default_information_originate_cmd, + "no default-information originate", + NO_STR + "Control distribution of default information\n" + "Distribute a default route\n") +{ + struct ospf *ospf = vty->index; + struct prefix_ipv4 p; + + p.family = AF_INET; + p.prefix.s_addr = 0; + p.prefixlen = 0; + + ospf_external_lsa_flush (ospf, DEFAULT_ROUTE, &p, 0); + + if (EXTERNAL_INFO (DEFAULT_ROUTE)) { + ospf_external_info_delete (DEFAULT_ROUTE, p); + route_table_finish (EXTERNAL_INFO (DEFAULT_ROUTE)); + EXTERNAL_INFO (DEFAULT_ROUTE) = NULL; + } + + ospf_routemap_unset (ospf, DEFAULT_ROUTE); + return ospf_redistribute_default_unset (ospf); +} + +DEFUN (ospf_default_metric, + ospf_default_metric_cmd, + "default-metric <0-16777214>", + "Set metric of redistributed routes\n" + "Default metric\n") +{ + struct ospf *ospf = vty->index; + int metric = -1; + + if (!str2metric (argv[0], &metric)) + return CMD_WARNING; + + ospf->default_metric = metric; + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_default_metric, + no_ospf_default_metric_cmd, + "no default-metric", + NO_STR + "Set metric of redistributed routes\n") +{ + struct ospf *ospf = vty->index; + + ospf->default_metric = -1; + + return CMD_SUCCESS; +} + +ALIAS (no_ospf_default_metric, + no_ospf_default_metric_val_cmd, + "no default-metric <0-16777214>", + NO_STR + "Set metric of redistributed routes\n" + "Default metric\n") + +DEFUN (ospf_distance, + ospf_distance_cmd, + "distance <1-255>", + "Define an administrative distance\n" + "OSPF Administrative distance\n") +{ + struct ospf *ospf = vty->index; + + ospf->distance_all = atoi (argv[0]); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_distance, + no_ospf_distance_cmd, + "no distance <1-255>", + NO_STR + "Define an administrative distance\n" + "OSPF Administrative distance\n") +{ + struct ospf *ospf = vty->index; + + ospf->distance_all = 0; + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_distance_ospf, + no_ospf_distance_ospf_cmd, + "no distance ospf {intra-area|inter-area|external}", + NO_STR + "Define an administrative distance\n" + "OSPF Administrative distance\n" + "OSPF Distance\n" + "Intra-area routes\n" + "Inter-area routes\n" + "External routes\n") +{ + struct ospf *ospf = vty->index; + + if (argc < 3) + return CMD_WARNING; + + if (argv[0] != NULL) + ospf->distance_intra = 0; + + if (argv[1] != NULL) + ospf->distance_inter = 0; + + if (argv[2] != NULL) + ospf->distance_external = 0; + + if (argv[0] || argv[1] || argv[2]) + return CMD_SUCCESS; + + /* 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, + 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" + "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; + + if (argc < 3) /* should not happen */ + return CMD_WARNING; + + if (!argv[0] && !argv[1] && !argv[2]) + { + vty_out(vty, "%% Command incomplete. (Arguments required)%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + if (argv[0] != NULL) + ospf->distance_intra = atoi(argv[0]); + + if (argv[1] != NULL) + ospf->distance_inter = atoi(argv[1]); + + if (argv[2] != NULL) + ospf->distance_external = atoi(argv[2]); + + return CMD_SUCCESS; +} + +DEFUN (ospf_distance_source, + ospf_distance_source_cmd, + "distance <1-255> A.B.C.D/M", + "Administrative distance\n" + "Distance value\n" + "IP source prefix\n") +{ + struct ospf *ospf = vty->index; + + ospf_distance_set (vty, ospf, argv[0], argv[1], NULL); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_distance_source, + no_ospf_distance_source_cmd, + "no distance <1-255> A.B.C.D/M", + NO_STR + "Administrative distance\n" + "Distance value\n" + "IP source prefix\n") +{ + struct ospf *ospf = vty->index; + + ospf_distance_unset (vty, ospf, argv[0], argv[1], NULL); + + return CMD_SUCCESS; +} + +DEFUN (ospf_distance_source_access_list, + ospf_distance_source_access_list_cmd, + "distance <1-255> A.B.C.D/M WORD", + "Administrative distance\n" + "Distance value\n" + "IP source prefix\n" + "Access list name\n") +{ + struct ospf *ospf = vty->index; + + ospf_distance_set (vty, ospf, argv[0], argv[1], argv[2]); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_distance_source_access_list, + no_ospf_distance_source_access_list_cmd, + "no distance <1-255> A.B.C.D/M WORD", + NO_STR + "Administrative distance\n" + "Distance value\n" + "IP source prefix\n" + "Access list name\n") +{ + struct ospf *ospf = vty->index; + + ospf_distance_unset (vty, ospf, argv[0], argv[1], argv[2]); + + return CMD_SUCCESS; +} + +DEFUN (ip_ospf_mtu_ignore, + ip_ospf_mtu_ignore_addr_cmd, + "ip ospf mtu-ignore A.B.C.D", + "IP Information\n" + "OSPF interface commands\n" + "Disable mtu mismatch detection\n" + "Address of interface") +{ + struct interface *ifp = vty->index; + struct in_addr addr; + int ret; + + struct ospf_if_params *params; + params = IF_DEF_PARAMS (ifp); + + if (argc == 1) + { + ret = inet_aton(argv[0], &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); + ospf_if_update_params (ifp, addr); + } + params->mtu_ignore = 1; + if (params->mtu_ignore != OSPF_MTU_IGNORE_DEFAULT) + SET_IF_PARAM (params, mtu_ignore); + else + { + UNSET_IF_PARAM (params, mtu_ignore); + if (params != IF_DEF_PARAMS (ifp)) + { + ospf_free_if_params (ifp, addr); + ospf_if_update_params (ifp, addr); + } + } + return CMD_SUCCESS; +} + +ALIAS (ip_ospf_mtu_ignore, + ip_ospf_mtu_ignore_cmd, + "ip ospf mtu-ignore", + "IP Information\n" + "OSPF interface commands\n" + "Disable mtu mismatch detection\n") + + +DEFUN (no_ip_ospf_mtu_ignore, + no_ip_ospf_mtu_ignore_addr_cmd, + "no ip ospf mtu-ignore A.B.C.D", + "IP Information\n" + "OSPF interface commands\n" + "Disable mtu mismatch detection\n" + "Address of interface") +{ + struct interface *ifp = vty->index; + struct in_addr addr; + int ret; + + struct ospf_if_params *params; + params = IF_DEF_PARAMS (ifp); + + if (argc == 1) + { + ret = inet_aton(argv[0], &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); + ospf_if_update_params (ifp, addr); + } + params->mtu_ignore = 0; + if (params->mtu_ignore != OSPF_MTU_IGNORE_DEFAULT) + SET_IF_PARAM (params, mtu_ignore); + else + { + UNSET_IF_PARAM (params, mtu_ignore); + if (params != IF_DEF_PARAMS (ifp)) + { + ospf_free_if_params (ifp, addr); + ospf_if_update_params (ifp, addr); + } + } + return CMD_SUCCESS; +} + +ALIAS (no_ip_ospf_mtu_ignore, + no_ip_ospf_mtu_ignore_cmd, + "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", + "OSPF maximum / infinite-distance metric\n" + "Advertise own Router-LSA with infinite distance (stub router)\n" + "Administratively applied, for an indefinite period\n") +{ + struct listnode *ln; + struct ospf_area *area; + struct ospf *ospf = vty->index; + + for (ALL_LIST_ELEMENTS_RO (ospf->areas, ln, area)) + { + SET_FLAG (area->stub_router_state, OSPF_AREA_ADMIN_STUB_ROUTED); + + 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; +} + +DEFUN (no_ospf_max_metric_router_lsa_admin, + no_ospf_max_metric_router_lsa_admin_cmd, + "no max-metric router-lsa administrative", + NO_STR + "OSPF maximum / infinite-distance metric\n" + "Advertise own Router-LSA with infinite distance (stub router)\n" + "Administratively applied, for an indefinite period\n") +{ + struct listnode *ln; + struct ospf_area *area; + struct ospf *ospf = vty->index; + + for (ALL_LIST_ELEMENTS_RO (ospf->areas, ln, area)) + { + UNSET_FLAG (area->stub_router_state, OSPF_AREA_ADMIN_STUB_ROUTED); + + /* Don't trample on the start-up stub timer */ + if (CHECK_FLAG (area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED) + && !area->t_stub_router) + { + UNSET_FLAG (area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED); + ospf_router_lsa_update_area (area); + } + } + ospf->stub_router_admin_set = OSPF_STUB_ROUTER_ADMINISTRATIVE_UNSET; + return CMD_SUCCESS; +} + +DEFUN (ospf_max_metric_router_lsa_startup, + ospf_max_metric_router_lsa_startup_cmd, + "max-metric router-lsa on-startup <5-86400>", + "OSPF maximum / infinite-distance metric\n" + "Advertise own Router-LSA with infinite distance (stub router)\n" + "Automatically advertise stub Router-LSA on startup of OSPF\n" + "Time (seconds) to advertise self as stub-router\n") +{ + unsigned int seconds; + struct ospf *ospf = vty->index; + + if (argc != 1) + { + vty_out (vty, "%% Must supply stub-router period"); + return CMD_WARNING; + } + + VTY_GET_INTEGER ("stub-router startup period", seconds, argv[0]); + + ospf->stub_router_startup_time = seconds; + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_max_metric_router_lsa_startup, + no_ospf_max_metric_router_lsa_startup_cmd, + "no max-metric router-lsa on-startup", + NO_STR + "OSPF maximum / infinite-distance metric\n" + "Advertise own Router-LSA with infinite distance (stub router)\n" + "Automatically advertise stub Router-LSA on startup of OSPF\n") +{ + struct listnode *ln; + struct ospf_area *area; + struct ospf *ospf = vty->index; + + ospf->stub_router_startup_time = OSPF_STUB_ROUTER_UNCONFIGURED; + + for (ALL_LIST_ELEMENTS_RO (ospf->areas, ln, area)) + { + SET_FLAG (area->stub_router_state, OSPF_AREA_WAS_START_STUB_ROUTED); + OSPF_TIMER_OFF (area->t_stub_router); + + /* Don't trample on admin stub routed */ + if (!CHECK_FLAG (area->stub_router_state, OSPF_AREA_ADMIN_STUB_ROUTED)) + { + UNSET_FLAG (area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED); + ospf_router_lsa_update_area (area); + } + } + return CMD_SUCCESS; +} + +DEFUN (ospf_max_metric_router_lsa_shutdown, + ospf_max_metric_router_lsa_shutdown_cmd, + "max-metric router-lsa on-shutdown <5-86400>", + "OSPF maximum / infinite-distance metric\n" + "Advertise own Router-LSA with infinite distance (stub router)\n" + "Advertise stub-router prior to full shutdown of OSPF\n" + "Time (seconds) to wait till full shutdown\n") +{ + unsigned int seconds; + struct ospf *ospf = vty->index; + + if (argc != 1) + { + vty_out (vty, "%% Must supply stub-router shutdown period"); + return CMD_WARNING; + } + + VTY_GET_INTEGER ("stub-router shutdown wait period", seconds, argv[0]); + + ospf->stub_router_shutdown_time = seconds; + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_max_metric_router_lsa_shutdown, + no_ospf_max_metric_router_lsa_shutdown_cmd, + "no max-metric router-lsa on-shutdown", + NO_STR + "OSPF maximum / infinite-distance metric\n" + "Advertise own Router-LSA with infinite distance (stub router)\n" + "Advertise stub-router prior to full shutdown of OSPF\n") +{ + struct ospf *ospf = vty->index; + + ospf->stub_router_shutdown_time = OSPF_STUB_ROUTER_UNCONFIGURED; + + return CMD_SUCCESS; +} + +static void +config_write_stub_router (struct vty *vty, struct ospf *ospf) +{ + struct listnode *ln; + struct ospf_area *area; + + if (ospf->stub_router_startup_time != OSPF_STUB_ROUTER_UNCONFIGURED) + vty_out (vty, " max-metric router-lsa on-startup %u%s", + ospf->stub_router_startup_time, VTY_NEWLINE); + if (ospf->stub_router_shutdown_time != OSPF_STUB_ROUTER_UNCONFIGURED) + vty_out (vty, " max-metric router-lsa on-shutdown %u%s", + ospf->stub_router_shutdown_time, VTY_NEWLINE); + for (ALL_LIST_ELEMENTS_RO (ospf->areas, ln, area)) + { + if (CHECK_FLAG (area->stub_router_state, OSPF_AREA_ADMIN_STUB_ROUTED)) + { + vty_out (vty, " max-metric router-lsa administrative%s", + VTY_NEWLINE); + break; + } + } + return; +} + +static void +show_ip_ospf_route_network (struct vty *vty, struct route_table *rt) +{ + struct route_node *rn; + struct ospf_route *or; + struct listnode *pnode, *pnnode; + struct ospf_path *path; + + vty_out (vty, "============ OSPF network routing table ============%s", + VTY_NEWLINE); + + for (rn = route_top (rt); rn; rn = route_next (rn)) + if ((or = rn->info) != NULL) + { + char buf1[19]; + snprintf (buf1, 19, "%s/%d", + inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen); + + switch (or->path_type) + { + case OSPF_PATH_INTER_AREA: + if (or->type == OSPF_DESTINATION_NETWORK) + vty_out (vty, "N IA %-18s [%d] area: %s%s", buf1, or->cost, + inet_ntoa (or->u.std.area_id), VTY_NEWLINE); + else if (or->type == OSPF_DESTINATION_DISCARD) + vty_out (vty, "D IA %-18s Discard entry%s", buf1, VTY_NEWLINE); + break; + case OSPF_PATH_INTRA_AREA: + vty_out (vty, "N %-18s [%d] area: %s%s", buf1, or->cost, + inet_ntoa (or->u.std.area_id), VTY_NEWLINE); + break; + default: + break; + } + + if (or->type == OSPF_DESTINATION_NETWORK) + for (ALL_LIST_ELEMENTS (or->paths, pnode, pnnode, path)) + { + if (if_lookup_by_index(path->ifindex)) + { + if (path->nexthop.s_addr == 0) + vty_out (vty, "%24s directly attached to %s%s", + "", ifindex2ifname (path->ifindex), VTY_NEWLINE); + else + vty_out (vty, "%24s via %s, %s%s", "", + inet_ntoa (path->nexthop), + ifindex2ifname (path->ifindex), VTY_NEWLINE); + } + } + } + vty_out (vty, "%s", VTY_NEWLINE); +} + +static void +show_ip_ospf_route_router (struct vty *vty, struct route_table *rtrs) +{ + struct route_node *rn; + struct ospf_route *or; + struct listnode *pnode; + struct listnode *node; + struct ospf_path *path; + + vty_out (vty, "============ OSPF router routing table =============%s", + VTY_NEWLINE); + for (rn = route_top (rtrs); rn; rn = route_next (rn)) + if (rn->info) + { + int flag = 0; + + vty_out (vty, "R %-15s ", inet_ntoa (rn->p.u.prefix4)); + + for (ALL_LIST_ELEMENTS_RO ((struct list *)rn->info, node, or)) + { + if (flag++) + vty_out (vty, "%24s", ""); + + /* Show path. */ + vty_out (vty, "%s [%d] area: %s", + (or->path_type == OSPF_PATH_INTER_AREA ? "IA" : " "), + or->cost, inet_ntoa (or->u.std.area_id)); + /* Show flags. */ + vty_out (vty, "%s%s%s", + (or->u.std.flags & ROUTER_LSA_BORDER ? ", ABR" : ""), + (or->u.std.flags & ROUTER_LSA_EXTERNAL ? ", ASBR" : ""), + VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO (or->paths, pnode, path)) + { + if (if_lookup_by_index(path->ifindex)) + { + if (path->nexthop.s_addr == 0) + vty_out (vty, "%24s directly attached to %s%s", + "", ifindex2ifname (path->ifindex), + VTY_NEWLINE); + else + vty_out (vty, "%24s via %s, %s%s", "", + inet_ntoa (path->nexthop), + ifindex2ifname (path->ifindex), + VTY_NEWLINE); + } + } + } + } + vty_out (vty, "%s", VTY_NEWLINE); +} + +static void +show_ip_ospf_route_external (struct vty *vty, struct route_table *rt) +{ + struct route_node *rn; + struct ospf_route *er; + struct listnode *pnode, *pnnode; + struct ospf_path *path; + + vty_out (vty, "============ OSPF external routing table ===========%s", + VTY_NEWLINE); + for (rn = route_top (rt); rn; rn = route_next (rn)) + if ((er = rn->info) != NULL) + { + char buf1[19]; + snprintf (buf1, 19, "%s/%d", + inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen); + + switch (er->path_type) + { + case OSPF_PATH_TYPE1_EXTERNAL: + vty_out (vty, "N E1 %-18s [%d] tag: %u%s", buf1, + er->cost, er->u.ext.tag, VTY_NEWLINE); + break; + case OSPF_PATH_TYPE2_EXTERNAL: + vty_out (vty, "N E2 %-18s [%d/%d] tag: %u%s", buf1, er->cost, + er->u.ext.type2_cost, er->u.ext.tag, VTY_NEWLINE); + break; + } + + for (ALL_LIST_ELEMENTS (er->paths, pnode, pnnode, path)) + { + if (if_lookup_by_index(path->ifindex)) + { + if (path->nexthop.s_addr == 0) + vty_out (vty, "%24s directly attached to %s%s", + "", ifindex2ifname (path->ifindex), VTY_NEWLINE); + else + vty_out (vty, "%24s via %s, %s%s", "", + inet_ntoa (path->nexthop), + ifindex2ifname (path->ifindex), + VTY_NEWLINE); + } + } + } + vty_out (vty, "%s", VTY_NEWLINE); +} + +DEFUN (show_ip_ospf_border_routers, + show_ip_ospf_border_routers_cmd, + "show ip ospf border-routers", + SHOW_STR + IP_STR + "show all the ABR's and ASBR's\n" + "for this area\n") +{ + struct ospf *ospf; + + if ((ospf = ospf_lookup ()) == NULL) + { + vty_out (vty, " OSPF Routing Process not enabled%s", VTY_NEWLINE); + return CMD_SUCCESS; + } + + if (ospf->new_table == NULL) + { + vty_out (vty, "No OSPF routing information exist%s", VTY_NEWLINE); + return CMD_SUCCESS; + } + + /* Show Network routes. + show_ip_ospf_route_network (vty, ospf->new_table); */ + + /* Show Router routes. */ + show_ip_ospf_route_router (vty, ospf->new_rtrs); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_ospf_route, + show_ip_ospf_route_cmd, + "show ip ospf route", + SHOW_STR + IP_STR + "OSPF information\n" + "OSPF routing table\n") +{ + struct ospf *ospf; + + if ((ospf = ospf_lookup ()) == NULL) + { + vty_out (vty, " OSPF Routing Process not enabled%s", VTY_NEWLINE); + return CMD_SUCCESS; + } + + if (ospf->new_table == NULL) + { + vty_out (vty, "No OSPF routing information exist%s", VTY_NEWLINE); + return CMD_SUCCESS; + } + + /* Show Network routes. */ + show_ip_ospf_route_network (vty, ospf->new_table); + + /* Show Router routes. */ + show_ip_ospf_route_router (vty, ospf->new_rtrs); + + /* Show AS External routes. */ + show_ip_ospf_route_external (vty, ospf->old_external_route); + + return CMD_SUCCESS; +} + + +const char *ospf_abr_type_str[] = +{ + "unknown", + "standard", + "ibm", + "cisco", + "shortcut" +}; + +const char *ospf_shortcut_mode_str[] = +{ + "default", + "enable", + "disable" +}; + + +static void +area_id2str (char *buf, int length, struct ospf_area *area) +{ + memset (buf, 0, length); + + if (area->format == OSPF_AREA_ID_FORMAT_ADDRESS) + strncpy (buf, inet_ntoa (area->area_id), length); + else + sprintf (buf, "%lu", (unsigned long) ntohl (area->area_id.s_addr)); +} + + +const char *ospf_int_type_str[] = +{ + "unknown", /* should never be used. */ + "point-to-point", + "broadcast", + "non-broadcast", + "point-to-multipoint", + "virtual-link", /* should never be used. */ + "loopback" +}; + +/* Configuration write function for ospfd. */ +static int +config_write_interface (struct vty *vty) +{ + struct listnode *n1, *n2; + struct interface *ifp; + struct crypt_key *ck; + int write = 0; + struct route_node *rn = NULL; + struct ospf_if_params *params; + + for (ALL_LIST_ELEMENTS_RO (iflist, n1, ifp)) + { + if (memcmp (ifp->name, "VLINK", 5) == 0) + continue; + + vty_out (vty, "!%s", VTY_NEWLINE); + vty_out (vty, "interface %s%s", ifp->name, + VTY_NEWLINE); + if (ifp->desc) + vty_out (vty, " description %s%s", ifp->desc, + VTY_NEWLINE); + + write++; + + params = IF_DEF_PARAMS (ifp); + + do { + /* Interface Network print. */ + if (OSPF_IF_PARAM_CONFIGURED (params, type) && + params->type != OSPF_IFTYPE_LOOPBACK) + { + if (params->type != ospf_default_iftype(ifp)) + { + vty_out (vty, " ip ospf network %s", + ospf_int_type_str[params->type]); + if (params != IF_DEF_PARAMS (ifp)) + vty_out (vty, " %s", inet_ntoa (rn->p.u.prefix4)); + vty_out (vty, "%s", VTY_NEWLINE); + } + } + + /* OSPF interface authentication print */ + if (OSPF_IF_PARAM_CONFIGURED (params, auth_type) && + params->auth_type != OSPF_AUTH_NOTSET) + { + const char *auth_str; + + /* Translation tables are not that much help here due to syntax + of the simple option */ + switch (params->auth_type) + { + + case OSPF_AUTH_NULL: + auth_str = " null"; + break; + + case OSPF_AUTH_SIMPLE: + auth_str = ""; + break; + + case OSPF_AUTH_CRYPTOGRAPHIC: + auth_str = " message-digest"; + break; + + default: + auth_str = ""; + break; + } + + vty_out (vty, " ip ospf authentication%s", auth_str); + if (params != IF_DEF_PARAMS (ifp)) + vty_out (vty, " %s", inet_ntoa (rn->p.u.prefix4)); + vty_out (vty, "%s", VTY_NEWLINE); + } + + /* Simple Authentication Password print. */ + if (OSPF_IF_PARAM_CONFIGURED (params, auth_simple) && + params->auth_simple[0] != '\0') + { + vty_out (vty, " ip ospf authentication-key %s", + params->auth_simple); + if (params != IF_DEF_PARAMS (ifp)) + vty_out (vty, " %s", inet_ntoa (rn->p.u.prefix4)); + vty_out (vty, "%s", VTY_NEWLINE); + } + + /* Cryptographic Authentication Key print. */ + for (ALL_LIST_ELEMENTS_RO (params->auth_crypt, n2, ck)) + { + vty_out (vty, " ip ospf message-digest-key %d md5 %s", + ck->key_id, ck->auth_key); + if (params != IF_DEF_PARAMS (ifp)) + vty_out (vty, " %s", inet_ntoa (rn->p.u.prefix4)); + vty_out (vty, "%s", VTY_NEWLINE); + } + + /* Interface Output Cost print. */ + if (OSPF_IF_PARAM_CONFIGURED (params, output_cost_cmd)) + { + vty_out (vty, " ip ospf cost %u", params->output_cost_cmd); + if (params != IF_DEF_PARAMS (ifp)) + vty_out (vty, " %s", inet_ntoa (rn->p.u.prefix4)); + vty_out (vty, "%s", VTY_NEWLINE); + } + + /* Hello Interval print. */ + if (OSPF_IF_PARAM_CONFIGURED (params, v_hello) && + params->v_hello != OSPF_HELLO_INTERVAL_DEFAULT) + { + vty_out (vty, " ip ospf hello-interval %u", params->v_hello); + if (params != IF_DEF_PARAMS (ifp)) + vty_out (vty, " %s", inet_ntoa (rn->p.u.prefix4)); + vty_out (vty, "%s", VTY_NEWLINE); + } + + + /* Router Dead Interval print. */ + if (OSPF_IF_PARAM_CONFIGURED (params, v_wait) && + params->v_wait != OSPF_ROUTER_DEAD_INTERVAL_DEFAULT) + { + vty_out (vty, " ip ospf dead-interval "); + + /* fast hello ? */ + if (OSPF_IF_PARAM_CONFIGURED (params, fast_hello)) + vty_out (vty, "minimal hello-multiplier %d", + params->fast_hello); + else + vty_out (vty, "%u", params->v_wait); + + if (params != IF_DEF_PARAMS (ifp)) + vty_out (vty, " %s", inet_ntoa (rn->p.u.prefix4)); + vty_out (vty, "%s", VTY_NEWLINE); + } + + /* Router Priority print. */ + if (OSPF_IF_PARAM_CONFIGURED (params, priority) && + params->priority != OSPF_ROUTER_PRIORITY_DEFAULT) + { + vty_out (vty, " ip ospf priority %u", params->priority); + if (params != IF_DEF_PARAMS (ifp)) + vty_out (vty, " %s", inet_ntoa (rn->p.u.prefix4)); + vty_out (vty, "%s", VTY_NEWLINE); + } + + /* Retransmit Interval print. */ + if (OSPF_IF_PARAM_CONFIGURED (params, retransmit_interval) && + params->retransmit_interval != OSPF_RETRANSMIT_INTERVAL_DEFAULT) + { + vty_out (vty, " ip ospf retransmit-interval %u", + params->retransmit_interval); + if (params != IF_DEF_PARAMS (ifp)) + vty_out (vty, " %s", inet_ntoa (rn->p.u.prefix4)); + vty_out (vty, "%s", VTY_NEWLINE); + } + + /* Transmit Delay print. */ + if (OSPF_IF_PARAM_CONFIGURED (params, transmit_delay) && + params->transmit_delay != OSPF_TRANSMIT_DELAY_DEFAULT) + { + vty_out (vty, " ip ospf transmit-delay %u", params->transmit_delay); + if (params != IF_DEF_PARAMS (ifp)) + vty_out (vty, " %s", inet_ntoa (rn->p.u.prefix4)); + 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) + { + if (params->mtu_ignore == 0) + vty_out (vty, " no ip ospf mtu-ignore"); + else + vty_out (vty, " ip ospf mtu-ignore"); + if (params != IF_DEF_PARAMS (ifp)) + vty_out (vty, " %s", inet_ntoa (rn->p.u.prefix4)); + vty_out (vty, "%s", VTY_NEWLINE); + } + + + while (1) + { + if (rn == NULL) + rn = route_top (IF_OIFS_PARAMS (ifp)); + else + rn = route_next (rn); + + if (rn == NULL) + break; + params = rn->info; + if (params != NULL) + break; + } + } while (rn); + + ospf_opaque_config_write_if (vty, ifp); + } + + return write; +} + +static int +config_write_network_area (struct vty *vty, struct ospf *ospf) +{ + struct route_node *rn; + u_char buf[INET_ADDRSTRLEN]; + + /* `network area' print. */ + for (rn = route_top (ospf->networks); rn; rn = route_next (rn)) + if (rn->info) + { + struct ospf_network *n = rn->info; + + memset (buf, 0, INET_ADDRSTRLEN); + + /* Create Area ID string by specified Area ID format. */ + if (n->format == OSPF_AREA_ID_FORMAT_ADDRESS) + strncpy ((char *) buf, inet_ntoa (n->area_id), INET_ADDRSTRLEN); + else + sprintf ((char *) buf, "%lu", + (unsigned long int) ntohl (n->area_id.s_addr)); + + /* Network print. */ + vty_out (vty, " network %s/%d area %s%s", + inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen, + buf, VTY_NEWLINE); + } + + return 0; +} + +static int +config_write_ospf_area (struct vty *vty, struct ospf *ospf) +{ + struct listnode *node; + struct ospf_area *area; + u_char buf[INET_ADDRSTRLEN]; + + /* Area configuration print. */ + for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area)) + { + struct route_node *rn1; + + area_id2str ((char *) buf, INET_ADDRSTRLEN, area); + + if (area->auth_type != OSPF_AUTH_NULL) + { + if (area->auth_type == OSPF_AUTH_SIMPLE) + vty_out (vty, " area %s authentication%s", buf, VTY_NEWLINE); + else + vty_out (vty, " area %s authentication message-digest%s", + buf, VTY_NEWLINE); + } + + if (area->shortcut_configured != OSPF_SHORTCUT_DEFAULT) + vty_out (vty, " area %s shortcut %s%s", buf, + ospf_shortcut_mode_str[area->shortcut_configured], + VTY_NEWLINE); + + if ((area->external_routing == OSPF_AREA_STUB) + || (area->external_routing == OSPF_AREA_NSSA) + ) + { + if (area->external_routing == OSPF_AREA_STUB) + vty_out (vty, " area %s stub", buf); + else if (area->external_routing == OSPF_AREA_NSSA) + { + vty_out (vty, " area %s nssa", buf); + switch (area->NSSATranslatorRole) + { + case OSPF_NSSA_ROLE_NEVER: + vty_out (vty, " translate-never"); + break; + case OSPF_NSSA_ROLE_ALWAYS: + vty_out (vty, " translate-always"); + break; + case OSPF_NSSA_ROLE_CANDIDATE: + default: + vty_out (vty, " translate-candidate"); + } + } + + if (area->no_summary) + vty_out (vty, " no-summary"); + + vty_out (vty, "%s", VTY_NEWLINE); + + if (area->default_cost != 1) + vty_out (vty, " area %s default-cost %d%s", buf, + area->default_cost, VTY_NEWLINE); + } + + for (rn1 = route_top (area->ranges); rn1; rn1 = route_next (rn1)) + if (rn1->info) + { + struct ospf_area_range *range = rn1->info; + + vty_out (vty, " area %s range %s/%d", buf, + inet_ntoa (rn1->p.u.prefix4), rn1->p.prefixlen); + + if (range->cost_config != OSPF_AREA_RANGE_COST_UNSPEC) + vty_out (vty, " cost %d", range->cost_config); + + if (!CHECK_FLAG (range->flags, OSPF_AREA_RANGE_ADVERTISE)) + vty_out (vty, " not-advertise"); + + if (CHECK_FLAG (range->flags, OSPF_AREA_RANGE_SUBSTITUTE)) + vty_out (vty, " substitute %s/%d", + inet_ntoa (range->subst_addr), range->subst_masklen); + + vty_out (vty, "%s", VTY_NEWLINE); + } + + if (EXPORT_NAME (area)) + vty_out (vty, " area %s export-list %s%s", buf, + EXPORT_NAME (area), VTY_NEWLINE); + + if (IMPORT_NAME (area)) + vty_out (vty, " area %s import-list %s%s", buf, + IMPORT_NAME (area), VTY_NEWLINE); + + if (PREFIX_NAME_IN (area)) + vty_out (vty, " area %s filter-list prefix %s in%s", buf, + PREFIX_NAME_IN (area), VTY_NEWLINE); + + if (PREFIX_NAME_OUT (area)) + vty_out (vty, " area %s filter-list prefix %s out%s", buf, + PREFIX_NAME_OUT (area), VTY_NEWLINE); + } + + return 0; +} + +static int +config_write_ospf_nbr_nbma (struct vty *vty, struct ospf *ospf) +{ + struct ospf_nbr_nbma *nbr_nbma; + struct route_node *rn; + + /* Static Neighbor configuration print. */ + for (rn = route_top (ospf->nbr_nbma); rn; rn = route_next (rn)) + if ((nbr_nbma = rn->info)) + { + vty_out (vty, " neighbor %s", inet_ntoa (nbr_nbma->addr)); + + if (nbr_nbma->priority != OSPF_NEIGHBOR_PRIORITY_DEFAULT) + vty_out (vty, " priority %d", nbr_nbma->priority); + + if (nbr_nbma->v_poll != OSPF_POLL_INTERVAL_DEFAULT) + vty_out (vty, " poll-interval %d", nbr_nbma->v_poll); + + vty_out (vty, "%s", VTY_NEWLINE); + } + + return 0; +} + +static int +config_write_virtual_link (struct vty *vty, struct ospf *ospf) +{ + struct listnode *node; + struct ospf_vl_data *vl_data; + u_char buf[INET_ADDRSTRLEN]; + + /* Virtual-Link print */ + for (ALL_LIST_ELEMENTS_RO (ospf->vlinks, node, vl_data)) + { + struct listnode *n2; + struct crypt_key *ck; + struct ospf_interface *oi; + + if (vl_data != NULL) + { + memset (buf, 0, INET_ADDRSTRLEN); + + if (vl_data->format == OSPF_AREA_ID_FORMAT_ADDRESS) + strncpy ((char *) buf, inet_ntoa (vl_data->vl_area_id), INET_ADDRSTRLEN); + else + sprintf ((char *) buf, "%lu", + (unsigned long int) ntohl (vl_data->vl_area_id.s_addr)); + oi = vl_data->vl_oi; + + /* timers */ + if (OSPF_IF_PARAM (oi, v_hello) != OSPF_HELLO_INTERVAL_DEFAULT || + OSPF_IF_PARAM (oi, v_wait) != OSPF_ROUTER_DEAD_INTERVAL_DEFAULT || + OSPF_IF_PARAM (oi, retransmit_interval) != OSPF_RETRANSMIT_INTERVAL_DEFAULT || + OSPF_IF_PARAM (oi, transmit_delay) != OSPF_TRANSMIT_DELAY_DEFAULT) + vty_out (vty, " area %s virtual-link %s hello-interval %d retransmit-interval %d transmit-delay %d dead-interval %d%s", + buf, + inet_ntoa (vl_data->vl_peer), + OSPF_IF_PARAM (oi, v_hello), + OSPF_IF_PARAM (oi, retransmit_interval), + OSPF_IF_PARAM (oi, transmit_delay), + OSPF_IF_PARAM (oi, v_wait), + VTY_NEWLINE); + else + vty_out (vty, " area %s virtual-link %s%s", buf, + inet_ntoa (vl_data->vl_peer), VTY_NEWLINE); + /* Auth key */ + if (IF_DEF_PARAMS (vl_data->vl_oi->ifp)->auth_simple[0] != '\0') + vty_out (vty, " area %s virtual-link %s authentication-key %s%s", + buf, + inet_ntoa (vl_data->vl_peer), + IF_DEF_PARAMS (vl_data->vl_oi->ifp)->auth_simple, + VTY_NEWLINE); + /* md5 keys */ + for (ALL_LIST_ELEMENTS_RO (IF_DEF_PARAMS (vl_data->vl_oi->ifp)->auth_crypt, + n2, ck)) + vty_out (vty, " area %s virtual-link %s" + " message-digest-key %d md5 %s%s", + buf, + inet_ntoa (vl_data->vl_peer), + ck->key_id, ck->auth_key, VTY_NEWLINE); + + } + } + + return 0; +} + + +static int +config_write_ospf_redistribute (struct vty *vty, struct ospf *ospf) +{ + int type; + + /* redistribute print. */ + for (type = 0; type < ZEBRA_ROUTE_MAX; 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) + vty_out (vty, " metric %d", ospf->dmetric[type].value); + + if (ospf->dmetric[type].type == EXTERNAL_METRIC_TYPE_1) + vty_out (vty, " metric-type 1"); + + if (ROUTEMAP_NAME (ospf, type)) + vty_out (vty, " route-map %s", ROUTEMAP_NAME (ospf, type)); + + vty_out (vty, "%s", VTY_NEWLINE); + } + + return 0; +} + +static int +config_write_ospf_default_metric (struct vty *vty, struct ospf *ospf) +{ + if (ospf->default_metric != -1) + vty_out (vty, " default-metric %d%s", ospf->default_metric, + VTY_NEWLINE); + return 0; +} + +static int +config_write_ospf_distribute (struct vty *vty, struct ospf *ospf) +{ + int type; + + if (ospf) + { + /* distribute-list print. */ + for (type = 0; type < ZEBRA_ROUTE_MAX; type++) + if (DISTRIBUTE_NAME (ospf, type)) + vty_out (vty, " distribute-list %s out %s%s", + DISTRIBUTE_NAME (ospf, type), + zebra_route_string(type), VTY_NEWLINE); + + /* default-information print. */ + if (ospf->default_originate != DEFAULT_ORIGINATE_NONE) + { + vty_out (vty, " default-information originate"); + if (ospf->default_originate == DEFAULT_ORIGINATE_ALWAYS) + vty_out (vty, " always"); + + if (ospf->dmetric[DEFAULT_ROUTE].value >= 0) + vty_out (vty, " metric %d", + ospf->dmetric[DEFAULT_ROUTE].value); + if (ospf->dmetric[DEFAULT_ROUTE].type == EXTERNAL_METRIC_TYPE_1) + vty_out (vty, " metric-type 1"); + + if (ROUTEMAP_NAME (ospf, DEFAULT_ROUTE)) + vty_out (vty, " route-map %s", + ROUTEMAP_NAME (ospf, DEFAULT_ROUTE)); + + vty_out (vty, "%s", VTY_NEWLINE); + } + + } + + return 0; +} + +static int +config_write_ospf_distance (struct vty *vty, struct ospf *ospf) +{ + struct route_node *rn; + struct ospf_distance *odistance; + + if (ospf->distance_all) + vty_out (vty, " distance %d%s", ospf->distance_all, VTY_NEWLINE); + + if (ospf->distance_intra + || ospf->distance_inter + || ospf->distance_external) + { + vty_out (vty, " distance ospf"); + + if (ospf->distance_intra) + vty_out (vty, " intra-area %d", ospf->distance_intra); + if (ospf->distance_inter) + vty_out (vty, " inter-area %d", ospf->distance_inter); + if (ospf->distance_external) + vty_out (vty, " external %d", ospf->distance_external); + + vty_out (vty, "%s", VTY_NEWLINE); + } + + for (rn = route_top (ospf->distance_table); rn; rn = route_next (rn)) + if ((odistance = rn->info) != NULL) + { + vty_out (vty, " distance %d %s/%d %s%s", odistance->distance, + inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen, + odistance->access_list ? odistance->access_list : "", + VTY_NEWLINE); + } + return 0; +} + +/* OSPF configuration write function. */ +static int +ospf_config_write (struct vty *vty) +{ + struct ospf *ospf; + struct interface *ifp; + struct ospf_interface *oi; + struct listnode *node; + int write = 0; + + ospf = ospf_lookup (); + if (ospf != NULL) + { + /* `router ospf' print. */ + vty_out (vty, "router ospf%s", VTY_NEWLINE); + + write++; + + if (!ospf->networks) + return write; + + /* Router ID print. */ + if (ospf->router_id_static.s_addr != 0) + vty_out (vty, " ospf router-id %s%s", + inet_ntoa (ospf->router_id_static), VTY_NEWLINE); + + /* ABR type print. */ + if (ospf->abr_type != OSPF_ABR_DEFAULT) + vty_out (vty, " ospf abr-type %s%s", + ospf_abr_type_str[ospf->abr_type], VTY_NEWLINE); + + /* log-adjacency-changes flag print. */ + if (CHECK_FLAG(ospf->config, OSPF_LOG_ADJACENCY_CHANGES)) + { + vty_out(vty, " log-adjacency-changes"); + if (CHECK_FLAG(ospf->config, OSPF_LOG_ADJACENCY_DETAIL)) + vty_out(vty, " detail"); + vty_out(vty, "%s", VTY_NEWLINE); + } + + /* RFC1583 compatibility flag print -- Compatible with CISCO 12.1. */ + if (CHECK_FLAG (ospf->config, OSPF_RFC1583_COMPATIBLE)) + vty_out (vty, " compatible rfc1583%s", VTY_NEWLINE); + + /* auto-cost reference-bandwidth configuration. */ + if (ospf->ref_bandwidth != OSPF_DEFAULT_REF_BANDWIDTH) + { + vty_out (vty, "! Important: ensure reference bandwidth " + "is consistent across all routers%s", VTY_NEWLINE); + vty_out (vty, " auto-cost reference-bandwidth %d%s", + 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 || + ospf->spf_max_holdtime != OSPF_SPF_MAX_HOLDTIME_DEFAULT) + vty_out (vty, " timers throttle spf %d %d %d%s", + ospf->spf_delay, ospf->spf_holdtime, + ospf->spf_max_holdtime, VTY_NEWLINE); + + /* Max-metric router-lsa print */ + config_write_stub_router (vty, ospf); + + /* SPF refresh parameters print. */ + if (ospf->lsa_refresh_interval != OSPF_LSA_REFRESH_INTERVAL_DEFAULT) + vty_out (vty, " refresh timer %d%s", + ospf->lsa_refresh_interval, VTY_NEWLINE); + + /* Redistribute information print. */ + config_write_ospf_redistribute (vty, ospf); + + /* passive-interface print. */ + if (ospf->passive_interface_default == OSPF_IF_PASSIVE) + vty_out (vty, " passive-interface default%s", VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO (om->iflist, node, ifp)) + if (OSPF_IF_PARAM_CONFIGURED (IF_DEF_PARAMS (ifp), passive_interface) + && IF_DEF_PARAMS (ifp)->passive_interface != + ospf->passive_interface_default) + { + vty_out (vty, " %spassive-interface %s%s", + IF_DEF_PARAMS (ifp)->passive_interface ? "" : "no ", + ifp->name, VTY_NEWLINE); + } + for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi)) + { + if (!OSPF_IF_PARAM_CONFIGURED (oi->params, passive_interface)) + continue; + if (OSPF_IF_PARAM_CONFIGURED (IF_DEF_PARAMS (oi->ifp), + passive_interface)) + { + if (oi->params->passive_interface == IF_DEF_PARAMS (oi->ifp)->passive_interface) + continue; + } + else if (oi->params->passive_interface == ospf->passive_interface_default) + continue; + + vty_out (vty, " %spassive-interface %s %s%s", + oi->params->passive_interface ? "" : "no ", + oi->ifp->name, + inet_ntoa (oi->address->u.prefix4), VTY_NEWLINE); + } + + /* Network area print. */ + config_write_network_area (vty, ospf); + + /* Area config print. */ + config_write_ospf_area (vty, ospf); + + /* static neighbor print. */ + config_write_ospf_nbr_nbma (vty, ospf); + + /* Virtual-Link print. */ + config_write_virtual_link (vty, ospf); + + /* Default metric configuration. */ + config_write_ospf_default_metric (vty, ospf); + + /* Distribute-list and default-information print. */ + config_write_ospf_distribute (vty, ospf); + + /* Distance configuration. */ + config_write_ospf_distance (vty, ospf); + + ospf_opaque_config_write_router (vty, ospf); + } + + return write; +} + +void +ospf_vty_show_init (void) +{ + /* "show ip ospf" commands. */ + install_element (VIEW_NODE, &show_ip_ospf_cmd); + + /* "show ip ospf database" commands. */ + install_element (VIEW_NODE, &show_ip_ospf_database_type_cmd); + install_element (VIEW_NODE, &show_ip_ospf_database_type_id_cmd); + install_element (VIEW_NODE, &show_ip_ospf_database_type_id_adv_router_cmd); + install_element (VIEW_NODE, &show_ip_ospf_database_type_adv_router_cmd); + 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); + + /* "show ip ospf interface" commands. */ + install_element (VIEW_NODE, &show_ip_ospf_interface_cmd); + + /* "show ip ospf neighbor" commands. */ + install_element (VIEW_NODE, &show_ip_ospf_neighbor_int_detail_cmd); + install_element (VIEW_NODE, &show_ip_ospf_neighbor_int_cmd); + install_element (VIEW_NODE, &show_ip_ospf_neighbor_id_cmd); + install_element (VIEW_NODE, &show_ip_ospf_neighbor_detail_all_cmd); + 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); + + /* "show ip ospf route" commands. */ + install_element (VIEW_NODE, &show_ip_ospf_route_cmd); + install_element (VIEW_NODE, &show_ip_ospf_border_routers_cmd); +} + + +/* ospfd's interface node. */ +static struct cmd_node interface_node = +{ + INTERFACE_NODE, + "%s(config-if)# ", + 1 +}; + +/* Initialization of OSPF interface. */ +static void +ospf_vty_if_init (void) +{ + /* Install interface node. */ + install_node (&interface_node, config_write_interface); + + install_element (CONFIG_NODE, &interface_cmd); + install_element (CONFIG_NODE, &no_interface_cmd); + install_default (INTERFACE_NODE); + + /* "description" commands. */ + install_element (INTERFACE_NODE, &interface_desc_cmd); + install_element (INTERFACE_NODE, &no_interface_desc_cmd); + + /* "ip ospf authentication" commands. */ + install_element (INTERFACE_NODE, &ip_ospf_authentication_args_addr_cmd); + install_element (INTERFACE_NODE, &ip_ospf_authentication_args_cmd); + install_element (INTERFACE_NODE, &ip_ospf_authentication_addr_cmd); + install_element (INTERFACE_NODE, &ip_ospf_authentication_cmd); + install_element (INTERFACE_NODE, &no_ip_ospf_authentication_addr_cmd); + install_element (INTERFACE_NODE, &no_ip_ospf_authentication_cmd); + install_element (INTERFACE_NODE, &ip_ospf_authentication_key_addr_cmd); + install_element (INTERFACE_NODE, &ip_ospf_authentication_key_cmd); + install_element (INTERFACE_NODE, &no_ip_ospf_authentication_key_addr_cmd); + install_element (INTERFACE_NODE, &no_ip_ospf_authentication_key_cmd); + + /* "ip ospf message-digest-key" commands. */ + install_element (INTERFACE_NODE, &ip_ospf_message_digest_key_addr_cmd); + install_element (INTERFACE_NODE, &ip_ospf_message_digest_key_cmd); + install_element (INTERFACE_NODE, &no_ip_ospf_message_digest_key_addr_cmd); + install_element (INTERFACE_NODE, &no_ip_ospf_message_digest_key_cmd); + + /* "ip ospf cost" commands. */ + install_element (INTERFACE_NODE, &ip_ospf_cost_u32_inet4_cmd); + install_element (INTERFACE_NODE, &ip_ospf_cost_u32_cmd); + install_element (INTERFACE_NODE, &no_ip_ospf_cost_u32_cmd); + install_element (INTERFACE_NODE, &no_ip_ospf_cost_u32_inet4_cmd); + install_element (INTERFACE_NODE, &no_ip_ospf_cost_inet4_cmd); + install_element (INTERFACE_NODE, &no_ip_ospf_cost_cmd); + + /* "ip ospf mtu-ignore" commands. */ + install_element (INTERFACE_NODE, &ip_ospf_mtu_ignore_addr_cmd); + install_element (INTERFACE_NODE, &ip_ospf_mtu_ignore_cmd); + install_element (INTERFACE_NODE, &no_ip_ospf_mtu_ignore_addr_cmd); + install_element (INTERFACE_NODE, &no_ip_ospf_mtu_ignore_cmd); + + /* "ip ospf dead-interval" commands. */ + install_element (INTERFACE_NODE, &ip_ospf_dead_interval_addr_cmd); + install_element (INTERFACE_NODE, &ip_ospf_dead_interval_cmd); + install_element (INTERFACE_NODE, &ip_ospf_dead_interval_minimal_addr_cmd); + 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); + install_element (INTERFACE_NODE, &no_ip_ospf_network_cmd); + + /* "ip ospf priority" commands. */ + install_element (INTERFACE_NODE, &ip_ospf_priority_addr_cmd); + install_element (INTERFACE_NODE, &ip_ospf_priority_cmd); + install_element (INTERFACE_NODE, &no_ip_ospf_priority_addr_cmd); + install_element (INTERFACE_NODE, &no_ip_ospf_priority_cmd); + + /* "ip ospf retransmit-interval" commands. */ + install_element (INTERFACE_NODE, &ip_ospf_retransmit_interval_addr_cmd); + install_element (INTERFACE_NODE, &ip_ospf_retransmit_interval_cmd); + install_element (INTERFACE_NODE, &no_ip_ospf_retransmit_interval_addr_cmd); + install_element (INTERFACE_NODE, &no_ip_ospf_retransmit_interval_cmd); + + /* "ip ospf transmit-delay" commands. */ + install_element (INTERFACE_NODE, &ip_ospf_transmit_delay_addr_cmd); + install_element (INTERFACE_NODE, &ip_ospf_transmit_delay_cmd); + 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); + install_element (INTERFACE_NODE, &ospf_message_digest_key_cmd); + install_element (INTERFACE_NODE, &no_ospf_message_digest_key_cmd); + install_element (INTERFACE_NODE, &ospf_cost_u32_cmd); + install_element (INTERFACE_NODE, &ospf_cost_u32_inet4_cmd); + install_element (INTERFACE_NODE, &no_ospf_cost_cmd); + install_element (INTERFACE_NODE, &no_ospf_cost_u32_cmd); + install_element (INTERFACE_NODE, &no_ospf_cost_u32_inet4_cmd); + install_element (INTERFACE_NODE, &no_ospf_cost_inet4_cmd); + install_element (INTERFACE_NODE, &ospf_dead_interval_cmd); + install_element (INTERFACE_NODE, &no_ospf_dead_interval_cmd); + install_element (INTERFACE_NODE, &ospf_hello_interval_cmd); + install_element (INTERFACE_NODE, &no_ospf_hello_interval_cmd); + install_element (INTERFACE_NODE, &ospf_network_cmd); + install_element (INTERFACE_NODE, &no_ospf_network_cmd); + install_element (INTERFACE_NODE, &ospf_priority_cmd); + install_element (INTERFACE_NODE, &no_ospf_priority_cmd); + install_element (INTERFACE_NODE, &ospf_retransmit_interval_cmd); + install_element (INTERFACE_NODE, &no_ospf_retransmit_interval_cmd); + install_element (INTERFACE_NODE, &ospf_transmit_delay_cmd); + install_element (INTERFACE_NODE, &no_ospf_transmit_delay_cmd); +} + +static void +ospf_vty_zebra_init (void) +{ + install_element (OSPF_NODE, &ospf_redistribute_source_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_cmd); + install_element (OSPF_NODE, &no_ospf_default_information_originate_cmd); + + install_element (OSPF_NODE, &ospf_default_metric_cmd); + install_element (OSPF_NODE, &no_ospf_default_metric_cmd); + install_element (OSPF_NODE, &no_ospf_default_metric_val_cmd); + + 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_cmd); +#if 0 + install_element (OSPF_NODE, &ospf_distance_source_cmd); + install_element (OSPF_NODE, &no_ospf_distance_source_cmd); + install_element (OSPF_NODE, &ospf_distance_source_access_list_cmd); + install_element (OSPF_NODE, &no_ospf_distance_source_access_list_cmd); +#endif /* 0 */ +} + +static struct cmd_node ospf_node = +{ + OSPF_NODE, + "%s(config-router)# ", + 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) +{ + /* Install ospf top node. */ + install_node (&ospf_node, ospf_config_write); + + /* "router ospf" commands. */ + install_element (CONFIG_NODE, &router_ospf_cmd); + install_element (CONFIG_NODE, &no_router_ospf_cmd); + + install_default (OSPF_NODE); + + /* "ospf router-id" commands. */ + install_element (OSPF_NODE, &ospf_router_id_cmd); + install_element (OSPF_NODE, &no_ospf_router_id_cmd); + install_element (OSPF_NODE, &router_ospf_id_cmd); + install_element (OSPF_NODE, &no_router_ospf_id_cmd); + + /* "passive-interface" commands. */ + install_element (OSPF_NODE, &ospf_passive_interface_addr_cmd); + install_element (OSPF_NODE, &ospf_passive_interface_cmd); + install_element (OSPF_NODE, &ospf_passive_interface_default_cmd); + install_element (OSPF_NODE, &no_ospf_passive_interface_addr_cmd); + install_element (OSPF_NODE, &no_ospf_passive_interface_cmd); + install_element (OSPF_NODE, &no_ospf_passive_interface_default_cmd); + + /* "ospf abr-type" commands. */ + install_element (OSPF_NODE, &ospf_abr_type_cmd); + install_element (OSPF_NODE, &no_ospf_abr_type_cmd); + + /* "ospf log-adjacency-changes" commands. */ + install_element (OSPF_NODE, &ospf_log_adjacency_changes_cmd); + install_element (OSPF_NODE, &ospf_log_adjacency_changes_detail_cmd); + install_element (OSPF_NODE, &no_ospf_log_adjacency_changes_cmd); + install_element (OSPF_NODE, &no_ospf_log_adjacency_changes_detail_cmd); + + /* "ospf rfc1583-compatible" commands. */ + install_element (OSPF_NODE, &ospf_rfc1583_flag_cmd); + install_element (OSPF_NODE, &no_ospf_rfc1583_flag_cmd); + install_element (OSPF_NODE, &ospf_compatible_rfc1583_cmd); + install_element (OSPF_NODE, &no_ospf_compatible_rfc1583_cmd); + + /* "network area" commands. */ + install_element (OSPF_NODE, &ospf_network_area_cmd); + install_element (OSPF_NODE, &no_ospf_network_area_cmd); + + /* "area authentication" commands. */ + install_element (OSPF_NODE, &ospf_area_authentication_message_digest_cmd); + install_element (OSPF_NODE, &ospf_area_authentication_cmd); + install_element (OSPF_NODE, &no_ospf_area_authentication_cmd); + + /* "area range" commands. */ + install_element (OSPF_NODE, &ospf_area_range_cmd); + install_element (OSPF_NODE, &ospf_area_range_advertise_cmd); + install_element (OSPF_NODE, &ospf_area_range_cost_cmd); + install_element (OSPF_NODE, &ospf_area_range_advertise_cost_cmd); + install_element (OSPF_NODE, &ospf_area_range_not_advertise_cmd); + install_element (OSPF_NODE, &no_ospf_area_range_cmd); + install_element (OSPF_NODE, &no_ospf_area_range_advertise_cmd); + install_element (OSPF_NODE, &no_ospf_area_range_cost_cmd); + install_element (OSPF_NODE, &no_ospf_area_range_advertise_cost_cmd); + install_element (OSPF_NODE, &ospf_area_range_substitute_cmd); + install_element (OSPF_NODE, &no_ospf_area_range_substitute_cmd); + + /* "area virtual-link" commands. */ + install_element (OSPF_NODE, &ospf_area_vlink_cmd); + install_element (OSPF_NODE, &no_ospf_area_vlink_cmd); + + install_element (OSPF_NODE, &ospf_area_vlink_param1_cmd); + install_element (OSPF_NODE, &no_ospf_area_vlink_param1_cmd); + + install_element (OSPF_NODE, &ospf_area_vlink_param2_cmd); + install_element (OSPF_NODE, &no_ospf_area_vlink_param2_cmd); + + install_element (OSPF_NODE, &ospf_area_vlink_param3_cmd); + install_element (OSPF_NODE, &no_ospf_area_vlink_param3_cmd); + + install_element (OSPF_NODE, &ospf_area_vlink_param4_cmd); + install_element (OSPF_NODE, &no_ospf_area_vlink_param4_cmd); + + install_element (OSPF_NODE, &ospf_area_vlink_authtype_args_cmd); + install_element (OSPF_NODE, &ospf_area_vlink_authtype_cmd); + install_element (OSPF_NODE, &no_ospf_area_vlink_authtype_cmd); + + install_element (OSPF_NODE, &ospf_area_vlink_md5_cmd); + install_element (OSPF_NODE, &no_ospf_area_vlink_md5_cmd); + + install_element (OSPF_NODE, &ospf_area_vlink_authkey_cmd); + install_element (OSPF_NODE, &no_ospf_area_vlink_authkey_cmd); + + install_element (OSPF_NODE, &ospf_area_vlink_authtype_args_authkey_cmd); + install_element (OSPF_NODE, &ospf_area_vlink_authtype_authkey_cmd); + install_element (OSPF_NODE, &no_ospf_area_vlink_authtype_authkey_cmd); + + install_element (OSPF_NODE, &ospf_area_vlink_authtype_args_md5_cmd); + install_element (OSPF_NODE, &ospf_area_vlink_authtype_md5_cmd); + install_element (OSPF_NODE, &no_ospf_area_vlink_authtype_md5_cmd); + + /* "area stub" commands. */ + install_element (OSPF_NODE, &ospf_area_stub_no_summary_cmd); + install_element (OSPF_NODE, &ospf_area_stub_cmd); + install_element (OSPF_NODE, &no_ospf_area_stub_no_summary_cmd); + install_element (OSPF_NODE, &no_ospf_area_stub_cmd); + + /* "area nssa" commands. */ + install_element (OSPF_NODE, &ospf_area_nssa_cmd); + install_element (OSPF_NODE, &ospf_area_nssa_translate_no_summary_cmd); + install_element (OSPF_NODE, &ospf_area_nssa_translate_cmd); + install_element (OSPF_NODE, &ospf_area_nssa_no_summary_cmd); + install_element (OSPF_NODE, &no_ospf_area_nssa_cmd); + install_element (OSPF_NODE, &no_ospf_area_nssa_no_summary_cmd); + + install_element (OSPF_NODE, &ospf_area_default_cost_cmd); + install_element (OSPF_NODE, &no_ospf_area_default_cost_cmd); + + install_element (OSPF_NODE, &ospf_area_shortcut_cmd); + install_element (OSPF_NODE, &no_ospf_area_shortcut_cmd); + + install_element (OSPF_NODE, &ospf_area_export_list_cmd); + install_element (OSPF_NODE, &no_ospf_area_export_list_cmd); + + install_element (OSPF_NODE, &ospf_area_filter_list_cmd); + install_element (OSPF_NODE, &no_ospf_area_filter_list_cmd); + + 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); + install_element (OSPF_NODE, &ospf_timers_throttle_spf_cmd); + install_element (OSPF_NODE, &no_ospf_timers_throttle_spf_cmd); + + /* refresh timer commands */ + install_element (OSPF_NODE, &ospf_refresh_timer_cmd); + install_element (OSPF_NODE, &no_ospf_refresh_timer_val_cmd); + install_element (OSPF_NODE, &no_ospf_refresh_timer_cmd); + + /* max-metric commands */ + install_element (OSPF_NODE, &ospf_max_metric_router_lsa_admin_cmd); + install_element (OSPF_NODE, &no_ospf_max_metric_router_lsa_admin_cmd); + install_element (OSPF_NODE, &ospf_max_metric_router_lsa_startup_cmd); + install_element (OSPF_NODE, &no_ospf_max_metric_router_lsa_startup_cmd); + install_element (OSPF_NODE, &ospf_max_metric_router_lsa_shutdown_cmd); + install_element (OSPF_NODE, &no_ospf_max_metric_router_lsa_shutdown_cmd); + + /* reference bandwidth commands */ + install_element (OSPF_NODE, &ospf_auto_cost_reference_bandwidth_cmd); + install_element (OSPF_NODE, &no_ospf_auto_cost_reference_bandwidth_cmd); + + /* "neighbor" commands. */ + install_element (OSPF_NODE, &ospf_neighbor_cmd); + install_element (OSPF_NODE, &ospf_neighbor_priority_poll_interval_cmd); + install_element (OSPF_NODE, &ospf_neighbor_priority_cmd); + install_element (OSPF_NODE, &ospf_neighbor_poll_interval_cmd); + install_element (OSPF_NODE, &ospf_neighbor_poll_interval_priority_cmd); + install_element (OSPF_NODE, &no_ospf_neighbor_cmd); + install_element (OSPF_NODE, &no_ospf_neighbor_priority_cmd); + install_element (OSPF_NODE, &no_ospf_neighbor_poll_interval_cmd); + + /* Init interface related vty commands. */ + ospf_vty_if_init (); + + /* Init zebra related vty commands. */ + ospf_vty_zebra_init (); +} diff --git a/ospfd/ospf_vty.h b/ospfd/ospf_vty.h new file mode 100644 index 0000000..4610638 --- /dev/null +++ b/ospfd/ospf_vty.h @@ -0,0 +1,59 @@ +/* OSPF VTY interface. + * Copyright (C) 2000 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 _QUAGGA_OSPF_VTY_H +#define _QUAGGA_OSPF_VTY_H + +/* Macros. */ +#define VTY_GET_OSPF_AREA_ID(V,F,STR) \ +{ \ + int retv; \ + retv = ospf_str2area_id ((STR), &(V), &(F)); \ + if (retv < 0) \ + { \ + vty_out (vty, "%% Invalid OSPF area ID%s", VTY_NEWLINE); \ + return CMD_WARNING; \ + } \ +} + +#define VTY_GET_OSPF_AREA_ID_NO_BB(NAME,V,F,STR) \ +{ \ + int retv; \ + retv = ospf_str2area_id ((STR), &(V), &(F)); \ + if (retv < 0) \ + { \ + vty_out (vty, "%% Invalid OSPF area ID%s", VTY_NEWLINE); \ + return CMD_WARNING; \ + } \ + if (OSPF_IS_AREA_ID_BACKBONE ((V))) \ + { \ + vty_out (vty, "%% You can't configure %s to backbone%s", \ + NAME, VTY_NEWLINE); \ + } \ +} + +/* 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 new file mode 100644 index 0000000..60d9852 --- /dev/null +++ b/ospfd/ospf_zebra.c @@ -0,0 +1,1369 @@ +/* + * Zebra connect library for OSPFd + * Copyright (C) 1997, 98, 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. + */ + +#include + +#include "thread.h" +#include "command.h" +#include "network.h" +#include "prefix.h" +#include "routemap.h" +#include "table.h" +#include "stream.h" +#include "memory.h" +#include "zclient.h" +#include "filter.h" +#include "plist.h" +#include "log.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_ism.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_abr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_zebra.h" +#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; + +/* For registering threads. */ +extern struct thread_master *master; +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, vrf_id_t vrf_id) +{ + struct ospf *ospf; + struct prefix router_id; + zebra_router_id_update_read(zclient->ibuf,&router_id); + + if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE)) + { + char buf[128]; + prefix2str(&router_id, buf, sizeof(buf)); + zlog_debug("Zebra rcvd: router id update %s", buf); + } + + router_id_zebra = router_id.u.prefix4; + + ospf = ospf_lookup (); + + if (ospf != NULL) + ospf_router_id_update (ospf); + + return 0; +} + +/* Inteface addition message from zebra. */ +static int +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, vrf_id); + + if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE)) + zlog_debug ("Zebra: interface add %s index %d flags %llx metric %d mtu %d", + ifp->name, ifp->ifindex, (unsigned long long)ifp->flags, + ifp->metric, ifp->mtu); + + assert (ifp->info); + + if (!OSPF_IF_PARAM_CONFIGURED (IF_DEF_PARAMS (ifp), type)) + { + SET_IF_PARAM (IF_DEF_PARAMS (ifp), type); + IF_DEF_PARAMS (ifp)->type = ospf_default_iftype(ifp); + } + + ospf_if_update (NULL, ifp); + +#ifdef HAVE_SNMP + ospf_snmp_if_update (ifp); +#endif /* HAVE_SNMP */ + + return 0; +} + +static int +ospf_interface_delete (int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct interface *ifp; + struct stream *s; + struct route_node *rn; + + s = zclient->ibuf; + /* zebra_interface_state_read() updates interface structure in iflist */ + ifp = zebra_interface_state_read (s, vrf_id); + + if (ifp == NULL) + return 0; + + if (if_is_up (ifp)) + zlog_warn ("Zebra: got delete of %s, but interface is still up", + ifp->name); + + if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE)) + zlog_debug + ("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); +#endif /* HAVE_SNMP */ + + for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn)) + if (rn->info) + ospf_if_free ((struct ospf_interface *) rn->info); + + ifp->ifindex = IFINDEX_INTERNAL; + return 0; +} + +static struct interface * +zebra_interface_if_lookup (struct stream *s, vrf_id_t vrf_id) +{ + 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 +ospf_interface_state_up (int command, struct zclient *zclient, + 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, vrf_id); + + if (ifp == NULL) + return 0; + + /* Interface is already up. */ + if (if_is_operative (ifp)) + { + /* Temporarily keep ifp values. */ + struct interface if_tmp; + memcpy (&if_tmp, ifp, sizeof (struct interface)); + + zebra_interface_if_set_value (zclient->ibuf, ifp); + + if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE)) + zlog_debug ("Zebra: Interface[%s] state update.", ifp->name); + + if (if_tmp.bandwidth != ifp->bandwidth) + { + if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE)) + zlog_debug ("Zebra: Interface[%s] bandwidth change %d -> %d.", + ifp->name, if_tmp.bandwidth, ifp->bandwidth); + + ospf_if_recalculate_output_cost (ifp); + } + + if (if_tmp.mtu != ifp->mtu) + { + if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE)) + zlog_debug ("Zebra: Interface[%s] MTU change %u -> %u.", + ifp->name, if_tmp.mtu, ifp->mtu); + + /* Must reset the interface (simulate down/up) when MTU changes. */ + ospf_if_reset(ifp); + } + return 0; + } + + zebra_interface_if_set_value (zclient->ibuf, ifp); + + if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE)) + zlog_debug ("Zebra: Interface[%s] state change to up.", ifp->name); + + for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn)) + { + if ((oi = rn->info) == NULL) + continue; + + ospf_if_up (oi); + } + + return 0; +} + +static int +ospf_interface_state_down (int command, struct zclient *zclient, + 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, vrf_id); + + if (ifp == NULL) + return 0; + + if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE)) + zlog_debug ("Zebra: Interface[%s] state change to down.", ifp->name); + + for (node = route_top (IF_OIFS (ifp)); node; node = route_next (node)) + { + if ((oi = node->info) == NULL) + continue; + ospf_if_down (oi); + } + + return 0; +} + +static int +ospf_interface_address_add (int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct connected *c; + + c = zebra_interface_address_read (command, zclient->ibuf, vrf_id); + + if (c == NULL) + return 0; + + if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE)) + { + char buf[128]; + prefix2str(c->address, buf, sizeof(buf)); + zlog_debug("Zebra: interface %s address add %s", c->ifp->name, buf); + } + + ospf_if_update (NULL, c->ifp); + +#ifdef HAVE_SNMP + ospf_snmp_if_update (c->ifp); +#endif /* HAVE_SNMP */ + + return 0; +} + +static int +ospf_interface_address_delete (int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct connected *c; + struct interface *ifp; + struct ospf_interface *oi; + struct route_node *rn; + struct prefix p; + + c = zebra_interface_address_read (command, zclient->ibuf, vrf_id); + + if (c == NULL) + return 0; + + if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE)) + { + char buf[128]; + prefix2str(c->address, buf, sizeof(buf)); + zlog_debug("Zebra: interface %s address delete %s", c->ifp->name, buf); + } + + ifp = c->ifp; + p = *c->address; + p.prefixlen = IPV4_MAX_PREFIXLEN; + + rn = route_node_lookup (IF_OIFS (ifp), &p); + if (!rn) + { + connected_free (c); + return 0; + } + + assert (rn->info); + oi = rn->info; + route_unlock_node (rn); + + /* Call interface hook functions to clean up */ + ospf_if_free (oi); + +#ifdef HAVE_SNMP + ospf_snmp_if_update (c->ifp); +#endif /* HAVE_SNMP */ + + connected_free (c); + + 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) +{ + u_char message; + u_char distance; + u_char flags; + int psize; + struct stream *s; + struct ospf_path *path; + struct listnode *node; + + if (vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_OSPF], VRF_DEFAULT)) + { + message = 0; + flags = 0; + + /* OSPF pass nexthop and metric */ + SET_FLAG (message, ZAPI_MESSAGE_NEXTHOP); + SET_FLAG (message, ZAPI_MESSAGE_METRIC); + + /* Distance value. */ + distance = ospf_distance_apply (p, 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, VRF_DEFAULT); + stream_putc (s, ZEBRA_ROUTE_OSPF); + stream_putc (s, flags); + stream_putc (s, message); + stream_putw (s, SAFI_UNICAST); + + /* Put prefix information. */ + psize = PSIZE (p->prefixlen); + stream_putc (s, p->prefixlen); + stream_write (s, (u_char *) & p->prefix, psize); + + /* 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); + if (path->ifindex) + stream_putl (s, path->ifindex); + else + stream_putl (s, 0); + } + + if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE)) + { + char buf[2][INET_ADDRSTRLEN]; + zlog_debug("Zebra: Route add %s/%d nexthop %s", + inet_ntop(AF_INET, &p->prefix, + buf[0], sizeof(buf[0])), + p->prefixlen, + inet_ntop(AF_INET, &path->nexthop, + buf[1], sizeof(buf[1]))); + } + } + + 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); + } + + 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); + } +} + +void +ospf_zebra_delete (struct prefix_ipv4 *p, struct ospf_route *or) +{ + u_char message; + u_char distance; + u_char flags; + int psize; + struct stream *s; + struct ospf_path *path; + struct listnode *node; + + if (vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_OSPF], VRF_DEFAULT)) + { + message = 0; + flags = 0; + /* Distance value. */ + distance = ospf_distance_apply (p, or); + /* Make packet. */ + s = zclient->obuf; + stream_reset (s); + + /* 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); + + /* Put prefix information. */ + psize = PSIZE (p->prefixlen); + stream_putc (s, p->prefixlen); + stream_write (s, (u_char *) & p->prefix, psize); + + /* 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])), + p->prefixlen, + inet_ntop(AF_INET, &path->nexthop, + buf[1], sizeof(buf[1]))); + } + } + + 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); + } +} + +void +ospf_zebra_add_discard (struct prefix_ipv4 *p) +{ + struct zapi_ipv4 api; + + 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; + api.safi = SAFI_UNICAST; + 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); + + if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE)) + zlog_debug ("Zebra: Route add discard %s/%d", + inet_ntoa (p->prefix), p->prefixlen); + } +} + +void +ospf_zebra_delete_discard (struct prefix_ipv4 *p) +{ + struct zapi_ipv4 api; + + 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; + api.safi = SAFI_UNICAST; + 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); + + if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE)) + zlog_debug ("Zebra: Route delete discard %s/%d", + inet_ntoa (p->prefix), p->prefixlen); + + } +} + +int +ospf_is_type_redistributed (int type) +{ + return (DEFAULT_ROUTE_TYPE (type)) ? + vrf_bitmap_check (zclient->default_information, VRF_DEFAULT) : \ + vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT); +} + +int +ospf_redistribute_set (struct ospf *ospf, int type, int mtype, int mvalue) +{ + int force = 0; + + if (ospf_is_type_redistributed (type)) + { + if (mtype != ospf->dmetric[type].type) + { + ospf->dmetric[type].type = mtype; + force = LSA_REFRESH_FORCE; + } + if (mvalue != ospf->dmetric[type].value) + { + ospf->dmetric[type].value = mvalue; + force = LSA_REFRESH_FORCE; + } + + ospf_external_lsa_refresh_type (ospf, type, force); + + if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE)) + zlog_debug ("Redistribute[%s]: Refresh Type[%d], Metric[%d]", + ospf_redist_string(type), + metric_type (ospf, type), metric_value (ospf, type)); + + return CMD_SUCCESS; + } + + ospf->dmetric[type].type = mtype; + ospf->dmetric[type].value = mvalue; + + 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]", + ospf_redist_string(type), + metric_type (ospf, type), metric_value (ospf, type)); + + ospf_asbr_status_update (ospf, ++ospf->redistribute); + + return CMD_SUCCESS; +} + +int +ospf_redistribute_unset (struct ospf *ospf, int type) +{ + if (type == zclient->redist_default) + return CMD_SUCCESS; + + if (!ospf_is_type_redistributed (type)) + return CMD_SUCCESS; + + zclient_redistribute (ZEBRA_REDISTRIBUTE_DELETE, zclient, type, VRF_DEFAULT); + + if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE)) + zlog_debug ("Redistribute[%s]: Stop", + ospf_redist_string(type)); + + ospf->dmetric[type].type = -1; + ospf->dmetric[type].value = -1; + + /* Remove the routes from OSPF table. */ + ospf_redistribute_withdraw (ospf, type); + + ospf_asbr_status_update (ospf, --ospf->redistribute); + + return CMD_SUCCESS; +} + +int +ospf_redistribute_default_set (struct ospf *ospf, int originate, + int mtype, int mvalue) +{ + ospf->default_originate = originate; + ospf->dmetric[DEFAULT_ROUTE].type = mtype; + ospf->dmetric[DEFAULT_ROUTE].value = mvalue; + + if (ospf_is_type_redistributed (DEFAULT_ROUTE)) + { + /* if ospf->default_originate changes value, is calling + ospf_external_lsa_refresh_default sufficient to implement + the change? */ + ospf_external_lsa_refresh_default (ospf); + + if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE)) + zlog_debug ("Redistribute[%s]: Refresh Type[%d], Metric[%d]", + ospf_redist_string(DEFAULT_ROUTE), + metric_type (ospf, DEFAULT_ROUTE), + metric_value (ospf, DEFAULT_ROUTE)); + return CMD_SUCCESS; + } + + 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]", + metric_type (ospf, DEFAULT_ROUTE), + metric_value (ospf, DEFAULT_ROUTE)); + + if (ospf->router_id.s_addr == 0) + ospf->external_origin |= (1 << DEFAULT_ROUTE); + else + thread_add_timer (master, ospf_default_originate_timer, ospf, 1); + + ospf_asbr_status_update (ospf, ++ospf->redistribute); + + return CMD_SUCCESS; +} + +int +ospf_redistribute_default_unset (struct ospf *ospf) +{ + if (!ospf_is_type_redistributed (DEFAULT_ROUTE)) + return CMD_SUCCESS; + + ospf->default_originate = DEFAULT_ORIGINATE_NONE; + ospf->dmetric[DEFAULT_ROUTE].type = -1; + ospf->dmetric[DEFAULT_ROUTE].value = -1; + + zclient_redistribute_default (ZEBRA_REDISTRIBUTE_DEFAULT_DELETE, zclient, + VRF_DEFAULT); + + if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE)) + zlog_debug ("Redistribute[DEFAULT]: Stop"); + + ospf_asbr_status_update (ospf, --ospf->redistribute); + + return CMD_SUCCESS; +} + +static int +ospf_external_lsa_originate_check (struct ospf *ospf, + struct external_info *ei) +{ + /* If prefix is multicast, then do not originate LSA. */ + if (IN_MULTICAST (htonl (ei->p.prefix.s_addr))) + { + zlog_info ("LSA[Type5:%s]: Not originate AS-external-LSA, " + "Prefix belongs multicast", inet_ntoa (ei->p.prefix)); + return 0; + } + + /* Take care of default-originate. */ + if (is_prefix_default (&ei->p)) + if (ospf->default_originate == DEFAULT_ORIGINATE_NONE) + { + zlog_info ("LSA[Type5:0.0.0.0]: Not originate AS-external-LSA " + "for default"); + return 0; + } + + return 1; +} + +/* If connected prefix is OSPF enable interface, then do not announce. */ +int +ospf_distribute_check_connected (struct ospf *ospf, struct external_info *ei) +{ + struct listnode *node; + struct ospf_interface *oi; + + + for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi)) + if (prefix_match (oi->address, (struct prefix *) &ei->p)) + return 0; + return 1; +} + +/* return 1 if external LSA must be originated, 0 otherwise */ +int +ospf_redistribute_check (struct ospf *ospf, + struct external_info *ei, int *changed) +{ + struct route_map_set_values save_values; + struct prefix_ipv4 *p = &ei->p; + u_char type = is_prefix_default (&ei->p) ? DEFAULT_ROUTE : ei->type; + + if (changed) + *changed = 0; + + if (!ospf_external_lsa_originate_check (ospf, ei)) + return 0; + + /* Take care connected route. */ + if (type == ZEBRA_ROUTE_CONNECT && + !ospf_distribute_check_connected (ospf, ei)) + return 0; + + if (!DEFAULT_ROUTE_TYPE (type) && DISTRIBUTE_NAME (ospf, type)) + /* distirbute-list exists, but access-list may not? */ + if (DISTRIBUTE_LIST (ospf, type)) + if (access_list_apply (DISTRIBUTE_LIST (ospf, type), p) == FILTER_DENY) + { + if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE)) + zlog_debug ("Redistribute[%s]: %s/%d filtered by ditribute-list.", + ospf_redist_string(type), + inet_ntoa (p->prefix), p->prefixlen); + return 0; + } + + save_values = ei->route_map_set; + ospf_reset_route_map_set_values (&ei->route_map_set); + + /* apply route-map if needed */ + if (ROUTEMAP_NAME (ospf, type)) + { + int ret; + + ret = route_map_apply (ROUTEMAP (ospf, type), (struct prefix *) p, + RMAP_OSPF, ei); + + if (ret == RMAP_DENYMATCH) + { + ei->route_map_set = save_values; + if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE)) + zlog_debug ("Redistribute[%s]: %s/%d filtered by route-map.", + ospf_redist_string(type), + inet_ntoa (p->prefix), p->prefixlen); + return 0; + } + + /* check if 'route-map set' changed something */ + if (changed) + *changed = !ospf_route_map_set_compare (&ei->route_map_set, + &save_values); + } + + return 1; +} + +/* OSPF route-map set for redistribution */ +void +ospf_routemap_set (struct ospf *ospf, int type, const char *name) +{ + if (ROUTEMAP_NAME (ospf, type)) + free (ROUTEMAP_NAME (ospf, type)); + + ROUTEMAP_NAME (ospf, type) = strdup (name); + ROUTEMAP (ospf, type) = route_map_lookup_by_name (name); +} + +void +ospf_routemap_unset (struct ospf *ospf, int type) +{ + if (ROUTEMAP_NAME (ospf, type)) + free (ROUTEMAP_NAME (ospf, type)); + + ROUTEMAP_NAME (ospf, type) = NULL; + ROUTEMAP (ospf, type) = NULL; +} + +/* Zebra route add and delete treatment. */ +static int +ospf_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; + struct external_info *ei; + struct ospf *ospf; + unsigned char plength = 0; + + 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. */ + memset (&p, 0, sizeof (struct prefix_ipv4)); + p.family = AF_INET; + 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))) + return 0; + + /* 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); + /* XXX assert(api.ifindex_num == 1); */ + ifindex = stream_getl (s); + } + if (CHECK_FLAG (api.message, ZAPI_MESSAGE_DISTANCE)) + 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) + return 0; + + if (command == ZEBRA_IPV4_ROUTE_ADD) + { + /* XXX|HACK|TODO|FIXME: + * Maybe we should ignore reject/blackhole routes? Testing shows that + * there is no problems though and this is only way to "summarize" + * routes in ASBR at the moment. Maybe we need just a better generalised + * solution for these types? + * + * if ( CHECK_FLAG (api.flags, ZEBRA_FLAG_BLACKHOLE) + * || CHECK_FLAG (api.flags, ZEBRA_FLAG_REJECT)) + * return 0; + */ + + /* 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 + for each redistributed protocols later. */ + ospf->external_origin |= (1 << api.type); + else + { + if (ei) + { + if (is_prefix_default (&p)) + ospf_external_lsa_refresh_default (ospf); + else + { + struct ospf_lsa *current; + + current = ospf_external_info_find_lsa (ospf, &ei->p); + if (!current) + ospf_external_lsa_originate (ospf, ei); + else if (IS_LSA_MAXAGE (current)) + ospf_external_lsa_refresh (ospf, current, + ei, LSA_REFRESH_FORCE); + else + zlog_warn ("ospf_zebra_read_ipv4() : %s already exists", + inet_ntoa (p.prefix)); + } + } + } + } + else /* if (command == ZEBRA_IPV4_ROUTE_DELETE) */ + { + ospf_external_info_delete (api.type, p); + if (is_prefix_default (&p)) + ospf_external_lsa_refresh_default (ospf); + else + ospf_external_lsa_flush (ospf, api.type, &p, ifindex /*, nexthop */); + } + + return 0; +} + + +int +ospf_distribute_list_out_set (struct ospf *ospf, int type, const char *name) +{ + /* Lookup access-list for distribute-list. */ + DISTRIBUTE_LIST (ospf, type) = access_list_lookup (AFI_IP, name); + + /* Clear previous distribute-name. */ + if (DISTRIBUTE_NAME (ospf, type)) + free (DISTRIBUTE_NAME (ospf, type)); + + /* Set distribute-name. */ + DISTRIBUTE_NAME (ospf, type) = strdup (name); + + /* If access-list have been set, schedule update timer. */ + if (DISTRIBUTE_LIST (ospf, type)) + ospf_distribute_list_update (ospf, type); + + return CMD_SUCCESS; +} + +int +ospf_distribute_list_out_unset (struct ospf *ospf, int type, const char *name) +{ + /* Schedule update timer. */ + if (DISTRIBUTE_LIST (ospf, type)) + ospf_distribute_list_update (ospf, type); + + /* Unset distribute-list. */ + DISTRIBUTE_LIST (ospf, type) = NULL; + + /* Clear distribute-name. */ + if (DISTRIBUTE_NAME (ospf, type)) + free (DISTRIBUTE_NAME (ospf, type)); + + DISTRIBUTE_NAME (ospf, type) = NULL; + + return CMD_SUCCESS; +} + +/* distribute-list update timer. */ +static int +ospf_distribute_list_update_timer (struct thread *thread) +{ + struct route_node *rn; + struct external_info *ei; + struct route_table *rt; + struct ospf_lsa *lsa; + int type, default_refresh = 0; + struct ospf *ospf; + + ospf = ospf_lookup (); + if (ospf == NULL) + return 0; + + ospf->t_distribute_update = NULL; + + zlog_info ("Zebra[Redistribute]: distribute-list update timer fired!"); + + /* foreach all external info. */ + for (type = 0; type <= ZEBRA_ROUTE_MAX; type++) + { + rt = EXTERNAL_INFO (type); + if (!rt) + continue; + for (rn = route_top (rt); rn; rn = route_next (rn)) + if ((ei = rn->info) != NULL) + { + if (is_prefix_default (&ei->p)) + default_refresh = 1; + else if ((lsa = ospf_external_info_find_lsa (ospf, &ei->p))) + ospf_external_lsa_refresh (ospf, lsa, ei, LSA_REFRESH_IF_CHANGED); + else + ospf_external_lsa_originate (ospf, ei); + } + } + if (default_refresh) + ospf_external_lsa_refresh_default (ospf); + return 0; +} + +/* Update distribute-list and set timer to apply access-list. */ +void +ospf_distribute_list_update (struct ospf *ospf, uintptr_t type) +{ + struct route_table *rt; + + /* External info does not exist. */ + if (!(rt = EXTERNAL_INFO (type))) + return; + + /* If exists previously invoked thread, then let it continue. */ + if (ospf->t_distribute_update) + return; + + /* Set timer. */ + ospf->t_distribute_update = + thread_add_timer_msec (master, ospf_distribute_list_update_timer, + (void *) type, ospf->min_ls_interval); +} + +/* If access-list is updated, apply some check. */ +static void +ospf_filter_update (struct access_list *access) +{ + struct ospf *ospf; + int type; + int abr_inv = 0; + struct ospf_area *area; + struct listnode *node; + + /* If OSPF instatnce does not exist, return right now. */ + ospf = ospf_lookup (); + if (ospf == NULL) + return; + + /* Update distribute-list, and apply filter. */ + for (type = 0; type <= ZEBRA_ROUTE_MAX; type++) + { + if (ROUTEMAP (ospf, type) != NULL) + { + /* if route-map is not NULL it may be using this access list */ + ospf_distribute_list_update (ospf, type); + continue; + } + + /* There is place for route-map for default-information (ZEBRA_ROUTE_MAX), + * but no distribute list. */ + if (type == ZEBRA_ROUTE_MAX) + break; + + if (DISTRIBUTE_NAME (ospf, type)) + { + /* Keep old access-list for distribute-list. */ + struct access_list *old = DISTRIBUTE_LIST (ospf, type); + + /* Update access-list for distribute-list. */ + DISTRIBUTE_LIST (ospf, type) = + access_list_lookup (AFI_IP, DISTRIBUTE_NAME (ospf, type)); + + /* No update for this distribute type. */ + if (old == NULL && DISTRIBUTE_LIST (ospf, type) == NULL) + continue; + + /* Schedule distribute-list update timer. */ + if (DISTRIBUTE_LIST (ospf, type) == NULL || + strcmp (DISTRIBUTE_NAME (ospf, type), access->name) == 0) + ospf_distribute_list_update (ospf, type); + } + } + + /* Update Area access-list. */ + for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area)) + { + if (EXPORT_NAME (area)) + { + EXPORT_LIST (area) = NULL; + abr_inv++; + } + + if (IMPORT_NAME (area)) + { + IMPORT_LIST (area) = NULL; + abr_inv++; + } + } + + /* Schedule ABR tasks -- this will be changed -- takada. */ + if (IS_OSPF_ABR (ospf) && abr_inv) + ospf_schedule_abr_task (ospf); +} + +/* If prefix-list is updated, do some updates. */ +void +ospf_prefix_list_update (struct prefix_list *plist) +{ + struct ospf *ospf; + int type; + int abr_inv = 0; + struct ospf_area *area; + struct listnode *node; + + /* If OSPF instatnce does not exist, return right now. */ + ospf = ospf_lookup (); + if (ospf == NULL) + return; + + /* Update all route-maps which are used as redistribution filters. + * They might use prefix-list. + */ + for (type = 0; type <= ZEBRA_ROUTE_MAX; type++) + { + if (ROUTEMAP (ospf, type) != NULL) + { + /* If route-map is not NULL it may be using this prefix list */ + ospf_distribute_list_update (ospf, type); + continue; + } + } + + /* Update area filter-lists. */ + for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area)) + { + /* Update filter-list in. */ + if (PREFIX_NAME_IN (area)) + if (strcmp (PREFIX_NAME_IN (area), prefix_list_name (plist)) == 0) + { + PREFIX_LIST_IN (area) = + prefix_list_lookup (AFI_IP, PREFIX_NAME_IN (area)); + abr_inv++; + } + + /* Update filter-list out. */ + if (PREFIX_NAME_OUT (area)) + if (strcmp (PREFIX_NAME_OUT (area), prefix_list_name (plist)) == 0) + { + PREFIX_LIST_IN (area) = + prefix_list_lookup (AFI_IP, PREFIX_NAME_OUT (area)); + abr_inv++; + } + } + + /* Schedule ABR task. */ + if (IS_OSPF_ABR (ospf) && abr_inv) + ospf_schedule_abr_task (ospf); +} + +static struct ospf_distance * +ospf_distance_new (void) +{ + return XCALLOC (MTYPE_OSPF_DISTANCE, sizeof (struct ospf_distance)); +} + +static void +ospf_distance_free (struct ospf_distance *odistance) +{ + XFREE (MTYPE_OSPF_DISTANCE, odistance); +} + +int +ospf_distance_set (struct vty *vty, struct ospf *ospf, + const char *distance_str, + const char *ip_str, + const char *access_list_str) +{ + int ret; + struct prefix_ipv4 p; + u_char distance; + struct route_node *rn; + struct ospf_distance *odistance; + + ret = str2prefix_ipv4 (ip_str, &p); + if (ret == 0) + { + vty_out (vty, "Malformed prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + + distance = atoi (distance_str); + + /* Get OSPF distance node. */ + rn = route_node_get (ospf->distance_table, (struct prefix *) &p); + if (rn->info) + { + odistance = rn->info; + route_unlock_node (rn); + } + else + { + odistance = ospf_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 +ospf_distance_unset (struct vty *vty, struct ospf *ospf, + const char *distance_str, + const char *ip_str, char + const *access_list_str) +{ + int ret; + struct prefix_ipv4 p; + struct route_node *rn; + struct ospf_distance *odistance; + + ret = str2prefix_ipv4 (ip_str, &p); + if (ret == 0) + { + vty_out (vty, "Malformed prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + + rn = route_node_lookup (ospf->distance_table, (struct prefix *) &p); + if (!rn) + { + vty_out (vty, "Can't find specified prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + + odistance = rn->info; + + if (odistance->access_list) + free (odistance->access_list); + ospf_distance_free (odistance); + + rn->info = NULL; + route_unlock_node (rn); + route_unlock_node (rn); + + return CMD_SUCCESS; +} + +void +ospf_distance_reset (struct ospf *ospf) +{ + struct route_node *rn; + struct ospf_distance *odistance; + + for (rn = route_top (ospf->distance_table); rn; rn = route_next (rn)) + if ((odistance = rn->info) != NULL) + { + if (odistance->access_list) + free (odistance->access_list); + ospf_distance_free (odistance); + rn->info = NULL; + route_unlock_node (rn); + } +} + +u_char +ospf_distance_apply (struct prefix_ipv4 *p, struct ospf_route *or) +{ + struct ospf *ospf; + + ospf = ospf_lookup (); + if (ospf == NULL) + return 0; + + if (ospf->distance_intra) + if (or->path_type == OSPF_PATH_INTRA_AREA) + return ospf->distance_intra; + + if (ospf->distance_inter) + if (or->path_type == OSPF_PATH_INTER_AREA) + return ospf->distance_inter; + + if (ospf->distance_external) + if (or->path_type == OSPF_PATH_TYPE1_EXTERNAL + || or->path_type == OSPF_PATH_TYPE2_EXTERNAL) + return ospf->distance_external; + + if (ospf->distance_all) + return ospf->distance_all; + + return 0; +} + +static void +ospf_zebra_connected (struct zclient *zclient) +{ + zclient_send_requests (zclient, VRF_DEFAULT); +} + +void +ospf_zebra_init (struct thread_master *master) +{ + /* Allocate zebra structure. */ + 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; + zclient->interface_up = ospf_interface_state_up; + 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; + + access_list_add_hook (ospf_filter_update); + access_list_delete_hook (ospf_filter_update); + prefix_list_add_hook (ospf_prefix_list_update); + prefix_list_delete_hook (ospf_prefix_list_update); +} diff --git a/ospfd/ospf_zebra.h b/ospfd/ospf_zebra.h new file mode 100644 index 0000000..32a0271 --- /dev/null +++ b/ospfd/ospf_zebra.h @@ -0,0 +1,78 @@ +/* + * Zebra connect library for OSPFd + * Copyright (C) 1997, 98, 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 _ZEBRA_OSPF_ZEBRA_H +#define _ZEBRA_OSPF_ZEBRA_H + +#include "vty.h" + +#define EXTERNAL_METRIC_TYPE_1 0 +#define EXTERNAL_METRIC_TYPE_2 1 + +#define DEFAULT_ROUTE ZEBRA_ROUTE_MAX +#define DEFAULT_ROUTE_TYPE(T) ((T) == DEFAULT_ROUTE) + +/* OSPF distance. */ +struct ospf_distance +{ + /* Distance value for the IP source prefix. */ + u_char distance; + + /* Name of the access-list to be matched. */ + char *access_list; +}; + +/* Prototypes */ +extern void ospf_zclient_start (void); + +extern void ospf_zebra_add (struct prefix_ipv4 *, struct ospf_route *); +extern void ospf_zebra_delete (struct prefix_ipv4 *, struct ospf_route *); + +extern void ospf_zebra_add_discard (struct prefix_ipv4 *); +extern void ospf_zebra_delete_discard (struct prefix_ipv4 *); + +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 *, uintptr_t); + +extern int ospf_is_type_redistributed (int); +extern void ospf_distance_reset (struct ospf *); +extern u_char ospf_distance_apply (struct prefix_ipv4 *, struct ospf_route *); + +extern int ospf_redistribute_set (struct ospf *, int, int, int); +extern int ospf_redistribute_unset (struct ospf *, int); +extern int ospf_redistribute_default_set (struct ospf *, int, int, int); +extern int ospf_redistribute_default_unset (struct ospf *); +extern int ospf_distribute_list_out_set (struct ospf *, int, const char *); +extern int ospf_distribute_list_out_unset (struct ospf *, int, const char *); +extern void ospf_routemap_set (struct ospf *, int, const char *); +extern void ospf_routemap_unset (struct ospf *, int); +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 (struct thread_master *); + +#endif /* _ZEBRA_OSPF_ZEBRA_H */ + diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c new file mode 100644 index 0000000..98d6d77 --- /dev/null +++ b/ospfd/ospfd.c @@ -0,0 +1,1796 @@ +/* OSPF version 2 daemon program. + Copyright (C) 1999, 2000 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. */ + +#include + +#include "thread.h" +#include "vty.h" +#include "command.h" +#include "linklist.h" +#include "prefix.h" +#include "table.h" +#include "if.h" +#include "memory.h" +#include "stream.h" +#include "log.h" +#include "sockunion.h" /* for inet_aton () */ +#include "zclient.h" +#include "plist.h" +#include "sockopt.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_network.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_spf.h" +#include "ospfd/ospf_packet.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_zebra.h" +#include "ospfd/ospf_abr.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_ase.h" + + + +/* OSPF process wide configuration. */ +static struct ospf_master ospf_master; + +/* OSPF process wide configuration pointer to export. */ +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 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) +{ + struct in_addr router_id, router_id_old; + struct ospf_interface *oi; + struct interface *ifp; + struct listnode *node; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Router-ID[OLD:%s]: Update", inet_ntoa (ospf->router_id)); + + router_id_old = ospf->router_id; + + /* Select the router ID based on these priorities: + 1. Statically assigned router ID is always the first choice. + 2. If there is no statically assigned router ID, then try to stick + with the most recent value, since changing router ID's is very + disruptive. + 3. Last choice: just go with whatever the zebra daemon recommends. + */ + if (ospf->router_id_static.s_addr != 0) + router_id = ospf->router_id_static; + else if (ospf->router_id.s_addr != 0) + router_id = ospf->router_id; + else + router_id = router_id_zebra; + + ospf->router_id = router_id; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Router-ID[NEW:%s]: Update", inet_ntoa (ospf->router_id)); + + if (!IPV4_ADDR_SAME (&router_id_old, &router_id)) + { + for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi)) + { + /* 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) + { + int type; + /* Originate each redistributed external route. */ + for (type = 0; type < ZEBRA_ROUTE_MAX; type++) + if (ospf->external_origin & (1 << type)) + thread_add_event (master, ospf_external_lsa_originate_timer, + ospf, type); + /* Originate Deafult. */ + if (ospf->external_origin & (1 << ZEBRA_ROUTE_MAX)) + thread_add_event (master, ospf_default_originate_timer, ospf, 0); + + ospf->external_origin = 0; + } + + /* update router-lsa's for each area */ + ospf_router_lsa_update (ospf); + + /* update ospf_interface's */ + for (ALL_LIST_ELEMENTS_RO (om->iflist, node, ifp)) + 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) +{ + if (ntohl (a1->area_id.s_addr) > ntohl (a2->area_id.s_addr)) + return 1; + if (ntohl (a1->area_id.s_addr) < ntohl (a2->area_id.s_addr)) + return -1; + return 0; +} + +/* Allocate new ospf structure. */ +static struct ospf * +ospf_new (void) +{ + int i; + + struct ospf *new = XCALLOC (MTYPE_OSPF_TOP, sizeof (struct ospf)); + + new->router_id.s_addr = htonl (0); + new->router_id_static.s_addr = htonl (0); + + new->abr_type = OSPF_ABR_DEFAULT; + new->oiflist = list_new (); + new->vlinks = list_new (); + new->areas = list_new (); + new->areas->cmp = (int (*)(void *, void *)) ospf_area_id_cmp; + new->networks = route_table_init (); + new->nbr_nbma = route_table_init (); + + new->lsdb = ospf_lsdb_new (); + + new->default_originate = DEFAULT_ORIGINATE_NONE; + + new->passive_interface_default = OSPF_IF_ACTIVE; + + new->new_external_route = route_table_init (); + new->old_external_route = route_table_init (); + new->external_lsas = route_table_init (); + + 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; + new->spf_max_holdtime = OSPF_SPF_MAX_HOLDTIME_DEFAULT; + new->spf_hold_multiplier = 1; + + /* MaxAge init. */ + 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); + + /* Distance table init. */ + new->distance_table = route_table_init (); + + new->lsa_refresh_queue.index = 0; + new->lsa_refresh_interval = OSPF_LSA_REFRESH_INTERVAL_DEFAULT; + new->t_lsa_refresher = thread_add_timer (master, ospf_lsa_refresh_walker, + new, new->lsa_refresh_interval); + new->lsa_refresher_started = quagga_time (NULL); + + if ((new->fd = ospf_sock_init()) < 0) + { + zlog_err("ospf_new: fatal error: ospf_sock_init was unable to open " + "a socket"); + exit(1); + } + new->maxsndbuflen = getsockopt_so_sendbuf (new->fd); + if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE)) + zlog_debug ("%s: starting with OSPF send buffer size %u", + __func__, new->maxsndbuflen); + if ((new->ibuf = stream_new(OSPF_MAX_PACKET_SIZE+1)) == NULL) + { + zlog_err("ospf_new: fatal error: stream_new(%u) failed allocating ibuf", + OSPF_MAX_PACKET_SIZE+1); + exit(1); + } + new->t_read = thread_add_read (master, ospf_read, new, new->fd); + new->oi_write_q = list_new (); + + return new; +} + +struct ospf * +ospf_lookup () +{ + if (listcount (om->ospf) == 0) + return NULL; + + 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 +ospf_add (struct ospf *ospf) +{ + listnode_add (om->ospf, ospf); +} + +static void +ospf_delete (struct ospf *ospf) +{ + listnode_delete (om->ospf, ospf); +} + +struct ospf * +ospf_get () +{ + struct ospf *ospf; + + ospf = ospf_lookup (); + if (ospf == NULL) + { + ospf = ospf_new (); + ospf_add (ospf); + + if (ospf->router_id_static.s_addr == 0) + ospf_router_id_update (ospf); + + ospf_opaque_type11_lsa_init (ospf); + } + + 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. + * + * Function is to cleanup G-R state, if required then call ospf_finish_final + * to complete shutdown of this ospf instance. Possibly exit if the + * whole process is being shutdown and this was the last OSPF instance. + */ +static void +ospf_deferred_shutdown_finish (struct ospf *ospf) +{ + ospf->stub_router_shutdown_time = OSPF_STUB_ROUTER_UNCONFIGURED; + OSPF_TIMER_OFF (ospf->t_deferred_shutdown); + + ospf_finish_final (ospf); + + /* *ospf is now invalid */ + + /* ospfd being shut-down? If so, was this the last ospf instance? */ + if (CHECK_FLAG (om->options, OSPF_MASTER_SHUTDOWN) + && (listcount (om->ospf) == 0)) + exit (0); + + return; +} + +/* Timer thread for G-R */ +static int +ospf_deferred_shutdown_timer (struct thread *t) +{ + struct ospf *ospf = THREAD_ARG(t); + + ospf_deferred_shutdown_finish (ospf); + + return 0; +} + +/* Check whether deferred-shutdown must be scheduled, otherwise call + * down directly into second-half of instance shutdown. + */ +static void +ospf_deferred_shutdown_check (struct ospf *ospf) +{ + unsigned long timeout; + struct listnode *ln; + struct ospf_area *area; + + /* deferred shutdown already running? */ + if (ospf->t_deferred_shutdown) + return; + + /* Should we try push out max-metric LSAs? */ + if (ospf->stub_router_shutdown_time != OSPF_STUB_ROUTER_UNCONFIGURED) + { + for (ALL_LIST_ELEMENTS_RO (ospf->areas, ln, area)) + { + SET_FLAG (area->stub_router_state, OSPF_AREA_ADMIN_STUB_ROUTED); + + if (!CHECK_FLAG (area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED)) + ospf_router_lsa_update_area (area); + } + timeout = ospf->stub_router_shutdown_time; + } + else + { + /* No timer needed */ + ospf_deferred_shutdown_finish (ospf); + return; + } + + OSPF_TIMER_ON (ospf->t_deferred_shutdown, ospf_deferred_shutdown_timer, + timeout); + return; +} + +/* Shut down the entire process */ +void +ospf_terminate (void) +{ + struct ospf *ospf; + struct listnode *node, *nnode; + + /* shutdown already in progress */ + if (CHECK_FLAG (om->options, OSPF_MASTER_SHUTDOWN)) + return; + + SET_FLAG (om->options, OSPF_MASTER_SHUTDOWN); + + /* exit immediately if OSPF not actually running */ + if (listcount(om->ospf) == 0) + exit(0); + + for (ALL_LIST_ELEMENTS (om->ospf, node, nnode, ospf)) + ospf_finish (ospf); + + /* Deliberately go back up, hopefully to thread scheduler, as + * One or more ospf_finish()'s may have deferred shutdown to a timer + * thread + */ +} + +void +ospf_finish (struct ospf *ospf) +{ + /* let deferred shutdown decide */ + ospf_deferred_shutdown_check (ospf); + + /* if ospf_deferred_shutdown returns, then ospf_finish_final is + * deferred to expiry of G-S timer thread. Return back up, hopefully + * to thread scheduler. + */ + return; +} + +/* Final cleanup of ospf instance */ +static void +ospf_finish_final (struct ospf *ospf) +{ + struct route_node *rn; + struct ospf_nbr_nbma *nbr_nbma; + struct ospf_lsa *lsa; + struct ospf_interface *oi; + struct ospf_area *area; + struct ospf_vl_data *vl_data; + struct listnode *node, *nnode; + int i; + + ospf_opaque_type11_lsa_term (ospf); + + /* be nice if this worked, but it doesn't */ + /*ospf_flush_self_originated_lsas_now (ospf);*/ + + /* Unregister redistribution */ + for (i = 0; i < ZEBRA_ROUTE_MAX; i++) + ospf_redistribute_unset (ospf, i); + ospf_redistribute_default_unset (ospf); + + for (ALL_LIST_ELEMENTS (ospf->areas, node, nnode, area)) + ospf_remove_vls_through_area (ospf, area); + + for (ALL_LIST_ELEMENTS (ospf->vlinks, node, nnode, vl_data)) + ospf_vl_delete (ospf, vl_data); + + list_delete (ospf->vlinks); + + /* Reset interface. */ + for (ALL_LIST_ELEMENTS (ospf->oiflist, node, nnode, oi)) + ospf_if_free (oi); + + /* Clear static neighbors */ + for (rn = route_top (ospf->nbr_nbma); rn; rn = route_next (rn)) + if ((nbr_nbma = rn->info)) + { + OSPF_POLL_TIMER_OFF (nbr_nbma->t_poll); + + if (nbr_nbma->nbr) + { + nbr_nbma->nbr->nbr_nbma = NULL; + nbr_nbma->nbr = NULL; + } + + if (nbr_nbma->oi) + { + listnode_delete (nbr_nbma->oi->nbr_nbma, nbr_nbma); + nbr_nbma->oi = NULL; + } + + XFREE (MTYPE_OSPF_NEIGHBOR_STATIC, nbr_nbma); + } + + route_table_finish (ospf->nbr_nbma); + + /* Clear networks and Areas. */ + for (rn = route_top (ospf->networks); rn; rn = route_next (rn)) + { + struct ospf_network *network; + + if ((network = rn->info) != NULL) + { + ospf_network_free (ospf, network); + rn->info = NULL; + route_unlock_node (rn); + } + } + + for (ALL_LIST_ELEMENTS (ospf->areas, node, nnode, area)) + { + listnode_delete (ospf->areas, area); + ospf_area_free (area); + } + + /* Cancel all timers. */ + OSPF_TIMER_OFF (ospf->t_external_lsa); + OSPF_TIMER_OFF (ospf->t_spf_calc); + OSPF_TIMER_OFF (ospf->t_ase_calc); + OSPF_TIMER_OFF (ospf->t_maxage); + OSPF_TIMER_OFF (ospf->t_maxage_walker); + OSPF_TIMER_OFF (ospf->t_abr_task); + OSPF_TIMER_OFF (ospf->t_asbr_check); + OSPF_TIMER_OFF (ospf->t_distribute_update); + OSPF_TIMER_OFF (ospf->t_lsa_refresher); + OSPF_TIMER_OFF (ospf->t_read); + OSPF_TIMER_OFF (ospf->t_write); + OSPF_TIMER_OFF (ospf->t_opaque_lsa_self); + + close (ospf->fd); + stream_free(ospf->ibuf); + + LSDB_LOOP (OPAQUE_AS_LSDB (ospf), rn, lsa) + ospf_discard_from_db (ospf, ospf->lsdb, 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 (rn = route_top (ospf->maxage_lsa); rn; rn = route_next (rn)) + { + struct ospf_lsa *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); + if (ospf->new_table) + { + ospf_route_delete (ospf->new_table); + ospf_route_table_free (ospf->new_table); + } + if (ospf->old_rtrs) + ospf_rtrs_free (ospf->old_rtrs); + if (ospf->new_rtrs) + ospf_rtrs_free (ospf->new_rtrs); + if (ospf->new_external_route) + { + ospf_route_delete (ospf->new_external_route); + ospf_route_table_free (ospf->new_external_route); + } + if (ospf->old_external_route) + { + ospf_route_delete (ospf->old_external_route); + ospf_route_table_free (ospf->old_external_route); + } + if (ospf->external_lsas) + { + ospf_ase_external_lsas_finish (ospf->external_lsas); + } + + list_delete (ospf->areas); + + for (i = ZEBRA_ROUTE_SYSTEM; i <= ZEBRA_ROUTE_MAX; i++) + if (EXTERNAL_INFO (i) != NULL) + for (rn = route_top (EXTERNAL_INFO (i)); rn; rn = route_next (rn)) + { + if (rn->info == NULL) + continue; + + XFREE (MTYPE_OSPF_EXTERNAL_INFO, rn->info); + rn->info = NULL; + route_unlock_node (rn); + } + + ospf_distance_reset (ospf); + route_table_finish (ospf->distance_table); + + ospf_delete (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) +{ + struct ospf_area *new; + + /* Allocate new config_network. */ + new = XCALLOC (MTYPE_OSPF_AREA, sizeof (struct ospf_area)); + + new->ospf = ospf; + + new->area_id = area_id; + + new->external_routing = OSPF_AREA_DEFAULT; + new->default_cost = 1; + new->auth_type = OSPF_AUTH_NULL; + + /* New LSDB init. */ + new->lsdb = ospf_lsdb_new (); + + /* Self-originated LSAs initialize. */ + new->router_lsa_self = NULL; + + ospf_opaque_type10_lsa_init (new); + + new->oiflist = list_new (); + new->ranges = route_table_init (); + + if (area_id.s_addr == OSPF_AREA_BACKBONE) + ospf->backbone = new; + + return new; +} + +static void +ospf_area_free (struct ospf_area *area) +{ + struct route_node *rn; + struct ospf_lsa *lsa; + + /* Free LSDBs. */ + LSDB_LOOP (ROUTER_LSDB (area), rn, lsa) + ospf_discard_from_db (area->ospf, area->lsdb, lsa); + LSDB_LOOP (NETWORK_LSDB (area), rn, lsa) + ospf_discard_from_db (area->ospf, area->lsdb, lsa); + LSDB_LOOP (SUMMARY_LSDB (area), rn, lsa) + ospf_discard_from_db (area->ospf, area->lsdb, lsa); + LSDB_LOOP (ASBR_SUMMARY_LSDB (area), rn, lsa) + ospf_discard_from_db (area->ospf, area->lsdb, lsa); + + LSDB_LOOP (NSSA_LSDB (area), rn, lsa) + ospf_discard_from_db (area->ospf, area->lsdb, 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); + + ospf_lsdb_delete_all (area->lsdb); + ospf_lsdb_free (area->lsdb); + + ospf_lsa_unlock (&area->router_lsa_self); + + route_table_finish (area->ranges); + list_delete (area->oiflist); + + if (EXPORT_NAME (area)) + free (EXPORT_NAME (area)); + + if (IMPORT_NAME (area)) + free (IMPORT_NAME (area)); + + /* Cancel timer. */ + OSPF_TIMER_OFF (area->t_stub_router); + OSPF_TIMER_OFF (area->t_opaque_lsa_self); + + if (OSPF_IS_AREA_BACKBONE (area)) + area->ospf->backbone = NULL; + + XFREE (MTYPE_OSPF_AREA, area); +} + +void +ospf_area_check_free (struct ospf *ospf, struct in_addr area_id) +{ + struct ospf_area *area; + + area = ospf_area_lookup_by_area_id (ospf, area_id); + if (area && + listcount (area->oiflist) == 0 && + area->ranges->top == NULL && + area->shortcut_configured == OSPF_SHORTCUT_DEFAULT && + area->external_routing == OSPF_AREA_DEFAULT && + area->no_summary == 0 && + area->default_cost == 1 && + EXPORT_NAME (area) == NULL && + IMPORT_NAME (area) == NULL && + area->auth_type == OSPF_AUTH_NULL) + { + listnode_delete (ospf->areas, area); + ospf_area_free (area); + } +} + +struct ospf_area * +ospf_area_get (struct ospf *ospf, struct in_addr area_id, int format) +{ + struct ospf_area *area; + + area = ospf_area_lookup_by_area_id (ospf, area_id); + if (!area) + { + area = ospf_area_new (ospf, area_id); + 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; +} + +struct ospf_area * +ospf_area_lookup_by_area_id (struct ospf *ospf, struct in_addr area_id) +{ + struct ospf_area *area; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO (ospf->areas, node, area)) + if (IPV4_ADDR_SAME (&area->area_id, &area_id)) + return area; + + return NULL; +} + +void +ospf_area_add_if (struct ospf_area *area, struct ospf_interface *oi) +{ + listnode_add (area->oiflist, oi); +} + +void +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) +{ + struct ospf_network *new; + new = XCALLOC (MTYPE_OSPF_NETWORK, sizeof (struct ospf_network)); + + new->area_id = area_id; + new->format = 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) +{ + ospf_area_check_free (ospf, network->area_id); + ospf_schedule_abr_task (ospf); + XFREE (MTYPE_OSPF_NETWORK, network); +} + +int +ospf_network_set (struct ospf *ospf, struct prefix_ipv4 *p, + struct in_addr area_id) +{ + struct ospf_network *network; + struct ospf_area *area; + struct route_node *rn; + int ret = OSPF_AREA_ID_FORMAT_ADDRESS; + + rn = route_node_get (ospf->networks, (struct prefix *)p); + if (rn->info) + { + /* There is already same network statement. */ + route_unlock_node (rn); + return 0; + } + + rn->info = network = ospf_network_new (area_id, ret); + area = ospf_area_get (ospf, area_id, ret); + + /* Run network config now. */ + ospf_network_run ((struct prefix *)p, area); + + /* Update connected redistribute. */ + update_redistributed(ospf, 1); + + ospf_area_check_free (ospf, area_id); + + return 1; +} + +int +ospf_network_unset (struct ospf *ospf, struct prefix_ipv4 *p, + struct in_addr area_id) +{ + struct route_node *rn; + struct ospf_network *network; + struct listnode *node, *nnode; + struct ospf_interface *oi; + + rn = route_node_lookup (ospf->networks, (struct prefix *)p); + if (rn == NULL) + return 0; + + network = rn->info; + route_unlock_node (rn); + if (!IPV4_ADDR_SAME (&area_id, &network->area_id)) + return 0; + + ospf_network_free (ospf, rn->info); + rn->info = NULL; + route_unlock_node (rn); /* initial reference */ + + /* Find interfaces that not configured already. */ + for (ALL_LIST_ELEMENTS (ospf->oiflist, node, nnode, oi)) + { + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) + continue; + + ospf_network_run_subnet (ospf, oi->connected, NULL, NULL); + } + + /* Update connected redistribute. */ + 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 + */ +static int +ospf_network_match_iface(const struct connected *co, const struct prefix *net) +{ + /* new approach: more elegant and conceptually clean */ + return prefix_match(net, CONNECTED_PREFIX(co)); +} + +static void +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; + + 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)) + ospf_network_run_subnet (ospf, co, p, given_area); +} + +static void +ospf_network_run (struct prefix *p, struct ospf_area *area) +{ + struct interface *ifp; + struct listnode *node; + + /* Schedule Router ID Update. */ + if (area->ospf->router_id.s_addr == 0) + ospf_router_id_update (area->ospf); + + /* Get target interface. */ + for (ALL_LIST_ELEMENTS_RO (om->iflist, node, ifp)) + ospf_network_run_interface (area->ospf, ifp, p, area); +} + +void +ospf_ls_upd_queue_empty (struct ospf_interface *oi) +{ + struct route_node *rn; + struct listnode *node, *nnode; + struct list *lst; + struct ospf_lsa *lsa; + + /* empty ls update queue */ + for (rn = route_top (oi->ls_upd_queue); rn; + rn = route_next (rn)) + if ((lst = (struct list *) rn->info)) + { + for (ALL_LIST_ELEMENTS (lst, node, nnode, lsa)) + ospf_lsa_unlock (&lsa); /* oi->ls_upd_queue */ + list_free (lst); + rn->info = NULL; + } + + /* remove update event */ + if (oi->t_ls_upd_event) + { + thread_cancel (oi->t_ls_upd_event); + oi->t_ls_upd_event = NULL; + } +} + +void +ospf_if_update (struct ospf *ospf, struct interface *ifp) +{ + if (!ospf) + ospf = ospf_lookup (); + + /* OSPF must be ready. */ + if (!ospf_is_ready (ospf)) + return; + + ospf_network_run_interface (ospf, ifp, NULL, NULL); + + /* Update connected redistribute. */ + update_redistributed(ospf, 1); +} + +void +ospf_remove_vls_through_area (struct ospf *ospf, struct ospf_area *area) +{ + struct listnode *node, *nnode; + struct ospf_vl_data *vl_data; + + for (ALL_LIST_ELEMENTS (ospf->vlinks, node, nnode, vl_data)) + if (IPV4_ADDR_SAME (&vl_data->vl_area_id, &area->area_id)) + ospf_vl_delete (ospf, vl_data); +} + + +static const struct message ospf_area_type_msg[] = +{ + { OSPF_AREA_DEFAULT, "Default" }, + { OSPF_AREA_STUB, "Stub" }, + { OSPF_AREA_NSSA, "NSSA" }, +}; +static const int ospf_area_type_msg_max = OSPF_AREA_TYPE_MAX; + +static void +ospf_area_type_set (struct ospf_area *area, int type) +{ + struct listnode *node; + struct ospf_interface *oi; + + if (area->external_routing == type) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Area[%s]: Types are the same, ignored.", + inet_ntoa (area->area_id)); + return; + } + + area->external_routing = type; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("Area[%s]: Configured as %s", inet_ntoa (area->area_id), + LOOKUP (ospf_area_type_msg, type)); + + switch (area->external_routing) + { + case OSPF_AREA_DEFAULT: + for (ALL_LIST_ELEMENTS_RO (area->oiflist, node, oi)) + if (oi->nbr_self != NULL) + { + UNSET_FLAG (oi->nbr_self->options, OSPF_OPTION_NP); + SET_FLAG (oi->nbr_self->options, OSPF_OPTION_E); + } + break; + case OSPF_AREA_STUB: + for (ALL_LIST_ELEMENTS_RO (area->oiflist, node, oi)) + if (oi->nbr_self != NULL) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("setting options on %s accordingly", IF_NAME (oi)); + UNSET_FLAG (oi->nbr_self->options, OSPF_OPTION_NP); + UNSET_FLAG (oi->nbr_self->options, OSPF_OPTION_E); + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("options set on %s: %x", + IF_NAME (oi), OPTIONS (oi)); + } + break; + case OSPF_AREA_NSSA: + for (ALL_LIST_ELEMENTS_RO (area->oiflist, node, oi)) + if (oi->nbr_self != NULL) + { + zlog_debug ("setting nssa options on %s accordingly", IF_NAME (oi)); + UNSET_FLAG (oi->nbr_self->options, OSPF_OPTION_E); + SET_FLAG (oi->nbr_self->options, OSPF_OPTION_NP); + zlog_debug ("options set on %s: %x", IF_NAME (oi), OPTIONS (oi)); + } + break; + default: + break; + } + + ospf_router_lsa_update_area (area); + ospf_schedule_abr_task (area->ospf); +} + +int +ospf_area_shortcut_set (struct ospf *ospf, struct ospf_area *area, int mode) +{ + if (area->shortcut_configured == mode) + return 0; + + area->shortcut_configured = mode; + ospf_router_lsa_update_area (area); + ospf_schedule_abr_task (ospf); + + ospf_area_check_free (ospf, area->area_id); + + return 1; +} + +int +ospf_area_shortcut_unset (struct ospf *ospf, struct ospf_area *area) +{ + area->shortcut_configured = OSPF_SHORTCUT_DEFAULT; + ospf_router_lsa_update_area (area); + ospf_area_check_free (ospf, area->area_id); + ospf_schedule_abr_task (ospf); + + return 1; +} + +static int +ospf_area_vlink_count (struct ospf *ospf, struct ospf_area *area) +{ + struct ospf_vl_data *vl; + struct listnode *node; + int count = 0; + + for (ALL_LIST_ELEMENTS_RO (ospf->vlinks, node, vl)) + if (IPV4_ADDR_SAME (&vl->vl_area_id, &area->area_id)) + count++; + + return count; +} + +int +ospf_area_stub_set (struct ospf *ospf, struct in_addr area_id) +{ + struct ospf_area *area; + int format = OSPF_AREA_ID_FORMAT_ADDRESS; + + area = ospf_area_get (ospf, area_id, format); + if (ospf_area_vlink_count (ospf, area)) + return 0; + + if (area->external_routing != OSPF_AREA_STUB) + ospf_area_type_set (area, OSPF_AREA_STUB); + + return 1; +} + +int +ospf_area_stub_unset (struct ospf *ospf, struct in_addr area_id) +{ + struct ospf_area *area; + + area = ospf_area_lookup_by_area_id (ospf, area_id); + if (area == NULL) + return 1; + + if (area->external_routing == OSPF_AREA_STUB) + ospf_area_type_set (area, OSPF_AREA_DEFAULT); + + ospf_area_check_free (ospf, area_id); + + return 1; +} + +int +ospf_area_no_summary_set (struct ospf *ospf, struct in_addr area_id) +{ + struct ospf_area *area; + int format = OSPF_AREA_ID_FORMAT_ADDRESS; + + area = ospf_area_get (ospf, area_id, format); + area->no_summary = 1; + + return 1; +} + +int +ospf_area_no_summary_unset (struct ospf *ospf, struct in_addr area_id) +{ + struct ospf_area *area; + + area = ospf_area_lookup_by_area_id (ospf, area_id); + if (area == NULL) + return 0; + + area->no_summary = 0; + ospf_area_check_free (ospf, area_id); + + return 1; +} + +int +ospf_area_nssa_set (struct ospf *ospf, struct in_addr area_id) +{ + struct ospf_area *area; + int format = OSPF_AREA_ID_FORMAT_ADDRESS; + + area = ospf_area_get (ospf, area_id, format); + if (ospf_area_vlink_count (ospf, area)) + return 0; + + if (area->external_routing != OSPF_AREA_NSSA) + { + ospf_area_type_set (area, OSPF_AREA_NSSA); + ospf->anyNSSA++; + } + + /* set NSSA area defaults */ + area->no_summary = 0; + area->NSSATranslatorRole = OSPF_NSSA_ROLE_CANDIDATE; + area->NSSATranslatorState = OSPF_NSSA_TRANSLATE_DISABLED; + area->NSSATranslatorStabilityInterval = OSPF_NSSA_TRANS_STABLE_DEFAULT; + + return 1; +} + +int +ospf_area_nssa_unset (struct ospf *ospf, struct in_addr area_id) +{ + struct ospf_area *area; + + area = ospf_area_lookup_by_area_id (ospf, area_id); + if (area == NULL) + return 0; + + if (area->external_routing == OSPF_AREA_NSSA) + { + ospf->anyNSSA--; + ospf_area_type_set (area, OSPF_AREA_DEFAULT); + } + + ospf_area_check_free (ospf, area_id); + + return 1; +} + +int +ospf_area_nssa_translator_role_set (struct ospf *ospf, struct in_addr area_id, + int role) +{ + struct ospf_area *area; + + area = ospf_area_lookup_by_area_id (ospf, area_id); + if (area == NULL) + return 0; + + area->NSSATranslatorRole = role; + + return 1; +} + +#if 0 +/* XXX: unused? Leave for symmetry? */ +static int +ospf_area_nssa_translator_role_unset (struct ospf *ospf, + struct in_addr area_id) +{ + struct ospf_area *area; + + area = ospf_area_lookup_by_area_id (ospf, area_id); + if (area == NULL) + return 0; + + area->NSSATranslatorRole = OSPF_NSSA_ROLE_CANDIDATE; + + ospf_area_check_free (ospf, area_id); + + return 1; +} +#endif + +int +ospf_area_export_list_set (struct ospf *ospf, + struct ospf_area *area, const char *list_name) +{ + struct access_list *list; + list = access_list_lookup (AFI_IP, list_name); + + EXPORT_LIST (area) = list; + + if (EXPORT_NAME (area)) + free (EXPORT_NAME (area)); + + EXPORT_NAME (area) = strdup (list_name); + ospf_schedule_abr_task (ospf); + + return 1; +} + +int +ospf_area_export_list_unset (struct ospf *ospf, struct ospf_area * area) +{ + + EXPORT_LIST (area) = 0; + + if (EXPORT_NAME (area)) + free (EXPORT_NAME (area)); + + EXPORT_NAME (area) = NULL; + + ospf_area_check_free (ospf, area->area_id); + + ospf_schedule_abr_task (ospf); + + return 1; +} + +int +ospf_area_import_list_set (struct ospf *ospf, struct ospf_area *area, + const char *name) +{ + struct access_list *list; + list = access_list_lookup (AFI_IP, name); + + IMPORT_LIST (area) = list; + + if (IMPORT_NAME (area)) + free (IMPORT_NAME (area)); + + IMPORT_NAME (area) = strdup (name); + ospf_schedule_abr_task (ospf); + + return 1; +} + +int +ospf_area_import_list_unset (struct ospf *ospf, struct ospf_area * area) +{ + IMPORT_LIST (area) = 0; + + if (IMPORT_NAME (area)) + free (IMPORT_NAME (area)); + + IMPORT_NAME (area) = NULL; + ospf_area_check_free (ospf, area->area_id); + + ospf_schedule_abr_task (ospf); + + return 1; +} + +int +ospf_timers_refresh_set (struct ospf *ospf, int interval) +{ + int time_left; + + if (ospf->lsa_refresh_interval == interval) + return 1; + + time_left = ospf->lsa_refresh_interval - + (quagga_time (NULL) - ospf->lsa_refresher_started); + + if (time_left > interval) + { + OSPF_TIMER_OFF (ospf->t_lsa_refresher); + ospf->t_lsa_refresher = + thread_add_timer (master, ospf_lsa_refresh_walker, ospf, interval); + } + ospf->lsa_refresh_interval = interval; + + return 1; +} + +int +ospf_timers_refresh_unset (struct ospf *ospf) +{ + int time_left; + + time_left = ospf->lsa_refresh_interval - + (quagga_time (NULL) - ospf->lsa_refresher_started); + + if (time_left > OSPF_LSA_REFRESH_INTERVAL_DEFAULT) + { + OSPF_TIMER_OFF (ospf->t_lsa_refresher); + ospf->t_lsa_refresher = + thread_add_timer (master, ospf_lsa_refresh_walker, ospf, + OSPF_LSA_REFRESH_INTERVAL_DEFAULT); + } + + ospf->lsa_refresh_interval = OSPF_LSA_REFRESH_INTERVAL_DEFAULT; + + return 1; +} + + +static struct ospf_nbr_nbma * +ospf_nbr_nbma_new (void) +{ + struct ospf_nbr_nbma *nbr_nbma; + + nbr_nbma = XCALLOC (MTYPE_OSPF_NEIGHBOR_STATIC, + sizeof (struct ospf_nbr_nbma)); + + nbr_nbma->priority = OSPF_NEIGHBOR_PRIORITY_DEFAULT; + nbr_nbma->v_poll = OSPF_POLL_INTERVAL_DEFAULT; + + return nbr_nbma; +} + +static void +ospf_nbr_nbma_free (struct ospf_nbr_nbma *nbr_nbma) +{ + XFREE (MTYPE_OSPF_NEIGHBOR_STATIC, nbr_nbma); +} + +static void +ospf_nbr_nbma_delete (struct ospf *ospf, struct ospf_nbr_nbma *nbr_nbma) +{ + struct route_node *rn; + struct prefix_ipv4 p; + + p.family = AF_INET; + p.prefix = nbr_nbma->addr; + p.prefixlen = IPV4_MAX_BITLEN; + + rn = route_node_lookup (ospf->nbr_nbma, (struct prefix *)&p); + if (rn) + { + ospf_nbr_nbma_free (rn->info); + rn->info = NULL; + route_unlock_node (rn); + route_unlock_node (rn); + } +} + +static void +ospf_nbr_nbma_down (struct ospf_nbr_nbma *nbr_nbma) +{ + OSPF_TIMER_OFF (nbr_nbma->t_poll); + + if (nbr_nbma->nbr) + { + nbr_nbma->nbr->nbr_nbma = NULL; + OSPF_NSM_EVENT_EXECUTE (nbr_nbma->nbr, NSM_KillNbr); + } + + if (nbr_nbma->oi) + listnode_delete (nbr_nbma->oi->nbr_nbma, nbr_nbma); +} + +static void +ospf_nbr_nbma_add (struct ospf_nbr_nbma *nbr_nbma, + struct ospf_interface *oi) +{ + struct ospf_neighbor *nbr; + struct route_node *rn; + struct prefix p; + + if (oi->type != OSPF_IFTYPE_NBMA) + return; + + if (nbr_nbma->nbr != NULL) + return; + + if (IPV4_ADDR_SAME (&oi->nbr_self->address.u.prefix4, &nbr_nbma->addr)) + return; + + nbr_nbma->oi = oi; + listnode_add (oi->nbr_nbma, nbr_nbma); + + /* Get neighbor information from table. */ + p.family = AF_INET; + p.prefixlen = IPV4_MAX_BITLEN; + p.u.prefix4 = nbr_nbma->addr; + + rn = route_node_get (oi->nbrs, (struct prefix *)&p); + if (rn->info) + { + nbr = rn->info; + nbr->nbr_nbma = nbr_nbma; + nbr_nbma->nbr = nbr; + + route_unlock_node (rn); + } + else + { + nbr = rn->info = ospf_nbr_new (oi); + nbr->state = NSM_Down; + nbr->src = nbr_nbma->addr; + nbr->nbr_nbma = nbr_nbma; + nbr->priority = nbr_nbma->priority; + nbr->address = p; + + nbr_nbma->nbr = nbr; + + OSPF_NSM_EVENT_EXECUTE (nbr, NSM_Start); + } +} + +void +ospf_nbr_nbma_if_update (struct ospf *ospf, struct ospf_interface *oi) +{ + struct ospf_nbr_nbma *nbr_nbma; + struct route_node *rn; + struct prefix_ipv4 p; + + if (oi->type != OSPF_IFTYPE_NBMA) + return; + + for (rn = route_top (ospf->nbr_nbma); rn; rn = route_next (rn)) + if ((nbr_nbma = rn->info)) + if (nbr_nbma->oi == NULL && nbr_nbma->nbr == NULL) + { + p.family = AF_INET; + p.prefix = nbr_nbma->addr; + p.prefixlen = IPV4_MAX_BITLEN; + + if (prefix_match (oi->address, (struct prefix *)&p)) + ospf_nbr_nbma_add (nbr_nbma, oi); + } +} + +struct ospf_nbr_nbma * +ospf_nbr_nbma_lookup (struct ospf *ospf, struct in_addr nbr_addr) +{ + struct route_node *rn; + struct prefix_ipv4 p; + + p.family = AF_INET; + p.prefix = nbr_addr; + p.prefixlen = IPV4_MAX_BITLEN; + + rn = route_node_lookup (ospf->nbr_nbma, (struct prefix *)&p); + if (rn) + { + route_unlock_node (rn); + return rn->info; + } + return NULL; +} + +struct ospf_nbr_nbma * +ospf_nbr_nbma_lookup_next (struct ospf *ospf, struct in_addr *addr, int first) +{ +#if 0 + struct ospf_nbr_nbma *nbr_nbma; + struct listnode *node; +#endif + + if (ospf == NULL) + return NULL; + +#if 0 + for (ALL_LIST_ELEMENTS_RO (ospf->nbr_nbma, node, nbr_nbma)) + { + if (first) + { + *addr = nbr_nbma->addr; + return nbr_nbma; + } + else if (ntohl (nbr_nbma->addr.s_addr) > ntohl (addr->s_addr)) + { + *addr = nbr_nbma->addr; + return nbr_nbma; + } + } +#endif + return NULL; +} + +int +ospf_nbr_nbma_set (struct ospf *ospf, struct in_addr nbr_addr) +{ + struct ospf_nbr_nbma *nbr_nbma; + struct ospf_interface *oi; + struct prefix_ipv4 p; + struct route_node *rn; + struct listnode *node; + + nbr_nbma = ospf_nbr_nbma_lookup (ospf, nbr_addr); + if (nbr_nbma) + return 0; + + nbr_nbma = ospf_nbr_nbma_new (); + nbr_nbma->addr = nbr_addr; + + p.family = AF_INET; + p.prefix = 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)) + { + if (oi->type == OSPF_IFTYPE_NBMA) + if (prefix_match (oi->address, (struct prefix *)&p)) + { + ospf_nbr_nbma_add (nbr_nbma, oi); + break; + } + } + + return 1; +} + +int +ospf_nbr_nbma_unset (struct ospf *ospf, struct in_addr nbr_addr) +{ + struct ospf_nbr_nbma *nbr_nbma; + + nbr_nbma = ospf_nbr_nbma_lookup (ospf, nbr_addr); + if (nbr_nbma == NULL) + return 0; + + ospf_nbr_nbma_down (nbr_nbma); + ospf_nbr_nbma_delete (ospf, nbr_nbma); + + return 1; +} + +int +ospf_nbr_nbma_priority_set (struct ospf *ospf, struct in_addr nbr_addr, + u_char priority) +{ + struct ospf_nbr_nbma *nbr_nbma; + + nbr_nbma = ospf_nbr_nbma_lookup (ospf, nbr_addr); + if (nbr_nbma == NULL) + return 0; + + if (nbr_nbma->priority != priority) + nbr_nbma->priority = priority; + + return 1; +} + +int +ospf_nbr_nbma_priority_unset (struct ospf *ospf, struct in_addr nbr_addr) +{ + struct ospf_nbr_nbma *nbr_nbma; + + nbr_nbma = ospf_nbr_nbma_lookup (ospf, nbr_addr); + if (nbr_nbma == NULL) + return 0; + + if (nbr_nbma != OSPF_NEIGHBOR_PRIORITY_DEFAULT) + nbr_nbma->priority = OSPF_NEIGHBOR_PRIORITY_DEFAULT; + + return 1; +} + +int +ospf_nbr_nbma_poll_interval_set (struct ospf *ospf, struct in_addr nbr_addr, + unsigned int interval) +{ + struct ospf_nbr_nbma *nbr_nbma; + + nbr_nbma = ospf_nbr_nbma_lookup (ospf, nbr_addr); + if (nbr_nbma == NULL) + return 0; + + if (nbr_nbma->v_poll != interval) + { + nbr_nbma->v_poll = interval; + if (nbr_nbma->oi && ospf_if_is_up (nbr_nbma->oi)) + { + OSPF_TIMER_OFF (nbr_nbma->t_poll); + OSPF_POLL_TIMER_ON (nbr_nbma->t_poll, ospf_poll_timer, + nbr_nbma->v_poll); + } + } + + return 1; +} + +int +ospf_nbr_nbma_poll_interval_unset (struct ospf *ospf, struct in_addr addr) +{ + struct ospf_nbr_nbma *nbr_nbma; + + nbr_nbma = ospf_nbr_nbma_lookup (ospf, addr); + if (nbr_nbma == NULL) + return 0; + + if (nbr_nbma->v_poll != OSPF_POLL_INTERVAL_DEFAULT) + nbr_nbma->v_poll = OSPF_POLL_INTERVAL_DEFAULT; + + return 1; +} + +void +ospf_master_init () +{ + memset (&ospf_master, 0, sizeof (struct ospf_master)); + + om = &ospf_master; + om->ospf = list_new (); + om->master = thread_master_create (); + om->start_time = quagga_time (NULL); +} diff --git a/ospfd/ospfd.conf.sample b/ospfd/ospfd.conf.sample new file mode 100644 index 0000000..0e8ac67 --- /dev/null +++ b/ospfd/ospfd.conf.sample @@ -0,0 +1,13 @@ +! -*- ospf -*- +! +! OSPFd sample configuration file +! +! +hostname ospfd +password zebra +!enable password please-set-at-here +! +!router ospf +! network 192.168.1.0/24 area 0 +! +log stdout diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h new file mode 100644 index 0000000..0315164 --- /dev/null +++ b/ospfd/ospfd.h @@ -0,0 +1,568 @@ +/* + * OSPFd main header. + * 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 _ZEBRA_OSPFD_H +#define _ZEBRA_OSPFD_H + +#include +#include "libospf.h" + +#include "filter.h" +#include "log.h" + +#define OSPF_VERSION 2 + +/* VTY port number. */ +#define OSPF_VTY_PORT 2604 + +/* IP TTL for OSPF protocol. */ +#define OSPF_IP_TTL 1 +#define OSPF_VL_IP_TTL 100 + +/* Default configuration file name for ospfd. */ +#define OSPF_DEFAULT_CONFIG "ospfd.conf" + +#define OSPF_NSSA_TRANS_STABLE_DEFAULT 40 + +#define OSPF_ALLSPFROUTERS 0xe0000005 /* 224.0.0.5 */ +#define OSPF_ALLDROUTERS 0xe0000006 /* 224.0.0.6 */ + + +/* OSPF Authentication Type. */ +#define OSPF_AUTH_NULL 0 +#define OSPF_AUTH_SIMPLE 1 +#define OSPF_AUTH_CRYPTOGRAPHIC 2 +/* For Interface authentication setting default */ +#define OSPF_AUTH_NOTSET -1 +/* For the consumption and sanity of the command handler */ +/* DO NIOT REMOVE!!! Need to detect whether a value has + been given or not in VLink command handlers */ +#define OSPF_AUTH_CMD_NOTSEEN -2 + +/* OSPF options. */ +#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 +#define OSPF_DD_FLAG_M 0x02 +#define OSPF_DD_FLAG_I 0x04 +#define OSPF_DD_FLAG_ALL 0x07 + +#define OSPF_LS_REFRESH_SHIFT (60 * 15) +#define OSPF_LS_REFRESH_JITTER 60 + +/* OSPF master for system wide configuration and variables. */ +struct ospf_master +{ + /* OSPF instance. */ + struct list *ospf; + + /* OSPF thread master. */ + struct thread_master *master; + + /* Zebra interface list. */ + struct list *iflist; + + /* Redistributed external information. */ + struct route_table *external_info[ZEBRA_ROUTE_MAX + 1]; +#define EXTERNAL_INFO(T) om->external_info[T] + + /* OSPF start time. */ + time_t start_time; + + /* Various OSPF global configuration. */ + u_char options; +#define OSPF_MASTER_SHUTDOWN (1 << 0) /* deferred-shutdown */ +}; + +/* OSPF instance structure. */ +struct ospf +{ + /* OSPF Router ID. */ + struct in_addr router_id; /* Configured automatically. */ + struct in_addr router_id_static; /* Configured manually. */ + + /* ABR/ASBR internal flags. */ + u_char flags; +#define OSPF_FLAG_ABR 0x0001 +#define OSPF_FLAG_ASBR 0x0002 + + /* ABR type. */ + u_char abr_type; +#define OSPF_ABR_UNKNOWN 0 +#define OSPF_ABR_STAND 1 +#define OSPF_ABR_IBM 2 +#define OSPF_ABR_CISCO 3 +#define OSPF_ABR_SHORTCUT 4 +#define OSPF_ABR_DEFAULT OSPF_ABR_CISCO + + /* NSSA ABR */ + u_char anyNSSA; /* Bump for every NSSA attached. */ + + /* Configured variables. */ + u_char config; +#define OSPF_RFC1583_COMPATIBLE (1 << 0) +#define OSPF_OPAQUE_CAPABLE (1 << 2) +#define OSPF_LOG_ADJACENCY_CHANGES (1 << 3) +#define OSPF_LOG_ADJACENCY_DETAIL (1 << 4) + + /* Opaque-LSA administrative flags. */ + u_char opaque; +#define OPAQUE_OPERATION_READY_BIT (1 << 0) + + /* 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. */ + 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 */ + + int default_originate; /* Default information originate. */ +#define DEFAULT_ORIGINATE_NONE 0 +#define DEFAULT_ORIGINATE_ZEBRA 1 +#define DEFAULT_ORIGINATE_ALWAYS 2 + u_int32_t ref_bandwidth; /* Reference Bandwidth (Kbps). */ + struct route_table *networks; /* OSPF config networks. */ + struct list *vlinks; /* Configured Virtual-Links. */ + struct list *areas; /* OSPF areas. */ + struct route_table *nbr_nbma; + struct ospf_area *backbone; /* Pointer to the Backbone Area. */ + + struct list *oiflist; /* ospf interfaces */ + u_char passive_interface_default; /* passive-interface default */ + + /* LSDB of AS-external-LSAs. */ + struct ospf_lsdb *lsdb; + + /* Flags. */ + int external_origin; /* AS-external-LSA origin flag. */ + int ase_calc; /* ASE calculation flag. */ + + struct list *opaque_lsa_self; /* Type-11 Opaque-LSAs */ + + /* Routing tables. */ + struct route_table *old_table; /* Old routing table. */ + struct route_table *new_table; /* Current routing table. */ + + struct route_table *old_rtrs; /* Old ABR/ASBR RT. */ + struct route_table *new_rtrs; /* New ABR/ASBR RT. */ + + struct route_table *new_external_route; /* New External Route. */ + struct route_table *old_external_route; /* Old External Route. */ + + struct route_table *external_lsas; /* Database of external LSAs, + prefix is LSA's adv. network*/ + + /* Time stamps */ + struct timeval ts_spf; /* SPF calculation time stamp. */ + struct timeval ts_spf_duration; /* Execution time of last SPF */ + + struct route_table *maxage_lsa; /* List of MaxAge LSA for deletion. */ + int redistribute; /* Num of redistributed protocols. */ + + /* Threads. */ + struct thread *t_abr_task; /* ABR task timer. */ + struct thread *t_asbr_check; /* ASBR check timer. */ + struct thread *t_distribute_update; /* Distirbute list update timer. */ + 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. */ + struct thread *t_opaque_lsa_self; /* Type-11 Opaque-LSAs origin event. */ + + unsigned int maxage_delay; /* Delay on Maxage remover timer, sec */ + struct thread *t_maxage; /* MaxAge LSA remover timer. */ + struct thread *t_maxage_walker; /* MaxAge LSA checking timer. */ + + struct thread *t_deferred_shutdown; /* deferred/stub-router shutdown timer*/ + + struct thread *t_write; + struct thread *t_read; + int fd; + unsigned int maxsndbuflen; + struct stream *ibuf; + struct list *oi_write_q; + + /* Distribute lists out of other route sources. */ + struct + { + char *name; + struct access_list *list; + } dlist[ZEBRA_ROUTE_MAX]; +#define DISTRIBUTE_NAME(O,T) (O)->dlist[T].name +#define DISTRIBUTE_LIST(O,T) (O)->dlist[T].list + + /* Redistribute metric info. */ + struct + { + int type; /* External metric type (E1 or E2). */ + int value; /* Value for static metric (24-bit). + -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 + { + char *name; + struct route_map *map; + } route_map [ZEBRA_ROUTE_MAX + 1]; /* +1 is for default-information */ +#define ROUTEMAP_NAME(O,T) (O)->route_map[T].name +#define ROUTEMAP(O,T) (O)->route_map[T].map + + int default_metric; /* Default metric for redistribute. */ + +#define OSPF_LSA_REFRESHER_GRANULARITY 10 +#define OSPF_LSA_REFRESHER_SLOTS ((OSPF_LS_REFRESH_TIME + \ + OSPF_LS_REFRESH_SHIFT)/10 + 1) + struct + { + u_int16_t index; + struct list *qs[OSPF_LSA_REFRESHER_SLOTS]; + } lsa_refresh_queue; + + struct thread *t_lsa_refresher; + time_t lsa_refresher_started; +#define OSPF_LSA_REFRESH_INTERVAL_DEFAULT 10 + u_int16_t lsa_refresh_interval; + + /* Distance parameter. */ + u_char distance_all; + u_char distance_intra; + u_char distance_inter; + u_char distance_external; + + /* Statistics for LSA origination. */ + u_int32_t lsa_originate_count; + + /* Statistics for LSA used for new instantiation. */ + u_int32_t rx_lsa_count; + + struct route_table *distance_table; +}; + +/* OSPF area structure. */ +struct ospf_area +{ + /* OSPF instance. */ + struct ospf *ospf; + + /* Zebra interface list belonging to the area. */ + struct list *oiflist; + + /* Area ID. */ + struct in_addr area_id; + + /* Area ID format. */ + char format; +#define OSPF_AREA_ID_FORMAT_ADDRESS 1 +#define OSPF_AREA_ID_FORMAT_DECIMAL 2 + + /* Address range. */ + struct list *address_range; + + /* Configured variables. */ + int external_routing; /* ExternalRoutingCapability. */ +#define OSPF_AREA_DEFAULT 0 +#define OSPF_AREA_STUB 1 +#define OSPF_AREA_NSSA 2 +#define OSPF_AREA_TYPE_MAX 3 + int no_summary; /* Don't inject summaries into stub.*/ + int shortcut_configured; /* Area configured as shortcut. */ +#define OSPF_SHORTCUT_DEFAULT 0 +#define OSPF_SHORTCUT_ENABLE 1 +#define OSPF_SHORTCUT_DISABLE 2 + int shortcut_capability; /* Other ABRs agree on S-bit */ + u_int32_t default_cost; /* StubDefaultCost. */ + int auth_type; /* Authentication type. */ + + + u_char NSSATranslatorRole; /* NSSA configured role */ +#define OSPF_NSSA_ROLE_NEVER 0 +#define OSPF_NSSA_ROLE_CANDIDATE 1 +#define OSPF_NSSA_ROLE_ALWAYS 2 + u_char NSSATranslatorState; /* NSSA operational role */ +#define OSPF_NSSA_TRANSLATE_DISABLED 0 +#define OSPF_NSSA_TRANSLATE_ENABLED 1 + int NSSATranslatorStabilityInterval; + + u_char transit; /* TransitCapability. */ +#define OSPF_TRANSIT_FALSE 0 +#define OSPF_TRANSIT_TRUE 1 + struct route_table *ranges; /* Configured Area Ranges. */ + + /* RFC3137 stub router state flags for area */ + u_char stub_router_state; +#define OSPF_AREA_ADMIN_STUB_ROUTED (1 << 0) /* admin stub-router set */ +#define OSPF_AREA_IS_STUB_ROUTED (1 << 1) /* stub-router active */ +#define OSPF_AREA_WAS_START_STUB_ROUTED (1 << 2) /* startup SR was done */ + + /* Area related LSDBs[Type1-4]. */ + struct ospf_lsdb *lsdb; + + /* Self-originated LSAs. */ + struct ospf_lsa *router_lsa_self; + struct list *opaque_lsa_self; /* Type-10 Opaque-LSAs */ + + /* Area announce list. */ + struct + { + char *name; + struct access_list *list; + } _export; +#define EXPORT_NAME(A) (A)->_export.name +#define EXPORT_LIST(A) (A)->_export.list + + /* Area acceptance list. */ + struct + { + char *name; + struct access_list *list; + } import; +#define IMPORT_NAME(A) (A)->import.name +#define IMPORT_LIST(A) (A)->import.list + + /* Type 3 LSA Area prefix-list. */ + struct + { + char *name; + struct prefix_list *list; + } plist_in; +#define PREFIX_LIST_IN(A) (A)->plist_in.list +#define PREFIX_NAME_IN(A) (A)->plist_in.name + + struct + { + char *name; + struct prefix_list *list; + } plist_out; +#define PREFIX_LIST_OUT(A) (A)->plist_out.list +#define PREFIX_NAME_OUT(A) (A)->plist_out.name + + /* Shortest Path Tree. */ + struct vertex *spf; + + /* Threads. */ + struct thread *t_stub_router; /* Stub-router timer */ + struct thread *t_opaque_lsa_self; /* Type-10 Opaque-LSAs origin. */ + + /* 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. */ + + /* Counters. */ + u_int32_t act_ints; /* Active interfaces. */ + u_int32_t full_nbrs; /* Fully adjacent neighbors. */ + u_int32_t full_vls; /* Fully adjacent virtual neighbors. */ +}; + +/* OSPF config network structure. */ +struct ospf_network +{ + /* Area ID. */ + struct in_addr area_id; + int format; +}; + +/* OSPF NBMA neighbor structure. */ +struct ospf_nbr_nbma +{ + /* Neighbor IP address. */ + struct in_addr addr; + + /* OSPF interface. */ + struct ospf_interface *oi; + + /* OSPF neighbor structure. */ + struct ospf_neighbor *nbr; + + /* Neighbor priority. */ + u_char priority; + + /* Poll timer value. */ + u_int32_t v_poll; + + /* Poll timer thread. */ + struct thread *t_poll; + + /* State change. */ + u_int32_t state_change; +}; + +/* Macro. */ +#define OSPF_AREA_SAME(X,Y) \ + (memcmp ((X->area_id), (Y->area_id), IPV4_MAX_BYTELEN) == 0) + +#define IS_OSPF_ABR(O) ((O)->flags & OSPF_FLAG_ABR) +#define IS_OSPF_ASBR(O) ((O)->flags & OSPF_FLAG_ASBR) + +#define OSPF_IS_AREA_ID_BACKBONE(I) ((I).s_addr == OSPF_AREA_BACKBONE) +#define OSPF_IS_AREA_BACKBONE(A) OSPF_IS_AREA_ID_BACKBONE ((A)->area_id) + +#ifdef roundup +# define ROUNDUP(val, gran) roundup(val, gran) +#else /* roundup */ +# define ROUNDUP(val, gran) (((val) - 1 | (gran) - 1) + 1) +#endif /* roundup */ + +#define LSA_OPTIONS_GET(area) \ + (((area)->external_routing == OSPF_AREA_DEFAULT) ? OSPF_OPTION_E : 0) +#define LSA_OPTIONS_NSSA_GET(area) \ + (((area)->external_routing == OSPF_AREA_NSSA) ? OSPF_OPTION_NP : 0) + +#define OSPF_TIMER_ON(T,F,V) \ + do { \ + if (!(T)) \ + (T) = thread_add_timer (master, (F), ospf, (V)); \ + } while (0) + +#define OSPF_AREA_TIMER_ON(T,F,V) \ + do { \ + if (!(T)) \ + (T) = thread_add_timer (master, (F), area, (V)); \ + } while (0) + +#define OSPF_POLL_TIMER_ON(T,F,V) \ + do { \ + if (!(T)) \ + (T) = thread_add_timer (master, (F), nbr_nbma, (V)); \ + } while (0) + +#define OSPF_POLL_TIMER_OFF(X) OSPF_TIMER_OFF((X)) + +#define OSPF_TIMER_OFF(X) \ + do { \ + if (X) \ + { \ + thread_cancel (X); \ + (X) = NULL; \ + } \ + } while (0) + +/* Extern variables. */ +extern struct ospf_master *om; +extern const struct message ospf_ism_state_msg[]; +extern const struct message ospf_nsm_state_msg[]; +extern const struct message ospf_lsa_type_msg[]; +extern const struct message ospf_link_state_id_type_msg[]; +extern const struct message ospf_network_type_msg[]; +extern const int ospf_ism_state_msg_max; +extern const int ospf_nsm_state_msg_max; +extern const int ospf_lsa_type_msg_max; +extern const int ospf_link_state_id_type_msg_max; +extern const int ospf_redistributed_proto_max; +extern const int ospf_network_type_msg_max; +extern struct zclient *zclient; +extern struct thread_master *master; +extern int ospf_zlog; + +/* Prototypes. */ +extern const char *ospf_redist_string(u_int route_type); +extern struct ospf *ospf_lookup (void); +extern struct ospf *ospf_get (void); +extern void ospf_finish (struct ospf *); +extern void ospf_router_id_update (struct ospf *ospf); +extern int ospf_network_set (struct ospf *, struct prefix_ipv4 *, + struct in_addr); +extern int ospf_network_unset (struct ospf *, struct prefix_ipv4 *, + struct in_addr); +extern int ospf_area_stub_set (struct ospf *, struct in_addr); +extern int ospf_area_stub_unset (struct ospf *, struct in_addr); +extern int ospf_area_no_summary_set (struct ospf *, struct in_addr); +extern int ospf_area_no_summary_unset (struct ospf *, struct in_addr); +extern int ospf_area_nssa_set (struct ospf *, struct in_addr); +extern int ospf_area_nssa_unset (struct ospf *, struct in_addr); +extern int ospf_area_nssa_translator_role_set (struct ospf *, struct in_addr, + int); +extern int ospf_area_export_list_set (struct ospf *, struct ospf_area *, + const char *); +extern int ospf_area_export_list_unset (struct ospf *, struct ospf_area *); +extern int ospf_area_import_list_set (struct ospf *, struct ospf_area *, + const char *); +extern int ospf_area_import_list_unset (struct ospf *, struct ospf_area *); +extern int ospf_area_shortcut_set (struct ospf *, struct ospf_area *, int); +extern int ospf_area_shortcut_unset (struct ospf *, struct ospf_area *); +extern int ospf_timers_refresh_set (struct ospf *, int); +extern int ospf_timers_refresh_unset (struct ospf *); +extern int ospf_nbr_nbma_set (struct ospf *, struct in_addr); +extern int ospf_nbr_nbma_unset (struct ospf *, struct in_addr); +extern int ospf_nbr_nbma_priority_set (struct ospf *, struct in_addr, u_char); +extern int ospf_nbr_nbma_priority_unset (struct ospf *, struct in_addr); +extern int ospf_nbr_nbma_poll_interval_set (struct ospf *, struct in_addr, + unsigned int); +extern int ospf_nbr_nbma_poll_interval_unset (struct ospf *, struct in_addr); +extern void ospf_prefix_list_update (struct prefix_list *); +extern void ospf_init (void); +extern void ospf_if_update (struct ospf *, struct interface *); +extern void ospf_ls_upd_queue_empty (struct ospf_interface *); +extern void ospf_terminate (void); +extern void ospf_nbr_nbma_if_update (struct ospf *, struct ospf_interface *); +extern struct ospf_nbr_nbma *ospf_nbr_nbma_lookup (struct ospf *, + struct in_addr); +extern struct ospf_nbr_nbma *ospf_nbr_nbma_lookup_next (struct ospf *, + struct in_addr *, + int); +extern int ospf_oi_count (struct interface *); + +extern struct ospf_area *ospf_area_get (struct ospf *, struct in_addr, int); +extern void ospf_area_check_free (struct ospf *, struct in_addr); +extern struct ospf_area *ospf_area_lookup_by_area_id (struct ospf *, + struct in_addr); +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); + +extern void ospf_master_init (void); + +#endif /* _ZEBRA_OSPFD_H */ diff --git a/pimd/.gitignore b/pimd/.gitignore new file mode 100644 index 0000000..51a2ac8 --- /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 0000000..f6135a4 --- /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 0000000..9f07bda --- /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 0000000..425ac82 --- /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 0000000..3912109 --- /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 0000000..72fb826 --- /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 0000000..e87e567 --- /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 0000000..d045436 --- /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 0000000..1e3f72c --- /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 0000000..2308573 --- /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 0000000..7d1f52d --- /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 0000000..2e8c966 --- /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 0000000..ad21e08 --- /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 0000000..bd3fb3e --- /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 0000000..749eeab --- /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 0000000..a1cb581 --- /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 0000000..bfc128b --- /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 0000000..b5e272d --- /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 0000000..ddad6cb --- /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 0000000..8cad3d1 --- /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 0000000..ad97879 --- /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 0000000..e6f1c29 --- /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 0000000..ad3c7b5 --- /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 0000000..ab39615 --- /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 0000000..042818a --- /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 0000000..67779ff --- /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 0000000..180de9d --- /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 0000000..bb7e926 --- /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 0000000..2080751 --- /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 0000000..d64b103 --- /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 0000000..9d8e001 --- /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 0000000..37ec0f4 --- /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 0000000..3f56532 --- /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 0000000..472fa9b --- /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 0000000..ea70a8f --- /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 0000000..fa460e2 --- /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 0000000..125d190 --- /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 0000000..8ead7ce --- /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 0000000..a884fc8 --- /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 0000000..8932dc3 --- /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 0000000..5b2172d --- /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 0000000..1aaece3 --- /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 0000000..1753545 --- /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 0000000..c52b0d3 --- /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 0000000..4b378fb --- /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 0000000..9cc13be --- /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 0000000..e7619a5 --- /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 0000000..9a48ea0 --- /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 0000000..3549331 --- /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 0000000..62523c0 --- /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 0000000..ceae4f2 --- /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 0000000..622fb47 --- /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 0000000..fe88eba --- /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 0000000..4bef20b --- /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 0000000..078beab --- /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 0000000..b3be09e --- /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 0000000..3a8353c --- /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 0000000..925f17f --- /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 0000000..4e5832c --- /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 0000000..2984d9a --- /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 0000000..ed4dbba --- /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 0000000..b802cf8 --- /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 0000000..a4d274a --- /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 0000000..5b5182d --- /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 0000000..fdfed2b --- /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 0000000..a8613e2 --- /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 0000000..f3a5ee3 --- /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 0000000..ef9f370 --- /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 0000000..56de83c --- /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 0000000..904ee55 --- /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 0000000..efff100 --- /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 0000000..af5baef --- /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 0000000..770fbf7 --- /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 0000000..f2be6d4 --- /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 0000000..97fb223 --- /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 0000000..6753085 --- /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 0000000..9a7e605 --- /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 0000000..81026ae --- /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/pkgsrc/.gitignore b/pkgsrc/.gitignore new file mode 100644 index 0000000..63e9a66 --- /dev/null +++ b/pkgsrc/.gitignore @@ -0,0 +1,8 @@ +Makefile +Makefile.in +*.sh +.arch-inventory +.arch-ids +*~ +*.loT + diff --git a/pkgsrc/Makefile.am b/pkgsrc/Makefile.am new file mode 100644 index 0000000..622fbf0 --- /dev/null +++ b/pkgsrc/Makefile.am @@ -0,0 +1,3 @@ +rcdir=@pkgsrcrcdir@ + +rc_SCRIPTS = bgpd.sh ospf6d.sh ospfd.sh ripd.sh ripngd.sh zebra.sh diff --git a/pkgsrc/README.txt b/pkgsrc/README.txt new file mode 100644 index 0000000..13ec449 --- /dev/null +++ b/pkgsrc/README.txt @@ -0,0 +1,7 @@ +$Id: README.txt,v 1.1 2004/08/27 15:57:35 gdt Exp $ + +This directory contains files for use with the pkgsrc framework +(http://www.pkgsrc.org) used with NetBSD and other operating systems. +Eventually it will be hooked into automake such that they can be +installed in /usr/pkg/etc/rc.d (via configure option, probably). + diff --git a/pkgsrc/bgpd.sh.in b/pkgsrc/bgpd.sh.in new file mode 100644 index 0000000..d234b54 --- /dev/null +++ b/pkgsrc/bgpd.sh.in @@ -0,0 +1,44 @@ +#!/bin/sh +# +# bgpd is part of the quagga routing beast +# +# PROVIDE: bgpd +# REQUIRE: zebra +## + +PATH=/sbin:/bin:/usr/sbin:/usr/bin:@prefix@/sbin:@prefix@/bin +export PATH + +if [ -f /etc/rc.subr ] +then + . /etc/rc.subr +fi + +name="bgpd" +rcvar=$name +required_files="@sysconfdir@/${name}.conf" +command="@prefix@/sbin/${name}" +command_args="-d" + +start_precmd="zebra_precmd" +socket_dir=@localstatedir@ +pidfile="${socket_dir}/${name}.pid" + +zebra_precmd() +{ + rc_flags="$( + set -- $rc_flags + while [ $# -ne 0 ]; do + if [ X"$1" = X-P -o X"$1" = X-A ]; then + break + fi + shift + done + if [ $# -eq 0 ]; then + echo "-P 0" + fi + ) $rc_flags" +} + +load_rc_config $name +run_rc_command "$1" diff --git a/pkgsrc/ospf6d.sh.in b/pkgsrc/ospf6d.sh.in new file mode 100644 index 0000000..3fbdb81 --- /dev/null +++ b/pkgsrc/ospf6d.sh.in @@ -0,0 +1,44 @@ +#!/bin/sh +# +# ospf6d is part of the quagga routing beast +# +# PROVIDE: ospf6d +# REQUIRE: zebra +## + +PATH=/sbin:/bin:/usr/sbin:/usr/bin:@prefix@/sbin:@prefix@/bin +export PATH + +if [ -f /etc/rc.subr ] +then + . /etc/rc.subr +fi + +name="ospf6d" +rcvar=$name +required_files="@sysconfdir@/${name}.conf" +command="@prefix@/sbin/${name}" +command_args="-d" + +start_precmd="zebra_precmd" +socket_dir=@localstatedir@ +pidfile="${socket_dir}/${name}.pid" + +zebra_precmd() +{ + rc_flags="$( + set -- $rc_flags + while [ $# -ne 0 ]; do + if [ X"$1" = X-P -o X"$1" = X-A ]; then + break + fi + shift + done + if [ $# -eq 0 ]; then + echo "-P 0" + fi + ) $rc_flags" +} + +load_rc_config $name +run_rc_command "$1" diff --git a/pkgsrc/ospfd.sh.in b/pkgsrc/ospfd.sh.in new file mode 100644 index 0000000..daa2252 --- /dev/null +++ b/pkgsrc/ospfd.sh.in @@ -0,0 +1,44 @@ +#!/bin/sh +# +# ospfd is part of the quagga routing beast +# +# PROVIDE: ospfd +# REQUIRE: zebra +## + +PATH=/sbin:/bin:/usr/sbin:/usr/bin:@prefix@/sbin:@prefix@/bin +export PATH + +if [ -f /etc/rc.subr ] +then + . /etc/rc.subr +fi + +name="ospfd" +rcvar=$name +required_files="@sysconfdir@/${name}.conf" +command="@prefix@/sbin/${name}" +command_args="-d" + +start_precmd="zebra_precmd" +socket_dir=@localstatedir@ +pidfile="${socket_dir}/${name}.pid" + +zebra_precmd() +{ + rc_flags="$( + set -- $rc_flags + while [ $# -ne 0 ]; do + if [ X"$1" = X-P -o X"$1" = X-A ]; then + break + fi + shift + done + if [ $# -eq 0 ]; then + echo "-P 0" + fi + ) $rc_flags" +} + +load_rc_config $name +run_rc_command "$1" diff --git a/pkgsrc/ripd.sh.in b/pkgsrc/ripd.sh.in new file mode 100644 index 0000000..3157541 --- /dev/null +++ b/pkgsrc/ripd.sh.in @@ -0,0 +1,44 @@ +#!/bin/sh +# +# ripd is part of the quagga routing beast +# +# PROVIDE: ripd +# REQUIRE: zebra +## + +PATH=/sbin:/bin:/usr/sbin:/usr/bin:@prefix@/sbin:@prefix@/bin +export PATH + +if [ -f /etc/rc.subr ] +then + . /etc/rc.subr +fi + +name="ripd" +rcvar=$name +required_files="@sysconfdir@/${name}.conf" +command="@prefix@/sbin/${name}" +command_args="-d" + +start_precmd="zebra_precmd" +socket_dir=@localstatedir@ +pidfile="${socket_dir}/${name}.pid" + +zebra_precmd() +{ + rc_flags="$( + set -- $rc_flags + while [ $# -ne 0 ]; do + if [ X"$1" = X-P -o X"$1" = X-A ]; then + break + fi + shift + done + if [ $# -eq 0 ]; then + echo "-P 0" + fi + ) $rc_flags" +} + +load_rc_config $name +run_rc_command "$1" diff --git a/pkgsrc/ripngd.sh.in b/pkgsrc/ripngd.sh.in new file mode 100644 index 0000000..d06ac90 --- /dev/null +++ b/pkgsrc/ripngd.sh.in @@ -0,0 +1,44 @@ +#!/bin/sh +# +# ripngd is part of the quagga routing beast +# +# PROVIDE: ripngd +# REQUIRE: zebra +## + +PATH=/sbin:/bin:/usr/sbin:/usr/bin:@prefix@/sbin:@prefix@/bin +export PATH + +if [ -f /etc/rc.subr ] +then + . /etc/rc.subr +fi + +name="ripngd" +rcvar=$name +required_files="@sysconfdir@/${name}.conf" +command="@prefix@/sbin/${name}" +command_args="-d" + +start_precmd="zebra_precmd" +socket_dir=@localstatedir@ +pidfile="${socket_dir}/${name}.pid" + +zebra_precmd() +{ + rc_flags="$( + set -- $rc_flags + while [ $# -ne 0 ]; do + if [ X"$1" = X-P -o X"$1" = X-A ]; then + break + fi + shift + done + if [ $# -eq 0 ]; then + echo "-P 0" + fi + ) $rc_flags" +} + +load_rc_config $name +run_rc_command "$1" diff --git a/pkgsrc/zebra.sh.in b/pkgsrc/zebra.sh.in new file mode 100644 index 0000000..c2f12a7 --- /dev/null +++ b/pkgsrc/zebra.sh.in @@ -0,0 +1,55 @@ +#!/bin/sh +# +# zebra is the head of the quagga routing beast +# +# PROVIDE: zebra +# REQUIRE: NETWORKING +## + +PATH=/sbin:/bin:/usr/sbin:/usr/bin:@prefix@/sbin:@prefix@/bin +export PATH + +if [ -f /etc/rc.subr ] +then + . /etc/rc.subr +fi + +name="zebra" +rcvar=$name +required_files="@sysconfdir@/${name}.conf" +command="@prefix@/sbin/${name}" +command_args="-d" + +start_precmd="zebra_precmd" +stop_postcmd="zebra_postcmd" +socket_dir=@localstatedir@ +pidfile="${socket_dir}/${name}.pid" + +zebra_precmd() +{ + mkdir -p "${socket_dir}" + chown quagga.quagga "${socket_dir}" + chmod 750 "${socket_dir}" + rc_flags="$( + set -- $rc_flags + while [ $# -ne 0 ]; do + if [ X"$1" = X-P -o X"$1" = X-A ]; then + break + fi + shift + done + if [ $# -eq 0 ]; then + echo "-P 0" + fi + ) $rc_flags" +} + +zebra_postcmd() +{ + if [ -d "${socketdir}" ]; then + rmdir ${socketdir} + fi +} + +load_rc_config $name +run_rc_command "$1" diff --git a/ports/.gitignore b/ports/.gitignore new file mode 100644 index 0000000..dd5bf7c --- /dev/null +++ b/ports/.gitignore @@ -0,0 +1,6 @@ +.arch-inventory +.arch-ids + +*~ +*.loT + diff --git a/ports/Makefile b/ports/Makefile new file mode 100644 index 0000000..86f77bd --- /dev/null +++ b/ports/Makefile @@ -0,0 +1,59 @@ +# New ports collection makefile for: zebra +# Version required: 2.1.5 +# Date created: 28 Feb 1998 +# Whom: seirios@matrix.iri.co.jp +# + +#DISTNAME= zebra-980224 +DISTNAME= zebra-current +PKGNAME= zebra +CATEGORIES= net +MASTER_SITES= ftp://ftp.zebra.org/pub/zebra/ + +MAINTAINER= seirios@matrix.iri.co.jp + +WRKSRC= ${WRKDIR}/zebra-current + +#### Under constructing, We cannot support md5 +NO_CHECKSUM= yes + +do-build: + @(cd ${WRKSRC}; sh ./configure; make) + +post-install: + @if [ ! -f ${PREFIX}/etc/rc.d/zebra.sh ]; then \ + echo "Installing ${PREFIX}/etc/rc.d/zebra.sh startup file."; \ + echo "#!/bin/sh" > ${PREFIX}/etc/rc.d/zebra.sh; \ + echo "# zebra" >> ${PREFIX}/etc/rc.d/zebra.sh; \ + echo "if [ -x /usr/local/sbin/zebra -a ! -f /var/run/zebra.pid -a -f /usr/local/etc/zebra.conf ]; then" >> ${PREFIX}/etc/rc.d/zebra.sh; \ + echo " /usr/local/sbin/zebra -d -f /usr/local/etc/zebra.conf" >> ${PREFIX}/etc/rc.d/zebra.sh; \ + echo " echo -n ' zebra'" >> ${PREFIX}/etc/rc.d/zebra.sh; \ + echo "fi" >> ${PREFIX}/etc/rc.d/zebra.sh; \ + echo "# bgpd" >> ${PREFIX}/etc/rc.d/zebra.sh; \ + echo "if [ -x /usr/local/sbin/bgpd -a ! -f /var/run/bgpd.pid -a -f /usr/local/etc/bgpd.conf ]; then" >> ${PREFIX}/etc/rc.d/zebra.sh; \ + echo " /usr/local/sbin/bgpd -d -f /usr/local/etc/bgpd.conf" >> ${PREFIX}/etc/rc.d/zebra.sh; \ + echo " echo -n ' bgpd'" >> ${PREFIX}/etc/rc.d/zebra.sh; \ + echo "fi" >> ${PREFIX}/etc/rc.d/zebra.sh; \ + echo "# ripd" >> ${PREFIX}/etc/rc.d/zebra.sh; \ + echo "if [ -x /usr/local/sbin/ripd -a ! -f /var/run/ripd.pid -a -f /usr/local/etc/ripd.conf ]; then" >> ${PREFIX}/etc/rc.d/zebra.sh; \ + echo " /usr/local/sbin/ripd -d -f /usr/local/etc/ripd.conf" >> ${PREFIX}/etc/rc.d/zebra.sh; \ + echo " echo -n ' ripd'" >> ${PREFIX}/etc/rc.d/zebra.sh; \ + echo "fi" >> ${PREFIX}/etc/rc.d/zebra.sh; \ + echo "# ripngd" >> ${PREFIX}/etc/rc.d/zebra.sh; \ + echo "if [ -x /usr/local/sbin/ripngd -a ! -f /var/run/ripd.pid -a -f /usr/local/etc/ripd.conf ]; then" >> ${PREFIX}/etc/rc.d/zebra.sh; \ + echo " /usr/local/sbin/ripngd -d -f /usr/local/etc/ripd.conf" >> ${PREFIX}/etc/rc.d/zebra.sh; \ + echo " echo -n ' ripngd'" >> ${PREFIX}/etc/rc.d/zebra.sh; \ + echo "fi" >> ${PREFIX}/etc/rc.d/zebra.sh; \ + chmod 751 ${PREFIX}/etc/rc.d/zebra.sh; \ + fi + @echo "If you will access zebra,bgpd,ripd,ripngd with telnet,"; + @echo "then you add some line (written under this line) to /etc/services"; + @echo " zebrasrv 2600/tcp # zebra service"; + @echo " zebra 2601/tcp # zebra vty"; + @echo " ripd 2602/tcp # RIPd vty"; + @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/README b/ports/README new file mode 100644 index 0000000..a650eaa --- /dev/null +++ b/ports/README @@ -0,0 +1 @@ +This directory contain files for making FreeBSD package. diff --git a/ports/files/.gitignore b/ports/files/.gitignore new file mode 100644 index 0000000..dd5bf7c --- /dev/null +++ b/ports/files/.gitignore @@ -0,0 +1,6 @@ +.arch-inventory +.arch-ids + +*~ +*.loT + diff --git a/ports/files/md5 b/ports/files/md5 new file mode 100644 index 0000000..520c348 --- /dev/null +++ b/ports/files/md5 @@ -0,0 +1 @@ +MD5 (zebra-980224.tar.gz) = c6887645741200c43341156c168c7034 diff --git a/ports/pkg/.gitignore b/ports/pkg/.gitignore new file mode 100644 index 0000000..dd5bf7c --- /dev/null +++ b/ports/pkg/.gitignore @@ -0,0 +1,6 @@ +.arch-inventory +.arch-ids + +*~ +*.loT + diff --git a/ports/pkg/COMMENT b/ports/pkg/COMMENT new file mode 100644 index 0000000..53c55e3 --- /dev/null +++ b/ports/pkg/COMMENT @@ -0,0 +1 @@ +Zebra Routing protocol daemon diff --git a/ports/pkg/DESCR b/ports/pkg/DESCR new file mode 100644 index 0000000..aeb1950 --- /dev/null +++ b/ports/pkg/DESCR @@ -0,0 +1,76 @@ +============= +WHAT IS ZEBRA +============= + Zebra 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. + + Currently zebra is still under development, so If you want to use zebra, +I strongly recommend you to get the latest version of zebra. + Zebra snapshot is released on every monday. + +=================== +SUPPORTED Protocols +=================== + Zebra supports both IPv4 and IPv6 :-) + For supporting IPv4 Routing protocols is here + RIP (both version1 and version2) + RIPv2 supports both Multicast and Broadcast + BGP (only support BGP4) + + For supporting IPv6 Routing protocols is here + RIPng + BGP4+ + +=================== +Supported plat-home +=================== + Now zebra is testing on + o FreeBSD 2.2.8 + -- without IPv6 ;-) + -- with KAME + -- with INRIA IPv6 protocol stack. + + o GNU/Linux 2.2.2 + o GNU/Linux 2.0.36 + +=========== +ZEBRA Ports +=========== + Each daemon has each own terminal interface. Also zebra has communication +port which provides several services to other daemons. Below is zebra ports +list. + +zebrasrv 2600/tcp # zebra service +zebra 2601/tcp # zebra vty +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. + +==================== +For More Information +==================== + Web page is located at: + http://www.zebra.org/ + + Alpha version source file can be found at: + ftp://ftp.zebra.org/pub/zebra/ + + Mailing List is here + zebra@zebra.org + zebra-jp@zebra.org + + If you want to join zebra mailing list, mail to + majordomo@zebra.org + and you write + subscribe zebra + -- if you want to talk with English + subscribe zebra-jp + -- if you want to talk with Japanese + on Mail BODY (Not Subject). + +Enjoy. diff --git a/ports/pkg/PLIST b/ports/pkg/PLIST new file mode 100644 index 0000000..ccc69eb --- /dev/null +++ b/ports/pkg/PLIST @@ -0,0 +1,8 @@ +sbin/zebra +sbin/bgpd +sbin/ripd +etc/bgpd.conf.sample +etc/ripd.conf.sample +etc/zebra.conf.sample +etc/rc.d/zebra.sh +info/zebra.info diff --git a/qpb/.gitignore b/qpb/.gitignore new file mode 100644 index 0000000..b133c52 --- /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 0000000..0fbda61 --- /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 0000000..99ccd05 --- /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 0000000..e3ebbc6 --- /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 0000000..1b2b47f --- /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 0000000..55c1deb --- /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 0000000..8323d3e --- /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 0000000..4b4830a --- /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 0000000..83ddf56 --- /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/.gitignore b/redhat/.gitignore new file mode 100644 index 0000000..e399bd8 --- /dev/null +++ b/redhat/.gitignore @@ -0,0 +1,10 @@ +zebra.spec +quagga.spec +Makefile +Makefile.in +.nfs* +.arch-inventory +.arch-ids +*~ +*.loT + diff --git a/redhat/Makefile.am b/redhat/Makefile.am new file mode 100644 index 0000000..b1d49ac --- /dev/null +++ b/redhat/Makefile.am @@ -0,0 +1,9 @@ + +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 0000000..3e8fa05 --- /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 new file mode 100644 index 0000000..e18511a --- /dev/null +++ b/redhat/bgpd.init @@ -0,0 +1,72 @@ +#!/bin/bash +# 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 + +# Get network config +. /etc/sysconfig/network + +# quagga command line options +. /etc/sysconfig/quagga + +RETVAL=0 +PROG="bgpd" +cmd=bgpd +LOCK_FILE=/var/lock/subsys/bgpd +CONF_FILE=/etc/quagga/bgpd.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 $BGPD_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/bgpd.service b/redhat/bgpd.service new file mode 100644 index 0000000..ef24841 --- /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 new file mode 100644 index 0000000..9e80530 --- /dev/null +++ b/redhat/isisd.init @@ -0,0 +1,72 @@ +#!/bin/bash +# 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 + +# quagga command line options +. /etc/sysconfig/quagga + +RETVAL=0 +PROG="isisd" +cmd=isisd +LOCK_FILE=/var/lock/subsys/isisd +CONF_FILE=/etc/quagga/isisd.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 $ISISD_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/isisd.service b/redhat/isisd.service new file mode 100644 index 0000000..edb6eea --- /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 0000000..63f138c --- /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 new file mode 100644 index 0000000..4133b4a --- /dev/null +++ b/redhat/ospf6d.init @@ -0,0 +1,72 @@ +#!/bin/bash +# 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 + +# Get network config +. /etc/sysconfig/network + +# quagga command line options +. /etc/sysconfig/quagga + +RETVAL=0 +PROG="ospf6d" +cmd=ospf6d +LOCK_FILE=/var/lock/subsys/ospf6d +CONF_FILE=/etc/quagga/ospf6d.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 $OSPF6D_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/ospf6d.service b/redhat/ospf6d.service new file mode 100644 index 0000000..b53b970 --- /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 new file mode 100644 index 0000000..d964f38 --- /dev/null +++ b/redhat/ospfd.init @@ -0,0 +1,72 @@ +#!/bin/bash +# 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 + +# Get network config +. /etc/sysconfig/network + +# quagga command line options +. /etc/sysconfig/quagga + +RETVAL=0 +PROG="ospfd" +cmd=ospfd +LOCK_FILE=/var/lock/subsys/ospfd +CONF_FILE=/etc/quagga/ospfd.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 $OSPFD_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/ospfd.service b/redhat/ospfd.service new file mode 100644 index 0000000..5d6c5bb --- /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 0000000..49f9075 --- /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 0000000..d62fe64 --- /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 0000000..db82cfd --- /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 0000000..8974b64 --- /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 new file mode 100644 index 0000000..afbd40c --- /dev/null +++ b/redhat/quagga.logrotate @@ -0,0 +1,55 @@ +/var/log/quagga/zebra.log { + notifempty + missingok + postrotate + /bin/kill -USR1 `cat /var/run/quagga/zebra.pid 2> /dev/null` 2> /dev/null || true + endscript +} + +/var/log/quagga/bgpd.log { + notifempty + missingok + postrotate + /bin/kill -USR1 `cat /var/run/quagga/bgpd.pid 2> /dev/null` 2> /dev/null || true + 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 + postrotate + /bin/kill -USR1 `cat /var/run/quagga/ospfd.pid 2> /dev/null` 2> /dev/null || true + endscript +} + +/var/log/quagga/ospf6d.log { + notifempty + missingok + postrotate + /bin/kill -USR1 `cat /var/run/quagga/ospf6d.pid 2> /dev/null` 2> /dev/null || true + endscript +} + +/var/log/quagga/ripd.log { + notifempty + missingok + postrotate + /bin/kill -USR1 `cat /var/run/quagga/ripd.pid 2> /dev/null` 2> /dev/null || true + endscript +} + +/var/log/quagga/ripngd.log { + notifempty + missingok + postrotate + /bin/kill -USR1 `cat /var/run/quagga/ripngd.pid 2> /dev/null` 2> /dev/null || true + endscript +} diff --git a/redhat/quagga.pam b/redhat/quagga.pam new file mode 100644 index 0000000..9a91ad8 --- /dev/null +++ b/redhat/quagga.pam @@ -0,0 +1,26 @@ +#%PAM-1.0 +# + +##### if running quagga as root: +# Only allow root (and possibly wheel) to use this because enable access +# is unrestricted. +auth sufficient pam_rootok.so + +# Uncomment the following line to implicitly trust users in the "wheel" group. +#auth sufficient pam_wheel.so trust use_uid +# Uncomment the following line to require a user to be in the "wheel" group. +#auth required 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 pam_securetty.so +#auth include system-auth +#auth required pam_nologin.so +#account include system-auth +#password include system-auth +#session include system-auth +#session optional pam_console.so diff --git a/redhat/quagga.spec.in b/redhat/quagga.spec.in new file mode 100644 index 0000000..3bf5b9d --- /dev/null +++ b/redhat/quagga.spec.in @@ -0,0 +1,799 @@ +# configure options +# +# 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: %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}-%{quaggaversion} +%define zeb_rh_src %{zeb_src}/redhat +%define zeb_docs %{zeb_src}/doc + +# defines for configure +%define _localstatedir /var/run/quagga +############################################################################ + +#### Version String tweak +# Remove invalid characters form version string and replace with _ +%{expand: %%global rpmversion %(echo '@VERSION@' | tr [:blank:]- _ )} +%define quaggaversion @VERSION@ + +#### 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 + +#### 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: %global quagga_uid 92 } +%{!?quagga_gid: %global quagga_gid 92 } +%{!?vty_gid: %global vty_gid 85 } + +%define daemon_list zebra ripd ospfd bgpd + +%define daemonv6_list ripngd ospf6d + +%if %{with_isisd} +%define daemon_isisd isisd +%else +%define daemon_isisd "" +%endif + +%if %{with_pimd} +%define daemon_pimd pimd +%else +%define daemon_pimd "" +%endif + +%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: %global keep_build 0 } + +#release sub-revision (the two digits after the CONFDATE) +%{!?release_rev: %global release_rev 01 } + +Summary: Routing daemon +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 +Requires: net-snmp +%endif +%if %{with_vtysh} +BuildRequires: readline readline-devel ncurses ncurses-devel +Requires: ncurses +%endif +%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 +Requires(pre): initscripts >= 5.60 +%endif +Provides: routingdaemon = %{version}-%{release} +BuildRoot: %{_tmppath}/%{name}-%{version}-root +#Obsoletes: mrt zebra quagga-sysvinit + +%define __perl_requires %{zeb_rh_src}/quagga-filter-perl-requires.sh + +%description +Quagga is a free software routing protocol suite. + +Quagga supports BGP, OSPFv2, OSPFv3, ISIS, RIP, RIPng, PIM-SSM and NHRP. + +%package contrib +Summary: contrib tools for quagga +Group: System Environment/Daemons + +%description contrib +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 -n quagga-%{quaggaversion} + +%build + +%configure \ + --sysconfdir=%{_sysconfdir} \ + --libdir=%{_libdir}/quagga \ + --libexecdir=%{_libexecdir} \ + --localstatedir=%{_localstatedir} \ + --disable-werror \ +%if !%{with_shared} + --disable-shared \ +%endif +%if %{with_snmp} + --enable-snmp \ +%endif +%if %{with_multipath} + --enable-multipath=%{with_multipath} \ +%endif +%if %{with_tcp_zebra} + --enable-tcp-zebra \ +%endif +%if %{with_vtysh} + --enable-vtysh \ +%endif +%if %{with_ospfclient} + --enable-ospfclient=yes \ +%else + --enable-ospfclient=no\ +%endif +%if %{with_ospfapi} + --enable-ospfapi=yes \ +%else + --enable-ospfapi=no \ +%endif +%if %{with_irdp} + --enable-irdp=yes \ +%else + --enable-irdp=no \ +%endif +%if %{with_rtadv} + --enable-rtadv=yes \ +%else + --enable-rtadv=no \ +%endif +%if %{with_isisd} + --enable-isisd \ +%else + --disable-isisd \ +%endif +%if %{with_nhrpd} + --enable-nhrpd \ +%else + --disable-nhrpd \ +%endif +%if %{with_pam} + --with-libpam \ +%endif +%if 0%{?quagga_user:1} + --enable-user=%quagga_user \ + --enable-group=%quagga_user \ +%endif +%if 0%{?vty_group:1} + --enable-vty-group=%vty_group \ +%endif +%if %{with_fpm} + --enable-fpm \ +%else + --disable-fpm \ +%endif +%if %{with_watchquagga} + --enable-watchquagga \ +%else + --disable-watchquagga \ +%endif + --enable-gcc-rdynamic + +make %{?_smp_mflags} + +%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 +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 %{buildroot}/usr/share/info/dir + +# 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 \ + %{buildroot}/etc/rc.d/init.d/${daemon} + fi +done +%endif + +install -m644 %{zeb_rh_src}/quagga.pam \ + %{buildroot}/etc/pam.d/quagga +install -m644 %{zeb_rh_src}/quagga.logrotate \ + %{buildroot}/etc/logrotate.d/quagga +install -m644 %{zeb_rh_src}/quagga.sysconfig \ + %{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 0%{?vty_group:1} +if getent group %vty_group > /dev/null ; then : ; else \ + /usr/sbin/groupadd -r -g %vty_gid %vty_group > /dev/null || : ; fi +%endif + +# add quagga user and group +%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 || : ; \ +fi +if getent passwd %quagga_user >/dev/null ; then : ; else \ + /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 + +%post +# zebra_spec_add_service +# e.g. zebra_spec_add_service zebrasrv 2600/tcp "zebra service" + +zebra_spec_add_service () +{ + # Add port /etc/services entry if it isn't already there + if [ -f /etc/services ] && \ + ! %__sed -e 's/#.*$//' /etc/services | %__grep -wq $1 ; then + echo "$1 $2 # $3" >> /etc/services + fi +} + +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" +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" +%endif +%if %{with_isisd} +zebra_spec_add_service isisd 2608/tcp "ISISd vty" +%endif +%if %{with_pimd} +zebra_spec_add_service pimd 2611/tcp "PIMd vty" +%endif +%if %{with_nhrpd} +zebra_spec_add_service nhrpd 2612/tcp "NHRPd vty" +%endif + +%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 + +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 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 + else + eval restart_${daemon}=no + fi + done + # Rename restart flags for daemons handled specially. + running_zebra="$restart_zebra" + restart_zebra=no + %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 + +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 !0%{?keep_build:1} +rm -rf %{buildroot} +%endif + +%files +%defattr(-,root,root) +%doc */*.sample* AUTHORS COPYING +%if %{with_texi2html} + %doc doc/quagga.html +%endif +%doc doc/mpls +%doc ChangeLog INSTALL NEWS README REPORTING-BUGS SERVICES TODO +%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(750,root,root) /var/run/quagga +%endif +%if 0%{?vty_group:1} +%attr(750,%quagga_user,%vty_group) %{_sysconfdir}/vtysh.conf.sample +%endif +%{_infodir}/quagga.info*.gz +%{_mandir}/man*/* +%if %{with_watchquagga} + %{_mandir}/man*/watchquagga.* +%endif +%{_sbindir}/zebra +%{_sbindir}/ospfd +%{_sbindir}/ripd +%{_sbindir}/bgpd +%if %{with_watchquagga} + %{_sbindir}/watchquagga +%endif +%{_sbindir}/ripngd +%{_sbindir}/ospf6d +%if %{with_pimd} +%{_sbindir}/pimd +%endif +%if %{with_isisd} +%{_sbindir}/isisd +%endif +%if %{with_nhrpd} +%{_sbindir}/nhrpd +%endif +%if %{with_shared} +%{_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]* +%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 AUTHORS COPYING %attr(0644,root,root) tools + +%files devel +%defattr(-,root,root) +%doc AUTHORS COPYING +%if %{with_ospfclient} +%{_sbindir}/ospfclient +%endif +%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 +%{_includedir}/%name/ospfd/*.h +%if %{with_ospfapi} +%dir %attr(755,root,root) %{_includedir}/%{name}/ospfapi +%{_includedir}/%name/ospfapi/*.h +%endif + +%changelog +* 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 +- Readline need not be an explicit prerequisite +- install-info delete should be postun, not preun + +* Wed Jan 12 2005 Andrew J. Schorr +- on package upgrade, implement careful, phased restart logic +- use gcc -rdynamic flag when linking for better backtraces + +* Wed Dec 22 2004 Andrew J. Schorr +- daemonv6_list should contain only IPv6 daemons + +* Wed Dec 22 2004 Andrew J. Schorr +- watchquagga added +- on upgrade, all daemons should be condrestart'ed +- on removal, all daemons should be stopped + +* Mon Nov 08 2004 Paul Jakma +- Use makeinfo --html to generate quagga.html + +* Sun Nov 07 2004 Paul Jakma +- Fix with_ipv6 set to 0 build + +* Sat Oct 23 2004 Paul Jakma +- Update to 0.97.2 + +* Sat Oct 23 2004 Andrew J. Schorr +- Make directories be owned by the packages concerned +- Update logrotate scripts to use correct path to killall and use pid files + +* Fri Oct 08 2004 Paul Jakma +- Update to 0.97.0 + +* Wed Sep 15 2004 Paul Jakma +- build snmp support by default +- build irdp support +- build with shared libs +- devel subpackage for archives and headers + +* Thu Jan 08 2004 Paul Jakma +- updated sysconfig files to specify local dir +- added ospf_dump.c crash quick fix patch +- added ospfd persistent interface configuration patch + +* Tue Dec 30 2003 Paul Jakma +- sync to CVS +- integrate RH sysconfig patch to specify daemon options (RH) +- default to have vty listen only to 127.1 (RH) +- 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) +- isisd added +- cleanup tasks carried out for every daemon + +* Sun Nov 2 2003 Paul Jakma +- Fix -devel package to include all files +- Sync to 0.96.4 + +* Tue Aug 12 2003 Paul Jakma +- Renamed to Quagga +- Sync to Quagga release 0.96 + +* Thu Mar 20 2003 Paul Jakma +- zebra privileges support + +* 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 + +* Sat Mar 1 2003 Paul Jakma +- ospfd IOS prefix to interface matching for 'network' statement +- temporary fix for PtP and IPv6 +- sync to zebra.org CVS + +* Mon Jan 20 2003 Paul Jakma +- update to latest cvs +- Yon's "show thread cpu" patch - 17217 +- walk up tree - 17218 +- ospfd NSSA fixes - 16681 +- ospfd nsm fixes - 16824 +- ospfd OLSA fixes and new feature - 16823 +- KAME and ifindex fixes - 16525 +- spec file changes to allow redhat files to be in tree + +* Sat Dec 28 2002 Alexander Hoogerhuis +- 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 + +* Mon Nov 11 2002 Paul Jakma +- update to latest CVS +- add Greg Troxel's md5 buffer copy/dup fix +- add RIPv1 fix +- add Frank's multicast flag fix + +* Wed Oct 09 2002 Paul Jakma +- update to latest CVS +- timestamped crypt_seqnum patch +- oi->on_write_q fix + +* Mon Sep 30 2002 Paul Jakma +- update to latest CVS +- add vtysh 'write-config (integrated|daemon)' patch +- always 'make rebuild' in vtysh/ to catch new commands + +* Fri Sep 13 2002 Paul Jakma +- update to 0.93b + +* Wed Sep 11 2002 Paul Jakma +- update to latest CVS +- add "/sbin/ip route flush proto zebra" to zebra RH init on startup + +* Sat Aug 24 2002 Paul Jakma +- update to current CVS +- add OSPF point to multipoint patch +- add OSPF bugfixes +- add BGP hash optimisation patch + +* Fri Jun 14 2002 Paul Jakma +- update to 0.93-pre1 / CVS +- add link state detection support +- add generic PtP and RFC3021 support +- various bug fixes + +* Thu Aug 09 2001 Elliot Lee 0.91a-6 +- Fix bug #51336 + +* Wed Aug 1 2001 Trond Eivind Glomsrød 0.91a-5 +- Use generic initscript strings instead of initscript specific + ( "Starting foo: " -> "Starting $prog:" ) + +* Fri Jul 27 2001 Elliot Lee 0.91a-4 +- Bump the release when rebuilding into the dist. + +* Tue Feb 6 2001 Tim Powers +- built for Powertools + +* Sun Feb 4 2001 Pekka Savola +- Hacked up from PLD Linux 0.90-1, Mandrake 0.90-1mdk and one from zebra.org. +- Update to 0.91a +- Very heavy modifications to init.d/*, .spec, pam, i18n, logrotate, etc. +- Should be quite Red Hat'isque now. diff --git a/redhat/quagga.sysconfig b/redhat/quagga.sysconfig new file mode 100644 index 0000000..caa0fff --- /dev/null +++ b/redhat/quagga.sysconfig @@ -0,0 +1,25 @@ +# +# Default: Bind all daemon vtys to the loopback(s) only +# +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 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 new file mode 100644 index 0000000..9b412cb --- /dev/null +++ b/redhat/ripd.init @@ -0,0 +1,72 @@ +#!/bin/bash +# 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 + +# Get network config +. /etc/sysconfig/network + +# quagga command line options +. /etc/sysconfig/quagga + +RETVAL=0 +PROG="ripd" +cmd=ripd +LOCK_FILE=/var/lock/subsys/ripd +CONF_FILE=/etc/quagga/ripd.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 $RIPD_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/ripd.service b/redhat/ripd.service new file mode 100644 index 0000000..ed7f922 --- /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 new file mode 100644 index 0000000..88f346f --- /dev/null +++ b/redhat/ripngd.init @@ -0,0 +1,72 @@ +#!/bin/bash +# 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 + +# Get network config +. /etc/sysconfig/network + +# quagga command line options +. /etc/sysconfig/quagga + +RETVAL=0 +PROG="ripngd" +cmd=ripngd +LOCK_FILE=/var/lock/subsys/ripngd +CONF_FILE=/etc/quagga/ripngd.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 $RIPNGD_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/ripngd.service b/redhat/ripngd.service new file mode 100644 index 0000000..2519b31 --- /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 new file mode 100644 index 0000000..dda3506 --- /dev/null +++ b/redhat/watchquagga.init @@ -0,0 +1,66 @@ +#!/bin/bash +# chkconfig: 2345 17 83 + +### 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 + +# Get network config +. /etc/sysconfig/network + +# quagga command line options +. /etc/sysconfig/quagga + +RETVAL=0 +PROG="watchquagga" +cmd=watchquagga +LOCK_FILE=/var/lock/subsys/watchquagga + +case "$1" in + start) + # 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 $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/zebra.init b/redhat/zebra.init new file mode 100644 index 0000000..4242b16 --- /dev/null +++ b/redhat/zebra.init @@ -0,0 +1,73 @@ +#!/bin/bash +# 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 + +RETVAL=0 +PROG="zebra" +cmd=zebra +LOCK_FILE=/var/lock/subsys/zebra +CONF_FILE=/etc/quagga/zebra.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: " + /sbin/ip route flush proto zebra + daemon $cmd -d $ZEBRA_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/zebra.service b/redhat/zebra.service new file mode 100644 index 0000000..f9107f1 --- /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 0000000..d57ea98 --- /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/.gitignore b/ripd/.gitignore new file mode 100644 index 0000000..9bcfb63 --- /dev/null +++ b/ripd/.gitignore @@ -0,0 +1,17 @@ +Makefile +Makefile.in +*.o +ripd +ripd.conf +tags +TAGS +.deps +.nfs* +*.lo +*.la +*.libs +.arch-inventory +.arch-ids +*~ +*.loT +*.a diff --git a/ripd/Makefile.am b/ripd/Makefile.am new file mode 100644 index 0000000..571a499 --- /dev/null +++ b/ripd/Makefile.am @@ -0,0 +1,28 @@ +## Process this file with automake to produce Makefile.in. + +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 = $(WERROR) + +noinst_LIBRARIES = librip.a +sbin_PROGRAMS = ripd + +librip_a_SOURCES = \ + ripd.c rip_zebra.c rip_interface.c rip_debug.c rip_snmp.c \ + rip_routemap.c rip_peer.c rip_offset.c + +noinst_HEADERS = \ + ripd.h rip_debug.h rip_interface.h + +ripd_SOURCES = \ + rip_main.c $(librip_a_SOURCES) + +ripd_LDADD = ../lib/libzebra.la @LIBCAP@ + +examplesdir = $(exampledir) +dist_examples_DATA = ripd.conf.sample + +EXTRA_DIST = RIPv2-MIB.txt + diff --git a/ripd/RIPv2-MIB.txt b/ripd/RIPv2-MIB.txt new file mode 100644 index 0000000..6c92fb5 --- /dev/null +++ b/ripd/RIPv2-MIB.txt @@ -0,0 +1,530 @@ + RIPv2-MIB DEFINITIONS ::= BEGIN + + IMPORTS + MODULE-IDENTITY, OBJECT-TYPE, Counter32, + TimeTicks, IpAddress FROM SNMPv2-SMI + TEXTUAL-CONVENTION, RowStatus FROM SNMPv2-TC + MODULE-COMPLIANCE, OBJECT-GROUP FROM SNMPv2-CONF + mib-2 FROM RFC1213-MIB; + + -- This MIB module uses the extended OBJECT-TYPE macro as + -- defined in [9]. + + rip2 MODULE-IDENTITY + LAST-UPDATED "9407272253Z" -- Wed Jul 27 22:53:04 PDT 1994 + ORGANIZATION "IETF RIP-II Working Group" + CONTACT-INFO + " Fred Baker + Postal: Cisco Systems + 519 Lado Drive + Santa Barbara, California 93111 + Tel: +1 805 681 0115 + E-Mail: fbaker@cisco.com + + Postal: Gary Malkin + Xylogics, Inc. + 53 Third Avenue + Burlington, MA 01803 + + Phone: (617) 272-8140 + EMail: gmalkin@Xylogics.COM" + DESCRIPTION + "The MIB module to describe the RIP2 Version 2 Protocol" + ::= { mib-2 23 } + + -- RIP-2 Management Information Base + + -- the RouteTag type represents the contents of the + -- Route Domain field in the packet header or route entry. + -- The use of the Route Domain is deprecated. + + RouteTag ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "the RouteTag type represents the contents of the Route Domain + field in the packet header or route entry" + SYNTAX OCTET STRING (SIZE (2)) + +--4.1 Global Counters + +-- The RIP-2 Globals Group. +-- Implementation of this group is mandatory for systems +-- which implement RIP-2. + +-- These counters are intended to facilitate debugging quickly +-- changing routes or failing neighbors + +rip2Globals OBJECT IDENTIFIER ::= { rip2 1 } + + rip2GlobalRouteChanges OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of route changes made to the IP Route + Database by RIP. This does not include the refresh + of a route's age." + ::= { rip2Globals 1 } + + rip2GlobalQueries OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of responses sent to RIP queries + from other systems." + ::= { rip2Globals 2 } + +--4.2 RIP Interface Tables + +-- RIP Interfaces Groups +-- Implementation of these Groups is mandatory for systems +-- which implement RIP-2. + +-- The RIP Interface Status Table. + + rip2IfStatTable OBJECT-TYPE + SYNTAX SEQUENCE OF Rip2IfStatEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of subnets which require separate + status monitoring in RIP." + ::= { rip2 2 } + + rip2IfStatEntry OBJECT-TYPE + SYNTAX Rip2IfStatEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A Single Routing Domain in a single Subnet." + INDEX { rip2IfStatAddress } + ::= { rip2IfStatTable 1 } + + Rip2IfStatEntry ::= + SEQUENCE { + rip2IfStatAddress + IpAddress, + rip2IfStatRcvBadPackets + Counter32, + rip2IfStatRcvBadRoutes + Counter32, + rip2IfStatSentUpdates + Counter32, + rip2IfStatStatus + RowStatus + } + + rip2IfStatAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The IP Address of this system on the indicated + subnet. For unnumbered interfaces, the value 0.0.0.N, + where the least significant 24 bits (N) is the ifIndex + for the IP Interface in network byte order." + ::= { rip2IfStatEntry 1 } + + rip2IfStatRcvBadPackets OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of RIP response packets received by + the RIP process which were subsequently discarded + for any reason (e.g. a version 0 packet, or an + unknown command type)." + ::= { rip2IfStatEntry 2 } + + rip2IfStatRcvBadRoutes OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of routes, in valid RIP packets, + which were ignored for any reason (e.g. unknown + address family, or invalid metric)." + ::= { rip2IfStatEntry 3 } + + rip2IfStatSentUpdates OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of triggered RIP updates actually + sent on this interface. This explicitly does + NOT include full updates sent containing new + information." + ::= { rip2IfStatEntry 4 } + + rip2IfStatStatus OBJECT-TYPE + SYNTAX RowStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "Writing invalid has the effect of deleting + this interface." + ::= { rip2IfStatEntry 5 } + +-- The RIP Interface Configuration Table. + + rip2IfConfTable OBJECT-TYPE + SYNTAX SEQUENCE OF Rip2IfConfEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of subnets which require separate + configuration in RIP." + ::= { rip2 3 } + + rip2IfConfEntry OBJECT-TYPE + SYNTAX Rip2IfConfEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A Single Routing Domain in a single Subnet." + INDEX { rip2IfConfAddress } + ::= { rip2IfConfTable 1 } + + Rip2IfConfEntry ::= + SEQUENCE { + rip2IfConfAddress + IpAddress, + rip2IfConfDomain + RouteTag, + rip2IfConfAuthType + INTEGER, + rip2IfConfAuthKey + OCTET STRING (SIZE(0..16)), + rip2IfConfSend + INTEGER, + rip2IfConfReceive + INTEGER, + rip2IfConfDefaultMetric + INTEGER, + rip2IfConfStatus + RowStatus, + rip2IfConfSrcAddress + IpAddress + } + + rip2IfConfAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The IP Address of this system on the indicated + subnet. For unnumbered interfaces, the value 0.0.0.N, + where the least significant 24 bits (N) is the ifIndex + for the IP Interface in network byte order." + ::= { rip2IfConfEntry 1 } + + rip2IfConfDomain OBJECT-TYPE + SYNTAX RouteTag + MAX-ACCESS read-create + STATUS obsolete + DESCRIPTION + "Value inserted into the Routing Domain field + of all RIP packets sent on this interface." + DEFVAL { '0000'h } + ::= { rip2IfConfEntry 2 } + + rip2IfConfAuthType OBJECT-TYPE + SYNTAX INTEGER { + noAuthentication (1), + simplePassword (2), + md5 (3) + } + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The type of Authentication used on this + interface." + DEFVAL { noAuthentication } + ::= { rip2IfConfEntry 3 } + + rip2IfConfAuthKey OBJECT-TYPE + SYNTAX OCTET STRING (SIZE(0..16)) + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The value to be used as the Authentication Key + whenever the corresponding instance of + rip2IfConfAuthType has a value other than + noAuthentication. A modification of the corresponding + instance of rip2IfConfAuthType does not modify + the rip2IfConfAuthKey value. If a string shorter + than 16 octets is supplied, it will be left- + justified and padded to 16 octets, on the right, + with nulls (0x00). + + Reading this object always results in an OCTET + STRING of length zero; authentication may not + be bypassed by reading the MIB object." + DEFVAL { ''h } + ::= { rip2IfConfEntry 4 } + + rip2IfConfSend OBJECT-TYPE + SYNTAX INTEGER { + doNotSend (1), + ripVersion1 (2), + rip1Compatible (3), + ripVersion2 (4), + ripV1Demand (5), + ripV2Demand (6) + } + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "What the router sends on this interface. + ripVersion1 implies sending RIP updates compliant + with RFC 1058. rip1Compatible implies + broadcasting RIP-2 updates using RFC 1058 route + subsumption rules. ripVersion2 implies + multicasting RIP-2 updates. ripV1Demand indicates + the use of Demand RIP on a WAN interface under RIP + Version 1 rules. ripV2Demand indicates the use of + Demand RIP on a WAN interface under Version 2 rules." + DEFVAL { rip1Compatible } + ::= { rip2IfConfEntry 5 } + + rip2IfConfReceive OBJECT-TYPE + SYNTAX INTEGER { + rip1 (1), + rip2 (2), + rip1OrRip2 (3), + doNotRecieve (4) + } + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "This indicates which version of RIP updates + are to be accepted. Note that rip2 and + rip1OrRip2 implies reception of multicast + packets." + DEFVAL { rip1OrRip2 } + ::= { rip2IfConfEntry 6 } + + rip2IfConfDefaultMetric OBJECT-TYPE + SYNTAX INTEGER ( 0..15 ) + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "This variable indicates the metric that is to + be used for the default route entry in RIP updates + originated on this interface. A value of zero + indicates that no default route should be + originated; in this case, a default route via + another router may be propagated." + ::= { rip2IfConfEntry 7 } + + rip2IfConfStatus OBJECT-TYPE + SYNTAX RowStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "Writing invalid has the effect of deleting + this interface." + ::= { rip2IfConfEntry 8 } + + rip2IfConfSrcAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The IP Address this system will use as a source + address on this interface. If it is a numbered + interface, this MUST be the same value as + rip2IfConfAddress. On unnumbered interfaces, + it must be the value of rip2IfConfAddress for + some interface on the system." + ::= { rip2IfConfEntry 9 } + +--4.3 Peer Table + +-- Peer Table + +-- The RIP Peer Group +-- Implementation of this Group is Optional + +-- This group provides information about active peer +-- relationships intended to assist in debugging. An +-- active peer is a router from which a valid RIP +-- updated has been heard in the last 180 seconds. + + rip2PeerTable OBJECT-TYPE + SYNTAX SEQUENCE OF Rip2PeerEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of RIP Peers." + ::= { rip2 4 } + + rip2PeerEntry OBJECT-TYPE + SYNTAX Rip2PeerEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Information regarding a single routing peer." + INDEX { rip2PeerAddress, rip2PeerDomain } + ::= { rip2PeerTable 1 } + + Rip2PeerEntry ::= + SEQUENCE { + rip2PeerAddress + IpAddress, + rip2PeerDomain + RouteTag, + rip2PeerLastUpdate + TimeTicks, + rip2PeerVersion + INTEGER, + rip2PeerRcvBadPackets + Counter32, + rip2PeerRcvBadRoutes + Counter32 + } + + rip2PeerAddress OBJECT-TYPE + SYNTAX IpAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The IP Address that the peer is using as its source + address. Note that on an unnumbered link, this may + not be a member of any subnet on the system." + ::= { rip2PeerEntry 1 } + + rip2PeerDomain OBJECT-TYPE + SYNTAX RouteTag + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value in the Routing Domain field in RIP + packets received from the peer. As domain suuport + is deprecated, this must be zero." + ::= { rip2PeerEntry 2 } + + rip2PeerLastUpdate OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value of sysUpTime when the most recent + RIP update was received from this system." + ::= { rip2PeerEntry 3 } + + rip2PeerVersion OBJECT-TYPE + SYNTAX INTEGER ( 0..255 ) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The RIP version number in the header of the + last RIP packet received." + ::= { rip2PeerEntry 4 } + + rip2PeerRcvBadPackets OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of RIP response packets from this + peer discarded as invalid." + ::= { rip2PeerEntry 5 } + + + rip2PeerRcvBadRoutes OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of routes from this peer that were + ignored because the entry format was invalid." + ::= { rip2PeerEntry 6 } + +-- conformance information + +rip2Conformance OBJECT IDENTIFIER ::= { rip2 5 } + +rip2Groups OBJECT IDENTIFIER ::= { rip2Conformance 1 } +rip2Compliances OBJECT IDENTIFIER ::= { rip2Conformance 2 } + +-- compliance statements +rip2Compliance MODULE-COMPLIANCE + STATUS current + DESCRIPTION + "The compliance statement " + MODULE -- this module + MANDATORY-GROUPS { + rip2GlobalGroup, + rip2IfStatGroup, + rip2IfConfGroup, + rip2PeerGroup + } + GROUP rip2GlobalGroup + DESCRIPTION + "This group defines global controls for RIP-II systems." + GROUP rip2IfStatGroup + DESCRIPTION + "This group defines interface statistics for RIP-II systems." + GROUP rip2IfConfGroup + DESCRIPTION + "This group defines interface configuration for RIP-II systems." + GROUP rip2PeerGroup + DESCRIPTION + "This group defines peer information for RIP-II systems." + ::= { rip2Compliances 1 } + +-- units of conformance + +rip2GlobalGroup OBJECT-GROUP + OBJECTS { + rip2GlobalRouteChanges, + rip2GlobalQueries + } + STATUS current + DESCRIPTION + "This group defines global controls for RIP-II systems." + ::= { rip2Groups 1 } +rip2IfStatGroup OBJECT-GROUP + OBJECTS { + rip2IfStatAddress, + rip2IfStatRcvBadPackets, + rip2IfStatRcvBadRoutes, + rip2IfStatSentUpdates, + rip2IfStatStatus + } + STATUS current + DESCRIPTION + "This group defines interface statistics for RIP-II systems." + ::= { rip2Groups 2 } +rip2IfConfGroup OBJECT-GROUP + OBJECTS { + rip2IfConfAddress, + rip2IfConfAuthType, + rip2IfConfAuthKey, + rip2IfConfSend, + rip2IfConfReceive, + rip2IfConfDefaultMetric, + rip2IfConfStatus, + rip2IfConfSrcAddress + } + STATUS current + DESCRIPTION + "This group defines interface configuration for RIP-II systems." + ::= { rip2Groups 3 } +rip2PeerGroup OBJECT-GROUP + OBJECTS { + rip2PeerAddress, + rip2PeerDomain, + rip2PeerLastUpdate, + rip2PeerVersion, + rip2PeerRcvBadPackets, + rip2PeerRcvBadRoutes + } + STATUS current + DESCRIPTION + "This group defines peer information for RIP-II systems." + ::= { rip2Groups 4 } +END diff --git a/ripd/rip_debug.c b/ripd/rip_debug.c new file mode 100644 index 0000000..a267874 --- /dev/null +++ b/ripd/rip_debug.c @@ -0,0 +1,284 @@ +/* RIP debug routines + * 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 "command.h" +#include "ripd/rip_debug.h" + +/* For debug statement. */ +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", + SHOW_STR + DEBUG_STR + RIP_STR) +{ + vty_out (vty, "RIP debugging status:%s", VTY_NEWLINE); + + if (IS_RIP_DEBUG_EVENT) + vty_out (vty, " RIP event debugging is on%s", VTY_NEWLINE); + + if (IS_RIP_DEBUG_PACKET) + { + if (IS_RIP_DEBUG_SEND && IS_RIP_DEBUG_RECV) + { + vty_out (vty, " RIP packet debugging is on%s", + VTY_NEWLINE); + } + else + { + if (IS_RIP_DEBUG_SEND) + vty_out (vty, " RIP packet send debugging is on%s", + VTY_NEWLINE); + else + vty_out (vty, " RIP packet receive debugging is on%s", + VTY_NEWLINE); + } + } + + if (IS_RIP_DEBUG_ZEBRA) + vty_out (vty, " RIP zebra debugging is on%s", VTY_NEWLINE); + + return CMD_SUCCESS; +} + +DEFUN (debug_rip_events, + debug_rip_events_cmd, + "debug rip events", + DEBUG_STR + RIP_STR + "RIP events\n") +{ + rip_debug_event = RIP_DEBUG_EVENT; + return CMD_WARNING; +} + +DEFUN (debug_rip_packet, + debug_rip_packet_cmd, + "debug rip packet", + DEBUG_STR + RIP_STR + "RIP packet\n") +{ + rip_debug_packet = RIP_DEBUG_PACKET; + rip_debug_packet |= RIP_DEBUG_SEND; + rip_debug_packet |= RIP_DEBUG_RECV; + return CMD_SUCCESS; +} + +DEFUN (debug_rip_packet_direct, + debug_rip_packet_direct_cmd, + "debug rip packet (recv|send)", + DEBUG_STR + RIP_STR + "RIP packet\n" + "RIP receive packet\n" + "RIP send packet\n") +{ + rip_debug_packet |= RIP_DEBUG_PACKET; + if (strncmp ("send", argv[0], strlen (argv[0])) == 0) + rip_debug_packet |= RIP_DEBUG_SEND; + if (strncmp ("recv", argv[0], strlen (argv[0])) == 0) + rip_debug_packet |= RIP_DEBUG_RECV; + return CMD_SUCCESS; +} + +/* N.B. the "detail" modifier is a no-op. we leave this command + for legacy compatibility. */ +DEFUN_DEPRECATED (debug_rip_packet_detail, + debug_rip_packet_detail_cmd, + "debug rip packet (recv|send) detail", + DEBUG_STR + RIP_STR + "RIP packet\n" + "RIP receive packet\n" + "RIP send packet\n" + "Detailed information display\n") +{ + rip_debug_packet |= RIP_DEBUG_PACKET; + if (strncmp ("send", argv[0], strlen (argv[0])) == 0) + rip_debug_packet |= RIP_DEBUG_SEND; + if (strncmp ("recv", argv[0], strlen (argv[0])) == 0) + rip_debug_packet |= RIP_DEBUG_RECV; + return CMD_SUCCESS; +} + +DEFUN (debug_rip_zebra, + debug_rip_zebra_cmd, + "debug rip zebra", + DEBUG_STR + RIP_STR + "RIP and ZEBRA communication\n") +{ + rip_debug_zebra = RIP_DEBUG_ZEBRA; + return CMD_WARNING; +} + +DEFUN (no_debug_rip_events, + no_debug_rip_events_cmd, + "no debug rip events", + NO_STR + DEBUG_STR + RIP_STR + "RIP events\n") +{ + rip_debug_event = 0; + return CMD_SUCCESS; +} + +DEFUN (no_debug_rip_packet, + no_debug_rip_packet_cmd, + "no debug rip packet", + NO_STR + DEBUG_STR + RIP_STR + "RIP packet\n") +{ + rip_debug_packet = 0; + return CMD_SUCCESS; +} + +DEFUN (no_debug_rip_packet_direct, + no_debug_rip_packet_direct_cmd, + "no debug rip packet (recv|send)", + NO_STR + DEBUG_STR + RIP_STR + "RIP packet\n" + "RIP option set for receive packet\n" + "RIP option set for send packet\n") +{ + if (strncmp ("send", argv[0], strlen (argv[0])) == 0) + { + if (IS_RIP_DEBUG_RECV) + rip_debug_packet &= ~RIP_DEBUG_SEND; + else + rip_debug_packet = 0; + } + else if (strncmp ("recv", argv[0], strlen (argv[0])) == 0) + { + if (IS_RIP_DEBUG_SEND) + rip_debug_packet &= ~RIP_DEBUG_RECV; + else + rip_debug_packet = 0; + } + return CMD_SUCCESS; +} + +DEFUN (no_debug_rip_zebra, + no_debug_rip_zebra_cmd, + "no debug rip zebra", + NO_STR + DEBUG_STR + RIP_STR + "RIP and ZEBRA communication\n") +{ + rip_debug_zebra = 0; + return CMD_WARNING; +} + +/* Debug node. */ +static struct cmd_node debug_node = +{ + DEBUG_NODE, + "", /* Debug node has no interface. */ + 1 +}; + +static int +config_write_debug (struct vty *vty) +{ + int write = 0; + + if (IS_RIP_DEBUG_EVENT) + { + vty_out (vty, "debug rip events%s", VTY_NEWLINE); + write++; + } + if (IS_RIP_DEBUG_PACKET) + { + if (IS_RIP_DEBUG_SEND && IS_RIP_DEBUG_RECV) + { + vty_out (vty, "debug rip packet%s", + VTY_NEWLINE); + write++; + } + else + { + if (IS_RIP_DEBUG_SEND) + vty_out (vty, "debug rip packet send%s", + VTY_NEWLINE); + else + vty_out (vty, "debug rip packet recv%s", + VTY_NEWLINE); + write++; + } + } + if (IS_RIP_DEBUG_ZEBRA) + { + vty_out (vty, "debug rip zebra%s", VTY_NEWLINE); + write++; + } + return write; +} + +void +rip_debug_reset (void) +{ + rip_debug_event = 0; + rip_debug_packet = 0; + rip_debug_zebra = 0; +} + +void +rip_debug_init (void) +{ + rip_debug_event = 0; + rip_debug_packet = 0; + rip_debug_zebra = 0; + + install_node (&debug_node, config_write_debug); + + install_element (ENABLE_NODE, &show_debugging_rip_cmd); + install_element (ENABLE_NODE, &debug_rip_events_cmd); + install_element (ENABLE_NODE, &debug_rip_packet_cmd); + install_element (ENABLE_NODE, &debug_rip_packet_direct_cmd); + install_element (ENABLE_NODE, &debug_rip_packet_detail_cmd); + install_element (ENABLE_NODE, &debug_rip_zebra_cmd); + install_element (ENABLE_NODE, &no_debug_rip_events_cmd); + install_element (ENABLE_NODE, &no_debug_rip_packet_cmd); + install_element (ENABLE_NODE, &no_debug_rip_packet_direct_cmd); + install_element (ENABLE_NODE, &no_debug_rip_zebra_cmd); + + install_element (CONFIG_NODE, &debug_rip_events_cmd); + install_element (CONFIG_NODE, &debug_rip_packet_cmd); + install_element (CONFIG_NODE, &debug_rip_packet_direct_cmd); + install_element (CONFIG_NODE, &debug_rip_packet_detail_cmd); + install_element (CONFIG_NODE, &debug_rip_zebra_cmd); + install_element (CONFIG_NODE, &no_debug_rip_events_cmd); + install_element (CONFIG_NODE, &no_debug_rip_packet_cmd); + install_element (CONFIG_NODE, &no_debug_rip_packet_direct_cmd); + install_element (CONFIG_NODE, &no_debug_rip_zebra_cmd); +} diff --git a/ripd/rip_debug.h b/ripd/rip_debug.h new file mode 100644 index 0000000..990ec90 --- /dev/null +++ b/ripd/rip_debug.h @@ -0,0 +1,53 @@ +/* RIP debug routines + * 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 _ZEBRA_RIP_DEBUG_H +#define _ZEBRA_RIP_DEBUG_H + +/* RIP debug event flags. */ +#define RIP_DEBUG_EVENT 0x01 + +/* RIP debug packet flags. */ +#define RIP_DEBUG_PACKET 0x01 +#define RIP_DEBUG_SEND 0x20 +#define RIP_DEBUG_RECV 0x40 +#define RIP_DEBUG_DETAIL 0x80 + +/* RIP debug zebra flags. */ +#define RIP_DEBUG_ZEBRA 0x01 + +/* Debug related macro. */ +#define IS_RIP_DEBUG_EVENT (rip_debug_event & RIP_DEBUG_EVENT) + +#define IS_RIP_DEBUG_PACKET (rip_debug_packet & RIP_DEBUG_PACKET) +#define IS_RIP_DEBUG_SEND (rip_debug_packet & RIP_DEBUG_SEND) +#define IS_RIP_DEBUG_RECV (rip_debug_packet & RIP_DEBUG_RECV) + +#define IS_RIP_DEBUG_ZEBRA (rip_debug_zebra & RIP_DEBUG_ZEBRA) + +extern unsigned long rip_debug_event; +extern unsigned long rip_debug_packet; +extern unsigned long rip_debug_zebra; + +extern void rip_debug_init (void); +extern void rip_debug_reset (void); + +#endif /* _ZEBRA_RIP_DEBUG_H */ diff --git a/ripd/rip_interface.c b/ripd/rip_interface.c new file mode 100644 index 0000000..7521fc7 --- /dev/null +++ b/ripd/rip_interface.c @@ -0,0 +1,2111 @@ +/* Interface related function for RIP. + * 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 "command.h" +#include "if.h" +#include "sockunion.h" +#include "prefix.h" +#include "memory.h" +#include "network.h" +#include "table.h" +#include "log.h" +#include "stream.h" +#include "thread.h" +#include "zclient.h" +#include "filter.h" +#include "sockopt.h" +#include "privs.h" + +#include "zebra/connected.h" + +#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 *); +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); + +const struct message ri_version_msg[] = +{ + {RI_RIP_VERSION_1, "1"}, + {RI_RIP_VERSION_2, "2"}, + {RI_RIP_VERSION_1_AND_2, "1 2"}, +}; + +extern struct zebra_privs_t ripd_privs; + +/* RIP enabled network vector. */ +vector rip_enable_interface; + +/* RIP enabled interface table. */ +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, + ifindex_t ifindex) +{ + int ret; + + ret = setsockopt_ipv4_multicast (sock, + IP_ADD_MEMBERSHIP, + group.s_addr, + ifindex); + + if (ret < 0) + zlog (NULL, LOG_INFO, "can't setsockopt IP_ADD_MEMBERSHIP %s", + safe_strerror (errno)); + + return ret; +} + +/* Leave from the RIP version 2 multicast group. */ +static int +ipv4_multicast_leave (int sock, + struct in_addr group, + struct in_addr ifa, + ifindex_t ifindex) +{ + int ret; + + ret = setsockopt_ipv4_multicast (sock, + IP_DROP_MEMBERSHIP, + group.s_addr, + ifindex); + + if (ret < 0) + zlog (NULL, LOG_INFO, "can't setsockopt IP_DROP_MEMBERSHIP"); + + return ret; +} + +static void rip_interface_reset (struct rip_interface *); + +/* Allocate new RIP's interface configuration. */ +static struct rip_interface * +rip_interface_new (void) +{ + struct rip_interface *ri; + + ri = XCALLOC (MTYPE_RIP_INTERFACE, sizeof (struct rip_interface)); + + rip_interface_reset (ri); + + return ri; +} + +void +rip_interface_multicast_set (int sock, struct connected *connected) +{ + assert (connected != NULL); + + if (setsockopt_ipv4_multicast_if (sock, connected->ifp->ifindex) < 0) + { + zlog_warn ("Can't setsockopt IP_MULTICAST_IF on fd %d to " + "ifindex %d for interface %s", + sock, connected->ifp->ifindex, + connected->ifp->name); + } + + return; +} + +/* Send RIP request packet to specified interface. */ +static void +rip_request_interface_send (struct interface *ifp, u_char version) +{ + struct sockaddr_in to; + + /* RIPv2 support multicast. */ + if (version == RIPv2 && if_is_multicast (ifp)) + { + + if (IS_RIP_DEBUG_EVENT) + zlog_debug ("multicast request on %s", ifp->name); + + rip_request_send (NULL, ifp, version, NULL); + return; + } + + /* RIPv1 and non multicast interface. */ + if (if_is_pointopoint (ifp) || if_is_broadcast (ifp)) + { + struct listnode *cnode, *cnnode; + struct connected *connected; + + if (IS_RIP_DEBUG_EVENT) + zlog_debug ("broadcast request to %s", ifp->name); + + for (ALL_LIST_ELEMENTS (ifp->connected, cnode, cnnode, connected)) + { + if (connected->address->family == AF_INET) + { + memset (&to, 0, sizeof (struct sockaddr_in)); + to.sin_port = htons (RIP_PORT_DEFAULT); + if (connected->destination) + /* use specified broadcast or peer destination addr */ + to.sin_addr = connected->destination->u.prefix4; + else if (connected->address->prefixlen < IPV4_MAX_PREFIXLEN) + /* calculate the appropriate broadcast address */ + to.sin_addr.s_addr = + ipv4_broadcast_addr(connected->address->u.prefix4.s_addr, + connected->address->prefixlen); + else + /* do not know where to send the packet */ + continue; + + if (IS_RIP_DEBUG_EVENT) + zlog_debug ("SEND request to %s", inet_ntoa (to.sin_addr)); + + rip_request_send (&to, ifp, version, connected); + } + } + } +} + +/* This will be executed when interface goes up. */ +static void +rip_request_interface (struct interface *ifp) +{ + struct rip_interface *ri; + + /* In default ripd doesn't send RIP_REQUEST to the loopback interface. */ + if (if_is_loopback (ifp)) + return; + + /* If interface is down, don't send RIP packet. */ + if (! if_is_operative (ifp)) + return; + + /* Fetch RIP interface information. */ + ri = ifp->info; + + + /* If there is no version configuration in the interface, + use rip's version setting. */ + { + int vsend = ((ri->ri_send == RI_RIP_UNSPEC) ? + rip->version_send : ri->ri_send); + if (vsend & RIPv1) + rip_request_interface_send (ifp, RIPv1); + if (vsend & RIPv2) + rip_request_interface_send (ifp, RIPv2); + } +} + +#if 0 +/* Send RIP request to the neighbor. */ +static void +rip_request_neighbor (struct in_addr addr) +{ + struct sockaddr_in to; + + memset (&to, 0, sizeof (struct sockaddr_in)); + to.sin_port = htons (RIP_PORT_DEFAULT); + to.sin_addr = addr; + + rip_request_send (&to, NULL, rip->version_send, NULL); +} + +/* Request routes at all interfaces. */ +static void +rip_request_neighbor_all (void) +{ + struct route_node *rp; + + if (! rip) + return; + + if (IS_RIP_DEBUG_EVENT) + zlog_debug ("request to the all neighbor"); + + /* Send request to all neighbor. */ + for (rp = route_top (rip->neighbor); rp; rp = route_next (rp)) + if (rp->info) + rip_request_neighbor (rp->p.u.prefix4); +} +#endif + +/* Multicast packet receive socket. */ +static int +rip_multicast_join (struct interface *ifp, int sock) +{ + struct listnode *cnode; + struct connected *ifc; + + if (if_is_operative (ifp) && if_is_multicast (ifp)) + { + if (IS_RIP_DEBUG_EVENT) + zlog_debug ("multicast join at %s", ifp->name); + + for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, ifc)) + { + struct prefix_ipv4 *p; + struct in_addr group; + + p = (struct prefix_ipv4 *) ifc->address; + + if (p->family != AF_INET) + continue; + + group.s_addr = htonl (INADDR_RIP_GROUP); + if (ipv4_multicast_join (sock, group, p->prefix, ifp->ifindex) < 0) + return -1; + else + return 0; + } + } + return 0; +} + +/* Leave from multicast group. */ +static void +rip_multicast_leave (struct interface *ifp, int sock) +{ + struct listnode *cnode; + struct connected *connected; + + if (if_is_up (ifp) && if_is_multicast (ifp)) + { + if (IS_RIP_DEBUG_EVENT) + zlog_debug ("multicast leave from %s", ifp->name); + + for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, connected)) + { + struct prefix_ipv4 *p; + struct in_addr group; + + p = (struct prefix_ipv4 *) connected->address; + + if (p->family != AF_INET) + continue; + + group.s_addr = htonl (INADDR_RIP_GROUP); + if (ipv4_multicast_leave (sock, group, p->prefix, ifp->ifindex) == 0) + return; + } + } +} + +/* Is there and address on interface that I could use ? */ +static int +rip_if_ipv4_address_check (struct interface *ifp) +{ + struct listnode *nn; + struct connected *connected; + int count = 0; + + for (ALL_LIST_ELEMENTS_RO (ifp->connected, nn, connected)) + { + struct prefix *p; + + p = connected->address; + + if (p->family == AF_INET) + count++; + } + + return count; +} + + + + +/* Does this address belongs to me ? */ +int +if_check_address (struct in_addr addr) +{ + struct listnode *node; + struct interface *ifp; + + for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) + { + struct listnode *cnode; + struct connected *connected; + + for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, connected)) + { + struct prefix_ipv4 *p; + + p = (struct prefix_ipv4 *) connected->address; + + if (p->family != AF_INET) + continue; + + if (IPV4_ADDR_CMP (&p->prefix, &addr) == 0) + return 1; + } + } + return 0; +} + +/* Inteface link down message processing. */ +int +rip_interface_down (int command, struct zclient *zclient, 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, vrf_id); + + if (ifp == NULL) + return 0; + + rip_if_down(ifp); + + if (IS_RIP_DEBUG_ZEBRA) + zlog_debug ("interface %s index %d flags %llx metric %d mtu %d is down", + ifp->name, ifp->ifindex, (unsigned long long)ifp->flags, + ifp->metric, ifp->mtu); + + return 0; +} + +/* Inteface link up message processing */ +int +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, vrf_id); + + if (ifp == NULL) + return 0; + + if (IS_RIP_DEBUG_ZEBRA) + zlog_debug ("interface %s index %d flags %#llx metric %d mtu %d is up", + ifp->name, ifp->ifindex, (unsigned long long) ifp->flags, + ifp->metric, ifp->mtu); + + /* Check if this interface is RIP enabled or not.*/ + rip_enable_apply (ifp); + + /* Check for a passive interface */ + rip_passive_interface_apply (ifp); + + /* Apply distribute list to the all interface. */ + rip_distribute_update_interface (ifp); + + return 0; +} + +/* Inteface addition message from zebra. */ +int +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, vrf_id); + + if (IS_RIP_DEBUG_ZEBRA) + zlog_debug ("interface add %s index %d flags %#llx metric %d mtu %d", + ifp->name, ifp->ifindex, (unsigned long long) ifp->flags, + ifp->metric, ifp->mtu); + + /* Check if this interface is RIP enabled or not.*/ + rip_enable_apply (ifp); + + /* Check for a passive interface */ + rip_passive_interface_apply (ifp); + + /* Apply distribute list to the all interface. */ + rip_distribute_update_interface (ifp); + + /* rip_request_neighbor_all (); */ + + /* Check interface routemap. */ + rip_if_rmap_update_interface (ifp); + + return 0; +} + +int +rip_interface_delete (int command, struct zclient *zclient, + 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, vrf_id); + + if (ifp == NULL) + return 0; + + if (if_is_up (ifp)) { + rip_if_down(ifp); + } + + zlog_info("interface delete %s index %d flags %#llx metric %d mtu %d", + ifp->name, ifp->ifindex, (unsigned long long) ifp->flags, + ifp->metric, ifp->mtu); + + /* To support pseudo interface do not free interface structure. */ + /* if_delete(ifp); */ + ifp->ifindex = IFINDEX_INTERNAL; + + return 0; +} + +static void +rip_interface_clean (struct rip_interface *ri) +{ + ri->enable_network = 0; + ri->enable_interface = 0; + ri->running = 0; + + if (ri->t_wakeup) + { + thread_cancel (ri->t_wakeup); + ri->t_wakeup = NULL; + } +} + +void +rip_interfaces_clean (void) +{ + struct listnode *node; + struct interface *ifp; + + for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) + rip_interface_clean (ifp->info); +} + +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; + + /* 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; + + 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->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->passive = 0; + + rip_interface_clean (ri); +} + +void +rip_interfaces_reset (void) +{ + struct listnode *node; + struct interface *ifp; + + for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) + rip_interface_reset (ifp->info); +} + +int +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 ((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) + { + if (IS_RIP_DEBUG_EVENT) + zlog_debug ("turn off %s", ifp->name); + + /* Leave from multicast group. */ + rip_multicast_leave (ifp, rip->sock); + + ri->running = 0; + } + + return 0; +} + +/* Needed for stop RIP process. */ +void +rip_if_down_all () +{ + struct interface *ifp; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS (iflist, node, nnode, ifp)) + rip_if_down (ifp); +} + +static void +rip_apply_address_add (struct connected *ifc) +{ + struct prefix_ipv4 address; + struct prefix *p; + + if (!rip) + return; + + if (! if_is_up(ifc->ifp)) + return; + + p = ifc->address; + + memset (&address, 0, sizeof (address)); + address.family = p->family; + address.prefix = p->u.prefix4; + address.prefixlen = p->prefixlen; + apply_mask_ipv4(&address); + + /* Check if this interface is RIP enabled or not + or Check if this address's prefix is RIP enabled */ + 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, 0); + +} + +int +rip_interface_address_add (int command, struct zclient *zclient, + 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, vrf_id); + + if (ifc == NULL) + return 0; + + p = ifc->address; + + if (p->family == AF_INET) + { + if (IS_RIP_DEBUG_ZEBRA) + zlog_debug ("connected address %s/%d is added", + inet_ntoa (p->u.prefix4), p->prefixlen); + + rip_enable_apply(ifc->ifp); + /* Check if this prefix needs to be redistributed */ + rip_apply_address_add(ifc); + +#ifdef HAVE_SNMP + rip_ifaddr_add (ifc->ifp, ifc); +#endif /* HAVE_SNMP */ + } + + return 0; +} + +static void +rip_apply_address_del (struct connected *ifc) { + struct prefix_ipv4 address; + struct prefix *p; + + if (!rip) + return; + + if (! if_is_up(ifc->ifp)) + return; + + p = ifc->address; + + memset (&address, 0, sizeof (address)); + address.family = p->family; + address.prefix = p->u.prefix4; + address.prefixlen = p->prefixlen; + apply_mask_ipv4(&address); + + rip_redistribute_delete(ZEBRA_ROUTE_CONNECT, RIP_ROUTE_INTERFACE, + &address, ifc->ifp->ifindex); +} + +int +rip_interface_address_delete (int command, struct zclient *zclient, + 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, vrf_id); + + if (ifc) + { + p = ifc->address; + if (p->family == AF_INET) + { + if (IS_RIP_DEBUG_ZEBRA) + zlog_debug ("connected address %s/%d is deleted", + inet_ntoa (p->u.prefix4), p->prefixlen); + +#ifdef HAVE_SNMP + rip_ifaddr_delete (ifc->ifp, ifc); +#endif /* HAVE_SNMP */ + + /* Chech wether this prefix needs to be removed */ + rip_apply_address_del(ifc); + + } + + connected_free (ifc); + + } + + 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. */ +static int +rip_enable_network_lookup_if (struct interface *ifp) +{ + struct listnode *node, *nnode; + struct connected *connected; + struct prefix_ipv4 address; + + for (ALL_LIST_ELEMENTS (ifp->connected, node, nnode, connected)) + { + struct prefix *p; + struct route_node *node; + + p = connected->address; + + if (p->family == AF_INET) + { + address.family = AF_INET; + address.prefix = p->u.prefix4; + address.prefixlen = IPV4_MAX_BITLEN; + + node = route_node_match (rip_enable_network, + (struct prefix *)&address); + if (node) + { + route_unlock_node (node); + return 1; + } + } + } + return -1; +} + +/* Check wether connected is within the ripng_enable_network table. */ +int +rip_enable_network_lookup2 (struct connected *connected) +{ + struct prefix_ipv4 address; + struct prefix *p; + + p = connected->address; + + if (p->family == AF_INET) { + struct route_node *node; + + address.family = p->family; + address.prefix = p->u.prefix4; + address.prefixlen = IPV4_MAX_BITLEN; + + /* LPM on p->family, p->u.prefix4/IPV4_MAX_BITLEN within rip_enable_network */ + node = route_node_match (rip_enable_network, + (struct prefix *)&address); + + if (node) { + route_unlock_node (node); + return 1; + } + } + + return -1; +} +/* Add RIP enable network. */ +static int +rip_enable_network_add (struct prefix *p) +{ + struct route_node *node; + + node = route_node_get (rip_enable_network, p); + + if (node->info) + { + route_unlock_node (node); + return -1; + } + else + node->info = (char *) "enabled"; + + /* XXX: One should find a better solution than a generic one */ + rip_enable_apply_all(); + + return 1; +} + +/* Delete RIP enable network. */ +static int +rip_enable_network_delete (struct prefix *p) +{ + struct route_node *node; + + node = route_node_lookup (rip_enable_network, p); + if (node) + { + node->info = NULL; + + /* Unlock info lock. */ + route_unlock_node (node); + + /* Unlock lookup lock. */ + route_unlock_node (node); + + /* XXX: One should find a better solution than a generic one */ + rip_enable_apply_all (); + + return 1; + } + return -1; +} + +/* Check interface is enabled by ifname statement. */ +static int +rip_enable_if_lookup (const char *ifname) +{ + unsigned int i; + char *str; + + for (i = 0; i < vector_active (rip_enable_interface); i++) + if ((str = vector_slot (rip_enable_interface, i)) != NULL) + if (strcmp (str, ifname) == 0) + return i; + return -1; +} + +/* Add interface to rip_enable_if. */ +static int +rip_enable_if_add (const char *ifname) +{ + int ret; + + ret = rip_enable_if_lookup (ifname); + if (ret >= 0) + return -1; + + vector_set (rip_enable_interface, strdup (ifname)); + + rip_enable_apply_all(); /* TODOVJ */ + + return 1; +} + +/* Delete interface from rip_enable_if. */ +static int +rip_enable_if_delete (const char *ifname) +{ + int index; + char *str; + + index = rip_enable_if_lookup (ifname); + if (index < 0) + return -1; + + str = vector_slot (rip_enable_interface, index); + free (str); + vector_unset (rip_enable_interface, index); + + rip_enable_apply_all(); /* TODOVJ */ + + return 1; +} + +/* Join to multicast group and send request to the interface. */ +static int +rip_interface_wakeup (struct thread *t) +{ + struct interface *ifp; + struct rip_interface *ri; + + /* Get interface. */ + ifp = THREAD_ARG (t); + + ri = ifp->info; + ri->t_wakeup = NULL; + + /* Join to multicast group. */ + if (rip_multicast_join (ifp, rip->sock) < 0) + { + zlog_err ("multicast join failed, interface %s not running", ifp->name); + return 0; + } + + /* Set running flag. */ + ri->running = 1; + + /* Send RIP request to the interface. */ + rip_request_interface (ifp); + + return 0; +} + +static void +rip_connect_set (struct interface *ifp, int set) +{ + struct listnode *node, *nnode; + struct connected *connected; + struct prefix_ipv4 address; + + for (ALL_LIST_ELEMENTS (ifp->connected, node, nnode, connected)) + { + struct prefix *p; + p = connected->address; + + if (p->family != AF_INET) + continue; + + address.family = AF_INET; + address.prefix = p->u.prefix4; + address.prefixlen = p->prefixlen; + apply_mask_ipv4 (&address); + + if (set) { + /* Check once more wether this prefix is within a "network IF_OR_PREF" one */ + if ((rip_enable_if_lookup(connected->ifp->name) >= 0) || + (rip_enable_network_lookup2(connected) >= 0)) + rip_redistribute_add (ZEBRA_ROUTE_CONNECT, RIP_ROUTE_INTERFACE, + &address, connected->ifp->ifindex, + NULL, 0, 0, 0); + } else + { + rip_redistribute_delete (ZEBRA_ROUTE_CONNECT, RIP_ROUTE_INTERFACE, + &address, connected->ifp->ifindex); + if (rip_redistribute_check (ZEBRA_ROUTE_CONNECT)) + rip_redistribute_add (ZEBRA_ROUTE_CONNECT, RIP_ROUTE_REDISTRIBUTE, + &address, connected->ifp->ifindex, + NULL, 0, 0, 0); + } + } +} + +/* Update interface status. */ +void +rip_enable_apply (struct interface *ifp) +{ + int ret; + struct rip_interface *ri = NULL; + + /* Check interface. */ + if (! if_is_operative (ifp)) + return; + + ri = ifp->info; + + /* Check network configuration. */ + ret = rip_enable_network_lookup_if (ifp); + + /* If the interface is matched. */ + if (ret > 0) + ri->enable_network = 1; + else + ri->enable_network = 0; + + /* Check interface name configuration. */ + ret = rip_enable_if_lookup (ifp->name); + if (ret >= 0) + ri->enable_interface = 1; + else + ri->enable_interface = 0; + + /* any interface MUST have an IPv4 address */ + if ( ! rip_if_ipv4_address_check (ifp) ) + { + ri->enable_network = 0; + ri->enable_interface = 0; + } + + /* Update running status of the interface. */ + if (ri->enable_network || ri->enable_interface) + { + { + if (IS_RIP_DEBUG_EVENT) + zlog_debug ("turn on %s", ifp->name); + + /* Add interface wake up thread. */ + if (! ri->t_wakeup) + ri->t_wakeup = thread_add_timer (master, rip_interface_wakeup, + ifp, 1); + rip_connect_set (ifp, 1); + } + } + else + { + if (ri->running) + { + /* Might as well clean up the route table as well + * rip_if_down sets to 0 ri->running, and displays "turn off %s" + **/ + rip_if_down(ifp); + + rip_connect_set (ifp, 0); + } + } +} + +/* Apply network configuration to all interface. */ +void +rip_enable_apply_all () +{ + struct interface *ifp; + struct listnode *node, *nnode; + + /* Check each interface. */ + for (ALL_LIST_ELEMENTS (iflist, node, nnode, ifp)) + rip_enable_apply (ifp); +} + +int +rip_neighbor_lookup (struct sockaddr_in *from) +{ + struct prefix_ipv4 p; + struct route_node *node; + + memset (&p, 0, sizeof (struct prefix_ipv4)); + p.family = AF_INET; + p.prefix = from->sin_addr; + p.prefixlen = IPV4_MAX_BITLEN; + + node = route_node_lookup (rip->neighbor, (struct prefix *) &p); + if (node) + { + route_unlock_node (node); + return 1; + } + return 0; +} + +/* Add new RIP neighbor to the neighbor tree. */ +static int +rip_neighbor_add (struct prefix_ipv4 *p) +{ + struct route_node *node; + + node = route_node_get (rip->neighbor, (struct prefix *) p); + + if (node->info) + return -1; + + node->info = rip->neighbor; + + return 0; +} + +/* Delete RIP neighbor from the neighbor tree. */ +static int +rip_neighbor_delete (struct prefix_ipv4 *p) +{ + struct route_node *node; + + /* Lock for look up. */ + node = route_node_lookup (rip->neighbor, (struct prefix *) p); + if (! node) + return -1; + + node->info = NULL; + + /* Unlock lookup lock. */ + route_unlock_node (node); + + /* Unlock real neighbor information lock. */ + route_unlock_node (node); + + return 0; +} + +/* Clear all network and neighbor configuration. */ +void +rip_clean_network () +{ + unsigned int i; + char *str; + struct route_node *rn; + + /* rip_enable_network. */ + for (rn = route_top (rip_enable_network); rn; rn = route_next (rn)) + if (rn->info) + { + rn->info = NULL; + route_unlock_node (rn); + } + + /* rip_enable_interface. */ + for (i = 0; i < vector_active (rip_enable_interface); i++) + if ((str = vector_slot (rip_enable_interface, i)) != NULL) + { + free (str); + vector_slot (rip_enable_interface, i) = NULL; + } +} + +/* Utility function for looking up passive interface settings. */ +static int +rip_passive_nondefault_lookup (const char *ifname) +{ + unsigned int i; + char *str; + + for (i = 0; i < vector_active (Vrip_passive_nondefault); i++) + if ((str = vector_slot (Vrip_passive_nondefault, i)) != NULL) + if (strcmp (str, ifname) == 0) + return i; + return -1; +} + +void +rip_passive_interface_apply (struct interface *ifp) +{ + struct rip_interface *ri; + + ri = ifp->info; + + ri->passive = ((rip_passive_nondefault_lookup (ifp->name) < 0) ? + passive_default : !passive_default); + + if (IS_RIP_DEBUG_ZEBRA) + zlog_debug ("interface %s: passive = %d",ifp->name,ri->passive); +} + +static void +rip_passive_interface_apply_all (void) +{ + struct interface *ifp; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS (iflist, node, nnode, ifp)) + rip_passive_interface_apply (ifp); +} + +/* Passive interface. */ +static int +rip_passive_nondefault_set (struct vty *vty, const char *ifname) +{ + if (rip_passive_nondefault_lookup (ifname) >= 0) + return CMD_WARNING; + + vector_set (Vrip_passive_nondefault, strdup (ifname)); + + rip_passive_interface_apply_all (); + + return CMD_SUCCESS; +} + +static int +rip_passive_nondefault_unset (struct vty *vty, const char *ifname) +{ + int i; + char *str; + + i = rip_passive_nondefault_lookup (ifname); + if (i < 0) + return CMD_WARNING; + + str = vector_slot (Vrip_passive_nondefault, i); + free (str); + vector_unset (Vrip_passive_nondefault, i); + + rip_passive_interface_apply_all (); + + return CMD_SUCCESS; +} + +/* Free all configured RIP passive-interface settings. */ +void +rip_passive_nondefault_clean (void) +{ + unsigned int i; + char *str; + + for (i = 0; i < vector_active (Vrip_passive_nondefault); i++) + if ((str = vector_slot (Vrip_passive_nondefault, i)) != NULL) + { + free (str); + vector_slot (Vrip_passive_nondefault, i) = NULL; + } + rip_passive_interface_apply_all (); +} + +/* RIP enable network or interface configuration. */ +DEFUN (rip_network, + rip_network_cmd, + "network (A.B.C.D/M|WORD)", + "Enable routing on an IP network\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Interface name\n") +{ + int ret; + struct prefix_ipv4 p; + + ret = str2prefix_ipv4 (argv[0], &p); + + if (ret) + ret = rip_enable_network_add ((struct prefix *) &p); + else + ret = rip_enable_if_add (argv[0]); + + if (ret < 0) + { + vty_out (vty, "There is a same network configuration %s%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +/* RIP enable network or interface configuration. */ +DEFUN (no_rip_network, + no_rip_network_cmd, + "no network (A.B.C.D/M|WORD)", + NO_STR + "Enable routing on an IP network\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Interface name\n") +{ + int ret; + struct prefix_ipv4 p; + + ret = str2prefix_ipv4 (argv[0], &p); + + if (ret) + ret = rip_enable_network_delete ((struct prefix *) &p); + else + ret = rip_enable_if_delete (argv[0]); + + if (ret < 0) + { + vty_out (vty, "Can't find network configuration %s%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +/* RIP neighbor configuration set. */ +DEFUN (rip_neighbor, + rip_neighbor_cmd, + "neighbor A.B.C.D", + "Specify a neighbor router\n" + "Neighbor address\n") +{ + int ret; + struct prefix_ipv4 p; + + ret = str2prefix_ipv4 (argv[0], &p); + + if (ret <= 0) + { + vty_out (vty, "Please specify address by A.B.C.D%s", VTY_NEWLINE); + return CMD_WARNING; + } + + rip_neighbor_add (&p); + + return CMD_SUCCESS; +} + +/* RIP neighbor configuration unset. */ +DEFUN (no_rip_neighbor, + no_rip_neighbor_cmd, + "no neighbor A.B.C.D", + NO_STR + "Specify a neighbor router\n" + "Neighbor address\n") +{ + int ret; + struct prefix_ipv4 p; + + ret = str2prefix_ipv4 (argv[0], &p); + + if (ret <= 0) + { + vty_out (vty, "Please specify address by A.B.C.D%s", VTY_NEWLINE); + return CMD_WARNING; + } + + rip_neighbor_delete (&p); + + return CMD_SUCCESS; +} + +DEFUN (ip_rip_receive_version, + ip_rip_receive_version_cmd, + "ip rip receive version (1|2)", + IP_STR + "Routing Information Protocol\n" + "Advertisement reception\n" + "Version control\n" + "RIP version 1\n" + "RIP version 2\n") +{ + struct interface *ifp; + struct rip_interface *ri; + + ifp = (struct interface *)vty->index; + ri = ifp->info; + + /* Version 1. */ + if (atoi (argv[0]) == 1) + { + ri->ri_receive = RI_RIP_VERSION_1; + return CMD_SUCCESS; + } + if (atoi (argv[0]) == 2) + { + ri->ri_receive = RI_RIP_VERSION_2; + return CMD_SUCCESS; + } + return CMD_WARNING; +} + +DEFUN (ip_rip_receive_version_1, + ip_rip_receive_version_1_cmd, + "ip rip receive version 1 2", + IP_STR + "Routing Information Protocol\n" + "Advertisement reception\n" + "Version control\n" + "RIP version 1\n" + "RIP version 2\n") +{ + struct interface *ifp; + struct rip_interface *ri; + + ifp = (struct interface *)vty->index; + ri = ifp->info; + + /* Version 1 and 2. */ + ri->ri_receive = RI_RIP_VERSION_1_AND_2; + return CMD_SUCCESS; +} + +DEFUN (ip_rip_receive_version_2, + ip_rip_receive_version_2_cmd, + "ip rip receive version 2 1", + IP_STR + "Routing Information Protocol\n" + "Advertisement reception\n" + "Version control\n" + "RIP version 2\n" + "RIP version 1\n") +{ + struct interface *ifp; + struct rip_interface *ri; + + ifp = (struct interface *)vty->index; + ri = ifp->info; + + /* Version 1 and 2. */ + ri->ri_receive = RI_RIP_VERSION_1_AND_2; + return CMD_SUCCESS; +} + +DEFUN (no_ip_rip_receive_version, + no_ip_rip_receive_version_cmd, + "no ip rip receive version", + NO_STR + IP_STR + "Routing Information Protocol\n" + "Advertisement reception\n" + "Version control\n") +{ + struct interface *ifp; + struct rip_interface *ri; + + ifp = (struct interface *)vty->index; + ri = ifp->info; + + ri->ri_receive = RI_RIP_UNSPEC; + return CMD_SUCCESS; +} + +ALIAS (no_ip_rip_receive_version, + no_ip_rip_receive_version_num_cmd, + "no ip rip receive version (1|2)", + NO_STR + IP_STR + "Routing Information Protocol\n" + "Advertisement reception\n" + "Version control\n" + "Version 1\n" + "Version 2\n") + +DEFUN (ip_rip_send_version, + ip_rip_send_version_cmd, + "ip rip send version (1|2)", + IP_STR + "Routing Information Protocol\n" + "Advertisement transmission\n" + "Version control\n" + "RIP version 1\n" + "RIP version 2\n") +{ + struct interface *ifp; + struct rip_interface *ri; + + ifp = (struct interface *)vty->index; + ri = ifp->info; + + /* Version 1. */ + if (atoi (argv[0]) == 1) + { + ri->ri_send = RI_RIP_VERSION_1; + return CMD_SUCCESS; + } + if (atoi (argv[0]) == 2) + { + ri->ri_send = RI_RIP_VERSION_2; + return CMD_SUCCESS; + } + return CMD_WARNING; +} + +DEFUN (ip_rip_send_version_1, + ip_rip_send_version_1_cmd, + "ip rip send version 1 2", + IP_STR + "Routing Information Protocol\n" + "Advertisement transmission\n" + "Version control\n" + "RIP version 1\n" + "RIP version 2\n") +{ + struct interface *ifp; + struct rip_interface *ri; + + ifp = (struct interface *)vty->index; + ri = ifp->info; + + /* Version 1 and 2. */ + ri->ri_send = RI_RIP_VERSION_1_AND_2; + return CMD_SUCCESS; +} + +DEFUN (ip_rip_send_version_2, + ip_rip_send_version_2_cmd, + "ip rip send version 2 1", + IP_STR + "Routing Information Protocol\n" + "Advertisement transmission\n" + "Version control\n" + "RIP version 2\n" + "RIP version 1\n") +{ + struct interface *ifp; + struct rip_interface *ri; + + ifp = (struct interface *)vty->index; + ri = ifp->info; + + /* Version 1 and 2. */ + ri->ri_send = RI_RIP_VERSION_1_AND_2; + return CMD_SUCCESS; +} + +DEFUN (no_ip_rip_send_version, + no_ip_rip_send_version_cmd, + "no ip rip send version", + NO_STR + IP_STR + "Routing Information Protocol\n" + "Advertisement transmission\n" + "Version control\n") +{ + struct interface *ifp; + struct rip_interface *ri; + + ifp = (struct interface *)vty->index; + ri = ifp->info; + + ri->ri_send = RI_RIP_UNSPEC; + return CMD_SUCCESS; +} + +ALIAS (no_ip_rip_send_version, + no_ip_rip_send_version_num_cmd, + "no ip rip send version (1|2)", + NO_STR + IP_STR + "Routing Information Protocol\n" + "Advertisement transmission\n" + "Version control\n" + "Version 1\n" + "Version 2\n") + +DEFUN (ip_rip_authentication_mode, + ip_rip_authentication_mode_cmd, + "ip rip authentication mode (md5|text)", + IP_STR + "Routing Information Protocol\n" + "Authentication control\n" + "Authentication mode\n" + "Keyed message digest\n" + "Clear text authentication\n") +{ + struct interface *ifp; + struct rip_interface *ri; + int auth_type; + + ifp = (struct interface *)vty->index; + ri = ifp->info; + + if ( (argc < 1) || (argc > 2) ) + { + vty_out (vty, "incorrect argument count%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (strncmp ("md5", argv[0], strlen (argv[0])) == 0) + auth_type = RIP_AUTH_MD5; + else if (strncmp ("text", argv[0], strlen (argv[0])) == 0) + auth_type = RIP_AUTH_SIMPLE_PASSWORD; + else + { + vty_out (vty, "mode should be md5 or text%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (argc == 1) + { + ri->auth_type = auth_type; + return CMD_SUCCESS; + } + + if ( (argc == 2) && (auth_type != RIP_AUTH_MD5) ) + { + vty_out (vty, "auth length argument only valid for md5%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (strncmp ("r", argv[1], 1) == 0) + ri->md5_auth_len = RIP_AUTH_MD5_SIZE; + else if (strncmp ("o", argv[1], 1) == 0) + ri->md5_auth_len = RIP_AUTH_MD5_COMPAT_SIZE; + else + return CMD_WARNING; + + ri->auth_type = auth_type; + + return CMD_SUCCESS; +} + +ALIAS (ip_rip_authentication_mode, + ip_rip_authentication_mode_authlen_cmd, + "ip rip authentication mode (md5|text) auth-length (rfc|old-ripd)", + IP_STR + "Routing Information Protocol\n" + "Authentication control\n" + "Authentication mode\n" + "Keyed message digest\n" + "Clear text authentication\n" + "MD5 authentication data length\n" + "RFC compatible\n" + "Old ripd compatible\n") + +DEFUN (no_ip_rip_authentication_mode, + no_ip_rip_authentication_mode_cmd, + "no ip rip authentication mode", + NO_STR + IP_STR + "Routing Information Protocol\n" + "Authentication control\n" + "Authentication mode\n") +{ + struct interface *ifp; + struct rip_interface *ri; + + ifp = (struct interface *)vty->index; + ri = ifp->info; + + ri->auth_type = RIP_NO_AUTH; + ri->md5_auth_len = RIP_AUTH_MD5_COMPAT_SIZE; + + return CMD_SUCCESS; +} + +ALIAS (no_ip_rip_authentication_mode, + no_ip_rip_authentication_mode_type_cmd, + "no ip rip authentication mode (md5|text)", + NO_STR + IP_STR + "Routing Information Protocol\n" + "Authentication control\n" + "Authentication mode\n" + "Keyed message digest\n" + "Clear text authentication\n") + +ALIAS (no_ip_rip_authentication_mode, + no_ip_rip_authentication_mode_type_authlen_cmd, + "no ip rip authentication mode (md5|text) auth-length (rfc|old-ripd)", + NO_STR + IP_STR + "Routing Information Protocol\n" + "Authentication control\n" + "Authentication mode\n" + "Keyed message digest\n" + "Clear text authentication\n" + "MD5 authentication data length\n" + "RFC compatible\n" + "Old ripd compatible\n") + +DEFUN (ip_rip_authentication_string, + ip_rip_authentication_string_cmd, + "ip rip authentication string LINE", + IP_STR + "Routing Information Protocol\n" + "Authentication control\n" + "Authentication string\n" + "Authentication string\n") +{ + struct interface *ifp; + struct rip_interface *ri; + + ifp = (struct interface *)vty->index; + ri = ifp->info; + + if (strlen (argv[0]) > 16) + { + vty_out (vty, "%% RIPv2 authentication string must be shorter than 16%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + if (ri->key_chain) + { + vty_out (vty, "%% key-chain configuration exists%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (ri->auth_str) + free (ri->auth_str); + + ri->auth_str = strdup (argv[0]); + + return CMD_SUCCESS; +} + +DEFUN (no_ip_rip_authentication_string, + no_ip_rip_authentication_string_cmd, + "no ip rip authentication string", + NO_STR + IP_STR + "Routing Information Protocol\n" + "Authentication control\n" + "Authentication string\n") +{ + struct interface *ifp; + struct rip_interface *ri; + + ifp = (struct interface *)vty->index; + ri = ifp->info; + + if (ri->auth_str) + free (ri->auth_str); + + ri->auth_str = NULL; + + return CMD_SUCCESS; +} + +ALIAS (no_ip_rip_authentication_string, + no_ip_rip_authentication_string2_cmd, + "no ip rip authentication string LINE", + NO_STR + IP_STR + "Routing Information Protocol\n" + "Authentication control\n" + "Authentication string\n" + "Authentication string\n") + +DEFUN (ip_rip_authentication_key_chain, + ip_rip_authentication_key_chain_cmd, + "ip rip authentication key-chain LINE", + IP_STR + "Routing Information Protocol\n" + "Authentication control\n" + "Authentication key-chain\n" + "name of key-chain\n") +{ + struct interface *ifp; + struct rip_interface *ri; + + ifp = (struct interface *) vty->index; + ri = ifp->info; + + if (ri->auth_str) + { + vty_out (vty, "%% authentication string configuration exists%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + if (ri->key_chain) + free (ri->key_chain); + + ri->key_chain = strdup (argv[0]); + + return CMD_SUCCESS; +} + +DEFUN (no_ip_rip_authentication_key_chain, + no_ip_rip_authentication_key_chain_cmd, + "no ip rip authentication key-chain", + NO_STR + IP_STR + "Routing Information Protocol\n" + "Authentication control\n" + "Authentication key-chain\n") +{ + struct interface *ifp; + struct rip_interface *ri; + + ifp = (struct interface *) vty->index; + ri = ifp->info; + + if (ri->key_chain) + free (ri->key_chain); + + ri->key_chain = NULL; + + return CMD_SUCCESS; +} + +ALIAS (no_ip_rip_authentication_key_chain, + no_ip_rip_authentication_key_chain2_cmd, + "no ip rip authentication key-chain LINE", + NO_STR + IP_STR + "Routing Information Protocol\n" + "Authentication control\n" + "Authentication key-chain\n" + "name of key-chain\n") + +/* CHANGED: ip rip split-horizon + Cisco and Zebra's command is + ip split-horizon + */ +DEFUN (ip_rip_split_horizon, + ip_rip_split_horizon_cmd, + "ip rip split-horizon", + IP_STR + "Routing Information Protocol\n" + "Perform split horizon\n") +{ + struct interface *ifp; + struct rip_interface *ri; + + ifp = vty->index; + ri = ifp->info; + + ri->split_horizon = RIP_SPLIT_HORIZON; + return CMD_SUCCESS; +} + +DEFUN (ip_rip_split_horizon_poisoned_reverse, + ip_rip_split_horizon_poisoned_reverse_cmd, + "ip rip split-horizon poisoned-reverse", + IP_STR + "Routing Information Protocol\n" + "Perform split horizon\n" + "With poisoned-reverse\n") +{ + struct interface *ifp; + struct rip_interface *ri; + + ifp = vty->index; + ri = ifp->info; + + ri->split_horizon = RIP_SPLIT_HORIZON_POISONED_REVERSE; + return CMD_SUCCESS; +} + +/* CHANGED: no ip rip split-horizon + Cisco and Zebra's command is + no ip split-horizon + */ +DEFUN (no_ip_rip_split_horizon, + no_ip_rip_split_horizon_cmd, + "no ip rip split-horizon", + NO_STR + IP_STR + "Routing Information Protocol\n" + "Perform split horizon\n") +{ + struct interface *ifp; + struct rip_interface *ri; + + ifp = vty->index; + ri = ifp->info; + + ri->split_horizon = RIP_NO_SPLIT_HORIZON; + return CMD_SUCCESS; +} + +DEFUN (no_ip_rip_split_horizon_poisoned_reverse, + no_ip_rip_split_horizon_poisoned_reverse_cmd, + "no ip rip split-horizon poisoned-reverse", + NO_STR + IP_STR + "Routing Information Protocol\n" + "Perform split horizon\n" + "With poisoned-reverse\n") +{ + struct interface *ifp; + struct rip_interface *ri; + + ifp = vty->index; + ri = ifp->info; + + switch( ri->split_horizon ) + { + case RIP_SPLIT_HORIZON_POISONED_REVERSE: + ri->split_horizon = RIP_SPLIT_HORIZON; + default: + break; + } + + return CMD_SUCCESS; +} + +DEFUN (rip_passive_interface, + rip_passive_interface_cmd, + "passive-interface (IFNAME|default)", + "Suppress routing updates on an interface\n" + "Interface name\n" + "default for all interfaces\n") +{ + const char *ifname = argv[0]; + + if (!strcmp(ifname,"default")) { + passive_default = 1; + rip_passive_nondefault_clean(); + return CMD_SUCCESS; + } + if (passive_default) + return rip_passive_nondefault_unset (vty, ifname); + else + return rip_passive_nondefault_set (vty, ifname); +} + +DEFUN (no_rip_passive_interface, + no_rip_passive_interface_cmd, + "no passive-interface (IFNAME|default)", + NO_STR + "Suppress routing updates on an interface\n" + "Interface name\n" + "default for all interfaces\n") +{ + const char *ifname = argv[0]; + + if (!strcmp(ifname,"default")) { + passive_default = 0; + rip_passive_nondefault_clean(); + return CMD_SUCCESS; + } + if (passive_default) + return rip_passive_nondefault_set (vty, ifname); + else + return rip_passive_nondefault_unset (vty, ifname); +} + +/* Write rip configuration of each interface. */ +static int +rip_interface_config_write (struct vty *vty) +{ + struct listnode *node; + struct interface *ifp; + + for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) + { + struct rip_interface *ri; + + ri = ifp->info; + + /* Do not display the interface if there is no + * configuration about it. + **/ + if ((!ifp->desc) && + (ri->split_horizon == ri->split_horizon_default) && + (ri->ri_send == RI_RIP_UNSPEC) && + (ri->ri_receive == RI_RIP_UNSPEC) && + (ri->auth_type != RIP_AUTH_MD5) && + (ri->md5_auth_len != RIP_AUTH_MD5_SIZE) && + (!ri->auth_str) && + (!ri->key_chain) ) + continue; + + vty_out (vty, "interface %s%s", ifp->name, + VTY_NEWLINE); + + if (ifp->desc) + vty_out (vty, " description %s%s", ifp->desc, + VTY_NEWLINE); + + /* Split horizon. */ + if (ri->split_horizon != ri->split_horizon_default) + { + switch (ri->split_horizon) { + case RIP_SPLIT_HORIZON: + vty_out (vty, " ip rip split-horizon%s", VTY_NEWLINE); + break; + case RIP_SPLIT_HORIZON_POISONED_REVERSE: + vty_out (vty, " ip rip split-horizon poisoned-reverse%s", + VTY_NEWLINE); + break; + case RIP_NO_SPLIT_HORIZON: + default: + vty_out (vty, " no ip rip split-horizon%s", VTY_NEWLINE); + break; + } + } + + /* RIP version setting. */ + if (ri->ri_send != RI_RIP_UNSPEC) + vty_out (vty, " ip rip send version %s%s", + lookup (ri_version_msg, ri->ri_send), + VTY_NEWLINE); + + if (ri->ri_receive != RI_RIP_UNSPEC) + vty_out (vty, " ip rip receive version %s%s", + lookup (ri_version_msg, ri->ri_receive), + VTY_NEWLINE); + + /* RIP authentication. */ + if (ri->auth_type == RIP_AUTH_SIMPLE_PASSWORD) + vty_out (vty, " ip rip authentication mode text%s", VTY_NEWLINE); + + if (ri->auth_type == RIP_AUTH_MD5) + { + vty_out (vty, " ip rip authentication mode md5"); + if (ri->md5_auth_len == RIP_AUTH_MD5_COMPAT_SIZE) + vty_out (vty, " auth-length old-ripd"); + else + vty_out (vty, " auth-length rfc"); + vty_out (vty, "%s", VTY_NEWLINE); + } + + if (ri->auth_str) + vty_out (vty, " ip rip authentication string %s%s", + ri->auth_str, VTY_NEWLINE); + + if (ri->key_chain) + vty_out (vty, " ip rip authentication key-chain %s%s", + ri->key_chain, VTY_NEWLINE); + + vty_out (vty, "!%s", VTY_NEWLINE); + } + return 0; +} + +int +config_write_rip_network (struct vty *vty, int config_mode) +{ + unsigned int i; + char *ifname; + struct route_node *node; + + /* Network type RIP enable interface statement. */ + for (node = route_top (rip_enable_network); node; node = route_next (node)) + if (node->info) + vty_out (vty, "%s%s/%d%s", + config_mode ? " network " : " ", + inet_ntoa (node->p.u.prefix4), + node->p.prefixlen, + VTY_NEWLINE); + + /* Interface name RIP enable statement. */ + for (i = 0; i < vector_active (rip_enable_interface); i++) + if ((ifname = vector_slot (rip_enable_interface, i)) != NULL) + vty_out (vty, "%s%s%s", + config_mode ? " network " : " ", + ifname, + VTY_NEWLINE); + + /* RIP neighbors listing. */ + for (node = route_top (rip->neighbor); node; node = route_next (node)) + if (node->info) + vty_out (vty, "%s%s%s", + config_mode ? " neighbor " : " ", + inet_ntoa (node->p.u.prefix4), + VTY_NEWLINE); + + /* RIP passive interface listing. */ + if (config_mode) { + if (passive_default) + vty_out (vty, " passive-interface default%s", VTY_NEWLINE); + for (i = 0; i < vector_active (Vrip_passive_nondefault); i++) + if ((ifname = vector_slot (Vrip_passive_nondefault, i)) != NULL) + vty_out (vty, " %spassive-interface %s%s", + (passive_default ? "no " : ""), ifname, VTY_NEWLINE); + } + + return 0; +} + +static struct cmd_node interface_node = +{ + INTERFACE_NODE, + "%s(config-if)# ", + 1, +}; + +/* Called when interface structure allocated. */ +static int +rip_interface_new_hook (struct interface *ifp) +{ + ifp->info = rip_interface_new (); + return 0; +} + +/* Called when interface structure deleted. */ +static int +rip_interface_delete_hook (struct interface *ifp) +{ + XFREE (MTYPE_RIP_INTERFACE, ifp->info); + ifp->info = NULL; + return 0; +} + +/* Allocate and initialize interface vector. */ +void +rip_if_init (void) +{ + /* Default initial size of interface vector. */ + if_add_hook (IF_NEW_HOOK, rip_interface_new_hook); + if_add_hook (IF_DELETE_HOOK, rip_interface_delete_hook); + + /* RIP network init. */ + rip_enable_interface = vector_init (1); + rip_enable_network = route_table_init (); + + /* RIP passive interface. */ + Vrip_passive_nondefault = vector_init (1); + + /* Install interface node. */ + install_node (&interface_node, rip_interface_config_write); + + /* Install commands. */ + 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 (RIP_NODE, &rip_network_cmd); + install_element (RIP_NODE, &no_rip_network_cmd); + install_element (RIP_NODE, &rip_neighbor_cmd); + install_element (RIP_NODE, &no_rip_neighbor_cmd); + + install_element (RIP_NODE, &rip_passive_interface_cmd); + install_element (RIP_NODE, &no_rip_passive_interface_cmd); + + install_element (INTERFACE_NODE, &ip_rip_send_version_cmd); + install_element (INTERFACE_NODE, &ip_rip_send_version_1_cmd); + install_element (INTERFACE_NODE, &ip_rip_send_version_2_cmd); + install_element (INTERFACE_NODE, &no_ip_rip_send_version_cmd); + install_element (INTERFACE_NODE, &no_ip_rip_send_version_num_cmd); + + install_element (INTERFACE_NODE, &ip_rip_receive_version_cmd); + install_element (INTERFACE_NODE, &ip_rip_receive_version_1_cmd); + install_element (INTERFACE_NODE, &ip_rip_receive_version_2_cmd); + install_element (INTERFACE_NODE, &no_ip_rip_receive_version_cmd); + install_element (INTERFACE_NODE, &no_ip_rip_receive_version_num_cmd); + + install_element (INTERFACE_NODE, &ip_rip_authentication_mode_cmd); + install_element (INTERFACE_NODE, &ip_rip_authentication_mode_authlen_cmd); + install_element (INTERFACE_NODE, &no_ip_rip_authentication_mode_cmd); + install_element (INTERFACE_NODE, &no_ip_rip_authentication_mode_type_cmd); + install_element (INTERFACE_NODE, &no_ip_rip_authentication_mode_type_authlen_cmd); + + install_element (INTERFACE_NODE, &ip_rip_authentication_key_chain_cmd); + install_element (INTERFACE_NODE, &no_ip_rip_authentication_key_chain_cmd); + install_element (INTERFACE_NODE, &no_ip_rip_authentication_key_chain2_cmd); + + install_element (INTERFACE_NODE, &ip_rip_authentication_string_cmd); + install_element (INTERFACE_NODE, &no_ip_rip_authentication_string_cmd); + install_element (INTERFACE_NODE, &no_ip_rip_authentication_string2_cmd); + + install_element (INTERFACE_NODE, &ip_rip_split_horizon_cmd); + install_element (INTERFACE_NODE, &ip_rip_split_horizon_poisoned_reverse_cmd); + install_element (INTERFACE_NODE, &no_ip_rip_split_horizon_cmd); + install_element (INTERFACE_NODE, &no_ip_rip_split_horizon_poisoned_reverse_cmd); +} diff --git a/ripd/rip_interface.h b/ripd/rip_interface.h new file mode 100644 index 0000000..d9dfbb7 --- /dev/null +++ b/ripd/rip_interface.h @@ -0,0 +1,37 @@ +/* RIP interface routines + * + * 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_RIP_INTERFACE_H +#define _QUAGGA_RIP_INTERFACE_H + +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 new file mode 100644 index 0000000..4ead9b0 --- /dev/null +++ b/ripd/rip_main.c @@ -0,0 +1,321 @@ +/* RIPd main routine. + * 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 +#include "getopt.h" +#include "thread.h" +#include "command.h" +#include "memory.h" +#include "prefix.h" +#include "filter.h" +#include "keychain.h" +#include "log.h" +#include "privs.h" +#include "sigevent.h" +#include "zclient.h" +#include "vrf.h" + +#include "ripd/ripd.h" + +/* ripd options. */ +static 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'}, + { "dryrun", no_argument, NULL, 'C'}, + { "vty_addr", required_argument, NULL, 'A'}, + { "vty_port", required_argument, NULL, 'P'}, + { "retain", no_argument, NULL, 'r'}, + { "user", required_argument, NULL, 'u'}, + { "group", required_argument, NULL, 'g'}, + { "version", no_argument, NULL, 'v'}, + { 0 } +}; + +/* ripd privileges */ +zebra_capabilities_t _caps_p [] = +{ + ZCAP_NET_RAW, + ZCAP_BIND +}; + +struct zebra_privs_t ripd_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 +}; + +/* Configuration file and directory. */ +char config_default[] = SYSCONFDIR RIPD_DEFAULT_CONFIG; +char *config_file = NULL; + +/* ripd program name */ + +/* Route retain mode flag. */ +int retain_mode = 0; + +/* RIP VTY bind address. */ +char *vty_addr = NULL; + +/* RIP VTY connection port. */ +int vty_port = RIP_VTY_PORT; + +/* Master of threads. */ +struct thread_master *master; + +/* Process ID saved for use by init system */ +const char *pid_file = PATH_RIPD_PID; + +/* Help information display. */ +static void +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 RIP version 1 and 2.\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\ +-C, --dryrun Check configuration for validity and exit\n\ +-r, --retain When program terminates, retain added route by ripd.\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); +} + +/* SIGHUP handler. */ +static void +sighup (void) +{ + zlog_info ("SIGHUP received"); + rip_clean (); + rip_reset (); + zlog_info ("ripd restarting!"); + + /* Reload config file. */ + vty_read_config (config_file, config_default); + + /* Create VTY's socket */ + vty_serv_sock (vty_addr, vty_port, RIP_VTYSH_PATH); + + /* Try to return to normal operation. */ +} + +/* SIGINT handler. */ +static void +sigint (void) +{ + zlog_notice ("Terminating on signal"); + + if (! retain_mode) + rip_clean (); + + exit (0); +} + +/* SIGUSR1 handler. */ +static void +sigusr1 (void) +{ + zlog_rotate (NULL); +} + +static struct quagga_signal_t ripd_signals[] = +{ + { + .signal = SIGHUP, + .handler = &sighup, + }, + { + .signal = SIGUSR1, + .handler = &sigusr1, + }, + { + .signal = SIGINT, + .handler = &sigint, + }, + { + .signal = SIGTERM, + .handler = &sigint, + }, +}; + +/* Main routine of ripd. */ +int +main (int argc, char **argv) +{ + char *p; + int daemon_mode = 0; + int dryrun = 0; + char *progname; + struct thread thread; + + /* Set umask before anything for security */ + umask (0027); + + /* Get program name. */ + progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]); + + /* First of all we need logging init. */ + zlog_default = openzlog (progname, ZLOG_RIP, + LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON); + + /* Command line option parse. */ + while (1) + { + int opt; + + opt = getopt_long (argc, argv, "df:i:z:hA:P:u:g:rvC", longopts, 0); + + if (opt == EOF) + break; + + switch (opt) + { + case 0: + break; + case 'd': + daemon_mode = 1; + break; + case 'f': + config_file = optarg; + break; + case 'A': + vty_addr = optarg; + break; + case 'i': + pid_file = optarg; + break; + case 'z': + zclient_serv_path_set (optarg); + break; + case 'P': + /* Deal with atoi() returning 0 on failure, and ripd not + listening on rip port... */ + if (strcmp(optarg, "0") == 0) + { + vty_port = 0; + break; + } + vty_port = atoi (optarg); + if (vty_port <= 0 || vty_port > 0xffff) + vty_port = RIP_VTY_PORT; + break; + case 'r': + retain_mode = 1; + break; + case 'C': + dryrun = 1; + break; + case 'u': + ripd_privs.user = optarg; + break; + case 'g': + ripd_privs.group = optarg; + break; + case 'v': + print_version (progname); + exit (0); + break; + case 'h': + usage (progname, 0); + break; + default: + usage (progname, 1); + break; + } + } + + /* Prepare master thread. */ + master = thread_master_create (); + + /* Library initialization. */ + zprivs_init (&ripd_privs); + 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 (master); + rip_peer_init (); + + /* Get configuration file. */ + vty_read_config (config_file, config_default); + + /* Start execution only if not in dry-run mode */ + if(dryrun) + return (0); + + /* Change to the daemon program. */ + if (daemon_mode && daemon (0, 0) < 0) + { + zlog_err("RIPd daemon failed: %s", strerror(errno)); + exit (1); + } + + /* Pid file create. */ + pid_output (pid_file); + + /* Create VTY's socket */ + vty_serv_sock (vty_addr, vty_port, RIP_VTYSH_PATH); + + /* Print banner. */ + zlog_notice ("RIPd %s starting: vty@%d", QUAGGA_VERSION, vty_port); + + /* Execute each thread. */ + while (thread_fetch (master, &thread)) + thread_call (&thread); + + /* Not reached. */ + return (0); +} diff --git a/ripd/rip_offset.c b/ripd/rip_offset.c new file mode 100644 index 0000000..0155f90 --- /dev/null +++ b/ripd/rip_offset.c @@ -0,0 +1,413 @@ +/* RIP offset-list + * 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 "if.h" +#include "prefix.h" +#include "filter.h" +#include "command.h" +#include "linklist.h" +#include "memory.h" + +#include "ripd/ripd.h" + +#define RIP_OFFSET_LIST_IN 0 +#define RIP_OFFSET_LIST_OUT 1 +#define RIP_OFFSET_LIST_MAX 2 + +struct rip_offset_list +{ + char *ifname; + + struct + { + char *alist_name; + /* struct access_list *alist; */ + int metric; + } direct[RIP_OFFSET_LIST_MAX]; +}; + +static struct list *rip_offset_list_master; + +static int +strcmp_safe (const char *s1, const char *s2) +{ + if (s1 == NULL && s2 == NULL) + return 0; + if (s1 == NULL) + return -1; + if (s2 == NULL) + return 1; + return strcmp (s1, s2); +} + +static struct rip_offset_list * +rip_offset_list_new (void) +{ + return XCALLOC (MTYPE_RIP_OFFSET_LIST, sizeof (struct rip_offset_list)); +} + +static void +rip_offset_list_free (struct rip_offset_list *offset) +{ + XFREE (MTYPE_RIP_OFFSET_LIST, offset); +} + +static struct rip_offset_list * +rip_offset_list_lookup (const char *ifname) +{ + struct rip_offset_list *offset; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS (rip_offset_list_master, node, nnode, offset)) + { + if (strcmp_safe (offset->ifname, ifname) == 0) + return offset; + } + return NULL; +} + +static struct rip_offset_list * +rip_offset_list_get (const char *ifname) +{ + struct rip_offset_list *offset; + + offset = rip_offset_list_lookup (ifname); + if (offset) + return offset; + + offset = rip_offset_list_new (); + if (ifname) + offset->ifname = strdup (ifname); + listnode_add_sort (rip_offset_list_master, offset); + + return offset; +} + +static int +rip_offset_list_set (struct vty *vty, const char *alist, const char *direct_str, + const char *metric_str, const char *ifname) +{ + int direct; + int metric; + struct rip_offset_list *offset; + + /* Check direction. */ + if (strncmp (direct_str, "i", 1) == 0) + direct = RIP_OFFSET_LIST_IN; + else if (strncmp (direct_str, "o", 1) == 0) + direct = RIP_OFFSET_LIST_OUT; + else + { + vty_out (vty, "Invalid direction: %s%s", direct_str, VTY_NEWLINE); + return CMD_WARNING; + } + + /* Check metric. */ + metric = atoi (metric_str); + if (metric < 0 || metric > 16) + { + vty_out (vty, "Invalid metric: %s%s", metric_str, VTY_NEWLINE); + return CMD_WARNING; + } + + /* Get offset-list structure with interface name. */ + offset = rip_offset_list_get (ifname); + + if (offset->direct[direct].alist_name) + free (offset->direct[direct].alist_name); + offset->direct[direct].alist_name = strdup (alist); + offset->direct[direct].metric = metric; + + return CMD_SUCCESS; +} + +static int +rip_offset_list_unset (struct vty *vty, const char *alist, + const char *direct_str, const char *metric_str, + const char *ifname) +{ + int direct; + int metric; + struct rip_offset_list *offset; + + /* Check direction. */ + if (strncmp (direct_str, "i", 1) == 0) + direct = RIP_OFFSET_LIST_IN; + else if (strncmp (direct_str, "o", 1) == 0) + direct = RIP_OFFSET_LIST_OUT; + else + { + vty_out (vty, "Invalid direction: %s%s", direct_str, VTY_NEWLINE); + return CMD_WARNING; + } + + /* Check metric. */ + metric = atoi (metric_str); + if (metric < 0 || metric > 16) + { + vty_out (vty, "Invalid metric: %s%s", metric_str, VTY_NEWLINE); + return CMD_WARNING; + } + + /* Get offset-list structure with interface name. */ + offset = rip_offset_list_lookup (ifname); + + if (offset) + { + if (offset->direct[direct].alist_name) + free (offset->direct[direct].alist_name); + offset->direct[direct].alist_name = NULL; + + if (offset->direct[RIP_OFFSET_LIST_IN].alist_name == NULL && + offset->direct[RIP_OFFSET_LIST_OUT].alist_name == NULL) + { + listnode_delete (rip_offset_list_master, offset); + if (offset->ifname) + free (offset->ifname); + rip_offset_list_free (offset); + } + } + else + { + vty_out (vty, "Can't find offset-list%s", VTY_NEWLINE); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +#define OFFSET_LIST_IN_NAME(O) ((O)->direct[RIP_OFFSET_LIST_IN].alist_name) +#define OFFSET_LIST_IN_METRIC(O) ((O)->direct[RIP_OFFSET_LIST_IN].metric) + +#define OFFSET_LIST_OUT_NAME(O) ((O)->direct[RIP_OFFSET_LIST_OUT].alist_name) +#define OFFSET_LIST_OUT_METRIC(O) ((O)->direct[RIP_OFFSET_LIST_OUT].metric) + +/* If metric is modifed return 1. */ +int +rip_offset_list_apply_in (struct prefix_ipv4 *p, struct interface *ifp, + u_int32_t *metric) +{ + struct rip_offset_list *offset; + struct access_list *alist; + + /* Look up offset-list with interface name. */ + offset = rip_offset_list_lookup (ifp->name); + if (offset && OFFSET_LIST_IN_NAME (offset)) + { + alist = access_list_lookup (AFI_IP, OFFSET_LIST_IN_NAME (offset)); + + if (alist + && access_list_apply (alist, (struct prefix *)p) == FILTER_PERMIT) + { + *metric += OFFSET_LIST_IN_METRIC (offset); + return 1; + } + return 0; + } + /* Look up offset-list without interface name. */ + offset = rip_offset_list_lookup (NULL); + if (offset && OFFSET_LIST_IN_NAME (offset)) + { + alist = access_list_lookup (AFI_IP, OFFSET_LIST_IN_NAME (offset)); + + if (alist + && access_list_apply (alist, (struct prefix *)p) == FILTER_PERMIT) + { + *metric += OFFSET_LIST_IN_METRIC (offset); + return 1; + } + return 0; + } + return 0; +} + +/* If metric is modifed return 1. */ +int +rip_offset_list_apply_out (struct prefix_ipv4 *p, struct interface *ifp, + u_int32_t *metric) +{ + struct rip_offset_list *offset; + struct access_list *alist; + + /* Look up offset-list with interface name. */ + offset = rip_offset_list_lookup (ifp->name); + if (offset && OFFSET_LIST_OUT_NAME (offset)) + { + alist = access_list_lookup (AFI_IP, OFFSET_LIST_OUT_NAME (offset)); + + if (alist + && access_list_apply (alist, (struct prefix *)p) == FILTER_PERMIT) + { + *metric += OFFSET_LIST_OUT_METRIC (offset); + return 1; + } + return 0; + } + + /* Look up offset-list without interface name. */ + offset = rip_offset_list_lookup (NULL); + if (offset && OFFSET_LIST_OUT_NAME (offset)) + { + alist = access_list_lookup (AFI_IP, OFFSET_LIST_OUT_NAME (offset)); + + if (alist + && access_list_apply (alist, (struct prefix *)p) == FILTER_PERMIT) + { + *metric += OFFSET_LIST_OUT_METRIC (offset); + return 1; + } + return 0; + } + return 0; +} + +DEFUN (rip_offset_list, + rip_offset_list_cmd, + "offset-list WORD (in|out) <0-16>", + "Modify RIP metric\n" + "Access-list name\n" + "For incoming updates\n" + "For outgoing updates\n" + "Metric value\n") +{ + return rip_offset_list_set (vty, argv[0], argv[1], argv[2], NULL); +} + +DEFUN (rip_offset_list_ifname, + rip_offset_list_ifname_cmd, + "offset-list WORD (in|out) <0-16> IFNAME", + "Modify RIP metric\n" + "Access-list name\n" + "For incoming updates\n" + "For outgoing updates\n" + "Metric value\n" + "Interface to match\n") +{ + return rip_offset_list_set (vty, argv[0], argv[1], argv[2], argv[3]); +} + +DEFUN (no_rip_offset_list, + no_rip_offset_list_cmd, + "no offset-list WORD (in|out) <0-16>", + NO_STR + "Modify RIP metric\n" + "Access-list name\n" + "For incoming updates\n" + "For outgoing updates\n" + "Metric value\n") +{ + return rip_offset_list_unset (vty, argv[0], argv[1], argv[2], NULL); +} + +DEFUN (no_rip_offset_list_ifname, + no_rip_offset_list_ifname_cmd, + "no offset-list WORD (in|out) <0-16> IFNAME", + NO_STR + "Modify RIP metric\n" + "Access-list name\n" + "For incoming updates\n" + "For outgoing updates\n" + "Metric value\n" + "Interface to match\n") +{ + return rip_offset_list_unset (vty, argv[0], argv[1], argv[2], argv[3]); +} + +static int +offset_list_cmp (struct rip_offset_list *o1, struct rip_offset_list *o2) +{ + return strcmp_safe (o1->ifname, o2->ifname); +} + +static void +offset_list_del (struct rip_offset_list *offset) +{ + if (OFFSET_LIST_IN_NAME (offset)) + free (OFFSET_LIST_IN_NAME (offset)); + if (OFFSET_LIST_OUT_NAME (offset)) + free (OFFSET_LIST_OUT_NAME (offset)); + if (offset->ifname) + free (offset->ifname); + rip_offset_list_free (offset); +} + +void +rip_offset_init () +{ + rip_offset_list_master = list_new (); + rip_offset_list_master->cmp = (int (*)(void *, void *)) offset_list_cmp; + rip_offset_list_master->del = (void (*)(void *)) offset_list_del; + + install_element (RIP_NODE, &rip_offset_list_cmd); + install_element (RIP_NODE, &rip_offset_list_ifname_cmd); + install_element (RIP_NODE, &no_rip_offset_list_cmd); + install_element (RIP_NODE, &no_rip_offset_list_ifname_cmd); +} + +void +rip_offset_clean () +{ + list_delete (rip_offset_list_master); + + rip_offset_list_master = list_new (); + rip_offset_list_master->cmp = (int (*)(void *, void *)) offset_list_cmp; + rip_offset_list_master->del = (void (*)(void *)) offset_list_del; +} + +int +config_write_rip_offset_list (struct vty *vty) +{ + struct listnode *node, *nnode; + struct rip_offset_list *offset; + + for (ALL_LIST_ELEMENTS (rip_offset_list_master, node, nnode, offset)) + { + if (! offset->ifname) + { + if (offset->direct[RIP_OFFSET_LIST_IN].alist_name) + vty_out (vty, " offset-list %s in %d%s", + offset->direct[RIP_OFFSET_LIST_IN].alist_name, + offset->direct[RIP_OFFSET_LIST_IN].metric, + VTY_NEWLINE); + if (offset->direct[RIP_OFFSET_LIST_OUT].alist_name) + vty_out (vty, " offset-list %s out %d%s", + offset->direct[RIP_OFFSET_LIST_OUT].alist_name, + offset->direct[RIP_OFFSET_LIST_OUT].metric, + VTY_NEWLINE); + } + else + { + if (offset->direct[RIP_OFFSET_LIST_IN].alist_name) + vty_out (vty, " offset-list %s in %d %s%s", + offset->direct[RIP_OFFSET_LIST_IN].alist_name, + offset->direct[RIP_OFFSET_LIST_IN].metric, + offset->ifname, VTY_NEWLINE); + if (offset->direct[RIP_OFFSET_LIST_OUT].alist_name) + vty_out (vty, " offset-list %s out %d %s%s", + offset->direct[RIP_OFFSET_LIST_OUT].alist_name, + offset->direct[RIP_OFFSET_LIST_OUT].metric, + offset->ifname, VTY_NEWLINE); + } + } + + return 0; +} diff --git a/ripd/rip_peer.c b/ripd/rip_peer.c new file mode 100644 index 0000000..6a3add6 --- /dev/null +++ b/ripd/rip_peer.c @@ -0,0 +1,207 @@ +/* RIP peer support + * 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 "if.h" +#include "prefix.h" +#include "command.h" +#include "linklist.h" +#include "thread.h" +#include "memory.h" + +#include "ripd/ripd.h" + +/* Linked list of RIP peer. */ +struct list *peer_list; + +static struct rip_peer * +rip_peer_new (void) +{ + return XCALLOC (MTYPE_RIP_PEER, sizeof (struct rip_peer)); +} + +static void +rip_peer_free (struct rip_peer *peer) +{ + XFREE (MTYPE_RIP_PEER, peer); +} + +struct rip_peer * +rip_peer_lookup (struct in_addr *addr) +{ + struct rip_peer *peer; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS (peer_list, node, nnode, peer)) + { + if (IPV4_ADDR_SAME (&peer->addr, addr)) + return peer; + } + return NULL; +} + +struct rip_peer * +rip_peer_lookup_next (struct in_addr *addr) +{ + struct rip_peer *peer; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS (peer_list, node, nnode, peer)) + { + if (htonl (peer->addr.s_addr) > htonl (addr->s_addr)) + return peer; + } + return NULL; +} + +/* RIP peer is timeout. */ +static int +rip_peer_timeout (struct thread *t) +{ + struct rip_peer *peer; + + peer = THREAD_ARG (t); + listnode_delete (peer_list, peer); + rip_peer_free (peer); + + return 0; +} + +/* Get RIP peer. At the same time update timeout thread. */ +static struct rip_peer * +rip_peer_get (struct in_addr *addr) +{ + struct rip_peer *peer; + + peer = rip_peer_lookup (addr); + + if (peer) + { + if (peer->t_timeout) + thread_cancel (peer->t_timeout); + } + else + { + peer = rip_peer_new (); + peer->addr = *addr; + listnode_add_sort (peer_list, peer); + } + + /* Update timeout thread. */ + peer->t_timeout = thread_add_timer (master, rip_peer_timeout, peer, + RIP_PEER_TIMER_DEFAULT); + + /* Last update time set. */ + time (&peer->uptime); + + return peer; +} + +void +rip_peer_update (struct sockaddr_in *from, u_char version) +{ + struct rip_peer *peer; + peer = rip_peer_get (&from->sin_addr); + peer->version = version; +} + +void +rip_peer_bad_route (struct sockaddr_in *from) +{ + struct rip_peer *peer; + peer = rip_peer_get (&from->sin_addr); + peer->recv_badroutes++; +} + +void +rip_peer_bad_packet (struct sockaddr_in *from) +{ + struct rip_peer *peer; + peer = rip_peer_get (&from->sin_addr); + peer->recv_badpackets++; +} + +/* Display peer uptime. */ +static char * +rip_peer_uptime (struct rip_peer *peer, char *buf, size_t len) +{ + time_t uptime; + struct tm *tm; + + /* If there is no connection has been done before print `never'. */ + if (peer->uptime == 0) + { + snprintf (buf, len, "never "); + return buf; + } + + /* Get current time. */ + uptime = time (NULL); + uptime -= peer->uptime; + tm = gmtime (&uptime); + + /* Making formatted timer strings. */ +#define ONE_DAY_SECOND 60*60*24 +#define ONE_WEEK_SECOND 60*60*24*7 + + if (uptime < ONE_DAY_SECOND) + snprintf (buf, len, "%02d:%02d:%02d", + tm->tm_hour, tm->tm_min, tm->tm_sec); + else if (uptime < ONE_WEEK_SECOND) + snprintf (buf, len, "%dd%02dh%02dm", + tm->tm_yday, tm->tm_hour, tm->tm_min); + else + snprintf (buf, len, "%02dw%dd%02dh", + tm->tm_yday/7, tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour); + return buf; +} + +void +rip_peer_display (struct vty *vty) +{ + struct rip_peer *peer; + struct listnode *node, *nnode; +#define RIP_UPTIME_LEN 25 + char timebuf[RIP_UPTIME_LEN]; + + for (ALL_LIST_ELEMENTS (peer_list, node, nnode, peer)) + { + vty_out (vty, " %-16s %9d %9d %9d %s%s", inet_ntoa (peer->addr), + peer->recv_badpackets, peer->recv_badroutes, + ZEBRA_RIP_DISTANCE_DEFAULT, + rip_peer_uptime (peer, timebuf, RIP_UPTIME_LEN), + VTY_NEWLINE); + } +} + +static int +rip_peer_list_cmp (struct rip_peer *p1, struct rip_peer *p2) +{ + return htonl (p1->addr.s_addr) > htonl (p2->addr.s_addr); +} + +void +rip_peer_init (void) +{ + peer_list = list_new (); + peer_list->cmp = (int (*)(void *, void *)) rip_peer_list_cmp; +} diff --git a/ripd/rip_routemap.c b/ripd/rip_routemap.c new file mode 100644 index 0000000..4e73694 --- /dev/null +++ b/ripd/rip_routemap.c @@ -0,0 +1,1113 @@ +/* RIPv2 routemap. + * Copyright (C) 2005 6WIND + * 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 "memory.h" +#include "prefix.h" +#include "routemap.h" +#include "command.h" +#include "filter.h" +#include "log.h" +#include "sockunion.h" /* for inet_aton () */ +#include "plist.h" + +#include "ripd/ripd.h" + +struct rip_metric_modifier +{ + enum + { + metric_increment, + metric_decrement, + metric_absolute + } type; + + u_char metric; +}; + +/* Add rip route map rule. */ +static int +rip_route_match_add (struct vty *vty, struct route_map_index *index, + const char *command, const char *arg) +{ + int ret; + + ret = route_map_add_match (index, command, arg); + if (ret) + { + switch (ret) + { + case RMAP_RULE_MISSING: + vty_out (vty, "%% RIP Can't find rule.%s", VTY_NEWLINE); + return CMD_WARNING; + case RMAP_COMPILE_ERROR: + vty_out (vty, "%% RIP Argument is malformed.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + return CMD_SUCCESS; +} + +/* Delete rip route map rule. */ +static int +rip_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, "%% RIP Can't find rule.%s", VTY_NEWLINE); + return CMD_WARNING; + case RMAP_COMPILE_ERROR: + vty_out (vty, "%% RIP Argument is malformed.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + return CMD_SUCCESS; +} + +/* Add rip route map rule. */ +static int +rip_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, "%% 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 + but only values from 0 to 16 are valid for rip and ripng + 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, "%% RIP Argument is malformed.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + } + return CMD_SUCCESS; +} + +/* Delete rip route map rule. */ +static int +rip_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, "%% RIP Can't find rule.%s", VTY_NEWLINE); + return CMD_WARNING; + case RMAP_COMPILE_ERROR: + vty_out (vty, "%% RIP Argument is malformed.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + return CMD_SUCCESS; +} + +/* Hook function for updating route_map assignment. */ +/* ARGSUSED */ +static void +rip_route_map_update (const char *notused) +{ + int i; + + if (rip) + { + for (i = 0; i < ZEBRA_ROUTE_MAX; i++) + { + if (rip->route_map[i].name) + rip->route_map[i].map = + route_map_lookup_by_name (rip->route_map[i].name); + } + } +} + +/* `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) +{ + u_int32_t *metric; + u_int32_t check; + struct rip_info *rinfo; + + if (type == RMAP_RIP) + { + metric = rule; + rinfo = object; + + /* If external metric is available, the route-map should + work on this one (for redistribute purpose) */ + check = (rinfo->external_metric) ? rinfo->external_metric : + rinfo->metric; + if (check == *metric) + return RMAP_MATCH; + else + return RMAP_NOMATCH; + } + return RMAP_NOMATCH; +} + +/* Route map `match metric' match statement. `arg' is METRIC value */ +static void * +route_match_metric_compile (const char *arg) +{ + u_int32_t *metric; + + metric = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_int32_t)); + *metric = atoi (arg); + + if(*metric > 0) + return metric; + + XFREE (MTYPE_ROUTE_MAP_COMPILED, metric); + return NULL; +} + +/* Free route map's compiled `match metric' value. */ +static void +route_match_metric_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* 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 +}; + +/* `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 rip_info *rinfo; + struct interface *ifp; + char *ifname; + + if (type == RMAP_RIP) + { + ifname = rule; + ifp = if_lookup_by_name(ifname); + + if (!ifp) + return RMAP_NOMATCH; + + rinfo = object; + + if (rinfo->ifindex_out == ifp->ifindex || rinfo->ifindex == ifp->ifindex) + return RMAP_MATCH; + else + return RMAP_NOMATCH; + } + return RMAP_NOMATCH; +} + +/* Route map `match interface' match statement. `arg' is IFNAME value */ +/* XXX I don`t know if I need to check does interface exist? */ +static void * +route_match_interface_compile (const char *arg) +{ + return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); +} + +/* Free route map's compiled `match interface' value. */ +static void +route_match_interface_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for interface matching. */ +struct route_map_rule_cmd route_match_interface_cmd = +{ + "interface", + route_match_interface, + route_match_interface_compile, + route_match_interface_free +}; + +/* `match ip next-hop IP_ACCESS_LIST' */ + +/* Match function return 1 if match is success else return zero. */ +static route_map_result_t +route_match_ip_next_hop (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct access_list *alist; + struct rip_info *rinfo; + struct prefix_ipv4 p; + + if (type == RMAP_RIP) + { + rinfo = object; + p.family = AF_INET; + p.prefix = (rinfo->nexthop.s_addr) ? rinfo->nexthop : rinfo->from; + p.prefixlen = IPV4_MAX_BITLEN; + + alist = access_list_lookup (AFI_IP, (char *) rule); + if (alist == NULL) + return RMAP_NOMATCH; + + return (access_list_apply (alist, &p) == FILTER_DENY ? + RMAP_NOMATCH : RMAP_MATCH); + } + return RMAP_NOMATCH; +} + +/* Route map `ip next-hop' match statement. `arg' should be + access-list name. */ +static void * +route_match_ip_next_hop_compile (const char *arg) +{ + return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); +} + +/* Free route map's compiled `. */ +static void +route_match_ip_next_hop_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for ip next-hop matching. */ +static struct route_map_rule_cmd route_match_ip_next_hop_cmd = +{ + "ip next-hop", + route_match_ip_next_hop, + 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 +route_match_ip_next_hop_prefix_list (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct prefix_list *plist; + struct rip_info *rinfo; + struct prefix_ipv4 p; + + if (type == RMAP_RIP) + { + rinfo = object; + p.family = AF_INET; + p.prefix = (rinfo->nexthop.s_addr) ? rinfo->nexthop : rinfo->from; + p.prefixlen = IPV4_MAX_BITLEN; + + plist = prefix_list_lookup (AFI_IP, (char *) rule); + if (plist == NULL) + return RMAP_NOMATCH; + + return (prefix_list_apply (plist, &p) == PREFIX_DENY ? + RMAP_NOMATCH : RMAP_MATCH); + } + return RMAP_NOMATCH; +} + +static void * +route_match_ip_next_hop_prefix_list_compile (const char *arg) +{ + return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); +} + +static void +route_match_ip_next_hop_prefix_list_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +static struct route_map_rule_cmd route_match_ip_next_hop_prefix_list_cmd = +{ + "ip next-hop prefix-list", + route_match_ip_next_hop_prefix_list, + 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 + zero. */ +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; + + if (type == RMAP_RIP) + { + alist = access_list_lookup (AFI_IP, (char *) rule); + if (alist == NULL) + return RMAP_NOMATCH; + + return (access_list_apply (alist, prefix) == FILTER_DENY ? + RMAP_NOMATCH : RMAP_MATCH); + } + return RMAP_NOMATCH; +} + +/* Route map `ip address' match statement. `arg' should be + access-list name. */ +static void * +route_match_ip_address_compile (const char *arg) +{ + return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); +} + +/* Free route map's compiled `ip address' value. */ +static void +route_match_ip_address_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for ip address matching. */ +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 +}; + +/* `match ip address prefix-list PREFIX_LIST' */ + +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_RIP) + { + plist = prefix_list_lookup (AFI_IP, (char *) rule); + if (plist == NULL) + return RMAP_NOMATCH; + + return (prefix_list_apply (plist, prefix) == PREFIX_DENY ? + RMAP_NOMATCH : 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); +} + +static 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 +}; + +/* `match tag TAG' */ +/* 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 rip_info *rinfo; + + if (type == RMAP_RIP) + { + tag = rule; + rinfo = object; + + /* The information stored by rinfo is host ordered. */ + if (rinfo->tag == *tag) + return RMAP_MATCH; + else + return 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 +route_set_metric (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + if (type == RMAP_RIP) + { + struct rip_metric_modifier *mod; + struct rip_info *rinfo; + + mod = rule; + rinfo = object; + + if (mod->type == metric_increment) + rinfo->metric_out += mod->metric; + else if (mod->type == metric_decrement) + rinfo->metric_out -= mod->metric; + else if (mod->type == metric_absolute) + rinfo->metric_out = mod->metric; + + if ((signed int)rinfo->metric_out < 1) + rinfo->metric_out = 1; + if (rinfo->metric_out > RIP_METRIC_INFINITY) + rinfo->metric_out = RIP_METRIC_INFINITY; + + rinfo->metric_set = 1; + } + return RMAP_OKAY; +} + +/* set metric compilation. */ +static void * +route_set_metric_compile (const char *arg) +{ + int len; + const char *pnt; + int type; + long metric; + char *endptr = NULL; + struct rip_metric_modifier *mod; + + len = strlen (arg); + pnt = arg; + + if (len == 0) + return NULL; + + /* Examine first character. */ + if (arg[0] == '+') + { + type = metric_increment; + pnt++; + } + else if (arg[0] == '-') + { + type = metric_decrement; + pnt++; + } + else + type = metric_absolute; + + /* Check beginning with digit string. */ + if (*pnt < '0' || *pnt > '9') + return NULL; + + /* Convert string to integer. */ + metric = strtol (pnt, &endptr, 10); + + if (metric == LONG_MAX || *endptr != '\0') + return NULL; + if (metric < 0 || metric > RIP_METRIC_INFINITY) + return NULL; + + mod = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, + sizeof (struct rip_metric_modifier)); + mod->type = type; + mod->metric = metric; + + return mod; +} + +/* 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. */ +static struct route_map_rule_cmd route_set_metric_cmd = +{ + "metric", + route_set_metric, + route_set_metric_compile, + route_set_metric_free, +}; + +/* `set ip next-hop IP_ADDRESS' */ + +/* Set nexthop to object. ojbect must be pointer to struct attr. */ +static route_map_result_t +route_set_ip_nexthop (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct in_addr *address; + struct rip_info *rinfo; + + if(type == RMAP_RIP) + { + /* Fetch routemap's rule information. */ + address = rule; + rinfo = object; + + /* Set next hop value. */ + rinfo->nexthop_out = *address; + } + + return RMAP_OKAY; +} + +/* Route map `ip nexthop' compile function. Given string is converted + to struct in_addr structure. */ +static void * +route_set_ip_nexthop_compile (const char *arg) +{ + int ret; + struct in_addr *address; + + address = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct in_addr)); + + ret = inet_aton (arg, address); + + if (ret == 0) + { + XFREE (MTYPE_ROUTE_MAP_COMPILED, address); + return NULL; + } + + return address; +} + +/* Free route map's compiled `ip nexthop' value. */ +static void +route_set_ip_nexthop_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for ip nexthop set. */ +static struct route_map_rule_cmd route_set_ip_nexthop_cmd = +{ + "ip next-hop", + route_set_ip_nexthop, + route_set_ip_nexthop_compile, + route_set_ip_nexthop_free +}; + +/* `set tag TAG' */ + +/* Set tag to object. ojbect must be pointer to struct attr. */ +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 rip_info *rinfo; + + if(type == RMAP_RIP) + { + /* Fetch routemap's rule information. */ + tag = rule; + rinfo = object; + + /* Set next hop value. */ + rinfo->tag_out = *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 +}; + +#define MATCH_STR "Match values from routing table\n" +#define SET_STR "Set values in destination routing protocol\n" + +DEFUN (match_metric, + match_metric_cmd, + "match metric <0-4294967295>", + MATCH_STR + "Match metric of route\n" + "Metric value\n") +{ + return rip_route_match_add (vty, vty->index, "metric", argv[0]); +} + +DEFUN (no_match_metric, + no_match_metric_cmd, + "no match metric", + NO_STR + MATCH_STR + "Match metric of route\n") +{ + if (argc == 0) + return rip_route_match_delete (vty, vty->index, "metric", NULL); + + return rip_route_match_delete (vty, vty->index, "metric", argv[0]); +} + +ALIAS (no_match_metric, + no_match_metric_val_cmd, + "no match metric <0-4294967295>", + NO_STR + MATCH_STR + "Match metric of route\n" + "Metric value\n") + +DEFUN (match_interface, + match_interface_cmd, + "match interface WORD", + MATCH_STR + "Match first hop interface of route\n" + "Interface name\n") +{ + return rip_route_match_add (vty, vty->index, "interface", argv[0]); +} + +DEFUN (no_match_interface, + no_match_interface_cmd, + "no match interface", + NO_STR + MATCH_STR + "Match first hop interface of route\n") +{ + if (argc == 0) + return rip_route_match_delete (vty, vty->index, "interface", NULL); + + return rip_route_match_delete (vty, vty->index, "interface", argv[0]); +} + +ALIAS (no_match_interface, + no_match_interface_val_cmd, + "no match interface WORD", + NO_STR + MATCH_STR + "Match first hop interface of route\n" + "Interface name\n") + +DEFUN (match_ip_next_hop, + match_ip_next_hop_cmd, + "match ip next-hop (<1-199>|<1300-2699>|WORD)", + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "IP access-list number\n" + "IP access-list number (expanded range)\n" + "IP Access-list name\n") +{ + return rip_route_match_add (vty, vty->index, "ip next-hop", argv[0]); +} + +DEFUN (no_match_ip_next_hop, + no_match_ip_next_hop_cmd, + "no match ip next-hop", + NO_STR + MATCH_STR + IP_STR + "Match next-hop address of route\n") +{ + if (argc == 0) + return rip_route_match_delete (vty, vty->index, "ip next-hop", NULL); + + return rip_route_match_delete (vty, vty->index, "ip next-hop", argv[0]); +} + +ALIAS (no_match_ip_next_hop, + no_match_ip_next_hop_val_cmd, + "no match ip next-hop (<1-199>|<1300-2699>|WORD)", + NO_STR + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "IP access-list number\n" + "IP access-list number (expanded range)\n" + "IP Access-list name\n") + +DEFUN (match_ip_next_hop_prefix_list, + match_ip_next_hop_prefix_list_cmd, + "match ip next-hop prefix-list WORD", + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") +{ + return rip_route_match_add (vty, vty->index, "ip next-hop prefix-list", argv[0]); +} + +DEFUN (no_match_ip_next_hop_prefix_list, + no_match_ip_next_hop_prefix_list_cmd, + "no match ip next-hop prefix-list", + NO_STR + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "Match entries of prefix-lists\n") +{ + if (argc == 0) + return rip_route_match_delete (vty, vty->index, "ip next-hop prefix-list", NULL); + + return rip_route_match_delete (vty, vty->index, "ip next-hop prefix-list", argv[0]); +} + +ALIAS (no_match_ip_next_hop_prefix_list, + no_match_ip_next_hop_prefix_list_val_cmd, + "no match ip next-hop prefix-list WORD", + NO_STR + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") + +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 rip_route_match_add (vty, vty->index, "ip address", argv[0]); +} + +DEFUN (no_match_ip_address, + no_match_ip_address_cmd, + "no match ip address", + NO_STR + MATCH_STR + IP_STR + "Match address of route\n") +{ + if (argc == 0) + return rip_route_match_delete (vty, vty->index, "ip address", NULL); + + return rip_route_match_delete (vty, vty->index, "ip address", argv[0]); +} + +ALIAS (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") + +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 rip_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 rip_route_match_delete (vty, vty->index, "ip address prefix-list", NULL); + + return rip_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_tag, + match_tag_cmd, + "match tag <1-4294967295>", + MATCH_STR + "Match tag of route\n" + "Metric value\n") +{ + return rip_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 rip_route_match_delete (vty, vty->index, "tag", NULL); + + return rip_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" + "Metric value\n") + +/* set functions */ + +DEFUN (set_metric, + set_metric_cmd, + "set metric <0-4294967295>", + SET_STR + "Metric value for destination routing protocol\n" + "Metric value\n") +{ + return rip_route_set_add (vty, vty->index, "metric", argv[0]); +} + +ALIAS (set_metric, + set_metric_addsub_cmd, + "set metric <+/-metric>", + SET_STR + "Metric value for destination routing protocol\n" + "Add or subtract metric\n") + +DEFUN (no_set_metric, + no_set_metric_cmd, + "no set metric", + NO_STR + SET_STR + "Metric value for destination routing protocol\n") +{ + if (argc == 0) + return rip_route_set_delete (vty, vty->index, "metric", NULL); + + return rip_route_set_delete (vty, vty->index, "metric", argv[0]); +} + +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") + +ALIAS (no_set_metric, + no_set_metric_addsub_cmd, + "no set metric <+/-metric>", + NO_STR + SET_STR + "Metric value for destination routing protocol\n" + "Add or subtract metric\n") + +DEFUN (set_ip_nexthop, + set_ip_nexthop_cmd, + "set ip next-hop A.B.C.D", + SET_STR + IP_STR + "Next hop address\n" + "IP address of next hop\n") +{ + union sockunion su; + int ret; + + ret = str2sockunion (argv[0], &su); + if (ret < 0) + { + vty_out (vty, "%% Malformed next-hop address%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return rip_route_set_add (vty, vty->index, "ip next-hop", argv[0]); +} + +DEFUN (no_set_ip_nexthop, + no_set_ip_nexthop_cmd, + "no set ip next-hop", + NO_STR + SET_STR + IP_STR + "Next hop address\n") +{ + if (argc == 0) + return rip_route_set_delete (vty, vty->index, "ip next-hop", NULL); + + return rip_route_set_delete (vty, vty->index, "ip next-hop", argv[0]); +} + +ALIAS (no_set_ip_nexthop, + no_set_ip_nexthop_val_cmd, + "no set ip next-hop A.B.C.D", + NO_STR + SET_STR + IP_STR + "Next hop address\n" + "IP address of next hop\n") + +DEFUN (set_tag, + set_tag_cmd, + "set tag <1-4294967295>", + SET_STR + "Tag value for routing protocol\n" + "Tag value\n") +{ + return rip_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) + return rip_route_set_delete (vty, vty->index, "tag", NULL); + + return rip_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") + +void +rip_route_map_reset () +{ + ; +} + +/* Route-map init */ +void +rip_route_map_init () +{ + route_map_init (); + route_map_init_vty (); + route_map_add_hook (rip_route_map_update); + route_map_delete_hook (rip_route_map_update); + + route_map_install_match (&route_match_metric_cmd); + route_map_install_match (&route_match_interface_cmd); + route_map_install_match (&route_match_ip_next_hop_cmd); + route_map_install_match (&route_match_ip_next_hop_prefix_list_cmd); + 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_tag_cmd); + + route_map_install_set (&route_set_metric_cmd); + route_map_install_set (&route_set_ip_nexthop_cmd); + route_map_install_set (&route_set_tag_cmd); + + 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_interface_cmd); + install_element (RMAP_NODE, &no_match_interface_cmd); + install_element (RMAP_NODE, &no_match_interface_val_cmd); + install_element (RMAP_NODE, &match_ip_next_hop_cmd); + install_element (RMAP_NODE, &no_match_ip_next_hop_cmd); + install_element (RMAP_NODE, &no_match_ip_next_hop_val_cmd); + install_element (RMAP_NODE, &match_ip_next_hop_prefix_list_cmd); + install_element (RMAP_NODE, &no_match_ip_next_hop_prefix_list_cmd); + install_element (RMAP_NODE, &no_match_ip_next_hop_prefix_list_val_cmd); + install_element (RMAP_NODE, &match_ip_address_cmd); + install_element (RMAP_NODE, &no_match_ip_address_cmd); + install_element (RMAP_NODE, &no_match_ip_address_val_cmd); + install_element (RMAP_NODE, &match_ip_address_prefix_list_cmd); + install_element (RMAP_NODE, &no_match_ip_address_prefix_list_cmd); + install_element (RMAP_NODE, &no_match_ip_address_prefix_list_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, &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); + 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/ripd/rip_snmp.c b/ripd/rip_snmp.c new file mode 100644 index 0000000..2c7cd2c --- /dev/null +++ b/ripd/rip_snmp.c @@ -0,0 +1,593 @@ +/* RIP 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 "if.h" +#include "log.h" +#include "prefix.h" +#include "command.h" +#include "table.h" +#include "smux.h" + +#include "ripd/ripd.h" + +/* RIPv2-MIB. */ +#define RIPV2MIB 1,3,6,1,2,1,23 + +/* RIPv2-MIB rip2Globals values. */ +#define RIP2GLOBALROUTECHANGES 1 +#define RIP2GLOBALQUERIES 2 + +/* RIPv2-MIB rip2IfStatEntry. */ +#define RIP2IFSTATENTRY 1 + +/* RIPv2-MIB rip2IfStatTable. */ +#define RIP2IFSTATADDRESS 1 +#define RIP2IFSTATRCVBADPACKETS 2 +#define RIP2IFSTATRCVBADROUTES 3 +#define RIP2IFSTATSENTUPDATES 4 +#define RIP2IFSTATSTATUS 5 + +/* RIPv2-MIB rip2IfConfTable. */ +#define RIP2IFCONFADDRESS 1 +#define RIP2IFCONFDOMAIN 2 +#define RIP2IFCONFAUTHTYPE 3 +#define RIP2IFCONFAUTHKEY 4 +#define RIP2IFCONFSEND 5 +#define RIP2IFCONFRECEIVE 6 +#define RIP2IFCONFDEFAULTMETRIC 7 +#define RIP2IFCONFSTATUS 8 +#define RIP2IFCONFSRCADDRESS 9 + +/* RIPv2-MIB rip2PeerTable. */ +#define RIP2PEERADDRESS 1 +#define RIP2PEERDOMAIN 2 +#define RIP2PEERLASTUPDATE 3 +#define RIP2PEERVERSION 4 +#define RIP2PEERRCVBADPACKETS 5 +#define RIP2PEERRCVBADROUTES 6 + +/* SNMP value hack. */ +#define COUNTER ASN_COUNTER +#define INTEGER ASN_INTEGER +#define TIMETICKS ASN_TIMETICKS +#define IPADDRESS ASN_IPADDRESS +#define STRING ASN_OCTET_STR + +/* Define SNMP local variables. */ +SNMP_LOCAL_VARIABLES + +/* RIP-MIB instances. */ +oid rip_oid [] = { RIPV2MIB }; + +/* Interface cache table sorted by interface's address. */ +struct route_table *rip_ifaddr_table; + +/* Hook functions. */ +static u_char *rip2Globals (struct variable *, oid [], size_t *, + int, size_t *, WriteMethod **); +static u_char *rip2IfStatEntry (struct variable *, oid [], size_t *, + int, size_t *, WriteMethod **); +static u_char *rip2IfConfAddress (struct variable *, oid [], size_t *, + int, size_t *, WriteMethod **); +static u_char *rip2PeerTable (struct variable *, oid [], size_t *, + int, size_t *, WriteMethod **); + +struct variable rip_variables[] = +{ + /* RIP Global Counters. */ + {RIP2GLOBALROUTECHANGES, COUNTER, RONLY, rip2Globals, + 2, {1, 1}}, + {RIP2GLOBALQUERIES, COUNTER, RONLY, rip2Globals, + 2, {1, 2}}, + /* RIP Interface Tables. */ + {RIP2IFSTATADDRESS, IPADDRESS, RONLY, rip2IfStatEntry, + 3, {2, 1, 1}}, + {RIP2IFSTATRCVBADPACKETS, COUNTER, RONLY, rip2IfStatEntry, + 3, {2, 1, 2}}, + {RIP2IFSTATRCVBADROUTES, COUNTER, RONLY, rip2IfStatEntry, + 3, {2, 1, 3}}, + {RIP2IFSTATSENTUPDATES, COUNTER, RONLY, rip2IfStatEntry, + 3, {2, 1, 4}}, + {RIP2IFSTATSTATUS, COUNTER, RWRITE, rip2IfStatEntry, + 3, {2, 1, 5}}, + {RIP2IFCONFADDRESS, IPADDRESS, RONLY, rip2IfConfAddress, + /* RIP Interface Configuration Table. */ + 3, {3, 1, 1}}, + {RIP2IFCONFDOMAIN, STRING, RONLY, rip2IfConfAddress, + 3, {3, 1, 2}}, + {RIP2IFCONFAUTHTYPE, COUNTER, RONLY, rip2IfConfAddress, + 3, {3, 1, 3}}, + {RIP2IFCONFAUTHKEY, STRING, RONLY, rip2IfConfAddress, + 3, {3, 1, 4}}, + {RIP2IFCONFSEND, COUNTER, RONLY, rip2IfConfAddress, + 3, {3, 1, 5}}, + {RIP2IFCONFRECEIVE, COUNTER, RONLY, rip2IfConfAddress, + 3, {3, 1, 6}}, + {RIP2IFCONFDEFAULTMETRIC, COUNTER, RONLY, rip2IfConfAddress, + 3, {3, 1, 7}}, + {RIP2IFCONFSTATUS, COUNTER, RONLY, rip2IfConfAddress, + 3, {3, 1, 8}}, + {RIP2IFCONFSRCADDRESS, IPADDRESS, RONLY, rip2IfConfAddress, + 3, {3, 1, 9}}, + {RIP2PEERADDRESS, IPADDRESS, RONLY, rip2PeerTable, + /* RIP Peer Table. */ + 3, {4, 1, 1}}, + {RIP2PEERDOMAIN, STRING, RONLY, rip2PeerTable, + 3, {4, 1, 2}}, + {RIP2PEERLASTUPDATE, TIMETICKS, RONLY, rip2PeerTable, + 3, {4, 1, 3}}, + {RIP2PEERVERSION, INTEGER, RONLY, rip2PeerTable, + 3, {4, 1, 4}}, + {RIP2PEERRCVBADPACKETS, COUNTER, RONLY, rip2PeerTable, + 3, {4, 1, 5}}, + {RIP2PEERRCVBADROUTES, COUNTER, RONLY, rip2PeerTable, + 3, {4, 1, 6}} +}; + +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) +{ + if (smux_header_generic(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + /* Retrun global counter. */ + switch (v->magic) + { + case RIP2GLOBALROUTECHANGES: + return SNMP_INTEGER (rip_global_route_changes); + break; + case RIP2GLOBALQUERIES: + return SNMP_INTEGER (rip_global_queries); + break; + default: + return NULL; + break; + } + return NULL; +} + +void +rip_ifaddr_add (struct interface *ifp, struct connected *ifc) +{ + struct prefix *p; + struct route_node *rn; + + p = ifc->address; + + if (p->family != AF_INET) + return; + + rn = route_node_get (rip_ifaddr_table, p); + rn->info = ifp; +} + +void +rip_ifaddr_delete (struct interface *ifp, struct connected *ifc) +{ + struct prefix *p; + struct route_node *rn; + struct interface *i; + + p = ifc->address; + + if (p->family != AF_INET) + return; + + rn = route_node_lookup (rip_ifaddr_table, p); + if (! rn) + return; + i = rn->info; + if (rn && !strncmp(i->name,ifp->name,INTERFACE_NAMSIZ)) + { + rn->info = NULL; + route_unlock_node (rn); + route_unlock_node (rn); + } +} + +static struct interface * +rip_ifaddr_lookup_next (struct in_addr *addr) +{ + struct prefix_ipv4 p; + struct route_node *rn; + struct interface *ifp; + + p.family = AF_INET; + p.prefixlen = IPV4_MAX_BITLEN; + p.prefix = *addr; + + rn = route_node_get (rip_ifaddr_table, (struct prefix *) &p); + + for (rn = route_next (rn); rn; rn = route_next (rn)) + if (rn->info) + break; + + if (rn && rn->info) + { + ifp = rn->info; + *addr = rn->p.u.prefix4; + route_unlock_node (rn); + return ifp; + } + return NULL; +} + +static struct interface * +rip2IfLookup (struct variable *v, oid name[], size_t *length, + struct in_addr *addr, int exact) +{ + int len; + struct interface *ifp; + + if (exact) + { + /* Check the length. */ + if (*length - v->namelen != sizeof (struct in_addr)) + return NULL; + + oid2in_addr (name + v->namelen, sizeof (struct in_addr), addr); + + return if_lookup_exact_address (*addr); + } + else + { + len = *length - v->namelen; + if (len > 4) len = 4; + + oid2in_addr (name + v->namelen, len, addr); + + ifp = rip_ifaddr_lookup_next (addr); + + if (ifp == NULL) + return NULL; + + oid_copy_addr (name + v->namelen, addr, sizeof (struct in_addr)); + + *length = v->namelen + sizeof (struct in_addr); + + return ifp; + } + return NULL; +} + +static struct rip_peer * +rip2PeerLookup (struct variable *v, oid name[], size_t *length, + struct in_addr *addr, int exact) +{ + int len; + struct rip_peer *peer; + + if (exact) + { + /* Check the length. */ + if (*length - v->namelen != sizeof (struct in_addr) + 1) + return NULL; + + oid2in_addr (name + v->namelen, sizeof (struct in_addr), addr); + + peer = rip_peer_lookup (addr); + + if (peer->domain == (int)name[v->namelen + sizeof (struct in_addr)]) + return peer; + + return NULL; + } + else + { + len = *length - v->namelen; + if (len > 4) len = 4; + + oid2in_addr (name + v->namelen, len, addr); + + len = *length - v->namelen; + peer = rip_peer_lookup (addr); + if (peer) + { + 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)); + name[v->namelen + sizeof (struct in_addr)] = peer->domain; + *length = sizeof (struct in_addr) + v->namelen + 1; + return peer; + } + } + peer = rip_peer_lookup_next (addr); + + if (! peer) + return NULL; + + oid_copy_addr (name + v->namelen, &peer->addr, + sizeof (struct in_addr)); + name[v->namelen + sizeof (struct in_addr)] = peer->domain; + *length = sizeof (struct in_addr) + v->namelen + 1; + + return peer; + } + return NULL; +} + +static u_char * +rip2IfStatEntry (struct variable *v, oid name[], size_t *length, + int exact, size_t *var_len, WriteMethod **write_method) +{ + struct interface *ifp; + struct rip_interface *ri; + 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. */ + ifp = rip2IfLookup (v, name, length, &addr, exact); + if (! ifp) + return NULL; + + /* Fetch rip_interface information. */ + ri = ifp->info; + + switch (v->magic) + { + case RIP2IFSTATADDRESS: + return SNMP_IPADDRESS (addr); + break; + case RIP2IFSTATRCVBADPACKETS: + *var_len = sizeof (long); + return (u_char *) &ri->recv_badpackets; + + case RIP2IFSTATRCVBADROUTES: + *var_len = sizeof (long); + return (u_char *) &ri->recv_badroutes; + + case RIP2IFSTATSENTUPDATES: + *var_len = sizeof (long); + return (u_char *) &ri->sent_updates; + + case RIP2IFSTATSTATUS: + *var_len = sizeof (long); + v->type = ASN_INTEGER; + return (u_char *) &valid; + + default: + return NULL; + + } + return NULL; +} + +static long +rip2IfConfSend (struct rip_interface *ri) +{ +#define doNotSend 1 +#define ripVersion1 2 +#define rip1Compatible 3 +#define ripVersion2 4 +#define ripV1Demand 5 +#define ripV2Demand 6 + + if (! ri->running) + return doNotSend; + + if (ri->ri_send & RIPv2) + return ripVersion2; + else if (ri->ri_send & RIPv1) + return ripVersion1; + else if (rip) + { + if (rip->version_send == RIPv2) + return ripVersion2; + else if (rip->version_send == RIPv1) + return ripVersion1; + } + return doNotSend; +} + +static long +rip2IfConfReceive (struct rip_interface *ri) +{ +#define rip1 1 +#define rip2 2 +#define rip1OrRip2 3 +#define doNotReceive 4 + + int recvv; + + if (! ri->running) + return doNotReceive; + + recvv = (ri->ri_receive == RI_RIP_UNSPEC) ? rip->version_recv : + ri->ri_receive; + if (recvv == RI_RIP_VERSION_1_AND_2) + return rip1OrRip2; + else if (recvv & RIPv2) + return rip2; + else if (recvv & RIPv1) + return rip1; + else + return doNotReceive; +} + +static u_char * +rip2IfConfAddress (struct variable *v, oid name[], size_t *length, + int exact, size_t *val_len, WriteMethod **write_method) +{ + static struct in_addr addr; + static long valid = SNMP_INVALID; + static long domain = 0; + static long config = 0; + static u_int auth = 0; + 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. */ + ifp = rip2IfLookup (v, name, length, &addr, exact); + if (! ifp) + return NULL; + + /* Fetch rip_interface information. */ + ri = ifp->info; + + switch (v->magic) + { + case RIP2IFCONFADDRESS: + *val_len = sizeof (struct in_addr); + return (u_char *) &addr; + + case RIP2IFCONFDOMAIN: + *val_len = 2; + return (u_char *) &domain; + + case RIP2IFCONFAUTHTYPE: + auth = ri->auth_type; + *val_len = sizeof (long); + v->type = ASN_INTEGER; + return (u_char *)&auth; + + case RIP2IFCONFAUTHKEY: + *val_len = 0; + return (u_char *) &domain; + case RIP2IFCONFSEND: + config = rip2IfConfSend (ri); + *val_len = sizeof (long); + v->type = ASN_INTEGER; + return (u_char *) &config; + case RIP2IFCONFRECEIVE: + config = rip2IfConfReceive (ri); + *val_len = sizeof (long); + v->type = ASN_INTEGER; + return (u_char *) &config; + + case RIP2IFCONFDEFAULTMETRIC: + *val_len = sizeof (long); + v->type = ASN_INTEGER; + return (u_char *) &ifp->metric; + case RIP2IFCONFSTATUS: + *val_len = sizeof (long); + v->type = ASN_INTEGER; + return (u_char *) &valid; + case RIP2IFCONFSRCADDRESS: + *val_len = sizeof (struct in_addr); + return (u_char *) &addr; + + default: + return NULL; + + } + return NULL; +} + +static u_char * +rip2PeerTable (struct variable *v, oid name[], size_t *length, + int exact, size_t *val_len, WriteMethod **write_method) +{ + static struct in_addr addr; + static int domain = 0; + static int version; + /* static time_t uptime; */ + + 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. */ + peer = rip2PeerLookup (v, name, length, &addr, exact); + if (! peer) + return NULL; + + switch (v->magic) + { + case RIP2PEERADDRESS: + *val_len = sizeof (struct in_addr); + return (u_char *) &peer->addr; + + case RIP2PEERDOMAIN: + *val_len = 2; + return (u_char *) &domain; + + case RIP2PEERLASTUPDATE: +#if 0 + /* We don't know the SNMP agent startup time. We have two choices here: + * - assume ripd startup time equals SNMP agent startup time + * - don't support this variable, at all + * Currently, we do the latter... + */ + *val_len = sizeof (time_t); + uptime = peer->uptime; /* now - snmp_agent_startup - peer->uptime */ + return (u_char *) &uptime; +#else + return (u_char *) NULL; +#endif + + case RIP2PEERVERSION: + *val_len = sizeof (int); + version = peer->version; + return (u_char *) &version; + + case RIP2PEERRCVBADPACKETS: + *val_len = sizeof (int); + return (u_char *) &peer->recv_badpackets; + + case RIP2PEERRCVBADROUTES: + *val_len = sizeof (int); + return (u_char *) &peer->recv_badroutes; + + default: + return NULL; + + } + return NULL; +} + +/* Register RIPv2-MIB. */ +void +rip_snmp_init () +{ + rip_ifaddr_table = route_table_init (); + + smux_init (master); + REGISTER_MIB("mibII/rip", rip_variables, variable, rip_oid); +} +#endif /* HAVE_SNMP */ diff --git a/ripd/rip_zebra.c b/ripd/rip_zebra.c new file mode 100644 index 0000000..0b51af5 --- /dev/null +++ b/ripd/rip_zebra.c @@ -0,0 +1,762 @@ +/* RIPd and zebra interface. + * Copyright (C) 1997, 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 "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; + +/* 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 (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); + 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 = rinfo->metric; + + 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 (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_add (struct route_node *rp) +{ + rip_zebra_ipv4_send (rp, ZEBRA_IPV4_ROUTE_ADD); +} + +/* 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, + 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; + + /* Type, flags, message. */ + api.type = stream_getc (s); + api.flags = stream_getc (s); + api.message = stream_getc (s); + + /* IPv4 prefix. */ + memset (&p, 0, sizeof (struct prefix_ipv4)); + p.family = AF_INET; + plength = stream_getc (s); + p.prefixlen = MIN(IPV4_MAX_PREFIXLEN, plength); + 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); + } + if (CHECK_FLAG (api.message, ZAPI_MESSAGE_DISTANCE)) + api.distance = stream_getc (s); + else + api.distance = 255; + if (CHECK_FLAG (api.message, ZAPI_MESSAGE_METRIC)) + api.metric = stream_getl (s); + 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, api.tag); + else + rip_redistribute_delete (api.type, RIP_ROUTE_REDISTRIBUTE, &p, ifindex); + + return 0; +} + +void +rip_zclient_reset (void) +{ + zclient_reset (zclient); +} + +/* RIP route-map set for redistribution */ +static void +rip_routemap_set (int type, const char *name) +{ + if (rip->route_map[type].name) + free(rip->route_map[type].name); + + rip->route_map[type].name = strdup (name); + rip->route_map[type].map = route_map_lookup_by_name (name); +} + +static void +rip_redistribute_metric_set (int type, unsigned int metric) +{ + rip->route_map[type].metric_config = 1; + rip->route_map[type].metric = metric; +} + +static int +rip_metric_unset (int type, unsigned int metric) +{ +#define DONT_CARE_METRIC_RIP 17 + if (metric != DONT_CARE_METRIC_RIP && + rip->route_map[type].metric != metric) + return 1; + rip->route_map[type].metric_config = 0; + rip->route_map[type].metric = 0; + return 0; +} + +/* RIP route-map unset for redistribution */ +static int +rip_routemap_unset (int type, const char *name) +{ + if (! rip->route_map[type].name || + (name != NULL && strcmp(rip->route_map[type].name,name))) + return 1; + + free (rip->route_map[type].name); + rip->route_map[type].name = NULL; + rip->route_map[type].map = NULL; + + return 0; +} + +/* Redistribution types */ +static struct { + int type; + int str_min_len; + const char *str; +} redist_type[] = { + {ZEBRA_ROUTE_KERNEL, 1, "kernel"}, + {ZEBRA_ROUTE_CONNECT, 1, "connected"}, + {ZEBRA_ROUTE_STATIC, 1, "static"}, + {ZEBRA_ROUTE_OSPF, 1, "ospf"}, + {ZEBRA_ROUTE_BGP, 2, "bgp"}, + {ZEBRA_ROUTE_BABEL, 2, "babel"}, + {0, 0, NULL} +}; + +DEFUN (router_zebra, + router_zebra_cmd, + "router zebra", + "Enable a routing process\n" + "Make connection to zebra daemon\n") +{ + vty->node = ZEBRA_NODE; + zclient->enable = 1; + zclient_start (zclient); + return CMD_SUCCESS; +} + +DEFUN (no_router_zebra, + no_router_zebra_cmd, + "no router zebra", + NO_STR + "Enable a routing process\n" + "Make connection to zebra daemon\n") +{ + zclient->enable = 0; + zclient_stop (zclient); + return CMD_SUCCESS; +} + +#if 0 +static int +rip_redistribute_set (int type) +{ + if (vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT)) + return CMD_SUCCESS; + + vrf_bitmap_set (zclient->redist[type], VRF_DEFAULT); + + if (zclient->sock > 0) + zebra_redistribute_send (ZEBRA_REDISTRIBUTE_ADD, zclient, type); + + return CMD_SUCCESS; +} +#endif + +static int +rip_redistribute_unset (int type) +{ + if (! vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT)) + return CMD_SUCCESS; + + vrf_bitmap_unset (zclient->redist[type], VRF_DEFAULT); + + if (zclient->sock > 0) + zebra_redistribute_send (ZEBRA_REDISTRIBUTE_DELETE, zclient, type, + VRF_DEFAULT); + + /* Remove the routes from RIP table. */ + rip_redistribute_withdraw (type); + + return CMD_SUCCESS; +} + +int +rip_redistribute_check (int type) +{ + return vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT); +} + +void +rip_redistribute_clean (void) +{ + int i; + + for (i = 0; redist_type[i].str; i++) + { + 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, + VRF_DEFAULT); + + vrf_bitmap_unset (zclient->redist[redist_type[i].type], VRF_DEFAULT); + + /* Remove the routes from RIP table. */ + rip_redistribute_withdraw (redist_type[i].type); + } + } +} + +DEFUN (rip_redistribute_rip, + rip_redistribute_rip_cmd, + "redistribute rip", + "Redistribute information from another routing protocol\n" + "Routing Information Protocol (RIP)\n") +{ + vrf_bitmap_set (zclient->redist[ZEBRA_ROUTE_RIP], VRF_DEFAULT); + return CMD_SUCCESS; +} + +DEFUN (no_rip_redistribute_rip, + no_rip_redistribute_rip_cmd, + "no redistribute rip", + NO_STR + "Redistribute information from another routing protocol\n" + "Routing Information Protocol (RIP)\n") +{ + vrf_bitmap_unset (zclient->redist[ZEBRA_ROUTE_RIP], VRF_DEFAULT); + return CMD_SUCCESS; +} + +DEFUN (rip_redistribute_type, + rip_redistribute_type_cmd, + "redistribute " QUAGGA_REDIST_STR_RIPD, + REDIST_STR + QUAGGA_REDIST_HELP_STR_RIPD) +{ + int i; + + for(i = 0; redist_type[i].str; i++) + { + if (strncmp (redist_type[i].str, argv[0], + redist_type[i].str_min_len) == 0) + { + zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, + redist_type[i].type, VRF_DEFAULT); + return CMD_SUCCESS; + } + } + + vty_out(vty, "Invalid type %s%s", argv[0], + VTY_NEWLINE); + + return CMD_WARNING; +} + +DEFUN (no_rip_redistribute_type, + no_rip_redistribute_type_cmd, + "no redistribute " QUAGGA_REDIST_STR_RIPD, + NO_STR + REDIST_STR + QUAGGA_REDIST_HELP_STR_RIPD) +{ + int i; + + for (i = 0; redist_type[i].str; i++) + { + if (strncmp(redist_type[i].str, argv[0], + redist_type[i].str_min_len) == 0) + { + rip_metric_unset (redist_type[i].type, DONT_CARE_METRIC_RIP); + rip_routemap_unset (redist_type[i].type,NULL); + rip_redistribute_unset (redist_type[i].type); + return CMD_SUCCESS; + } + } + + vty_out(vty, "Invalid type %s%s", argv[0], + VTY_NEWLINE); + + return CMD_WARNING; +} + +DEFUN (rip_redistribute_type_routemap, + rip_redistribute_type_routemap_cmd, + "redistribute " QUAGGA_REDIST_STR_RIPD " route-map WORD", + REDIST_STR + QUAGGA_REDIST_HELP_STR_RIPD + "Route map reference\n" + "Pointer to route-map entries\n") +{ + int i; + + for (i = 0; redist_type[i].str; i++) { + if (strncmp(redist_type[i].str, argv[0], + 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, + VRF_DEFAULT); + return CMD_SUCCESS; + } + } + + vty_out(vty, "Invalid type %s%s", argv[0], + VTY_NEWLINE); + + return CMD_WARNING; +} + +DEFUN (no_rip_redistribute_type_routemap, + no_rip_redistribute_type_routemap_cmd, + "no redistribute " QUAGGA_REDIST_STR_RIPD " route-map WORD", + NO_STR + REDIST_STR + QUAGGA_REDIST_HELP_STR_RIPD + "Route map reference\n" + "Pointer to route-map entries\n") +{ + int i; + + for (i = 0; redist_type[i].str; i++) + { + if (strncmp(redist_type[i].str, argv[0], + redist_type[i].str_min_len) == 0) + { + if (rip_routemap_unset (redist_type[i].type,argv[1])) + return CMD_WARNING; + rip_redistribute_unset (redist_type[i].type); + return CMD_SUCCESS; + } + } + + vty_out(vty, "Invalid type %s%s", argv[0], + VTY_NEWLINE); + + return CMD_WARNING; +} + +DEFUN (rip_redistribute_type_metric, + rip_redistribute_type_metric_cmd, + "redistribute " QUAGGA_REDIST_STR_RIPD " metric <0-16>", + REDIST_STR + QUAGGA_REDIST_HELP_STR_RIPD + "Metric\n" + "Metric value\n") +{ + int i; + int metric; + + metric = atoi (argv[1]); + + for (i = 0; redist_type[i].str; i++) { + if (strncmp(redist_type[i].str, argv[0], + 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, + VRF_DEFAULT); + return CMD_SUCCESS; + } + } + + vty_out(vty, "Invalid type %s%s", argv[0], + VTY_NEWLINE); + + return CMD_WARNING; +} + +DEFUN (no_rip_redistribute_type_metric, + no_rip_redistribute_type_metric_cmd, + "no redistribute " QUAGGA_REDIST_STR_RIPD " metric <0-16>", + NO_STR + REDIST_STR + QUAGGA_REDIST_HELP_STR_RIPD + "Metric\n" + "Metric value\n") +{ + int i; + + for (i = 0; redist_type[i].str; i++) + { + if (strncmp(redist_type[i].str, argv[0], + redist_type[i].str_min_len) == 0) + { + if (rip_metric_unset (redist_type[i].type, atoi(argv[1]))) + return CMD_WARNING; + rip_redistribute_unset (redist_type[i].type); + return CMD_SUCCESS; + } + } + + vty_out(vty, "Invalid type %s%s", argv[0], + VTY_NEWLINE); + + return CMD_WARNING; +} + +DEFUN (rip_redistribute_type_metric_routemap, + rip_redistribute_type_metric_routemap_cmd, + "redistribute " QUAGGA_REDIST_STR_RIPD " metric <0-16> route-map WORD", + REDIST_STR + QUAGGA_REDIST_HELP_STR_RIPD + "Metric\n" + "Metric value\n" + "Route map reference\n" + "Pointer to route-map entries\n") +{ + int i; + int metric; + + metric = atoi (argv[1]); + + for (i = 0; redist_type[i].str; i++) { + if (strncmp(redist_type[i].str, argv[0], + redist_type[i].str_min_len) == 0) + { + 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, + VRF_DEFAULT); + return CMD_SUCCESS; + } + } + + vty_out(vty, "Invalid type %s%s", argv[0], + VTY_NEWLINE); + + return CMD_WARNING; +} + + +DEFUN (no_rip_redistribute_type_metric_routemap, + no_rip_redistribute_type_metric_routemap_cmd, + "no redistribute " QUAGGA_REDIST_STR_RIPD + " metric <0-16> route-map WORD", + NO_STR + REDIST_STR + QUAGGA_REDIST_HELP_STR_RIPD + "Metric\n" + "Metric value\n" + "Route map reference\n" + "Pointer to route-map entries\n") +{ + int i; + + for (i = 0; redist_type[i].str; i++) + { + if (strncmp(redist_type[i].str, argv[0], + redist_type[i].str_min_len) == 0) + { + if (rip_metric_unset (redist_type[i].type, atoi(argv[1]))) + return CMD_WARNING; + if (rip_routemap_unset (redist_type[i].type, argv[2])) + { + rip_redistribute_metric_set(redist_type[i].type, atoi(argv[1])); + return CMD_WARNING; + } + rip_redistribute_unset (redist_type[i].type); + return CMD_SUCCESS; + } + } + + vty_out(vty, "Invalid type %s%s", argv[0], + VTY_NEWLINE); + + return CMD_WARNING; +} + +/* Default information originate. */ + +DEFUN (rip_default_information_originate, + rip_default_information_originate_cmd, + "default-information originate", + "Control distribution of default route\n" + "Distribute a default route\n") +{ + struct prefix_ipv4 p; + + if (! rip->default_information) + { + memset (&p, 0, sizeof (struct prefix_ipv4)); + p.family = AF_INET; + + rip->default_information = 1; + + rip_redistribute_add (ZEBRA_ROUTE_RIP, RIP_ROUTE_DEFAULT, &p, 0, + NULL, 0, 0, 0); + } + + return CMD_SUCCESS; +} + +DEFUN (no_rip_default_information_originate, + no_rip_default_information_originate_cmd, + "no default-information originate", + NO_STR + "Control distribution of default route\n" + "Distribute a default route\n") +{ + struct prefix_ipv4 p; + + if (rip->default_information) + { + memset (&p, 0, sizeof (struct prefix_ipv4)); + p.family = AF_INET; + + rip->default_information = 0; + + rip_redistribute_delete (ZEBRA_ROUTE_RIP, RIP_ROUTE_DEFAULT, &p, 0); + } + + return CMD_SUCCESS; +} + +/* RIP configuration write function. */ +static int +config_write_zebra (struct vty *vty) +{ + if (! zclient->enable) + { + vty_out (vty, "no router zebra%s", VTY_NEWLINE); + return 1; + } + 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); + return 1; + } + return 0; +} + +int +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 && + vrf_bitmap_check (zclient->redist[i], VRF_DEFAULT)) + { + if (config_mode) + { + if (rip->route_map[i].metric_config) + { + if (rip->route_map[i].name) + vty_out (vty, " redistribute %s metric %d route-map %s%s", + zebra_route_string(i), rip->route_map[i].metric, + rip->route_map[i].name, + VTY_NEWLINE); + else + vty_out (vty, " redistribute %s metric %d%s", + zebra_route_string(i), rip->route_map[i].metric, + VTY_NEWLINE); + } + else + { + if (rip->route_map[i].name) + vty_out (vty, " redistribute %s route-map %s%s", + zebra_route_string(i), rip->route_map[i].name, + VTY_NEWLINE); + else + vty_out (vty, " redistribute %s%s", zebra_route_string(i), + VTY_NEWLINE); + } + } + else + vty_out (vty, " %s", zebra_route_string(i)); + } + return 0; +} + +/* Zebra node structure. */ +static struct cmd_node zebra_node = +{ + ZEBRA_NODE, + "%s(config-router)# ", +}; + +static void +rip_zebra_connected (struct zclient *zclient) +{ + zclient_send_requests (zclient, VRF_DEFAULT); +} + +void +rip_zclient_init (struct thread_master *master) +{ + /* Set default value to the zebra client structure. */ + 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; + zclient->interface_address_delete = rip_interface_address_delete; + zclient->ipv4_route_add = rip_zebra_read_ipv4; + zclient->ipv4_route_delete = rip_zebra_read_ipv4; + zclient->interface_up = rip_interface_up; + zclient->interface_down = rip_interface_down; + + /* Install zebra node. */ + install_node (&zebra_node, config_write_zebra); + + /* Install command elements to zebra node. */ + install_element (CONFIG_NODE, &router_zebra_cmd); + install_element (CONFIG_NODE, &no_router_zebra_cmd); + install_default (ZEBRA_NODE); + install_element (ZEBRA_NODE, &rip_redistribute_rip_cmd); + install_element (ZEBRA_NODE, &no_rip_redistribute_rip_cmd); + + /* Install command elements to rip node. */ + install_element (RIP_NODE, &rip_redistribute_type_cmd); + install_element (RIP_NODE, &rip_redistribute_type_routemap_cmd); + install_element (RIP_NODE, &rip_redistribute_type_metric_cmd); + install_element (RIP_NODE, &rip_redistribute_type_metric_routemap_cmd); + install_element (RIP_NODE, &no_rip_redistribute_type_cmd); + install_element (RIP_NODE, &no_rip_redistribute_type_routemap_cmd); + install_element (RIP_NODE, &no_rip_redistribute_type_metric_cmd); + install_element (RIP_NODE, &no_rip_redistribute_type_metric_routemap_cmd); + install_element (RIP_NODE, &rip_default_information_originate_cmd); + install_element (RIP_NODE, &no_rip_default_information_originate_cmd); +} diff --git a/ripd/ripd.c b/ripd/ripd.c new file mode 100644 index 0000000..c073eca --- /dev/null +++ b/ripd/ripd.c @@ -0,0 +1,4143 @@ +/* RIP version 1 and 2. + * Copyright (C) 2005 6WIND + * 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. + */ + +#include + +#include "if.h" +#include "command.h" +#include "prefix.h" +#include "table.h" +#include "thread.h" +#include "memory.h" +#include "log.h" +#include "stream.h" +#include "filter.h" +#include "sockunion.h" +#include "sockopt.h" +#include "routemap.h" +#include "if_rmap.h" +#include "plist.h" +#include "distribute.h" +#include "md5.h" +#include "keychain.h" +#include "privs.h" + +#include "ripd/ripd.h" +#include "ripd/rip_debug.h" + +/* UDP receive buffer size */ +#define RIP_UDP_RCV_BUF 41600 + +/* privileges global */ +extern struct zebra_privs_t ripd_privs; + +/* RIP Structure. */ +struct rip *rip = NULL; + +/* RIP neighbor address table. */ +struct route_table *rip_neighbor_table; + +/* RIP route changes. */ +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[] = +{ + {RIP_REQUEST, "REQUEST"}, + {RIP_RESPONSE, "RESPONSE"}, + {RIP_TRACEON, "TRACEON"}, + {RIP_TRACEOFF, "TRACEOFF"}, + {RIP_POLL, "POLL"}, + {RIP_POLL_ENTRY, "POLL ENTRY"}, + {0, NULL}, +}; + +/* Utility function to set boradcast option to the socket. */ +static int +sockopt_broadcast (int sock) +{ + int ret; + int on = 1; + + ret = setsockopt (sock, SOL_SOCKET, SO_BROADCAST, (char *) &on, sizeof on); + if (ret < 0) + { + zlog_warn ("can't set sockopt SO_BROADCAST to socket %d", sock); + return -1; + } + return 0; +} + +static int +rip_route_rte (struct rip_info *rinfo) +{ + return (rinfo->type == ZEBRA_ROUTE_RIP && rinfo->sub_type == RIP_ROUTE_RTE); +} + +static struct rip_info * +rip_info_new (void) +{ + return XCALLOC (MTYPE_RIP_INFO, sizeof (struct rip_info)); +} + +void +rip_info_free (struct rip_info *rinfo) +{ + XFREE (MTYPE_RIP_INFO, rinfo); +} + +/* RIP route garbage collect timer. */ +static int +rip_garbage_collect (struct thread *t) +{ + struct rip_info *rinfo; + struct route_node *rp; + + rinfo = THREAD_ARG (t); + rinfo->t_garbage_collect = NULL; + + /* Off timeout timer. */ + RIP_TIMER_OFF (rinfo->t_timeout); + + /* Get route_node pointer. */ + rp = rinfo->rp; + + /* Unlock route_node. */ + 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); + + return 0; +} + +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 route_node *rp = rinfo_new->rp; + struct rip_info *rinfo = NULL; + struct list *list = NULL; + + if (rp->info == NULL) + rp->info = list_new (); + list = (struct list *)rp->info; + + /* If ECMP is not allowed and some entry already exists in the list, + * do nothing. */ + if (listcount (list) && !rip->ecmp) + return NULL; + + rinfo = rip_info_new (); + memcpy (rinfo, rinfo_new, sizeof (struct rip_info)); + listnode_add (list, rinfo); + + if (rip_route_rte (rinfo)) + { + rip_timeout_update (rinfo); + rip_zebra_ipv4_add (rp); + } + + /* Set the route change flag on the first entry. */ + rinfo = listgetdata (listhead (list)); + SET_FLAG (rinfo->flags, RIP_RTF_CHANGED); + + /* Signal the output process to trigger an update (see section 2.5). */ + rip_event (RIP_TRIGGERED_UPDATE, 0); + + return rinfo; +} + +/* Replace the ECMP list with the new route. + * RETURN: the new entry added in the list + */ +struct rip_info * +rip_ecmp_replace (struct rip_info *rinfo_new) +{ + struct route_node *rp = rinfo_new->rp; + struct list *list = (struct list *)rp->info; + struct rip_info *rinfo = NULL, *tmp_rinfo = NULL; + struct listnode *node = NULL, *nextnode = NULL; + + if (list == NULL || listcount (list) == 0) + return rip_ecmp_add (rinfo_new); + + /* Get the first entry */ + rinfo = listgetdata (listhead (list)); + + /* Learnt route replaced by a local one. Delete it from zebra. */ + if (rip_route_rte (rinfo) && !rip_route_rte (rinfo_new)) + if (CHECK_FLAG (rinfo->flags, RIP_RTF_FIB)) + rip_zebra_ipv4_delete (rp); + + /* Re-use the first entry, and delete the others. */ + for (ALL_LIST_ELEMENTS (list, node, nextnode, tmp_rinfo)) + if (tmp_rinfo != rinfo) + { + RIP_TIMER_OFF (tmp_rinfo->t_timeout); + RIP_TIMER_OFF (tmp_rinfo->t_garbage_collect); + list_delete_node (list, node); + rip_info_free (tmp_rinfo); + } + + RIP_TIMER_OFF (rinfo->t_timeout); + RIP_TIMER_OFF (rinfo->t_garbage_collect); + memcpy (rinfo, rinfo_new, sizeof (struct rip_info)); + + if (rip_route_rte (rinfo)) + { + rip_timeout_update (rinfo); + /* The ADD message implies an update. */ + rip_zebra_ipv4_add (rp); + } + + /* Set the route change flag. */ + SET_FLAG (rinfo->flags, RIP_RTF_CHANGED); + + /* Signal the output process to trigger an update (see section 2.5). */ + rip_event (RIP_TRIGGERED_UPDATE, 0); + + return rinfo; +} + +/* Delete one route from the ECMP list. + * RETURN: + * null - the entry is freed, and other entries exist in the list + * the entry - the entry is the last one in the list; its metric is set + * to INFINITY, and the garbage collector is started for it + */ +struct rip_info * +rip_ecmp_delete (struct rip_info *rinfo) +{ + struct route_node *rp = rinfo->rp; + struct list *list = (struct list *)rp->info; + + RIP_TIMER_OFF (rinfo->t_timeout); + + if (listcount (list) > 1) + { + /* Some other ECMP entries still exist. Just delete this entry. */ + RIP_TIMER_OFF (rinfo->t_garbage_collect); + listnode_delete (list, rinfo); + if (rip_route_rte (rinfo) && CHECK_FLAG (rinfo->flags, RIP_RTF_FIB)) + /* The ADD message implies the update. */ + rip_zebra_ipv4_add (rp); + rip_info_free (rinfo); + rinfo = NULL; + } + else + { + assert (rinfo == listgetdata (listhead (list))); + + /* This is the only entry left in the list. We must keep it in + * the list for garbage collection time, with INFINITY metric. */ + + rinfo->metric = RIP_METRIC_INFINITY; + RIP_TIMER_ON (rinfo->t_garbage_collect, + rip_garbage_collect, rip->garbage_time); + + if (rip_route_rte (rinfo) && CHECK_FLAG (rinfo->flags, RIP_RTF_FIB)) + rip_zebra_ipv4_delete (rp); + } + + /* Set the route change flag on the first entry. */ + rinfo = listgetdata (listhead (list)); + SET_FLAG (rinfo->flags, RIP_RTF_CHANGED); + + /* Signal the output process to trigger an update (see section 2.5). */ + rip_event (RIP_TRIGGERED_UPDATE, 0); + + return rinfo; +} + +/* Timeout RIP routes. */ +static int +rip_timeout (struct thread *t) +{ + rip_ecmp_delete ((struct rip_info *)THREAD_ARG (t)); + return 0; +} + +static void +rip_timeout_update (struct rip_info *rinfo) +{ + if (rinfo->metric != RIP_METRIC_INFINITY) + { + RIP_TIMER_OFF (rinfo->t_timeout); + RIP_TIMER_ON (rinfo->t_timeout, rip_timeout, rip->timeout_time); + } +} + +static int +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"; + + /* Input distribute-list filtering. */ + if (ri->list[rip_distribute]) + { + if (access_list_apply (ri->list[rip_distribute], + (struct prefix *) p) == FILTER_DENY) + { + if (IS_RIP_DEBUG_PACKET) + zlog_debug ("%s/%d filtered by distribute %s", + inet_ntoa (p->prefix), p->prefixlen, inout); + return -1; + } + } + if (ri->prefix[rip_distribute]) + { + if (prefix_list_apply (ri->prefix[rip_distribute], + (struct prefix *) p) == PREFIX_DENY) + { + if (IS_RIP_DEBUG_PACKET) + zlog_debug ("%s/%d filtered by prefix-list %s", + inet_ntoa (p->prefix), p->prefixlen, inout); + return -1; + } + } + + /* All interface filter check. */ + dist = distribute_lookup (NULL); + if (dist) + { + if (dist->list[distribute]) + { + alist = access_list_lookup (AFI_IP, dist->list[distribute]); + + if (alist) + { + if (access_list_apply (alist, (struct prefix *) p) == FILTER_DENY) + { + if (IS_RIP_DEBUG_PACKET) + zlog_debug ("%s/%d filtered by distribute %s", + inet_ntoa (p->prefix), p->prefixlen, inout); + return -1; + } + } + } + if (dist->prefix[distribute]) + { + 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 %s", + inet_ntoa (p->prefix), p->prefixlen, inout); + return -1; + } + } + } + } + return 0; +} + +/* Check nexthop address validity. */ +static int +rip_nexthop_check (struct in_addr *addr) +{ + struct listnode *node; + struct listnode *cnode; + struct interface *ifp; + struct connected *ifc; + struct prefix *p; + + /* If nexthop address matches local configured address then it is + invalid nexthop. */ + for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) + { + for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, ifc)) + { + p = ifc->address; + + if (p->family == AF_INET + && IPV4_ADDR_SAME (&p->u.prefix4, addr)) + return -1; + } + } + return 0; +} + +/* RIP add route to routing table. */ +static void +rip_rte_process (struct rte *rte, struct sockaddr_in *from, + struct interface *ifp) +{ + int ret; + struct prefix_ipv4 p; + struct route_node *rp; + struct rip_info *rinfo = NULL, newinfo; + struct rip_interface *ri; + struct in_addr *nexthop; + int same = 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)); + p.family = AF_INET; + p.prefix = rte->prefix; + p.prefixlen = ip_masklen (rte->mask); + + /* Make sure mask is applied. */ + apply_mask_ipv4 (&p); + + /* Apply input filters. */ + ri = ifp->info; + + 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; + + /* The object should be of the type of rip_info */ + ret = route_map_apply (ri->routemap[RIP_FILTER_IN], + (struct prefix *) &p, RMAP_RIP, &newinfo); + + if (ret == RMAP_DENYMATCH) + { + if (IS_RIP_DEBUG_PACKET) + zlog_debug ("RIP %s/%d is filtered by route-map in", + inet_ntoa (p.prefix), p.prefixlen); + return; + } + + /* Get back the object */ + rte->nexthop = newinfo.nexthop_out; + rte->tag = htons (newinfo.tag_out); /* XXX */ + rte->metric = newinfo.metric_out; /* XXX: the routemap uses the metric_out field */ + } + + /* Once the entry has been validated, update the metric by + adding the cost of the network on wich the message + arrived. If the result is greater than infinity, use infinity + (RFC2453 Sec. 3.9.2) */ + /* Zebra ripd can handle offset-list in. */ + ret = rip_offset_list_apply_in (&p, ifp, &rte->metric); + + /* If offset-list does not modify the metric use interface's + metric. */ + if (!ret) + rte->metric += ifp->metric ? ifp->metric : 1; + + if (rte->metric > RIP_METRIC_INFINITY) + rte->metric = RIP_METRIC_INFINITY; + + /* Set nexthop pointer. */ + if (rte->nexthop.s_addr == 0) + nexthop = &from->sin_addr; + else + nexthop = &rte->nexthop; + + /* Check if nexthop address is myself, then do nothing. */ + if (rip_nexthop_check (nexthop) < 0) + { + if (IS_RIP_DEBUG_PACKET) + zlog_debug ("Nexthop address %s is myself", inet_ntoa (*nexthop)); + return; + } + + /* 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. */ + 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) + { + /* Local static route. */ + if (rinfo->type == ZEBRA_ROUTE_RIP + && ((rinfo->sub_type == RIP_ROUTE_STATIC) || + (rinfo->sub_type == RIP_ROUTE_DEFAULT)) + && rinfo->metric != RIP_METRIC_INFINITY) + { + route_unlock_node (rp); + return; + } + + /* Redistributed route check. */ + if (rinfo->type != ZEBRA_ROUTE_RIP + && rinfo->metric != RIP_METRIC_INFINITY) + { + old_dist = rinfo->distance; + /* 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) + 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) + rip_ecmp_add (&newinfo); + } + else + { + /* Route is there but we are not sure the route is RIP or not. */ + + /* 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 + route, reinitialize the timeout. */ + same = (IPV4_ADDR_SAME (&rinfo->from, &from->sin_addr) + && (rinfo->ifindex == ifp->ifindex)); + + 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 + than the old one; or, if the new metric is lower than the old + one, or if the tag has been changed; or if there is a route + with a lower administrave distance; or an update of the + distance on the actual route; do the following actions: */ + if ((same && rinfo->metric != rte->metric) + || (rte->metric < rinfo->metric) + || ((same) + && (rinfo->metric == rte->metric) + && (newinfo.tag != rinfo->tag)) + || (old_dist > new_dist) + || ((old_dist != new_dist) && same)) + { + if (listcount (list) == 1) + { + if (newinfo.metric != RIP_METRIC_INFINITY) + rip_ecmp_replace (&newinfo); + else + rip_ecmp_delete (rinfo); + } + else + { + 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; + + assert (newinfo.metric != RIP_METRIC_INFINITY); + + 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); + + /* - 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 /* same & no change */ + rip_timeout_update (rinfo); + + /* Unlock tempolary lock of the route. */ + route_unlock_node (rp); + } +} + +/* Dump RIP packet */ +static void +rip_packet_dump (struct rip_packet *packet, int size, const char *sndrcv) +{ + caddr_t lim; + struct rte *rte; + const char *command_str; + char pbuf[BUFSIZ], nbuf[BUFSIZ]; + u_char netmask = 0; + u_char *p; + + /* Set command string. */ + if (packet->command > 0 && packet->command < RIP_COMMAND_MAX) + command_str = lookup (rip_msg, packet->command); + else + command_str = "unknown"; + + /* Dump packet header. */ + zlog_debug ("%s %s version %d packet size %d", + sndrcv, command_str, packet->version, size); + + /* Dump each routing table entry. */ + rte = packet->rte; + + for (lim = (caddr_t) packet + size; (caddr_t) rte < lim; rte++) + { + if (packet->version == RIPv2) + { + netmask = ip_masklen (rte->mask); + + if (rte->family == htons (RIP_FAMILY_AUTH)) + { + if (rte->tag == htons (RIP_AUTH_SIMPLE_PASSWORD)) + { + p = (u_char *)&rte->prefix; + + zlog_debug (" family 0x%X type %d auth string: %s", + ntohs (rte->family), ntohs (rte->tag), p); + } + else if (rte->tag == htons (RIP_AUTH_MD5)) + { + struct rip_md5_info *md5; + + md5 = (struct rip_md5_info *) &packet->rte; + + zlog_debug (" family 0x%X type %d (MD5 authentication)", + ntohs (md5->family), ntohs (md5->type)); + zlog_debug (" RIP-2 packet len %d Key ID %d" + " Auth Data len %d", + ntohs (md5->packet_len), md5->keyid, + md5->auth_len); + zlog_debug (" Sequence Number %ld", + (u_long) ntohl (md5->sequence)); + } + else if (rte->tag == htons (RIP_AUTH_DATA)) + { + p = (u_char *)&rte->prefix; + + 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", + p[0], p[1], p[2], p[3], p[4], p[5], p[6], + p[7], p[8], p[9], p[10], p[11], p[12], p[13], + p[14], p[15]); + } + else + { + zlog_debug (" family 0x%X type %d (Unknown auth type)", + ntohs (rte->family), ntohs (rte->tag)); + } + } + else + zlog_debug (" %s/%d -> %s family %d tag %d metric %ld", + inet_ntop (AF_INET, &rte->prefix, pbuf, BUFSIZ), + netmask, inet_ntop (AF_INET, &rte->nexthop, nbuf, + BUFSIZ), ntohs (rte->family), + ntohs (rte->tag), (u_long) ntohl (rte->metric)); + } + else + { + zlog_debug (" %s family %d tag %d metric %ld", + inet_ntop (AF_INET, &rte->prefix, pbuf, BUFSIZ), + ntohs (rte->family), ntohs (rte->tag), + (u_long)ntohl (rte->metric)); + } + } +} + +/* Check if the destination address is valid (unicast; not net 0 + or 127) (RFC2453 Section 3.9.2 - Page 26). But we don't + check net 0 because we accept default route. */ +static int +rip_destination_check (struct in_addr addr) +{ + u_int32_t destination; + + /* Convert to host byte order. */ + destination = ntohl (addr.s_addr); + + if (IPV4_NET127 (destination)) + return 0; + + /* Net 0 may match to the default route. */ + if (IPV4_NET0 (destination) && destination != 0) + return 0; + + /* Unicast address must belong to class A, B, C. */ + if (IN_CLASSA (destination)) + return 1; + if (IN_CLASSB (destination)) + return 1; + if (IN_CLASSC (destination)) + return 1; + + return 0; +} + +/* RIP version 2 authentication. */ +static int +rip_auth_simple_password (struct rte *rte, struct sockaddr_in *from, + struct interface *ifp) +{ + struct rip_interface *ri; + char *auth_str; + + if (IS_RIP_DEBUG_EVENT) + zlog_debug ("RIPv2 simple password authentication from %s", + inet_ntoa (from->sin_addr)); + + ri = ifp->info; + + if (ri->auth_type != RIP_AUTH_SIMPLE_PASSWORD + || rte->tag != htons(RIP_AUTH_SIMPLE_PASSWORD)) + return 0; + + /* Simple password authentication. */ + if (ri->auth_str) + { + auth_str = (char *) &rte->prefix; + + if (strncmp (auth_str, ri->auth_str, 16) == 0) + return 1; + } + if (ri->key_chain) + { + struct keychain *keychain; + struct key *key; + + keychain = keychain_lookup (ri->key_chain); + if (keychain == NULL) + return 0; + + key = key_match_for_accept (keychain, (char *) &rte->prefix); + if (key) + return 1; + } + return 0; +} + +/* RIP version 2 authentication with MD5. */ +static int +rip_auth_md5 (struct rip_packet *packet, struct sockaddr_in *from, + int length, struct interface *ifp) +{ + struct rip_interface *ri; + struct rip_md5_info *md5; + struct rip_md5_data *md5data; + struct keychain *keychain; + struct key *key; + MD5_CTX ctx; + u_char digest[RIP_AUTH_MD5_SIZE]; + u_int16_t packet_len; + char auth_str[RIP_AUTH_MD5_SIZE]; + + if (IS_RIP_DEBUG_EVENT) + zlog_debug ("RIPv2 MD5 authentication from %s", + inet_ntoa (from->sin_addr)); + + ri = ifp->info; + md5 = (struct rip_md5_info *) &packet->rte; + + /* Check auth type. */ + if (ri->auth_type != RIP_AUTH_MD5 || md5->type != htons(RIP_AUTH_MD5)) + return 0; + + /* If the authentication length is less than 16, then it must be wrong for + * any interpretation of rfc2082. Some implementations also interpret + * this as RIP_HEADER_SIZE+ RIP_AUTH_MD5_SIZE, aka RIP_AUTH_MD5_COMPAT_SIZE. + */ + if ( !((md5->auth_len == RIP_AUTH_MD5_SIZE) + || (md5->auth_len == RIP_AUTH_MD5_COMPAT_SIZE))) + { + if (IS_RIP_DEBUG_EVENT) + zlog_debug ("RIPv2 MD5 authentication, strange authentication " + "length field %d", md5->auth_len); + return 0; + } + + /* grab and verify check packet length */ + packet_len = ntohs (md5->packet_len); + + if (packet_len > (length - RIP_HEADER_SIZE - RIP_AUTH_MD5_SIZE)) + { + if (IS_RIP_DEBUG_EVENT) + zlog_debug ("RIPv2 MD5 authentication, packet length field %d " + "greater than received length %d!", + md5->packet_len, length); + return 0; + } + + /* retrieve authentication data */ + md5data = (struct rip_md5_data *) (((u_char *) packet) + packet_len); + + memset (auth_str, 0, RIP_AUTH_MD5_SIZE); + + if (ri->key_chain) + { + keychain = keychain_lookup (ri->key_chain); + if (keychain == NULL) + return 0; + + key = key_lookup_for_accept (keychain, md5->keyid); + if (key == NULL) + return 0; + + strncpy (auth_str, key->string, RIP_AUTH_MD5_SIZE); + } + else if (ri->auth_str) + strncpy (auth_str, ri->auth_str, RIP_AUTH_MD5_SIZE); + + if (auth_str[0] == 0) + return 0; + + /* MD5 digest authentication. */ + memset (&ctx, 0, sizeof(ctx)); + MD5Init(&ctx); + MD5Update(&ctx, packet, packet_len + RIP_HEADER_SIZE); + MD5Update(&ctx, auth_str, RIP_AUTH_MD5_SIZE); + MD5Final(digest, &ctx); + + if (memcmp (md5data->digest, digest, RIP_AUTH_MD5_SIZE) == 0) + return packet_len; + else + return 0; +} + +/* Pick correct auth string for sends, prepare auth_str buffer for use. + * (left justified and padded). + * + * presumes one of ri or key is valid, and that the auth strings they point + * to are nul terminated. If neither are present, auth_str will be fully + * zero padded. + * + */ +static void +rip_auth_prepare_str_send (struct rip_interface *ri, struct key *key, + char *auth_str, int len) +{ + assert (ri || key); + + memset (auth_str, 0, len); + if (key && key->string) + strncpy (auth_str, key->string, len); + else if (ri->auth_str) + strncpy (auth_str, ri->auth_str, len); + + return; +} + +/* Write RIPv2 simple password authentication information + * + * auth_str is presumed to be 2 bytes and correctly prepared + * (left justified and zero padded). + */ +static void +rip_auth_simple_write (struct stream *s, char *auth_str, int len) +{ + assert (s && len == RIP_AUTH_SIMPLE_SIZE); + + stream_putw (s, RIP_FAMILY_AUTH); + stream_putw (s, RIP_AUTH_SIMPLE_PASSWORD); + stream_put (s, auth_str, RIP_AUTH_SIMPLE_SIZE); + + return; +} + +/* write RIPv2 MD5 "authentication header" + * (uses the auth key data field) + * + * Digest offset field is set to 0. + * + * returns: offset of the digest offset field, which must be set when + * length to the auth-data MD5 digest is known. + */ +static size_t +rip_auth_md5_ah_write (struct stream *s, struct rip_interface *ri, + struct key *key) +{ + size_t doff = 0; + + assert (s && ri && ri->auth_type == RIP_AUTH_MD5); + + /* MD5 authentication. */ + stream_putw (s, RIP_FAMILY_AUTH); + stream_putw (s, RIP_AUTH_MD5); + + /* MD5 AH digest offset field. + * + * Set to placeholder value here, to true value when RIP-2 Packet length + * is known. Actual value is set in .....(). + */ + doff = stream_get_endp(s); + stream_putw (s, 0); + + /* Key ID. */ + if (key) + stream_putc (s, key->index % 256); + else + stream_putc (s, 1); + + /* Auth Data Len. Set 16 for MD5 authentication data. Older ripds + * however expect RIP_HEADER_SIZE + RIP_AUTH_MD5_SIZE so we allow for this + * to be configurable. + */ + stream_putc (s, ri->md5_auth_len); + + /* Sequence Number (non-decreasing). */ + /* RFC2080: The value used in the sequence number is + arbitrary, but two suggestions are the time of the + message's creation or a simple message counter. */ + stream_putl (s, time (NULL)); + + /* Reserved field must be zero. */ + stream_putl (s, 0); + stream_putl (s, 0); + + return doff; +} + +/* If authentication is in used, write the appropriate header + * returns stream offset to which length must later be written + * or 0 if this is not required + */ +static size_t +rip_auth_header_write (struct stream *s, struct rip_interface *ri, + struct key *key, char *auth_str, int len) +{ + assert (ri->auth_type != RIP_NO_AUTH); + + switch (ri->auth_type) + { + case RIP_AUTH_SIMPLE_PASSWORD: + rip_auth_prepare_str_send (ri, key, auth_str, len); + rip_auth_simple_write (s, auth_str, len); + return 0; + case RIP_AUTH_MD5: + return rip_auth_md5_ah_write (s, ri, key); + } + assert (1); + return 0; +} + +/* Write RIPv2 MD5 authentication data trailer */ +static void +rip_auth_md5_set (struct stream *s, struct rip_interface *ri, size_t doff, + char *auth_str, int authlen) +{ + unsigned long len; + MD5_CTX ctx; + unsigned char digest[RIP_AUTH_MD5_SIZE]; + + /* Make it sure this interface is configured as MD5 + authentication. */ + assert ((ri->auth_type == RIP_AUTH_MD5) && (authlen == RIP_AUTH_MD5_SIZE)); + assert (doff > 0); + + /* Get packet length. */ + len = stream_get_endp(s); + + /* Check packet length. */ + if (len < (RIP_HEADER_SIZE + RIP_RTE_SIZE)) + { + zlog_err ("rip_auth_md5_set(): packet length %ld is less than minimum length.", len); + return; + } + + /* Set the digest offset length in the header */ + stream_putw_at (s, doff, len); + + /* Set authentication data. */ + stream_putw (s, RIP_FAMILY_AUTH); + stream_putw (s, RIP_AUTH_DATA); + + /* Generate a digest for the RIP packet. */ + memset(&ctx, 0, sizeof(ctx)); + MD5Init(&ctx); + MD5Update(&ctx, STREAM_DATA (s), stream_get_endp (s)); + MD5Update(&ctx, auth_str, RIP_AUTH_MD5_SIZE); + MD5Final(digest, &ctx); + + /* Copy the digest to the packet. */ + stream_write (s, digest, RIP_AUTH_MD5_SIZE); +} + +/* RIP routing information. */ +static void +rip_response_process (struct rip_packet *packet, int size, + struct sockaddr_in *from, struct connected *ifc) +{ + caddr_t lim; + struct rte *rte; + struct prefix_ipv4 ifaddr; + struct prefix_ipv4 ifaddrclass; + int subnetted; + + memset(&ifaddr, 0, sizeof(ifaddr)); + /* We don't know yet. */ + subnetted = -1; + + /* The Response must be ignored if it is not from the RIP + port. (RFC2453 - Sec. 3.9.2)*/ + if (from->sin_port != htons(RIP_PORT_DEFAULT)) + { + zlog_info ("response doesn't come from RIP port: %d", + from->sin_port); + rip_peer_bad_packet (from); + return; + } + + /* The datagram's IPv4 source address should be checked to see + whether the datagram is from a valid neighbor; the source of the + datagram must be on a directly connected network (RFC2453 - Sec. 3.9.2) */ + if (if_lookup_address(from->sin_addr) == NULL) + { + zlog_info ("This datagram doesn't came from a valid neighbor: %s", + inet_ntoa (from->sin_addr)); + rip_peer_bad_packet (from); + return; + } + + /* It is also worth checking to see whether the response is from one + of the router's own addresses. */ + + ; /* Alredy done in rip_read () */ + + /* Update RIP peer. */ + rip_peer_update (from, packet->version); + + /* Set RTE pointer. */ + rte = packet->rte; + + for (lim = (caddr_t) packet + size; (caddr_t) rte < lim; rte++) + { + /* RIPv2 authentication check. */ + /* If the Address Family Identifier of the first (and only the + first) entry in the message is 0xFFFF, then the remainder of + the entry contains the authentication. */ + /* If the packet gets here it means authentication enabled */ + /* Check is done in rip_read(). So, just skipping it */ + if (packet->version == RIPv2 && + rte == packet->rte && + rte->family == htons(RIP_FAMILY_AUTH)) + continue; + + if (rte->family != htons(AF_INET)) + { + /* Address family check. RIP only supports AF_INET. */ + zlog_info ("Unsupported family %d from %s.", + ntohs (rte->family), inet_ntoa (from->sin_addr)); + continue; + } + + /* - is the destination address valid (e.g., unicast; not net 0 + or 127) */ + if (! rip_destination_check (rte->prefix)) + { + zlog_info ("Network is net 0 or net 127 or it is not unicast network"); + rip_peer_bad_route (from); + continue; + } + + /* Convert metric value to host byte order. */ + rte->metric = ntohl (rte->metric); + + /* - is the metric valid (i.e., between 1 and 16, inclusive) */ + if (! (rte->metric >= 1 && rte->metric <= 16)) + { + zlog_info ("Route's metric is not in the 1-16 range."); + rip_peer_bad_route (from); + continue; + } + + /* RIPv1 does not have nexthop value. */ + if (packet->version == RIPv1 && rte->nexthop.s_addr != 0) + { + zlog_info ("RIPv1 packet with nexthop value %s", + inet_ntoa (rte->nexthop)); + rip_peer_bad_route (from); + continue; + } + + /* That is, if the provided information is ignored, a possibly + sub-optimal, but absolutely valid, route may be taken. If + the received Next Hop is not directly reachable, it should be + treated as 0.0.0.0. */ + if (packet->version == RIPv2 && rte->nexthop.s_addr != 0) + { + u_int32_t addrval; + + /* Multicast address check. */ + addrval = ntohl (rte->nexthop.s_addr); + if (IN_CLASSD (addrval)) + { + zlog_info ("Nexthop %s is multicast address, skip this rte", + inet_ntoa (rte->nexthop)); + continue; + } + + if (! if_lookup_address (rte->nexthop)) + { + struct route_node *rn; + struct rip_info *rinfo; + + rn = route_node_match_ipv4 (rip->table, &rte->nexthop); + + if (rn) + { + rinfo = rn->info; + + if (rinfo->type == ZEBRA_ROUTE_RIP + && rinfo->sub_type == RIP_ROUTE_RTE) + { + if (IS_RIP_DEBUG_EVENT) + zlog_debug ("Next hop %s is on RIP network. Set nexthop to the packet's originator", inet_ntoa (rte->nexthop)); + rte->nexthop = rinfo->from; + } + else + { + if (IS_RIP_DEBUG_EVENT) + zlog_debug ("Next hop %s is not directly reachable. Treat it as 0.0.0.0", inet_ntoa (rte->nexthop)); + rte->nexthop.s_addr = 0; + } + + route_unlock_node (rn); + } + else + { + if (IS_RIP_DEBUG_EVENT) + zlog_debug ("Next hop %s is not directly reachable. Treat it as 0.0.0.0", inet_ntoa (rte->nexthop)); + rte->nexthop.s_addr = 0; + } + + } + } + + /* For RIPv1, there won't be a valid netmask. + + This is a best guess at the masks. If everyone was using old + Ciscos before the 'ip subnet zero' option, it would be almost + right too :-) + + Cisco summarize ripv1 advertisments to the classful boundary + (/16 for class B's) except when the RIP packet does to inside + the classful network in question. */ + + if ((packet->version == RIPv1 && rte->prefix.s_addr != 0) + || (packet->version == RIPv2 + && (rte->prefix.s_addr != 0 && rte->mask.s_addr == 0))) + { + u_int32_t destination; + + if (subnetted == -1) + { + memcpy (&ifaddr, ifc->address, sizeof (struct prefix_ipv4)); + memcpy (&ifaddrclass, &ifaddr, sizeof (struct prefix_ipv4)); + apply_classful_mask_ipv4 (&ifaddrclass); + subnetted = 0; + if (ifaddr.prefixlen > ifaddrclass.prefixlen) + subnetted = 1; + } + + destination = ntohl (rte->prefix.s_addr); + + if (IN_CLASSA (destination)) + masklen2ip (8, &rte->mask); + else if (IN_CLASSB (destination)) + masklen2ip (16, &rte->mask); + else if (IN_CLASSC (destination)) + masklen2ip (24, &rte->mask); + + if (subnetted == 1) + masklen2ip (ifaddrclass.prefixlen, + (struct in_addr *) &destination); + if ((subnetted == 1) && ((rte->prefix.s_addr & destination) == + ifaddrclass.prefix.s_addr)) + { + masklen2ip (ifaddr.prefixlen, &rte->mask); + if ((rte->prefix.s_addr & rte->mask.s_addr) != rte->prefix.s_addr) + masklen2ip (32, &rte->mask); + if (IS_RIP_DEBUG_EVENT) + zlog_debug ("Subnetted route %s", inet_ntoa (rte->prefix)); + } + else + { + if ((rte->prefix.s_addr & rte->mask.s_addr) != rte->prefix.s_addr) + continue; + } + + if (IS_RIP_DEBUG_EVENT) + { + zlog_debug ("Resultant route %s", inet_ntoa (rte->prefix)); + zlog_debug ("Resultant mask %s", inet_ntoa (rte->mask)); + } + } + + /* In case of RIPv2, if prefix in RTE is not netmask applied one + ignore the entry. */ + if ((packet->version == RIPv2) + && (rte->mask.s_addr != 0) + && ((rte->prefix.s_addr & rte->mask.s_addr) != rte->prefix.s_addr)) + { + zlog_warn ("RIPv2 address %s is not mask /%d applied one", + inet_ntoa (rte->prefix), ip_masklen (rte->mask)); + rip_peer_bad_route (from); + continue; + } + + /* Default route sanity check */ + if (packet->version == RIPv2 + && (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); + } +} + +/* Make socket for RIP protocol. */ +static int +rip_create_socket (struct sockaddr_in *from) +{ + int ret; + int sock; + struct sockaddr_in addr; + + memset (&addr, 0, sizeof (struct sockaddr_in)); + + if (!from) + { + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + addr.sin_len = sizeof (struct sockaddr_in); +#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ + } else { + memcpy(&addr, from, sizeof(addr)); + } + + /* sending port must always be the RIP port */ + addr.sin_port = htons (RIP_PORT_DEFAULT); + + /* Make datagram socket. */ + sock = socket (AF_INET, SOCK_DGRAM, 0); + if (sock < 0) + { + zlog_err("Cannot create UDP socket: %s", safe_strerror(errno)); + exit (1); + } + + sockopt_broadcast (sock); + sockopt_reuseaddr (sock); + sockopt_reuseport (sock); +#ifdef RIP_RECVMSG + setsockopt_pktinfo (sock); +#endif /* RIP_RECVMSG */ +#ifdef IPTOS_PREC_INTERNETCONTROL + setsockopt_ipv4_tos (sock, IPTOS_PREC_INTERNETCONTROL); +#endif + + if (ripd_privs.change (ZPRIVS_RAISE)) + zlog_err ("rip_create_socket: could not raise privs"); + setsockopt_so_recvbuf (sock, RIP_UDP_RCV_BUF); + if ( (ret = bind (sock, (struct sockaddr *) & addr, sizeof (addr))) < 0) + + { + int save_errno = errno; + if (ripd_privs.change (ZPRIVS_LOWER)) + zlog_err ("rip_create_socket: could not lower privs"); + + zlog_err("%s: Can't bind socket %d to %s port %d: %s", __func__, + sock, inet_ntoa(addr.sin_addr), + (int) ntohs(addr.sin_port), + safe_strerror(save_errno)); + + close (sock); + return ret; + } + + if (ripd_privs.change (ZPRIVS_LOWER)) + zlog_err ("rip_create_socket: could not lower privs"); + + return sock; +} + +/* RIP packet send to destination address, on interface denoted by + * by connected argument. NULL to argument denotes destination should be + * should be RIP multicast group + */ +static int +rip_send_packet (u_char * buf, int size, struct sockaddr_in *to, + struct connected *ifc) +{ + int ret, send_sock; + struct sockaddr_in sin; + + assert (ifc != NULL); + + if (IS_RIP_DEBUG_PACKET) + { +#define ADDRESS_SIZE 20 + char dst[ADDRESS_SIZE]; + dst[ADDRESS_SIZE - 1] = '\0'; + + if (to) + { + strncpy (dst, inet_ntoa(to->sin_addr), ADDRESS_SIZE - 1); + } + else + { + sin.sin_addr.s_addr = htonl (INADDR_RIP_GROUP); + strncpy (dst, inet_ntoa(sin.sin_addr), ADDRESS_SIZE - 1); + } +#undef ADDRESS_SIZE + zlog_debug("rip_send_packet %s > %s (%s)", + inet_ntoa(ifc->address->u.prefix4), + dst, ifc->ifp->name); + } + + if ( CHECK_FLAG (ifc->flags, ZEBRA_IFA_SECONDARY) ) + { + /* + * ZEBRA_IFA_SECONDARY is set on linux when an interface is configured + * with multiple addresses on the same subnet: the first address + * on the subnet is configured "primary", and all subsequent addresses + * on that subnet are treated as "secondary" addresses. + * In order to avoid routing-table bloat on other rip listeners, + * we do not send out RIP packets with ZEBRA_IFA_SECONDARY source addrs. + * XXX Since Linux is the only system for which the ZEBRA_IFA_SECONDARY + * flag is set, we would end up sending a packet for a "secondary" + * source address on non-linux systems. + */ + if (IS_RIP_DEBUG_PACKET) + zlog_debug("duplicate dropped"); + return 0; + } + + /* Make destination address. */ + memset (&sin, 0, sizeof (struct sockaddr_in)); + sin.sin_family = AF_INET; +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + sin.sin_len = sizeof (struct sockaddr_in); +#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ + + /* When destination is specified, use it's port and address. */ + if (to) + { + sin.sin_port = to->sin_port; + sin.sin_addr = to->sin_addr; + send_sock = rip->sock; + } + else + { + struct sockaddr_in from; + + sin.sin_port = htons (RIP_PORT_DEFAULT); + 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; +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + from.sin_len = sizeof (struct sockaddr_in); +#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ + + /* + * we have to open a new socket for each packet because this + * is the most portable way to bind to a different source + * ipv4 address for each packet. + */ + if ( (send_sock = rip_create_socket (&from)) < 0) + { + zlog_warn("rip_send_packet could not create socket."); + return -1; + } + rip_interface_multicast_set (send_sock, ifc); + } + + ret = sendto (send_sock, buf, size, 0, (struct sockaddr *)&sin, + sizeof (struct sockaddr_in)); + + if (IS_RIP_DEBUG_EVENT) + zlog_debug ("SEND to %s.%d", inet_ntoa(sin.sin_addr), + ntohs (sin.sin_port)); + + if (ret < 0) + zlog_warn ("can't send packet : %s", safe_strerror (errno)); + + if (!to) + close(send_sock); + + return ret; +} + +/* Add redistributed route to RIP table. */ +void +rip_redistribute_add (int type, int sub_type, struct prefix_ipv4 *p, + ifindex_t ifindex, struct in_addr *nexthop, + unsigned int metric, unsigned char distance, + route_tag_t tag) +{ + int ret; + struct route_node *rp = NULL; + struct rip_info *rinfo = NULL, newinfo; + struct list *list = NULL; + + /* Redistribute route */ + ret = rip_destination_check (p->prefix); + if (! ret) + return; + + rp = route_node_get (rip->table, (struct prefix *) p); + + 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 ((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) + { + route_unlock_node (rp); + return; + } + + /* Manually configured RIP route check. */ + if (rinfo->type == ZEBRA_ROUTE_RIP + && ((rinfo->sub_type == RIP_ROUTE_STATIC) || + (rinfo->sub_type == RIP_ROUTE_DEFAULT)) ) + { + if (type != ZEBRA_ROUTE_RIP || ((sub_type != RIP_ROUTE_STATIC) && + (sub_type != RIP_ROUTE_DEFAULT))) + { + route_unlock_node (rp); + return; + } + } + + rinfo = rip_ecmp_replace (&newinfo); + route_unlock_node (rp); + } + else + rinfo = rip_ecmp_add (&newinfo); + + if (IS_RIP_DEBUG_EVENT) { + if (!nexthop) + zlog_debug ("Redistribute new prefix %s/%d on the interface %s", + inet_ntoa(p->prefix), p->prefixlen, + ifindex2ifname(ifindex)); + else + zlog_debug ("Redistribute new prefix %s/%d with nexthop %s on the interface %s", + inet_ntoa(p->prefix), p->prefixlen, inet_ntoa(rinfo->nexthop), + 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, + ifindex_t ifindex) +{ + int ret; + struct route_node *rp; + struct rip_info *rinfo; + + ret = rip_destination_check (p->prefix); + if (! ret) + return; + + rp = route_node_lookup (rip->table, (struct prefix *) p); + if (rp) + { + 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 = 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)); + + rip_event (RIP_TRIGGERED_UPDATE, 0); + } + } + route_unlock_node (rp); + } +} + +/* Response to request called from rip_read ().*/ +static void +rip_request_process (struct rip_packet *packet, int size, + struct sockaddr_in *from, struct connected *ifc) +{ + caddr_t lim; + struct rte *rte; + struct prefix_ipv4 p; + struct route_node *rp; + struct rip_info *rinfo; + struct rip_interface *ri; + + /* Does not reponse to the requests on the loopback interfaces */ + if (if_is_loopback (ifc->ifp)) + return; + + /* Check RIP process is enabled on this interface. */ + ri = ifc->ifp->info; + if (! ri->running) + return; + + /* When passive interface is specified, suppress responses */ + if (ri->passive) + return; + + /* RIP peer update. */ + rip_peer_update (from, packet->version); + + lim = ((caddr_t) packet) + size; + rte = packet->rte; + + /* The Request is processed entry by entry. If there are no + entries, no response is given. */ + if (lim == (caddr_t) rte) + return; + + /* There is one special case. If there is exactly one entry in the + request, and it has an address family identifier of zero and a + metric of infinity (i.e., 16), then this is a request to send the + entire routing table. */ + if (lim == ((caddr_t) (rte + 1)) && + ntohs (rte->family) == 0 && + ntohl (rte->metric) == RIP_METRIC_INFINITY) + { + /* All route with split horizon */ + rip_output_process (ifc, from, rip_all_route, packet->version); + } + else + { + /* Examine the list of RTEs in the Request one by one. For each + entry, look up the destination in the router's routing + database and, if there is a route, put that route's metric in + the metric field of the RTE. If there is no explicit route + to the specified destination, put infinity in the metric + field. Once all the entries have been filled in, change the + command from Request to Response and send the datagram back + to the requestor. */ + p.family = AF_INET; + + for (; ((caddr_t) rte) < lim; rte++) + { + p.prefix = rte->prefix; + p.prefixlen = ip_masklen (rte->mask); + apply_mask_ipv4 (&p); + + rp = route_node_lookup (rip->table, (struct prefix *) &p); + if (rp) + { + rinfo = listgetdata (listhead ((struct list *)rp->info)); + rte->metric = htonl (rinfo->metric); + route_unlock_node (rp); + } + else + rte->metric = htonl (RIP_METRIC_INFINITY); + } + packet->command = RIP_RESPONSE; + + rip_send_packet ((u_char *)packet, size, from, ifc); + } + rip_global_queries++; +} + +#if RIP_RECVMSG +/* Set IPv6 packet info to the socket. */ +static int +setsockopt_pktinfo (int sock) +{ + int ret; + int val = 1; + + ret = setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &val, sizeof(val)); + if (ret < 0) + zlog_warn ("Can't setsockopt IP_PKTINFO : %s", safe_strerror (errno)); + return ret; +} + +/* Read RIP packet by recvmsg function. */ +int +rip_recvmsg (int sock, u_char *buf, int size, struct sockaddr_in *from, + ifindex_t *ifindex) +{ + int ret; + struct msghdr msg; + struct iovec iov; + struct cmsghdr *ptr; + char adata[1024]; + + msg.msg_name = (void *) from; + msg.msg_namelen = sizeof (struct sockaddr_in); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = (void *) adata; + msg.msg_controllen = sizeof adata; + iov.iov_base = buf; + iov.iov_len = size; + + ret = recvmsg (sock, &msg, 0); + if (ret < 0) + return ret; + + for (ptr = ZCMSG_FIRSTHDR(&msg); ptr != NULL; ptr = CMSG_NXTHDR(&msg, ptr)) + if (ptr->cmsg_level == IPPROTO_IP && ptr->cmsg_type == IP_PKTINFO) + { + struct in_pktinfo *pktinfo; + int i; + + pktinfo = (struct in_pktinfo *) CMSG_DATA (ptr); + i = pktinfo->ipi_ifindex; + } + return ret; +} + +/* RIP packet read function. */ +int +rip_read_new (struct thread *t) +{ + int ret; + int sock; + char buf[RIP_PACKET_MAXSIZ]; + struct sockaddr_in from; + ifindex_t ifindex; + + /* Fetch socket then register myself. */ + sock = THREAD_FD (t); + rip_event (RIP_READ, sock); + + /* Read RIP packet. */ + ret = rip_recvmsg (sock, buf, RIP_PACKET_MAXSIZ, &from, (int *)&ifindex); + if (ret < 0) + { + zlog_warn ("Can't read RIP packet: %s", safe_strerror (errno)); + return ret; + } + + return ret; +} +#endif /* RIP_RECVMSG */ + +/* First entry point of RIP packet. */ +static int +rip_read (struct thread *t) +{ + int sock; + int ret; + int rtenum; + union rip_buf rip_buf; + struct rip_packet *packet; + struct sockaddr_in from; + int len; + int vrecv; + socklen_t fromlen; + struct interface *ifp; + struct connected *ifc; + struct rip_interface *ri; + + /* Fetch socket then register myself. */ + sock = THREAD_FD (t); + rip->t_read = NULL; + + /* Add myself to tne next event */ + rip_event (RIP_READ, sock); + + /* RIPd manages only IPv4. */ + memset (&from, 0, sizeof (struct sockaddr_in)); + fromlen = sizeof (struct sockaddr_in); + + len = recvfrom (sock, (char *)&rip_buf.buf, sizeof (rip_buf.buf), 0, + (struct sockaddr *) &from, &fromlen); + if (len < 0) + { + zlog_info ("recvfrom failed: %s", safe_strerror (errno)); + return len; + } + + /* Check is this packet comming from myself? */ + if (if_check_address (from.sin_addr)) + { + if (IS_RIP_DEBUG_PACKET) + zlog_debug ("ignore packet comes from myself"); + return -1; + } + + /* Which interface is this packet comes from. */ + ifp = if_lookup_address (from.sin_addr); + + /* RIP packet received */ + if (IS_RIP_DEBUG_EVENT) + zlog_debug ("RECV packet from %s port %d on %s", + inet_ntoa (from.sin_addr), ntohs (from.sin_port), + ifp ? ifp->name : "unknown"); + + /* If this packet come from unknown interface, ignore it. */ + if (ifp == NULL) + { + zlog_info ("rip_read: cannot find interface for packet from %s port %d", + inet_ntoa(from.sin_addr), ntohs (from.sin_port)); + return -1; + } + + ifc = connected_lookup_address (ifp, from.sin_addr); + + if (ifc == NULL) + { + zlog_info ("rip_read: cannot find connected address for packet from %s " + "port %d on interface %s", + inet_ntoa(from.sin_addr), ntohs (from.sin_port), ifp->name); + return -1; + } + + /* Packet length check. */ + if (len < RIP_PACKET_MINSIZ) + { + zlog_warn ("packet size %d is smaller than minimum size %d", + len, RIP_PACKET_MINSIZ); + rip_peer_bad_packet (&from); + return len; + } + if (len > RIP_PACKET_MAXSIZ) + { + zlog_warn ("packet size %d is larger than max size %d", + len, RIP_PACKET_MAXSIZ); + rip_peer_bad_packet (&from); + return len; + } + + /* Packet alignment check. */ + if ((len - RIP_PACKET_MINSIZ) % 20) + { + zlog_warn ("packet size %d is wrong for RIP packet alignment", len); + rip_peer_bad_packet (&from); + return len; + } + + /* Set RTE number. */ + rtenum = ((len - RIP_PACKET_MINSIZ) / 20); + + /* For easy to handle. */ + packet = &rip_buf.rip_packet; + + /* RIP version check. */ + if (packet->version == 0) + { + zlog_info ("version 0 with command %d received.", packet->command); + rip_peer_bad_packet (&from); + return -1; + } + + /* Dump RIP packet. */ + if (IS_RIP_DEBUG_RECV) + rip_packet_dump (packet, len, "RECV"); + + /* RIP version adjust. This code should rethink now. RFC1058 says + that "Version 1 implementations are to ignore this extra data and + process only the fields specified in this document.". So RIPv3 + packet should be treated as RIPv1 ignoring must be zero field. */ + if (packet->version > RIPv2) + packet->version = RIPv2; + + /* Is RIP running or is this RIP neighbor ?*/ + ri = ifp->info; + if (! ri->running && ! rip_neighbor_lookup (&from)) + { + if (IS_RIP_DEBUG_EVENT) + zlog_debug ("RIP is not enabled on interface %s.", ifp->name); + rip_peer_bad_packet (&from); + return -1; + } + + /* RIP Version check. RFC2453, 4.6 and 5.1 */ + vrecv = ((ri->ri_receive == RI_RIP_UNSPEC) ? + rip->version_recv : ri->ri_receive); + if ((packet->version == RIPv1) && !(vrecv & RIPv1)) + { + if (IS_RIP_DEBUG_PACKET) + zlog_debug (" packet's v%d doesn't fit to if version spec", + packet->version); + rip_peer_bad_packet (&from); + return -1; + } + if ((packet->version == RIPv2) && !(vrecv & RIPv2)) + { + if (IS_RIP_DEBUG_PACKET) + zlog_debug (" packet's v%d doesn't fit to if version spec", + packet->version); + rip_peer_bad_packet (&from); + return -1; + } + + /* RFC2453 5.2 If the router is not configured to authenticate RIP-2 + messages, then RIP-1 and unauthenticated RIP-2 messages will be + accepted; authenticated RIP-2 messages shall be discarded. */ + if ((ri->auth_type == RIP_NO_AUTH) + && rtenum + && (packet->version == RIPv2) + && (packet->rte->family == htons(RIP_FAMILY_AUTH))) + { + if (IS_RIP_DEBUG_EVENT) + zlog_debug ("packet RIPv%d is dropped because authentication disabled", + packet->version); + rip_peer_bad_packet (&from); + return -1; + } + + /* RFC: + If the router is configured to authenticate RIP-2 messages, then + RIP-1 messages and RIP-2 messages which pass authentication + testing shall be accepted; unauthenticated and failed + authentication RIP-2 messages shall be discarded. For maximum + security, RIP-1 messages should be ignored when authentication is + in use (see section 4.1); otherwise, the routing information from + authenticated messages will be propagated by RIP-1 routers in an + unauthenticated manner. + */ + /* We make an exception for RIPv1 REQUEST packets, to which we'll + * always reply regardless of authentication settings, because: + * + * - if there other authorised routers on-link, the REQUESTor can + * passively obtain the routing updates anyway + * - if there are no other authorised routers on-link, RIP can + * easily be disabled for the link to prevent giving out information + * on state of this routers RIP routing table.. + * + * I.e. if RIPv1 has any place anymore these days, it's as a very + * simple way to distribute routing information (e.g. to embedded + * hosts / appliances) and the ability to give out RIPv1 + * routing-information freely, while still requiring RIPv2 + * authentication for any RESPONSEs might be vaguely useful. + */ + if (ri->auth_type != RIP_NO_AUTH + && packet->version == RIPv1) + { + /* Discard RIPv1 messages other than REQUESTs */ + if (packet->command != RIP_REQUEST) + { + if (IS_RIP_DEBUG_PACKET) + zlog_debug ("RIPv1" " dropped because authentication enabled"); + rip_peer_bad_packet (&from); + return -1; + } + } + else if (ri->auth_type != RIP_NO_AUTH) + { + const char *auth_desc; + + if (rtenum == 0) + { + /* There definitely is no authentication in the packet. */ + if (IS_RIP_DEBUG_PACKET) + zlog_debug ("RIPv2 authentication failed: no auth RTE in packet"); + rip_peer_bad_packet (&from); + return -1; + } + + /* First RTE must be an Authentication Family RTE */ + if (packet->rte->family != htons(RIP_FAMILY_AUTH)) + { + if (IS_RIP_DEBUG_PACKET) + zlog_debug ("RIPv2" " dropped because authentication enabled"); + rip_peer_bad_packet (&from); + return -1; + } + + /* Check RIPv2 authentication. */ + switch (ntohs(packet->rte->tag)) + { + case RIP_AUTH_SIMPLE_PASSWORD: + auth_desc = "simple"; + ret = rip_auth_simple_password (packet->rte, &from, ifp); + break; + + case RIP_AUTH_MD5: + auth_desc = "MD5"; + ret = rip_auth_md5 (packet, &from, len, ifp); + /* Reset RIP packet length to trim MD5 data. */ + len = ret; + break; + + default: + ret = 0; + auth_desc = "unknown type"; + if (IS_RIP_DEBUG_PACKET) + zlog_debug ("RIPv2 Unknown authentication type %d", + ntohs (packet->rte->tag)); + } + + if (ret) + { + if (IS_RIP_DEBUG_PACKET) + zlog_debug ("RIPv2 %s authentication success", auth_desc); + } + else + { + if (IS_RIP_DEBUG_PACKET) + zlog_debug ("RIPv2 %s authentication failure", auth_desc); + rip_peer_bad_packet (&from); + return -1; + } + } + + /* Process each command. */ + switch (packet->command) + { + case RIP_RESPONSE: + rip_response_process (packet, len, &from, ifc); + break; + case RIP_REQUEST: + case RIP_POLL: + rip_request_process (packet, len, &from, ifc); + break; + case RIP_TRACEON: + case RIP_TRACEOFF: + zlog_info ("Obsolete command %s received, please sent it to routed", + lookup (rip_msg, packet->command)); + rip_peer_bad_packet (&from); + break; + case RIP_POLL_ENTRY: + zlog_info ("Obsolete command %s received", + lookup (rip_msg, packet->command)); + rip_peer_bad_packet (&from); + break; + default: + zlog_info ("Unknown RIP command %d received", packet->command); + rip_peer_bad_packet (&from); + break; + } + + return len; +} + +/* Write routing table entry to the stream and return next index of + the routing table entry in the stream. */ +static int +rip_write_rte (int num, struct stream *s, struct prefix_ipv4 *p, + u_char version, struct rip_info *rinfo) +{ + struct in_addr mask; + + /* Write routing table entry. */ + if (version == RIPv1) + { + stream_putw (s, AF_INET); + stream_putw (s, 0); + stream_put_ipv4 (s, p->prefix.s_addr); + stream_put_ipv4 (s, 0); + stream_put_ipv4 (s, 0); + stream_putl (s, rinfo->metric_out); + } + else + { + masklen2ip (p->prefixlen, &mask); + + stream_putw (s, AF_INET); + stream_putw (s, rinfo->tag_out); + stream_put_ipv4 (s, p->prefix.s_addr); + stream_put_ipv4 (s, mask.s_addr); + stream_put_ipv4 (s, rinfo->nexthop_out.s_addr); + stream_putl (s, rinfo->metric_out); + } + + return ++num; +} + +/* Send update to the ifp or spcified neighbor. */ +void +rip_output_process (struct connected *ifc, struct sockaddr_in *to, + int route_type, u_char version) +{ + int ret; + struct stream *s; + struct route_node *rp; + struct rip_info *rinfo; + struct rip_interface *ri; + struct prefix_ipv4 *p; + struct prefix_ipv4 classfull; + struct prefix_ipv4 ifaddrclass; + struct key *key = NULL; + /* this might need to made dynamic if RIP ever supported auth methods + with larger key string sizes */ + char auth_str[RIP_AUTH_SIMPLE_SIZE]; + size_t doff = 0; /* offset of digest offset field */ + int num = 0; + int rtemax; + int subnetted = 0; + struct list *list = NULL; + struct listnode *listnode = NULL; + + /* Logging output event. */ + if (IS_RIP_DEBUG_EVENT) + { + if (to) + zlog_debug ("update routes to neighbor %s", inet_ntoa (to->sin_addr)); + else + zlog_debug ("update routes on interface %s ifindex %d", + ifc->ifp->name, ifc->ifp->ifindex); + } + + /* Set output stream. */ + s = rip->obuf; + + /* Reset stream and RTE counter. */ + stream_reset (s); + rtemax = RIP_MAX_RTE; + + /* Get RIP interface. */ + ri = ifc->ifp->info; + + /* If output interface is in simple password authentication mode, we + need space for authentication data. */ + if (ri->auth_type == RIP_AUTH_SIMPLE_PASSWORD) + rtemax -= 1; + + /* If output interface is in MD5 authentication mode, we need space + for authentication header and data. */ + if (ri->auth_type == RIP_AUTH_MD5) + rtemax -= 2; + + /* If output interface is in simple password authentication mode + and string or keychain is specified we need space for auth. data */ + if (ri->auth_type != RIP_NO_AUTH) + { + if (ri->key_chain) + { + struct keychain *keychain; + + keychain = keychain_lookup (ri->key_chain); + if (keychain) + key = key_lookup_for_send (keychain); + } + /* to be passed to auth functions later */ + rip_auth_prepare_str_send (ri, key, auth_str, RIP_AUTH_SIMPLE_SIZE); + } + + if (version == RIPv1) + { + memcpy (&ifaddrclass, ifc->address, sizeof (struct prefix_ipv4)); + apply_classful_mask_ipv4 (&ifaddrclass); + subnetted = 0; + if (ifc->address->prefixlen > ifaddrclass.prefixlen) + subnetted = 1; + } + + for (rp = route_top (rip->table); rp; rp = route_next (rp)) + 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. */ + + if (version == RIPv1) + { + p = (struct prefix_ipv4 *) &rp->p; + + if (IS_RIP_DEBUG_PACKET) + zlog_debug("RIPv1 mask check, %s/%d considered for output", + inet_ntoa (rp->p.u.prefix4), rp->p.prefixlen); + + if (subnetted && + prefix_match ((struct prefix *) &ifaddrclass, &rp->p)) + { + if ((ifc->address->prefixlen != rp->p.prefixlen) && + (rp->p.prefixlen != 32)) + continue; + } + else + { + memcpy (&classfull, &rp->p, sizeof(struct prefix_ipv4)); + apply_classful_mask_ipv4(&classfull); + if (rp->p.u.prefix4.s_addr != 0 && + classfull.prefixlen != rp->p.prefixlen) + continue; + } + if (IS_RIP_DEBUG_PACKET) + zlog_debug("RIPv1 mask check, %s/%d made it through", + inet_ntoa (rp->p.u.prefix4), rp->p.prefixlen); + } + else + p = (struct prefix_ipv4 *) &rp->p; + + /* Apply output filters. */ + ret = rip_filter (RIP_FILTER_OUT, p, ri); + if (ret < 0) + continue; + + /* Changed route only output. */ + if (route_type == rip_changed_route && + (! (rinfo->flags & RIP_RTF_CHANGED))) + continue; + + /* Split horizon. */ + /* if (split_horizon == rip_split_horizon) */ + if (ri->split_horizon == RIP_SPLIT_HORIZON) + { + /* + * We perform split horizon for RIP and connected route. + * For rip routes, we want to suppress the route if we would + * end up sending the route back on the interface that we + * learned it from, with a higher metric. For connected routes, + * we suppress the route if the prefix is a subset of the + * source address that we are going to use for the packet + * (in order to handle the case when multiple subnets are + * configured on the same interface). + */ + 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; + } + + /* Preparation for route-map. */ + rinfo->metric_set = 0; + rinfo->nexthop_out.s_addr = 0; + rinfo->metric_out = rinfo->metric; + rinfo->tag_out = rinfo->tag; + rinfo->ifindex_out = ifc->ifp->ifindex; + + /* In order to avoid some local loops, + * if the RIP route has a nexthop via this interface, keep the nexthop, + * otherwise set it to 0. The nexthop should not be propagated + * beyond the local broadcast/multicast area in order + * to avoid an IGP multi-level recursive look-up. + * see (4.4) + */ + if (rinfo->ifindex == ifc->ifp->ifindex) + rinfo->nexthop_out = rinfo->nexthop; + + /* Interface route-map */ + if (ri->routemap[RIP_FILTER_OUT]) + { + ret = route_map_apply (ri->routemap[RIP_FILTER_OUT], + (struct prefix *) p, RMAP_RIP, + rinfo); + + if (ret == RMAP_DENYMATCH) + { + if (IS_RIP_DEBUG_PACKET) + zlog_debug ("RIP %s/%d is filtered by route-map out", + inet_ntoa (p->prefix), p->prefixlen); + continue; + } + } + + /* Apply redistribute route map - continue, if deny */ + if (rip->route_map[rinfo->type].name + && rinfo->sub_type != RIP_ROUTE_INTERFACE) + { + ret = route_map_apply (rip->route_map[rinfo->type].map, + (struct prefix *)p, RMAP_RIP, rinfo); + + if (ret == RMAP_DENYMATCH) + { + if (IS_RIP_DEBUG_PACKET) + zlog_debug ("%s/%d is filtered by route-map", + inet_ntoa (p->prefix), p->prefixlen); + continue; + } + } + + /* When route-map does not set metric. */ + if (! rinfo->metric_set) + { + /* If redistribute metric is set. */ + if (rip->route_map[rinfo->type].metric_config + && rinfo->metric != RIP_METRIC_INFINITY) + { + rinfo->metric_out = rip->route_map[rinfo->type].metric; + } + else + { + /* If the route is not connected or localy generated + one, use default-metric value*/ + if (rinfo->type != ZEBRA_ROUTE_RIP + && rinfo->type != ZEBRA_ROUTE_CONNECT + && rinfo->metric != RIP_METRIC_INFINITY) + rinfo->metric_out = rip->default_metric; + } + } + + /* Apply offset-list */ + if (rinfo->metric != RIP_METRIC_INFINITY) + rip_offset_list_apply_out (p, ifc->ifp, &rinfo->metric_out); + + if (rinfo->metric_out > RIP_METRIC_INFINITY) + rinfo->metric_out = RIP_METRIC_INFINITY; + + /* Perform split-horizon with poisoned reverse + * for RIP and connected routes. + **/ + if (ri->split_horizon == RIP_SPLIT_HORIZON_POISONED_REVERSE) { + /* + * We perform split horizon for RIP and connected route. + * For rip routes, we want to suppress the route if we would + * end up sending the route back on the interface that we + * learned it from, with a higher metric. For connected routes, + * we suppress the route if the prefix is a subset of the + * source address that we are going to use for the packet + * (in order to handle the case when multiple subnets are + * configured on the same interface). + */ + 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 */ + if (num == 0) + { + stream_putc (s, RIP_RESPONSE); + stream_putc (s, version); + stream_putw (s, 0); + + /* auth header for !v1 && !no_auth */ + if ( (ri->auth_type != RIP_NO_AUTH) && (version != RIPv1) ) + doff = rip_auth_header_write (s, ri, key, auth_str, + RIP_AUTH_SIMPLE_SIZE); + } + + /* Write RTE to the stream. */ + num = rip_write_rte (num, s, p, version, rinfo); + if (num == rtemax) + { + if (version == RIPv2 && ri->auth_type == RIP_AUTH_MD5) + rip_auth_md5_set (s, ri, doff, auth_str, RIP_AUTH_SIMPLE_SIZE); + + ret = rip_send_packet (STREAM_DATA (s), stream_get_endp (s), + to, ifc); + + if (ret >= 0 && IS_RIP_DEBUG_SEND) + rip_packet_dump ((struct rip_packet *)STREAM_DATA (s), + stream_get_endp(s), "SEND"); + num = 0; + stream_reset (s); + } + } + + /* Flush unwritten RTE. */ + if (num != 0) + { + if (version == RIPv2 && ri->auth_type == RIP_AUTH_MD5) + rip_auth_md5_set (s, ri, doff, auth_str, RIP_AUTH_SIMPLE_SIZE); + + ret = rip_send_packet (STREAM_DATA (s), stream_get_endp (s), to, ifc); + + if (ret >= 0 && IS_RIP_DEBUG_SEND) + rip_packet_dump ((struct rip_packet *)STREAM_DATA (s), + stream_get_endp (s), "SEND"); + num = 0; + stream_reset (s); + } + + /* Statistics updates. */ + ri->sent_updates++; +} + +/* Send RIP packet to the interface. */ +static void +rip_update_interface (struct connected *ifc, u_char version, int route_type) +{ + struct sockaddr_in to; + + /* When RIP version is 2 and multicast enable interface. */ + if (version == RIPv2 && if_is_multicast (ifc->ifp)) + { + if (IS_RIP_DEBUG_EVENT) + zlog_debug ("multicast announce on %s ", ifc->ifp->name); + + rip_output_process (ifc, NULL, route_type, version); + return; + } + + /* If we can't send multicast packet, send it with unicast. */ + if (if_is_broadcast (ifc->ifp) || if_is_pointopoint (ifc->ifp)) + { + if (ifc->address->family == AF_INET) + { + /* Destination address and port setting. */ + memset (&to, 0, sizeof (struct sockaddr_in)); + if (ifc->destination) + /* use specified broadcast or peer destination addr */ + to.sin_addr = ifc->destination->u.prefix4; + else if (ifc->address->prefixlen < IPV4_MAX_PREFIXLEN) + /* calculate the appropriate broadcast address */ + to.sin_addr.s_addr = + ipv4_broadcast_addr(ifc->address->u.prefix4.s_addr, + ifc->address->prefixlen); + else + /* do not know where to send the packet */ + return; + to.sin_port = htons (RIP_PORT_DEFAULT); + + if (IS_RIP_DEBUG_EVENT) + zlog_debug("%s announce to %s on %s", + CONNECTED_PEER(ifc) ? "unicast" : "broadcast", + inet_ntoa (to.sin_addr), ifc->ifp->name); + + rip_output_process (ifc, &to, route_type, version); + } + } +} + +/* Update send to all interface and neighbor. */ +static void +rip_update_process (int route_type) +{ + struct listnode *node; + struct listnode *ifnode, *ifnnode; + struct connected *connected; + struct interface *ifp; + struct rip_interface *ri; + struct route_node *rp; + struct sockaddr_in to; + struct prefix_ipv4 *p; + + /* Send RIP update to each interface. */ + for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) + { + if (if_is_loopback (ifp)) + continue; + + if (! if_is_operative (ifp)) + continue; + + /* Fetch RIP interface information. */ + ri = ifp->info; + + /* When passive interface is specified, suppress announce to the + interface. */ + if (ri->passive) + continue; + + if (ri->running) + { + /* + * If there is no version configuration in the interface, + * use rip's version setting. + */ + int vsend = ((ri->ri_send == RI_RIP_UNSPEC) ? + rip->version_send : ri->ri_send); + + if (IS_RIP_DEBUG_EVENT) + zlog_debug("SEND UPDATE to %s ifindex %d", + ifp->name, ifp->ifindex); + + /* send update on each connected network */ + for (ALL_LIST_ELEMENTS (ifp->connected, ifnode, ifnnode, connected)) + { + if (connected->address->family == AF_INET) + { + if (vsend & RIPv1) + rip_update_interface (connected, RIPv1, route_type); + if (vsend & RIPv2) + rip_update_interface (connected, RIPv2, route_type); + } + } + } + } + + /* RIP send updates to each neighbor. */ + for (rp = route_top (rip->neighbor); rp; rp = route_next (rp)) + if (rp->info != NULL) + { + p = (struct prefix_ipv4 *) &rp->p; + + ifp = if_lookup_address (p->prefix); + if (! ifp) + { + zlog_warn ("Neighbor %s doesnt have connected interface!", + inet_ntoa (p->prefix)); + continue; + } + + if ( (connected = connected_lookup_address (ifp, p->prefix)) == NULL) + { + zlog_warn ("Neighbor %s doesnt have connected network", + inet_ntoa (p->prefix)); + continue; + } + + /* Set destination address and port */ + memset (&to, 0, sizeof (struct sockaddr_in)); + to.sin_addr = p->prefix; + to.sin_port = htons (RIP_PORT_DEFAULT); + + /* RIP version is rip's configuration. */ + rip_output_process (connected, &to, route_type, rip->version_send); + } +} + +/* RIP's periodical timer. */ +static int +rip_update (struct thread *t) +{ + /* Clear timer pointer. */ + rip->t_update = NULL; + + if (IS_RIP_DEBUG_EVENT) + zlog_debug ("update timer fire!"); + + /* Process update output. */ + rip_update_process (rip_all_route); + + /* Triggered updates may be suppressed if a regular update is due by + the time the triggered update would be sent. */ + if (rip->t_triggered_interval) + { + thread_cancel (rip->t_triggered_interval); + rip->t_triggered_interval = NULL; + } + rip->trigger = 0; + + /* Register myself. */ + rip_event (RIP_UPDATE_EVENT, 0); + + return 0; +} + +/* Walk down the RIP routing table then clear changed flag. */ +static void +rip_clear_changed_flag (void) +{ + struct route_node *rp; + 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 ((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. */ +static int +rip_triggered_interval (struct thread *t) +{ + int rip_triggered_update (struct thread *); + + rip->t_triggered_interval = NULL; + + if (rip->trigger) + { + rip->trigger = 0; + rip_triggered_update (t); + } + return 0; +} + +/* Execute triggered update. */ +static int +rip_triggered_update (struct thread *t) +{ + int interval; + + /* Clear thred pointer. */ + rip->t_triggered_update = NULL; + + /* Cancel interval timer. */ + if (rip->t_triggered_interval) + { + thread_cancel (rip->t_triggered_interval); + rip->t_triggered_interval = NULL; + } + rip->trigger = 0; + + /* Logging triggered update. */ + if (IS_RIP_DEBUG_EVENT) + zlog_debug ("triggered update!"); + + /* Split Horizon processing is done when generating triggered + updates as well as normal updates (see section 2.6). */ + rip_update_process (rip_changed_route); + + /* Once all of the triggered updates have been generated, the route + change flags should be cleared. */ + rip_clear_changed_flag (); + + /* After a triggered update is sent, a timer should be set for a + random interval between 1 and 5 seconds. If other changes that + would trigger updates occur before the timer expires, a single + update is triggered when the timer expires. */ + interval = (random () % 5) + 1; + + rip->t_triggered_interval = + thread_add_timer (master, rip_triggered_interval, NULL, interval); + + return 0; +} + +/* Withdraw redistributed route. */ +void +rip_redistribute_withdraw (int type) +{ + struct route_node *rp; + struct rip_info *rinfo = NULL; + struct list *list = NULL; + + if (!rip) + return; + + for (rp = route_top (rip->table); rp; rp = route_next (rp)) + if ((list = rp->info) != NULL) + { + rinfo = listgetdata (listhead (list)); + if (rinfo->type == type + && rinfo->sub_type != RIP_ROUTE_INTERFACE) + { + /* 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) { + struct prefix_ipv4 *p = (struct prefix_ipv4 *) &rp->p; + + zlog_debug ("Poisone %s/%d on the interface %s with an infinity metric [withdraw]", + inet_ntoa(p->prefix), p->prefixlen, + ifindex2ifname(rinfo->ifindex)); + } + + rip_event (RIP_TRIGGERED_UPDATE, 0); + } + } +} + +/* Create new RIP instance and set it to global variable. */ +static int +rip_create (void) +{ + rip = XCALLOC (MTYPE_RIP, sizeof (struct rip)); + + /* Set initial value. */ + rip->version_send = RI_RIP_VERSION_2; + rip->version_recv = RI_RIP_VERSION_1_AND_2; + rip->update_time = RIP_UPDATE_TIMER_DEFAULT; + rip->timeout_time = RIP_TIMEOUT_TIMER_DEFAULT; + rip->garbage_time = RIP_GARBAGE_TIMER_DEFAULT; + rip->default_metric = RIP_DEFAULT_METRIC_DEFAULT; + + /* Initialize RIP routig table. */ + rip->table = route_table_init (); + rip->route = route_table_init (); + rip->neighbor = route_table_init (); + + /* Make output stream. */ + rip->obuf = stream_new (1500); + + /* Make socket. */ + rip->sock = rip_create_socket (NULL); + if (rip->sock < 0) + return rip->sock; + + /* Create read and timer thread. */ + rip_event (RIP_READ, rip->sock); + rip_event (RIP_UPDATE_EVENT, 1); + + return 0; +} + +/* Sned RIP request to the destination. */ +int +rip_request_send (struct sockaddr_in *to, struct interface *ifp, + u_char version, struct connected *connected) +{ + struct rte *rte; + struct rip_packet rip_packet; + struct listnode *node, *nnode; + + memset (&rip_packet, 0, sizeof (rip_packet)); + + rip_packet.command = RIP_REQUEST; + rip_packet.version = version; + rte = rip_packet.rte; + rte->metric = htonl (RIP_METRIC_INFINITY); + + if (connected) + { + /* + * connected is only sent for ripv1 case, or when + * interface does not support multicast. Caller loops + * over each connected address for this case. + */ + if (rip_send_packet ((u_char *) &rip_packet, sizeof (rip_packet), + to, connected) != sizeof (rip_packet)) + return -1; + else + return sizeof (rip_packet); + } + + /* send request on each connected network */ + for (ALL_LIST_ELEMENTS (ifp->connected, node, nnode, connected)) + { + struct prefix_ipv4 *p; + + p = (struct prefix_ipv4 *) connected->address; + + if (p->family != AF_INET) + continue; + + if (rip_send_packet ((u_char *) &rip_packet, sizeof (rip_packet), + to, connected) != sizeof (rip_packet)) + return -1; + } + return sizeof (rip_packet); +} + +static int +rip_update_jitter (unsigned long time) +{ +#define JITTER_BOUND 4 + /* We want to get the jitter to +/- 1/JITTER_BOUND the interval. + Given that, we cannot let time be less than JITTER_BOUND seconds. + The RIPv2 RFC says jitter should be small compared to + update_time. We consider 1/JITTER_BOUND to be small. + */ + + int jitter_input = time; + int jitter; + + if (jitter_input < JITTER_BOUND) + jitter_input = JITTER_BOUND; + + jitter = (((random () % ((jitter_input * 2) + 1)) - jitter_input)); + + return jitter/JITTER_BOUND; +} + +void +rip_event (enum rip_event event, int sock) +{ + int jitter = 0; + + switch (event) + { + case RIP_READ: + rip->t_read = thread_add_read (master, rip_read, NULL, sock); + break; + case RIP_UPDATE_EVENT: + if (rip->t_update) + { + thread_cancel (rip->t_update); + rip->t_update = NULL; + } + jitter = rip_update_jitter (rip->update_time); + rip->t_update = + thread_add_timer (master, rip_update, NULL, + sock ? 2 : rip->update_time + jitter); + break; + case RIP_TRIGGERED_UPDATE: + if (rip->t_triggered_interval) + rip->trigger = 1; + else if (! rip->t_triggered_update) + rip->t_triggered_update = + thread_add_event (master, rip_triggered_update, NULL, 0); + break; + default: + break; + } +} + +DEFUN (router_rip, + router_rip_cmd, + "router rip", + "Enable a routing process\n" + "Routing Information Protocol (RIP)\n") +{ + int ret; + + /* If rip is not enabled before. */ + if (! rip) + { + ret = rip_create (); + if (ret < 0) + { + zlog_info ("Can't create RIP"); + return CMD_WARNING; + } + } + vty->node = RIP_NODE; + vty->index = rip; + + return CMD_SUCCESS; +} + +DEFUN (no_router_rip, + no_router_rip_cmd, + "no router rip", + NO_STR + "Enable a routing process\n" + "Routing Information Protocol (RIP)\n") +{ + if (rip) + rip_clean (); + return CMD_SUCCESS; +} + +DEFUN (rip_version, + rip_version_cmd, + "version <1-2>", + "Set routing protocol version\n" + "version\n") +{ + int version; + + version = atoi (argv[0]); + if (version != RIPv1 && version != RIPv2) + { + vty_out (vty, "invalid rip version %d%s", version, + VTY_NEWLINE); + return CMD_WARNING; + } + rip->version_send = version; + rip->version_recv = version; + + return CMD_SUCCESS; +} + +DEFUN (no_rip_version, + no_rip_version_cmd, + "no version", + NO_STR + "Set routing protocol version\n") +{ + /* Set RIP version to the default. */ + rip->version_send = RI_RIP_VERSION_2; + rip->version_recv = RI_RIP_VERSION_1_AND_2; + + return CMD_SUCCESS; +} + +ALIAS (no_rip_version, + no_rip_version_val_cmd, + "no version <1-2>", + NO_STR + "Set routing protocol version\n" + "version\n") + +DEFUN (rip_route, + rip_route_cmd, + "route A.B.C.D/M", + "RIP static route configuration\n" + "IP prefix /\n") +{ + int ret; + struct prefix_ipv4 p; + struct route_node *node; + + ret = str2prefix_ipv4 (argv[0], &p); + if (ret < 0) + { + vty_out (vty, "Malformed address%s", VTY_NEWLINE); + return CMD_WARNING; + } + apply_mask_ipv4 (&p); + + /* For router rip configuration. */ + node = route_node_get (rip->route, (struct prefix *) &p); + + if (node->info) + { + vty_out (vty, "There is already same static route.%s", VTY_NEWLINE); + route_unlock_node (node); + return CMD_WARNING; + } + + node->info = (char *)"static"; + + rip_redistribute_add (ZEBRA_ROUTE_RIP, RIP_ROUTE_STATIC, &p, 0, NULL, 0, 0, 0); + + return CMD_SUCCESS; +} + +DEFUN (no_rip_route, + no_rip_route_cmd, + "no route A.B.C.D/M", + NO_STR + "RIP static route configuration\n" + "IP prefix /\n") +{ + int ret; + struct prefix_ipv4 p; + struct route_node *node; + + ret = str2prefix_ipv4 (argv[0], &p); + if (ret < 0) + { + vty_out (vty, "Malformed address%s", VTY_NEWLINE); + return CMD_WARNING; + } + apply_mask_ipv4 (&p); + + /* For router rip configuration. */ + node = route_node_lookup (rip->route, (struct prefix *) &p); + if (! node) + { + vty_out (vty, "Can't find route %s.%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + + rip_redistribute_delete (ZEBRA_ROUTE_RIP, RIP_ROUTE_STATIC, &p, 0); + route_unlock_node (node); + + node->info = NULL; + route_unlock_node (node); + + return CMD_SUCCESS; +} + +#if 0 +static void +rip_update_default_metric (void) +{ + struct route_node *np; + 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 ((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 + +DEFUN (rip_default_metric, + rip_default_metric_cmd, + "default-metric <1-16>", + "Set a metric of redistribute routes\n" + "Default metric\n") +{ + if (rip) + { + rip->default_metric = atoi (argv[0]); + /* rip_update_default_metric (); */ + } + return CMD_SUCCESS; +} + +DEFUN (no_rip_default_metric, + no_rip_default_metric_cmd, + "no default-metric", + NO_STR + "Set a metric of redistribute routes\n" + "Default metric\n") +{ + if (rip) + { + rip->default_metric = RIP_DEFAULT_METRIC_DEFAULT; + /* rip_update_default_metric (); */ + } + return CMD_SUCCESS; +} + +ALIAS (no_rip_default_metric, + no_rip_default_metric_val_cmd, + "no default-metric <1-16>", + NO_STR + "Set a metric of redistribute routes\n" + "Default metric\n") + +DEFUN (rip_timers, + rip_timers_cmd, + "timers basic <5-2147483647> <5-2147483647> <5-2147483647>", + "Adjust routing timers\n" + "Basic routing protocol update timers\n" + "Routing table update timer value in second. Default is 30.\n" + "Routing information timeout timer. Default is 180.\n" + "Garbage collection timer. Default is 120.\n") +{ + unsigned long update; + unsigned long timeout; + unsigned long garbage; + char *endptr = NULL; + unsigned long RIP_TIMER_MAX = 2147483647; + unsigned long RIP_TIMER_MIN = 5; + + update = strtoul (argv[0], &endptr, 10); + if (update > RIP_TIMER_MAX || update < RIP_TIMER_MIN || *endptr != '\0') + { + vty_out (vty, "update timer value error%s", VTY_NEWLINE); + return CMD_WARNING; + } + + timeout = strtoul (argv[1], &endptr, 10); + if (timeout > RIP_TIMER_MAX || timeout < RIP_TIMER_MIN || *endptr != '\0') + { + vty_out (vty, "timeout timer value error%s", VTY_NEWLINE); + return CMD_WARNING; + } + + garbage = strtoul (argv[2], &endptr, 10); + if (garbage > RIP_TIMER_MAX || garbage < RIP_TIMER_MIN || *endptr != '\0') + { + vty_out (vty, "garbage timer value error%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Set each timer value. */ + rip->update_time = update; + rip->timeout_time = timeout; + rip->garbage_time = garbage; + + /* Reset update timer thread. */ + rip_event (RIP_UPDATE_EVENT, 0); + + return CMD_SUCCESS; +} + +DEFUN (no_rip_timers, + no_rip_timers_cmd, + "no timers basic", + NO_STR + "Adjust routing timers\n" + "Basic routing protocol update timers\n") +{ + /* Set each timer value to the default. */ + rip->update_time = RIP_UPDATE_TIMER_DEFAULT; + rip->timeout_time = RIP_TIMEOUT_TIMER_DEFAULT; + rip->garbage_time = RIP_GARBAGE_TIMER_DEFAULT; + + /* Reset update timer thread. */ + rip_event (RIP_UPDATE_EVENT, 0); + + return CMD_SUCCESS; +} + +ALIAS (no_rip_timers, + no_rip_timers_val_cmd, + "no timers basic <0-65535> <0-65535> <0-65535>", + NO_STR + "Adjust routing timers\n" + "Basic routing protocol update timers\n" + "Routing table update timer value in second. Default is 30.\n" + "Routing information timeout timer. Default is 180.\n" + "Garbage collection timer. Default is 120.\n") + + +struct route_table *rip_distance_table; + +struct rip_distance +{ + /* Distance value for the IP source prefix. */ + u_char distance; + + /* Name of the access-list to be matched. */ + char *access_list; +}; + +static struct rip_distance * +rip_distance_new (void) +{ + return XCALLOC (MTYPE_RIP_DISTANCE, sizeof (struct rip_distance)); +} + +static void +rip_distance_free (struct rip_distance *rdistance) +{ + XFREE (MTYPE_RIP_DISTANCE, rdistance); +} + +static int +rip_distance_set (struct vty *vty, const char *distance_str, const char *ip_str, + const char *access_list_str) +{ + int ret; + struct prefix_ipv4 p; + u_char distance; + struct route_node *rn; + struct rip_distance *rdistance; + + ret = str2prefix_ipv4 (ip_str, &p); + if (ret == 0) + { + vty_out (vty, "Malformed prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + + distance = atoi (distance_str); + + /* Get RIP distance node. */ + rn = route_node_get (rip_distance_table, (struct prefix *) &p); + if (rn->info) + { + rdistance = rn->info; + route_unlock_node (rn); + } + else + { + rdistance = rip_distance_new (); + rn->info = rdistance; + } + + /* Set distance value. */ + rdistance->distance = distance; + + /* Reset access-list configuration. */ + if (rdistance->access_list) + { + free (rdistance->access_list); + rdistance->access_list = NULL; + } + if (access_list_str) + rdistance->access_list = strdup (access_list_str); + + return CMD_SUCCESS; +} + +static int +rip_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 route_node *rn; + struct rip_distance *rdistance; + + ret = str2prefix_ipv4 (ip_str, &p); + if (ret == 0) + { + vty_out (vty, "Malformed prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + + rn = route_node_lookup (rip_distance_table, (struct prefix *)&p); + if (! rn) + { + vty_out (vty, "Can't find specified prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + + rdistance = rn->info; + + if (rdistance->access_list) + free (rdistance->access_list); + rip_distance_free (rdistance); + + rn->info = NULL; + route_unlock_node (rn); + route_unlock_node (rn); + + return CMD_SUCCESS; +} + +static void +rip_distance_reset (void) +{ + struct route_node *rn; + struct rip_distance *rdistance; + + for (rn = route_top (rip_distance_table); rn; rn = route_next (rn)) + if ((rdistance = rn->info) != NULL) + { + if (rdistance->access_list) + free (rdistance->access_list); + rip_distance_free (rdistance); + rn->info = NULL; + route_unlock_node (rn); + } +} + +/* Apply RIP information to distance method. */ +u_char +rip_distance_apply (struct rip_info *rinfo) +{ + struct route_node *rn; + struct prefix_ipv4 p; + struct rip_distance *rdistance; + struct access_list *alist; + + if (! rip) + return 0; + + memset (&p, 0, sizeof (struct prefix_ipv4)); + p.family = AF_INET; + p.prefix = rinfo->from; + p.prefixlen = IPV4_MAX_BITLEN; + + /* Check source address. */ + rn = route_node_match (rip_distance_table, (struct prefix *) &p); + if (rn) + { + rdistance = rn->info; + route_unlock_node (rn); + + if (rdistance->access_list) + { + alist = access_list_lookup (AFI_IP, rdistance->access_list); + if (alist == NULL) + return 0; + if (access_list_apply (alist, &rinfo->rp->p) == FILTER_DENY) + return 0; + + return rdistance->distance; + } + else + return rdistance->distance; + } + + if (rip->distance) + return rip->distance; + + return 0; +} + +static void +rip_distance_show (struct vty *vty) +{ + struct route_node *rn; + struct rip_distance *rdistance; + int header = 1; + char buf[BUFSIZ]; + + vty_out (vty, " Distance: (default is %d)%s", + rip->distance ? rip->distance :ZEBRA_RIP_DISTANCE_DEFAULT, + VTY_NEWLINE); + + for (rn = route_top (rip_distance_table); rn; rn = route_next (rn)) + if ((rdistance = rn->info) != NULL) + { + if (header) + { + vty_out (vty, " Address Distance List%s", + VTY_NEWLINE); + header = 0; + } + sprintf (buf, "%s/%d", inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen); + vty_out (vty, " %-20s %4d %s%s", + buf, rdistance->distance, + rdistance->access_list ? rdistance->access_list : "", + VTY_NEWLINE); + } +} + +DEFUN (rip_distance, + rip_distance_cmd, + "distance <1-255>", + "Administrative distance\n" + "Distance value\n") +{ + rip->distance = atoi (argv[0]); + return CMD_SUCCESS; +} + +DEFUN (no_rip_distance, + no_rip_distance_cmd, + "no distance <1-255>", + NO_STR + "Administrative distance\n" + "Distance value\n") +{ + rip->distance = 0; + return CMD_SUCCESS; +} + +DEFUN (rip_distance_source, + rip_distance_source_cmd, + "distance <1-255> A.B.C.D/M", + "Administrative distance\n" + "Distance value\n" + "IP source prefix\n") +{ + rip_distance_set (vty, argv[0], argv[1], NULL); + return CMD_SUCCESS; +} + +DEFUN (no_rip_distance_source, + no_rip_distance_source_cmd, + "no distance <1-255> A.B.C.D/M", + NO_STR + "Administrative distance\n" + "Distance value\n" + "IP source prefix\n") +{ + rip_distance_unset (vty, argv[0], argv[1], NULL); + return CMD_SUCCESS; +} + +DEFUN (rip_distance_source_access_list, + rip_distance_source_access_list_cmd, + "distance <1-255> A.B.C.D/M WORD", + "Administrative distance\n" + "Distance value\n" + "IP source prefix\n" + "Access list name\n") +{ + rip_distance_set (vty, argv[0], argv[1], argv[2]); + return CMD_SUCCESS; +} + +DEFUN (no_rip_distance_source_access_list, + no_rip_distance_source_access_list_cmd, + "no distance <1-255> A.B.C.D/M WORD", + NO_STR + "Administrative distance\n" + "Distance value\n" + "IP source prefix\n" + "Access list name\n") +{ + 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) +{ + time_t clock; + struct tm *tm; +#define TIME_BUF 25 + char timebuf [TIME_BUF]; + struct thread *thread; + + if ((thread = rinfo->t_timeout) != NULL) + { + clock = thread_timer_remain_second (thread); + tm = gmtime (&clock); + strftime (timebuf, TIME_BUF, "%M:%S", tm); + vty_out (vty, "%5s", timebuf); + } + else if ((thread = rinfo->t_garbage_collect) != NULL) + { + clock = thread_timer_remain_second (thread); + tm = gmtime (&clock); + strftime (timebuf, TIME_BUF, "%M:%S", tm); + vty_out (vty, "%5s", timebuf); + } +} + +static const char * +rip_route_type_print (int sub_type) +{ + switch (sub_type) + { + case RIP_ROUTE_RTE: + return "n"; + case RIP_ROUTE_STATIC: + return "s"; + case RIP_ROUTE_DEFAULT: + return "d"; + case RIP_ROUTE_REDISTRIBUTE: + return "r"; + case RIP_ROUTE_INTERFACE: + return "i"; + default: + return "?"; + } +} + +DEFUN (show_ip_rip, + show_ip_rip_cmd, + "show ip rip", + SHOW_STR + IP_STR + "Show RIP routes\n") +{ + struct route_node *np; + struct rip_info *rinfo = NULL; + struct list *list = NULL; + struct listnode *listnode = NULL; + + if (! rip) + return CMD_SUCCESS; + + vty_out (vty, "Codes: R - RIP, C - connected, S - Static, O - OSPF, B - BGP%s" + "Sub-codes:%s" + " (n) - normal, (s) - static, (d) - default, (r) - redistribute,%s" + " (i) - interface%s%s" + " Network Next Hop Metric From Tag Time%s", + VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + + for (np = route_top (rip->table); np; np = route_next (np)) + if ((list = np->info) != NULL) + for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo)) + { + int len; + + len = vty_out (vty, "%c(%s) %s/%d", + /* np->lock, For debugging. */ + zebra_route_char(rinfo->type), + rip_route_type_print (rinfo->sub_type), + inet_ntoa (np->p.u.prefix4), np->p.prefixlen); + + len = 24 - len; + + if (len > 0) + vty_out (vty, "%*s", len, " "); + + if (rinfo->nexthop.s_addr) + vty_out (vty, "%-20s %2d ", inet_ntoa (rinfo->nexthop), + rinfo->metric); + else + vty_out (vty, "0.0.0.0 %2d ", rinfo->metric); + + /* Route which exist in kernel routing table. */ + if ((rinfo->type == ZEBRA_ROUTE_RIP) && + (rinfo->sub_type == RIP_ROUTE_RTE)) + { + vty_out (vty, "%-15s ", inet_ntoa (rinfo->from)); + vty_out (vty, "%3d ", rinfo->tag); + rip_vty_out_uptime (vty, rinfo); + } + else if (rinfo->metric == RIP_METRIC_INFINITY) + { + vty_out (vty, "self "); + vty_out (vty, "%3d ", rinfo->tag); + rip_vty_out_uptime (vty, rinfo); + } + else + { + if (rinfo->external_metric) + { + len = vty_out (vty, "self (%s:%d)", + zebra_route_string(rinfo->type), + rinfo->external_metric); + len = 16 - len; + if (len > 0) + vty_out (vty, "%*s", len, " "); + } + else + vty_out (vty, "self "); + vty_out (vty, "%3d", rinfo->tag); + } + + vty_out (vty, "%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +/* Vincent: formerly, it was show_ip_protocols_rip: "show ip protocols" */ +DEFUN (show_ip_rip_status, + show_ip_rip_status_cmd, + "show ip rip status", + SHOW_STR + IP_STR + "Show RIP routes\n" + "IP routing protocol process parameters and statistics\n") +{ + struct listnode *node; + struct interface *ifp; + struct rip_interface *ri; + extern const struct message ri_version_msg[]; + const char *send_version; + const char *receive_version; + + if (! rip) + return CMD_SUCCESS; + + vty_out (vty, "Routing Protocol is \"rip\"%s", VTY_NEWLINE); + vty_out (vty, " Sending updates every %ld seconds with +/-50%%,", + rip->update_time); + vty_out (vty, " next due in %lu seconds%s", + thread_timer_remain_second(rip->t_update), + VTY_NEWLINE); + vty_out (vty, " Timeout after %ld seconds,", rip->timeout_time); + vty_out (vty, " garbage collect after %ld seconds%s", rip->garbage_time, + VTY_NEWLINE); + + /* Filtering status show. */ + config_show_distribute (vty); + + /* Default metric information. */ + vty_out (vty, " Default redistribution metric is %d%s", + rip->default_metric, VTY_NEWLINE); + + /* Redistribute information. */ + vty_out (vty, " Redistributing:"); + config_write_rip_redistribute (vty, 0); + vty_out (vty, "%s", VTY_NEWLINE); + + vty_out (vty, " Default version control: send version %s,", + lookup(ri_version_msg,rip->version_send)); + if (rip->version_recv == RI_RIP_VERSION_1_AND_2) + vty_out (vty, " receive any version %s", VTY_NEWLINE); + else + vty_out (vty, " receive version %s %s", + lookup(ri_version_msg,rip->version_recv), VTY_NEWLINE); + + vty_out (vty, " Interface Send Recv Key-chain%s", VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) + { + ri = ifp->info; + + if (!ri->running) + continue; + + if (ri->enable_network || ri->enable_interface) + { + if (ri->ri_send == RI_RIP_UNSPEC) + send_version = lookup (ri_version_msg, rip->version_send); + else + send_version = lookup (ri_version_msg, ri->ri_send); + + if (ri->ri_receive == RI_RIP_UNSPEC) + receive_version = lookup (ri_version_msg, rip->version_recv); + else + receive_version = lookup (ri_version_msg, ri->ri_receive); + + vty_out (vty, " %-17s%-3s %-3s %s%s", ifp->name, + send_version, + receive_version, + ri->key_chain ? ri->key_chain : "", + VTY_NEWLINE); + } + } + + vty_out (vty, " Routing for Networks:%s", VTY_NEWLINE); + config_write_rip_network (vty, 0); + + { + int found_passive = 0; + for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) + { + ri = ifp->info; + + if ((ri->enable_network || ri->enable_interface) && ri->passive) + { + if (!found_passive) + { + vty_out (vty, " Passive Interface(s):%s", VTY_NEWLINE); + found_passive = 1; + } + vty_out (vty, " %s%s", ifp->name, VTY_NEWLINE); + } + } + } + + vty_out (vty, " Routing Information Sources:%s", VTY_NEWLINE); + vty_out (vty, " Gateway BadPackets BadRoutes Distance Last Update%s", VTY_NEWLINE); + rip_peer_display (vty); + + rip_distance_show (vty); + + return CMD_SUCCESS; +} + +/* RIP configuration write function. */ +static int +config_write_rip (struct vty *vty) +{ + int write = 0; + struct route_node *rn; + struct rip_distance *rdistance; + + if (rip) + { + /* Router RIP statement. */ + vty_out (vty, "router rip%s", VTY_NEWLINE); + write++; + + /* RIP version statement. Default is RIP version 2. */ + if (rip->version_send != RI_RIP_VERSION_2 + || rip->version_recv != RI_RIP_VERSION_1_AND_2) + vty_out (vty, " version %d%s", rip->version_send, + VTY_NEWLINE); + + /* RIP timer configuration. */ + if (rip->update_time != RIP_UPDATE_TIMER_DEFAULT + || rip->timeout_time != RIP_TIMEOUT_TIMER_DEFAULT + || rip->garbage_time != RIP_GARBAGE_TIMER_DEFAULT) + vty_out (vty, " timers basic %lu %lu %lu%s", + rip->update_time, + rip->timeout_time, + rip->garbage_time, + VTY_NEWLINE); + + /* Default information configuration. */ + if (rip->default_information) + { + if (rip->default_information_route_map) + vty_out (vty, " default-information originate route-map %s%s", + rip->default_information_route_map, VTY_NEWLINE); + else + vty_out (vty, " default-information originate%s", + VTY_NEWLINE); + } + + /* Redistribute configuration. */ + config_write_rip_redistribute (vty, 1); + + /* RIP offset-list configuration. */ + config_write_rip_offset_list (vty); + + /* RIP enabled network and interface configuration. */ + config_write_rip_network (vty, 1); + + /* RIP default metric configuration */ + if (rip->default_metric != RIP_DEFAULT_METRIC_DEFAULT) + vty_out (vty, " default-metric %d%s", + rip->default_metric, VTY_NEWLINE); + + /* Distribute configuration. */ + write += config_write_distribute (vty); + + /* Interface routemap configuration */ + write += config_write_if_rmap (vty); + + /* Distance configuration. */ + if (rip->distance) + vty_out (vty, " distance %d%s", rip->distance, VTY_NEWLINE); + + /* RIP source IP prefix distance configuration. */ + for (rn = route_top (rip_distance_table); rn; rn = route_next (rn)) + if ((rdistance = rn->info) != NULL) + vty_out (vty, " distance %d %s/%d %s%s", rdistance->distance, + inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen, + 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) + vty_out (vty, " route %s/%d%s", + inet_ntoa (rn->p.u.prefix4), + rn->p.prefixlen, + VTY_NEWLINE); + + } + return write; +} + +/* RIP node structure. */ +static struct cmd_node rip_node = +{ + RIP_NODE, + "%s(config-router)# ", + 1 +}; + +/* Distribute-list update functions. */ +static void +rip_distribute_update (struct distribute *dist) +{ + struct interface *ifp; + struct rip_interface *ri; + struct access_list *alist; + struct prefix_list *plist; + + if (! dist->ifname) + return; + + ifp = if_lookup_by_name (dist->ifname); + if (ifp == NULL) + return; + + ri = ifp->info; + + if (dist->list[DISTRIBUTE_V4_IN]) + { + alist = access_list_lookup (AFI_IP, dist->list[DISTRIBUTE_V4_IN]); + if (alist) + ri->list[RIP_FILTER_IN] = alist; + else + ri->list[RIP_FILTER_IN] = NULL; + } + else + ri->list[RIP_FILTER_IN] = NULL; + + if (dist->list[DISTRIBUTE_V4_OUT]) + { + alist = access_list_lookup (AFI_IP, dist->list[DISTRIBUTE_V4_OUT]); + if (alist) + ri->list[RIP_FILTER_OUT] = alist; + else + ri->list[RIP_FILTER_OUT] = NULL; + } + else + ri->list[RIP_FILTER_OUT] = NULL; + + if (dist->prefix[DISTRIBUTE_V4_IN]) + { + plist = prefix_list_lookup (AFI_IP, dist->prefix[DISTRIBUTE_V4_IN]); + if (plist) + ri->prefix[RIP_FILTER_IN] = plist; + else + ri->prefix[RIP_FILTER_IN] = NULL; + } + else + ri->prefix[RIP_FILTER_IN] = NULL; + + if (dist->prefix[DISTRIBUTE_V4_OUT]) + { + plist = prefix_list_lookup (AFI_IP, dist->prefix[DISTRIBUTE_V4_OUT]); + if (plist) + ri->prefix[RIP_FILTER_OUT] = plist; + else + ri->prefix[RIP_FILTER_OUT] = NULL; + } + else + ri->prefix[RIP_FILTER_OUT] = NULL; +} + +void +rip_distribute_update_interface (struct interface *ifp) +{ + struct distribute *dist; + + dist = distribute_lookup (ifp->name); + if (dist) + rip_distribute_update (dist); +} + +/* Update all interface's distribute list. */ +/* ARGSUSED */ +static void +rip_distribute_update_all (struct prefix_list *notused) +{ + struct interface *ifp; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS (iflist, node, nnode, ifp)) + rip_distribute_update_interface (ifp); +} +/* ARGSUSED */ +static void +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 = 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 ((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); + RIP_TIMER_OFF (rip->t_triggered_update); + RIP_TIMER_OFF (rip->t_triggered_interval); + + /* Cancel read thread. */ + if (rip->t_read) + { + thread_cancel (rip->t_read); + rip->t_read = NULL; + } + + /* Close RIP socket. */ + if (rip->sock >= 0) + { + close (rip->sock); + rip->sock = -1; + } + + /* Static RIP route configuration. */ + for (rp = route_top (rip->route); rp; rp = route_next (rp)) + if (rp->info) + { + rp->info = NULL; + route_unlock_node (rp); + } + + /* RIP neighbor configuration. */ + for (rp = route_top (rip->neighbor); rp; rp = route_next (rp)) + if (rp->info) + { + rp->info = NULL; + route_unlock_node (rp); + } + + /* Redistribute related clear. */ + if (rip->default_information_route_map) + free (rip->default_information_route_map); + + for (i = 0; i < ZEBRA_ROUTE_MAX; i++) + if (rip->route_map[i].name) + free (rip->route_map[i].name); + + XFREE (MTYPE_ROUTE_TABLE, rip->table); + XFREE (MTYPE_ROUTE_TABLE, rip->route); + XFREE (MTYPE_ROUTE_TABLE, rip->neighbor); + + XFREE (MTYPE_RIP, rip); + rip = NULL; + } + + rip_clean_network (); + rip_passive_nondefault_clean (); + rip_offset_clean (); + rip_interfaces_clean (); + rip_distance_reset (); + rip_redistribute_clean (); +} + +/* Reset all values to the default settings. */ +void +rip_reset (void) +{ + /* Reset global counters. */ + rip_global_route_changes = 0; + rip_global_queries = 0; + + /* Call ripd related reset functions. */ + rip_debug_reset (); + rip_route_map_reset (); + + /* Call library reset functions. */ + vty_reset (); + access_list_reset (); + prefix_list_reset (); + + distribute_list_reset (); + + rip_interfaces_reset (); + rip_distance_reset (); + + rip_zclient_reset (); +} + +static void +rip_if_rmap_update (struct if_rmap *if_rmap) +{ + struct interface *ifp; + struct rip_interface *ri; + struct route_map *rmap; + + ifp = if_lookup_by_name (if_rmap->ifname); + if (ifp == NULL) + return; + + ri = ifp->info; + + if (if_rmap->routemap[IF_RMAP_IN]) + { + rmap = route_map_lookup_by_name (if_rmap->routemap[IF_RMAP_IN]); + if (rmap) + ri->routemap[IF_RMAP_IN] = rmap; + else + ri->routemap[IF_RMAP_IN] = NULL; + } + else + ri->routemap[RIP_FILTER_IN] = NULL; + + if (if_rmap->routemap[IF_RMAP_OUT]) + { + rmap = route_map_lookup_by_name (if_rmap->routemap[IF_RMAP_OUT]); + if (rmap) + ri->routemap[IF_RMAP_OUT] = rmap; + else + ri->routemap[IF_RMAP_OUT] = NULL; + } + else + ri->routemap[RIP_FILTER_OUT] = NULL; +} + +void +rip_if_rmap_update_interface (struct interface *ifp) +{ + struct if_rmap *if_rmap; + + if_rmap = if_rmap_lookup (ifp->name); + if (if_rmap) + rip_if_rmap_update (if_rmap); +} + +static void +rip_routemap_update_redistribute (void) +{ + int i; + + if (rip) + { + for (i = 0; i < ZEBRA_ROUTE_MAX; i++) + { + if (rip->route_map[i].name) + rip->route_map[i].map = + route_map_lookup_by_name (rip->route_map[i].name); + } + } +} + +/* ARGSUSED */ +static void +rip_routemap_update (const char *notused) +{ + struct interface *ifp; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS (iflist, node, nnode, ifp)) + rip_if_rmap_update_interface (ifp); + + rip_routemap_update_redistribute (); +} + +/* Allocate new rip structure and set default value. */ +void +rip_init (void) +{ + /* Randomize for triggered update random(). */ + srandom (time (NULL)); + + /* Install top nodes. */ + install_node (&rip_node, config_write_rip); + + /* Install rip commands. */ + install_element (VIEW_NODE, &show_ip_rip_cmd); + install_element (VIEW_NODE, &show_ip_rip_status_cmd); + install_element (CONFIG_NODE, &router_rip_cmd); + install_element (CONFIG_NODE, &no_router_rip_cmd); + + install_default (RIP_NODE); + install_element (RIP_NODE, &rip_version_cmd); + install_element (RIP_NODE, &no_rip_version_cmd); + install_element (RIP_NODE, &no_rip_version_val_cmd); + install_element (RIP_NODE, &rip_default_metric_cmd); + install_element (RIP_NODE, &no_rip_default_metric_cmd); + install_element (RIP_NODE, &no_rip_default_metric_val_cmd); + install_element (RIP_NODE, &rip_timers_cmd); + install_element (RIP_NODE, &no_rip_timers_cmd); + install_element (RIP_NODE, &no_rip_timers_val_cmd); + install_element (RIP_NODE, &rip_route_cmd); + install_element (RIP_NODE, &no_rip_route_cmd); + install_element (RIP_NODE, &rip_distance_cmd); + install_element (RIP_NODE, &no_rip_distance_cmd); + install_element (RIP_NODE, &rip_distance_source_cmd); + 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 (); + + /* SNMP init. */ +#ifdef HAVE_SNMP + rip_snmp_init (); +#endif /* HAVE_SNMP */ + + /* Access list install. */ + access_list_init (); + access_list_add_hook (rip_distribute_update_all_wrapper); + access_list_delete_hook (rip_distribute_update_all_wrapper); + + /* Prefix list initialize.*/ + prefix_list_init (); + prefix_list_add_hook (rip_distribute_update_all); + prefix_list_delete_hook (rip_distribute_update_all); + + /* Distribute list install. */ + distribute_list_init (RIP_NODE); + distribute_list_add_hook (rip_distribute_update); + distribute_list_delete_hook (rip_distribute_update); + + /* Route-map */ + rip_route_map_init (); + rip_offset_init (); + + route_map_add_hook (rip_routemap_update); + route_map_delete_hook (rip_routemap_update); + + if_rmap_init (RIP_NODE); + if_rmap_hook_add (rip_if_rmap_update); + if_rmap_hook_delete (rip_if_rmap_update); + + /* Distance control. */ + rip_distance_table = route_table_init (); +} diff --git a/ripd/ripd.conf.sample b/ripd/ripd.conf.sample new file mode 100644 index 0000000..2902ff9 --- /dev/null +++ b/ripd/ripd.conf.sample @@ -0,0 +1,24 @@ +! -*- rip -*- +! +! RIPd sample configuration file +! +! $Id: ripd.conf.sample,v 1.1 2002/12/13 20:15:30 paul Exp $ +! +hostname ripd +password zebra +! +! debug rip events +! debug rip packet +! +router rip +! network 11.0.0.0/8 +! network eth0 +! route 10.0.0.0/8 +! distribute-list private-only in eth0 +! +!access-list private-only permit 10.0.0.0/8 +!access-list private-only deny any +! +!log file ripd.log +! +log stdout diff --git a/ripd/ripd.h b/ripd/ripd.h new file mode 100644 index 0000000..4f82525 --- /dev/null +++ b/ripd/ripd.h @@ -0,0 +1,449 @@ +/* RIP related values and structures. + * Copyright (C) 1997, 1998, 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 _ZEBRA_RIP_H +#define _ZEBRA_RIP_H + +/* RIP version number. */ +#define RIPv1 1 +#define RIPv2 2 +/* N.B. stuff will break if + (RIPv1 != RI_RIP_VERSION_1) || (RIPv2 != RI_RIP_VERSION_2) */ + + +/* RIP command list. */ +#define RIP_REQUEST 1 +#define RIP_RESPONSE 2 +#define RIP_TRACEON 3 /* Obsolete */ +#define RIP_TRACEOFF 4 /* Obsolete */ +#define RIP_POLL 5 +#define RIP_POLL_ENTRY 6 +#define RIP_COMMAND_MAX 7 + +/* RIP metric infinity value.*/ +#define RIP_METRIC_INFINITY 16 + +/* Normal RIP packet min and max size. */ +#define RIP_PACKET_MINSIZ 4 +#define RIP_PACKET_MAXSIZ 512 + +#define RIP_HEADER_SIZE 4 +#define RIP_RTE_SIZE 20 + +/* Max count of routing table entry in one rip packet. */ +#define RIP_MAX_RTE ((RIP_PACKET_MAXSIZ - RIP_HEADER_SIZE) / RIP_RTE_SIZE) + +/* RIP version 2 multicast address. */ +#ifndef INADDR_RIP_GROUP +#define INADDR_RIP_GROUP 0xe0000009 /* 224.0.0.9 */ +#endif + +/* RIP timers */ +#define RIP_UPDATE_TIMER_DEFAULT 30 +#define RIP_TIMEOUT_TIMER_DEFAULT 180 +#define RIP_GARBAGE_TIMER_DEFAULT 120 + +/* RIP peer timeout value. */ +#define RIP_PEER_TIMER_DEFAULT 180 + +/* RIP port number. */ +#define RIP_PORT_DEFAULT 520 +#define RIP_VTY_PORT 2602 + +/* Default configuration file name. */ +#define RIPD_DEFAULT_CONFIG "ripd.conf" + +/* RIP route types. */ +#define RIP_ROUTE_RTE 0 +#define RIP_ROUTE_STATIC 1 +#define RIP_ROUTE_DEFAULT 2 +#define RIP_ROUTE_REDISTRIBUTE 3 +#define RIP_ROUTE_INTERFACE 4 + +/* RIPv2 special RTE family types */ +#define RIP_FAMILY_AUTH 0xffff + +/* RIPv2 authentication types, for RIP_FAMILY_AUTH RTE's */ +#define RIP_NO_AUTH 0 +#define RIP_AUTH_DATA 1 +#define RIP_AUTH_SIMPLE_PASSWORD 2 +#define RIP_AUTH_MD5 3 + +/* RIPv2 Simple authentication */ +#define RIP_AUTH_SIMPLE_SIZE 16 + +/* RIPv2 MD5 authentication. */ +#define RIP_AUTH_MD5_SIZE 16 +#define RIP_AUTH_MD5_COMPAT_SIZE RIP_RTE_SIZE + +/* RIP structure. */ +struct rip +{ + /* RIP socket. */ + int sock; + + /* Default version of rip instance. */ + int version_send; /* version 1 or 2 (but not both) */ + int version_recv; /* version 1 or 2 or both */ + + /* Output buffer of RIP. */ + struct stream *obuf; + + /* RIP routing information base. */ + struct route_table *table; + + /* RIP only static routing information. */ + struct route_table *route; + + /* RIP neighbor. */ + struct route_table *neighbor; + + /* RIP threads. */ + struct thread *t_read; + + /* Update and garbage timer. */ + struct thread *t_update; + + /* Triggered update hack. */ + int trigger; + struct thread *t_triggered_update; + struct thread *t_triggered_interval; + + /* RIP timer values. */ + unsigned long update_time; + unsigned long timeout_time; + unsigned long garbage_time; + + /* RIP default metric. */ + int default_metric; + + /* RIP default-information originate. */ + u_char default_information; + char *default_information_route_map; + + /* RIP default distance. */ + u_char distance; + struct route_table *distance_table; + + /* RIP ECMP flag */ + unsigned int ecmp; + + /* For redistribute route map. */ + struct + { + char *name; + struct route_map *map; + int metric_config; + u_int32_t metric; + } route_map[ZEBRA_ROUTE_MAX]; +}; + +/* RIP routing table entry which belong to rip_packet. */ +struct rte +{ + u_int16_t family; /* Address family of this route. */ + u_int16_t tag; /* Route Tag which included in RIP2 packet. */ + struct in_addr prefix; /* Prefix of rip route. */ + struct in_addr mask; /* Netmask of rip route. */ + struct in_addr nexthop; /* Next hop of rip route. */ + u_int32_t metric; /* Metric value of rip route. */ +}; + +/* RIP packet structure. */ +struct rip_packet +{ + unsigned char command; /* Command type of RIP packet. */ + unsigned char version; /* RIP version which coming from peer. */ + unsigned char pad1; /* Padding of RIP packet header. */ + unsigned char pad2; /* Same as above. */ + struct rte rte[1]; /* Address structure. */ +}; + +/* Buffer to read RIP packet. */ +union rip_buf +{ + struct rip_packet rip_packet; + char buf[RIP_PACKET_MAXSIZ]; +}; + +/* RIP route information. */ +struct rip_info +{ + /* This route's type. */ + int type; + + /* Sub type. */ + int sub_type; + + /* RIP nexthop. */ + struct in_addr nexthop; + struct in_addr from; + + /* Which interface does this route come from. */ + ifindex_t ifindex; + + /* Metric of this route. */ + u_int32_t metric; + + /* External metric of this route. + if learnt from an externalm proto */ + u_int32_t external_metric; + + /* Tag information of this route. */ + u_int16_t tag; + + /* Flags of RIP route. */ +#define RIP_RTF_FIB 1 +#define RIP_RTF_CHANGED 2 + u_char flags; + + /* Garbage collect timer. */ + struct thread *t_timeout; + struct thread *t_garbage_collect; + + /* Route-map futures - this variables can be changed. */ + struct in_addr nexthop_out; + u_char metric_set; + u_int32_t metric_out; + u_int16_t tag_out; + ifindex_t ifindex_out; + + struct route_node *rp; + + u_char distance; + +#ifdef NEW_RIP_TABLE + struct rip_info *next; + struct rip_info *prev; +#endif /* NEW_RIP_TABLE */ +}; + +typedef enum { + RIP_NO_SPLIT_HORIZON = 0, + RIP_SPLIT_HORIZON, + RIP_SPLIT_HORIZON_POISONED_REVERSE +} split_horizon_policy_t; + +/* RIP specific interface configuration. */ +struct rip_interface +{ + /* RIP is enabled on this interface. */ + int enable_network; + int enable_interface; + + /* RIP is running on this interface. */ + int running; + + /* RIP version control. */ + int ri_send; + int ri_receive; + + /* RIPv2 authentication type. */ + int auth_type; + + /* RIPv2 authentication string. */ + char *auth_str; + + /* RIPv2 authentication key chain. */ + char *key_chain; + + /* value to use for md5->auth_len */ + u_int8_t md5_auth_len; + + /* Split horizon flag. */ + split_horizon_policy_t split_horizon; + split_horizon_policy_t split_horizon_default; + + /* For filter type slot. */ +#define RIP_FILTER_IN 0 +#define RIP_FILTER_OUT 1 +#define RIP_FILTER_MAX 2 + + /* Access-list. */ + struct access_list *list[RIP_FILTER_MAX]; + + /* Prefix-list. */ + struct prefix_list *prefix[RIP_FILTER_MAX]; + + /* Route-map. */ + struct route_map *routemap[RIP_FILTER_MAX]; + + /* Wake up thread. */ + struct thread *t_wakeup; + + /* Interface statistics. */ + int recv_badpackets; + int recv_badroutes; + int sent_updates; + + /* Passive interface. */ + int passive; +}; + +/* RIP peer information. */ +struct rip_peer +{ + /* Peer address. */ + struct in_addr addr; + + /* Peer RIP tag value. */ + int domain; + + /* Last update time. */ + time_t uptime; + + /* Peer RIP version. */ + u_char version; + + /* Statistics. */ + int recv_badpackets; + int recv_badroutes; + + /* Timeout thread. */ + struct thread *t_timeout; +}; + +struct rip_md5_info +{ + u_int16_t family; + u_int16_t type; + u_int16_t packet_len; + u_char keyid; + u_char auth_len; + u_int32_t sequence; + u_int32_t reserv1; + u_int32_t reserv2; +}; + +struct rip_md5_data +{ + u_int16_t family; + u_int16_t type; + u_char digest[16]; +}; + +/* RIP accepet/announce methods. */ +#define RI_RIP_UNSPEC 0 +#define RI_RIP_VERSION_1 1 +#define RI_RIP_VERSION_2 2 +#define RI_RIP_VERSION_1_AND_2 3 +/* N.B. stuff will break if + (RIPv1 != RI_RIP_VERSION_1) || (RIPv2 != RI_RIP_VERSION_2) */ + +/* Default value for "default-metric" command. */ +#define RIP_DEFAULT_METRIC_DEFAULT 1 + +/* RIP event. */ +enum rip_event +{ + RIP_READ, + RIP_UPDATE_EVENT, + RIP_TRIGGERED_UPDATE, +}; + +/* Macro for timer turn on. */ +#define RIP_TIMER_ON(T,F,V) \ + do { \ + if (!(T)) \ + (T) = thread_add_timer (master, (F), rinfo, (V)); \ + } while (0) + +/* Macro for timer turn off. */ +#define RIP_TIMER_OFF(X) \ + do { \ + if (X) \ + { \ + thread_cancel (X); \ + (X) = NULL; \ + } \ + } while (0) + +/* Prototypes. */ +extern void rip_init (void); +extern void rip_reset (void); +extern void rip_clean (void); +extern void rip_clean_network (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 (struct thread_master *); +extern void rip_zclient_start (void); +extern void rip_zclient_reset (void); +extern void rip_offset_init (void); +extern int if_check_address (struct in_addr addr); + +extern int rip_request_send (struct sockaddr_in *, struct interface *, u_char, + struct connected *); +extern int rip_neighbor_lookup (struct sockaddr_in *); + +extern int rip_redistribute_check (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 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 *); + +extern int config_write_rip_network (struct vty *, int); +extern int config_write_rip_offset_list (struct vty *); +extern int config_write_rip_redistribute (struct vty *, int); + +extern void rip_peer_init (void); +extern void rip_peer_update (struct sockaddr_in *, u_char); +extern void rip_peer_bad_route (struct sockaddr_in *); +extern void rip_peer_bad_packet (struct sockaddr_in *); +extern void rip_peer_display (struct vty *); +extern struct rip_peer *rip_peer_lookup (struct in_addr *); +extern struct rip_peer *rip_peer_lookup_next (struct in_addr *); + +extern int rip_offset_list_apply_in (struct prefix_ipv4 *, struct interface *, u_int32_t *); +extern int rip_offset_list_apply_out (struct prefix_ipv4 *, struct interface *, u_int32_t *); +extern void rip_offset_clean (void); + +extern void rip_info_free (struct rip_info *); +extern u_char rip_distance_apply (struct rip_info *); +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; + +/* Master thread strucutre. */ +extern struct thread_master *master; + +/* RIP statistics for SNMP. */ +extern long rip_global_route_changes; +extern long rip_global_queries; +#endif /* _ZEBRA_RIP_H */ diff --git a/ripngd/.gitignore b/ripngd/.gitignore new file mode 100644 index 0000000..e871fae --- /dev/null +++ b/ripngd/.gitignore @@ -0,0 +1,17 @@ +Makefile +Makefile.in +*.o +ripngd +ripngd.conf +tags +TAGS +.deps +.nfs* +*.lo +*.la +*.libs +.arch-inventory +.arch-ids +*~ +*.loT +*.a diff --git a/ripngd/Makefile.am b/ripngd/Makefile.am new file mode 100644 index 0000000..df0f7d3 --- /dev/null +++ b/ripngd/Makefile.am @@ -0,0 +1,26 @@ +## Process this file with automake to produce Makefile.in. + +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 = $(WERROR) + +noinst_LIBRARIES = libripng.a +sbin_PROGRAMS = ripngd + +libripng_a_SOURCES = \ + ripng_interface.c ripngd.c ripng_zebra.c ripng_route.c ripng_debug.c \ + ripng_routemap.c ripng_offset.c ripng_peer.c ripng_nexthop.c + +noinst_HEADERS = \ + ripng_debug.h ripng_route.h ripngd.h ripng_nexthop.h + +ripngd_SOURCES = \ + ripng_main.c $(libripng_a_SOURCES) + +ripngd_LDADD = ../lib/libzebra.la @LIBCAP@ + +examplesdir = $(exampledir) +dist_examples_DATA = ripngd.conf.sample + diff --git a/ripngd/ripng_debug.c b/ripngd/ripng_debug.c new file mode 100644 index 0000000..eb8ff5d --- /dev/null +++ b/ripngd/ripng_debug.c @@ -0,0 +1,288 @@ +/* + * RIPng debug output routines + * Copyright (C) 1998 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 "ripngd/ripng_debug.h" + +/* For debug statement. */ +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", + SHOW_STR + DEBUG_STR + "RIPng configuration\n") +{ + vty_out (vty, "RIPng debugging status:%s", VTY_NEWLINE); + + if (IS_RIPNG_DEBUG_EVENT) + vty_out (vty, " RIPng event debugging is on%s", VTY_NEWLINE); + + if (IS_RIPNG_DEBUG_PACKET) + { + if (IS_RIPNG_DEBUG_SEND && IS_RIPNG_DEBUG_RECV) + { + vty_out (vty, " RIPng packet debugging is on%s", + VTY_NEWLINE); + } + else + { + if (IS_RIPNG_DEBUG_SEND) + vty_out (vty, " RIPng packet send debugging is on%s", + VTY_NEWLINE); + else + vty_out (vty, " RIPng packet receive debugging is on%s", + VTY_NEWLINE); + } + } + + if (IS_RIPNG_DEBUG_ZEBRA) + vty_out (vty, " RIPng zebra debugging is on%s", VTY_NEWLINE); + + return CMD_SUCCESS; +} + +DEFUN (debug_ripng_events, + debug_ripng_events_cmd, + "debug ripng events", + DEBUG_STR + "RIPng configuration\n" + "Debug option set for ripng events\n") +{ + ripng_debug_event = RIPNG_DEBUG_EVENT; + return CMD_WARNING; +} + +DEFUN (debug_ripng_packet, + debug_ripng_packet_cmd, + "debug ripng packet", + DEBUG_STR + "RIPng configuration\n" + "Debug option set for ripng packet\n") +{ + ripng_debug_packet = RIPNG_DEBUG_PACKET; + ripng_debug_packet |= RIPNG_DEBUG_SEND; + ripng_debug_packet |= RIPNG_DEBUG_RECV; + return CMD_SUCCESS; +} + +DEFUN (debug_ripng_packet_direct, + debug_ripng_packet_direct_cmd, + "debug ripng packet (recv|send)", + DEBUG_STR + "RIPng configuration\n" + "Debug option set for ripng packet\n" + "Debug option set for receive packet\n" + "Debug option set for send packet\n") +{ + ripng_debug_packet |= RIPNG_DEBUG_PACKET; + if (strncmp ("send", argv[0], strlen (argv[0])) == 0) + ripng_debug_packet |= RIPNG_DEBUG_SEND; + if (strncmp ("recv", argv[0], strlen (argv[0])) == 0) + ripng_debug_packet |= RIPNG_DEBUG_RECV; + + return CMD_SUCCESS; +} + +/* N.B. the "detail" modifier is a no-op. we leave this command + for legacy compatibility. */ +DEFUN_DEPRECATED (debug_ripng_packet_detail, + debug_ripng_packet_detail_cmd, + "debug ripng packet (recv|send) detail", + DEBUG_STR + "RIPng configuration\n" + "Debug option set for ripng packet\n" + "Debug option set for receive packet\n" + "Debug option set for send packet\n" + "Debug option set detaied information\n") +{ + ripng_debug_packet |= RIPNG_DEBUG_PACKET; + if (strncmp ("send", argv[0], strlen (argv[0])) == 0) + ripng_debug_packet |= RIPNG_DEBUG_SEND; + if (strncmp ("recv", argv[0], strlen (argv[0])) == 0) + ripng_debug_packet |= RIPNG_DEBUG_RECV; + + return CMD_SUCCESS; +} + +DEFUN (debug_ripng_zebra, + debug_ripng_zebra_cmd, + "debug ripng zebra", + DEBUG_STR + "RIPng configuration\n" + "Debug option set for ripng and zebra communication\n") +{ + ripng_debug_zebra = RIPNG_DEBUG_ZEBRA; + return CMD_WARNING; +} + +DEFUN (no_debug_ripng_events, + no_debug_ripng_events_cmd, + "no debug ripng events", + NO_STR + DEBUG_STR + "RIPng configuration\n" + "Debug option set for ripng events\n") +{ + ripng_debug_event = 0; + return CMD_SUCCESS; +} + +DEFUN (no_debug_ripng_packet, + no_debug_ripng_packet_cmd, + "no debug ripng packet", + NO_STR + DEBUG_STR + "RIPng configuration\n" + "Debug option set for ripng packet\n") +{ + ripng_debug_packet = 0; + return CMD_SUCCESS; +} + +DEFUN (no_debug_ripng_packet_direct, + no_debug_ripng_packet_direct_cmd, + "no debug ripng packet (recv|send)", + NO_STR + DEBUG_STR + "RIPng configuration\n" + "Debug option set for ripng packet\n" + "Debug option set for receive packet\n" + "Debug option set for send packet\n") +{ + if (strncmp ("send", argv[0], strlen (argv[0])) == 0) + { + if (IS_RIPNG_DEBUG_RECV) + ripng_debug_packet &= ~RIPNG_DEBUG_SEND; + else + ripng_debug_packet = 0; + } + else if (strncmp ("recv", argv[0], strlen (argv[0])) == 0) + { + if (IS_RIPNG_DEBUG_SEND) + ripng_debug_packet &= ~RIPNG_DEBUG_RECV; + else + ripng_debug_packet = 0; + } + return CMD_SUCCESS; +} + +DEFUN (no_debug_ripng_zebra, + no_debug_ripng_zebra_cmd, + "no debug ripng zebra", + NO_STR + DEBUG_STR + "RIPng configuration\n" + "Debug option set for ripng and zebra communication\n") +{ + ripng_debug_zebra = 0; + return CMD_WARNING; +} + +/* Debug node. */ +static struct cmd_node debug_node = +{ + DEBUG_NODE, + "", /* Debug node has no interface. */ + 1 /* VTYSH */ +}; + +static int +config_write_debug (struct vty *vty) +{ + int write = 0; + + if (IS_RIPNG_DEBUG_EVENT) + { + vty_out (vty, "debug ripng events%s", VTY_NEWLINE); + write++; + } + if (IS_RIPNG_DEBUG_PACKET) + { + if (IS_RIPNG_DEBUG_SEND && IS_RIPNG_DEBUG_RECV) + { + vty_out (vty, "debug ripng packet%s", + VTY_NEWLINE); + write++; + } + else + { + if (IS_RIPNG_DEBUG_SEND) + vty_out (vty, "debug ripng packet send%s", + VTY_NEWLINE); + else + vty_out (vty, "debug ripng packet recv%s", + VTY_NEWLINE); + write++; + } + } + if (IS_RIPNG_DEBUG_ZEBRA) + { + vty_out (vty, "debug ripng zebra%s", VTY_NEWLINE); + write++; + } + return write; +} + +void +ripng_debug_reset () +{ + ripng_debug_event = 0; + ripng_debug_packet = 0; + ripng_debug_zebra = 0; +} + +void +ripng_debug_init () +{ + ripng_debug_event = 0; + ripng_debug_packet = 0; + ripng_debug_zebra = 0; + + install_node (&debug_node, config_write_debug); + + install_element (VIEW_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); + install_element (ENABLE_NODE, &debug_ripng_packet_detail_cmd); + install_element (ENABLE_NODE, &debug_ripng_zebra_cmd); + install_element (ENABLE_NODE, &no_debug_ripng_events_cmd); + install_element (ENABLE_NODE, &no_debug_ripng_packet_cmd); + install_element (ENABLE_NODE, &no_debug_ripng_packet_direct_cmd); + install_element (ENABLE_NODE, &no_debug_ripng_zebra_cmd); + + install_element (CONFIG_NODE, &debug_ripng_events_cmd); + install_element (CONFIG_NODE, &debug_ripng_packet_cmd); + install_element (CONFIG_NODE, &debug_ripng_packet_direct_cmd); + install_element (CONFIG_NODE, &debug_ripng_packet_detail_cmd); + install_element (CONFIG_NODE, &debug_ripng_zebra_cmd); + install_element (CONFIG_NODE, &no_debug_ripng_events_cmd); + install_element (CONFIG_NODE, &no_debug_ripng_packet_cmd); + install_element (CONFIG_NODE, &no_debug_ripng_packet_direct_cmd); + install_element (CONFIG_NODE, &no_debug_ripng_zebra_cmd); +} diff --git a/ripngd/ripng_debug.h b/ripngd/ripng_debug.h new file mode 100644 index 0000000..674345c --- /dev/null +++ b/ripngd/ripng_debug.h @@ -0,0 +1,51 @@ +/* + * RIPng debug output routines + * Copyright (C) 1998, 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 _ZEBRA_RIPNG_DEBUG_H +#define _ZEBRA_RIPNG_DEBUG_H + +/* Debug flags. */ +#define RIPNG_DEBUG_EVENT 0x01 + +#define RIPNG_DEBUG_PACKET 0x01 +#define RIPNG_DEBUG_SEND 0x20 +#define RIPNG_DEBUG_RECV 0x40 + +#define RIPNG_DEBUG_ZEBRA 0x01 + +/* Debug related macro. */ +#define IS_RIPNG_DEBUG_EVENT (ripng_debug_event & RIPNG_DEBUG_EVENT) + +#define IS_RIPNG_DEBUG_PACKET (ripng_debug_packet & RIPNG_DEBUG_PACKET) +#define IS_RIPNG_DEBUG_SEND (ripng_debug_packet & RIPNG_DEBUG_SEND) +#define IS_RIPNG_DEBUG_RECV (ripng_debug_packet & RIPNG_DEBUG_RECV) + +#define IS_RIPNG_DEBUG_ZEBRA (ripng_debug_zebra & RIPNG_DEBUG_ZEBRA) + +extern unsigned long ripng_debug_event; +extern unsigned long ripng_debug_packet; +extern unsigned long ripng_debug_zebra; + +extern void ripng_debug_init (void); +extern void ripng_debug_reset (void); + +#endif /* _ZEBRA_RIPNG_DEBUG_H */ diff --git a/ripngd/ripng_interface.c b/ripngd/ripng_interface.c new file mode 100644 index 0000000..1d3757a --- /dev/null +++ b/ripngd/ripng_interface.c @@ -0,0 +1,1214 @@ +/* + * Interface related function for RIPng. + * Copyright (C) 1998 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 "linklist.h" +#include "if.h" +#include "prefix.h" +#include "memory.h" +#include "network.h" +#include "filter.h" +#include "log.h" +#include "stream.h" +#include "zclient.h" +#include "command.h" +#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 +#endif +#ifndef IPV6_LEAVE_GROUP +#define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP +#endif + +extern struct zebra_privs_t ripngd_privs; + +/* Static utility function. */ +static void ripng_enable_apply (struct interface *); +static void ripng_passive_interface_apply (struct interface *); +static int ripng_enable_if_lookup (const char *); +static int ripng_enable_network_lookup2 (struct connected *); +static void ripng_enable_apply_all (void); + +/* Join to the all rip routers multicast group. */ +static int +ripng_multicast_join (struct interface *ifp) +{ + int ret; + struct ipv6_mreq mreq; + int save_errno; + + if (if_is_up (ifp) && if_is_multicast (ifp)) { + memset (&mreq, 0, sizeof (mreq)); + inet_pton(AF_INET6, RIPNG_GROUP, &mreq.ipv6mr_multiaddr); + mreq.ipv6mr_interface = ifp->ifindex; + + /* + * NetBSD 1.6.2 requires root to join groups on gif(4). + * While this is bogus, privs are available and easy to use + * for this call as a workaround. + */ + if (ripngd_privs.change (ZPRIVS_RAISE)) + zlog_err ("ripng_multicast_join: could not raise privs"); + + ret = setsockopt (ripng->sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, + (char *) &mreq, sizeof (mreq)); + save_errno = errno; + + if (ripngd_privs.change (ZPRIVS_LOWER)) + zlog_err ("ripng_multicast_join: could not lower privs"); + + if (ret < 0 && save_errno == EADDRINUSE) + { + /* + * Group is already joined. This occurs due to sloppy group + * management, in particular declining to leave the group on + * an interface that has just gone down. + */ + zlog_warn ("ripng join on %s EADDRINUSE (ignoring)\n", ifp->name); + return 0; /* not an error */ + } + + if (ret < 0) + zlog_warn ("can't setsockopt IPV6_JOIN_GROUP: %s", + safe_strerror (save_errno)); + + if (IS_RIPNG_DEBUG_EVENT) + zlog_debug ("RIPng %s join to all-rip-routers multicast group", ifp->name); + + if (ret < 0) + return -1; + } + return 0; +} + +/* Leave from the all rip routers multicast group. */ +static int +ripng_multicast_leave (struct interface *ifp) +{ + int ret; + struct ipv6_mreq mreq; + + if (if_is_up (ifp) && if_is_multicast (ifp)) { + memset (&mreq, 0, sizeof (mreq)); + inet_pton(AF_INET6, RIPNG_GROUP, &mreq.ipv6mr_multiaddr); + mreq.ipv6mr_interface = ifp->ifindex; + + ret = setsockopt (ripng->sock, IPPROTO_IPV6, IPV6_LEAVE_GROUP, + (char *) &mreq, sizeof (mreq)); + if (ret < 0) + zlog_warn ("can't setsockopt IPV6_LEAVE_GROUP: %s\n", safe_strerror (errno)); + + if (IS_RIPNG_DEBUG_EVENT) + zlog_debug ("RIPng %s leave from all-rip-routers multicast group", + ifp->name); + + if (ret < 0) + return -1; + } + + return 0; +} + +/* How many link local IPv6 address could be used on the interface ? */ +static int +ripng_if_ipv6_lladdress_check (struct interface *ifp) +{ + struct listnode *nn; + struct connected *connected; + int count = 0; + + for (ALL_LIST_ELEMENTS_RO (ifp->connected, nn, connected)) + { + struct prefix *p; + p = connected->address; + + if ((p->family == AF_INET6) && + IN6_IS_ADDR_LINKLOCAL (&p->u.prefix6)) + count++; + } + + return count; +} + +static int +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 ((list = rp->info) != NULL) + for (ALL_LIST_ELEMENTS (list, listnode, nextnode, rinfo)) + if (rinfo->ifindex == ifp->ifindex) + ripng_ecmp_delete (rinfo); + + ri = ifp->info; + + if (ri->running) + { + if (IS_RIPNG_DEBUG_EVENT) + zlog_debug ("turn off %s", ifp->name); + + /* Leave from multicast group. */ + ripng_multicast_leave (ifp); + + ri->running = 0; + } + + return 0; +} + +/* Inteface link up message processing. */ +int +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, vrf_id); + + if (ifp == NULL) + return 0; + + if (IS_RIPNG_DEBUG_ZEBRA) + zlog_debug ("interface up %s index %d flags %llx metric %d mtu %d", + ifp->name, ifp->ifindex, (unsigned long long)ifp->flags, + ifp->metric, ifp->mtu6); + + /* Check if this interface is RIPng enabled or not. */ + ripng_enable_apply (ifp); + + /* Check for a passive interface. */ + ripng_passive_interface_apply (ifp); + + /* Apply distribute list to the all interface. */ + ripng_distribute_update_interface (ifp); + + return 0; +} + +/* Inteface link down message processing. */ +int +ripng_interface_down (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, vrf_id); + + if (ifp == NULL) + return 0; + + ripng_if_down (ifp); + + if (IS_RIPNG_DEBUG_ZEBRA) + zlog_debug ("interface down %s index %d flags %#llx metric %d mtu %d", + ifp->name, ifp->ifindex, + (unsigned long long) ifp->flags, ifp->metric, ifp->mtu6); + + return 0; +} + +/* Inteface addition message from zebra. */ +int +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, vrf_id); + + if (IS_RIPNG_DEBUG_ZEBRA) + zlog_debug ("RIPng interface add %s index %d flags %#llx metric %d mtu %d", + ifp->name, ifp->ifindex, (unsigned long long) ifp->flags, + ifp->metric, ifp->mtu6); + + /* Check is this interface is RIP enabled or not.*/ + ripng_enable_apply (ifp); + + /* Apply distribute list to the interface. */ + ripng_distribute_update_interface (ifp); + + /* Check interface routemap. */ + ripng_if_rmap_update_interface (ifp); + + return 0; +} + +int +ripng_interface_delete (int command, struct zclient *zclient, + 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, vrf_id); + + if (ifp == NULL) + return 0; + + if (if_is_up (ifp)) { + ripng_if_down(ifp); + } + + zlog_info("interface delete %s index %d flags %#llx metric %d mtu %d", + ifp->name, ifp->ifindex, (unsigned long long) ifp->flags, + ifp->metric, ifp->mtu6); + + /* To support pseudo interface do not free interface structure. */ + /* if_delete(ifp); */ + ifp->ifindex = IFINDEX_INTERNAL; + + return 0; +} + +void +ripng_interface_clean (void) +{ + struct listnode *node, *nnode; + struct interface *ifp; + struct ripng_interface *ri; + + for (ALL_LIST_ELEMENTS (iflist, node, nnode, ifp)) + { + 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; + } + } +} + +void +ripng_interface_reset (void) +{ + struct listnode *node; + struct interface *ifp; + struct ripng_interface *ri; + + for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) + { + ri = ifp->info; + + ri->enable_network = 0; + ri->enable_interface = 0; + ri->running = 0; + + ri->split_horizon = RIPNG_NO_SPLIT_HORIZON; + ri->split_horizon_default = RIPNG_NO_SPLIT_HORIZON; + + ri->list[RIPNG_FILTER_IN] = NULL; + ri->list[RIPNG_FILTER_OUT] = NULL; + + ri->prefix[RIPNG_FILTER_IN] = NULL; + ri->prefix[RIPNG_FILTER_OUT] = NULL; + + if (ri->t_wakeup) + { + thread_cancel (ri->t_wakeup); + ri->t_wakeup = NULL; + } + + ri->passive = 0; + } +} + +static void +ripng_apply_address_add (struct connected *ifc) { + struct prefix_ipv6 address; + struct prefix *p; + + if (!ripng) + return; + + if (! if_is_up(ifc->ifp)) + return; + + p = ifc->address; + + memset (&address, 0, sizeof (address)); + address.family = p->family; + address.prefix = p->u.prefix6; + address.prefixlen = p->prefixlen; + apply_mask_ipv6(&address); + + /* Check if this interface is RIP enabled or not + or Check if this address's prefix is RIP enabled */ + 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, 0); + +} + +int +ripng_interface_address_add (int command, struct zclient *zclient, + 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, vrf_id); + + if (c == NULL) + return 0; + + p = c->address; + + if (p->family == AF_INET6) + { + struct ripng_interface *ri = c->ifp->info; + + if (IS_RIPNG_DEBUG_ZEBRA) + zlog_debug ("RIPng connected address %s/%d add", + inet6_ntoa(p->u.prefix6), + p->prefixlen); + + /* Check is this prefix needs to be redistributed. */ + ripng_apply_address_add(c); + + /* Let's try once again whether the interface could be activated */ + if (!ri->running) { + /* Check if this interface is RIP enabled or not.*/ + ripng_enable_apply (c->ifp); + + /* Apply distribute list to the interface. */ + ripng_distribute_update_interface (c->ifp); + + /* Check interface routemap. */ + ripng_if_rmap_update_interface (c->ifp); + } + + } + + return 0; +} + +static void +ripng_apply_address_del (struct connected *ifc) { + struct prefix_ipv6 address; + struct prefix *p; + + if (!ripng) + return; + + if (! if_is_up(ifc->ifp)) + return; + + p = ifc->address; + + memset (&address, 0, sizeof (address)); + address.family = p->family; + address.prefix = p->u.prefix6; + address.prefixlen = p->prefixlen; + apply_mask_ipv6(&address); + + ripng_redistribute_delete(ZEBRA_ROUTE_CONNECT, RIPNG_ROUTE_INTERFACE, + &address, ifc->ifp->ifindex); +} + +int +ripng_interface_address_delete (int command, struct zclient *zclient, + 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, vrf_id); + + if (ifc) + { + p = ifc->address; + + if (p->family == AF_INET6) + { + if (IS_RIPNG_DEBUG_ZEBRA) + zlog_debug ("RIPng connected address %s/%d delete", + inet_ntop (AF_INET6, &p->u.prefix6, buf, + INET6_ADDRSTRLEN), + p->prefixlen); + + /* Check wether this prefix needs to be removed. */ + ripng_apply_address_del(ifc); + } + connected_free (ifc); + } + + return 0; +} + +/* RIPng enable interface vector. */ +vector ripng_enable_if; + +/* RIPng enable network table. */ +struct route_table *ripng_enable_network; + +/* Lookup RIPng enable network. */ +/* Check wether the interface has at least a connected prefix that + * is within the ripng_enable_network table. */ +static int +ripng_enable_network_lookup_if (struct interface *ifp) +{ + struct listnode *node; + struct connected *connected; + struct prefix_ipv6 address; + + for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, connected)) + { + struct prefix *p; + struct route_node *node; + + p = connected->address; + + if (p->family == AF_INET6) + { + address.family = AF_INET6; + address.prefix = p->u.prefix6; + address.prefixlen = IPV6_MAX_BITLEN; + + node = route_node_match (ripng_enable_network, + (struct prefix *)&address); + if (node) + { + route_unlock_node (node); + return 1; + } + } + } + return -1; +} + +/* Check wether connected is within the ripng_enable_network table. */ +static int +ripng_enable_network_lookup2 (struct connected *connected) +{ + struct prefix_ipv6 address; + struct prefix *p; + + p = connected->address; + + if (p->family == AF_INET6) { + struct route_node *node; + + address.family = p->family; + address.prefix = p->u.prefix6; + address.prefixlen = IPV6_MAX_BITLEN; + + /* LPM on p->family, p->u.prefix6/IPV6_MAX_BITLEN within ripng_enable_network */ + node = route_node_match (ripng_enable_network, + (struct prefix *)&address); + + if (node) { + route_unlock_node (node); + return 1; + } + } + + return -1; +} + +/* Add RIPng enable network. */ +static int +ripng_enable_network_add (struct prefix *p) +{ + struct route_node *node; + + node = route_node_get (ripng_enable_network, p); + + if (node->info) + { + route_unlock_node (node); + return -1; + } + else + node->info = (char *) "enabled"; + + /* XXX: One should find a better solution than a generic one */ + ripng_enable_apply_all(); + + return 1; +} + +/* Delete RIPng enable network. */ +static int +ripng_enable_network_delete (struct prefix *p) +{ + struct route_node *node; + + node = route_node_lookup (ripng_enable_network, p); + if (node) + { + node->info = NULL; + + /* Unlock info lock. */ + route_unlock_node (node); + + /* Unlock lookup lock. */ + route_unlock_node (node); + + return 1; + } + return -1; +} + +/* Lookup function. */ +static int +ripng_enable_if_lookup (const char *ifname) +{ + unsigned int i; + char *str; + + for (i = 0; i < vector_active (ripng_enable_if); i++) + if ((str = vector_slot (ripng_enable_if, i)) != NULL) + if (strcmp (str, ifname) == 0) + return i; + return -1; +} + +/* Add interface to ripng_enable_if. */ +static int +ripng_enable_if_add (const char *ifname) +{ + int ret; + + ret = ripng_enable_if_lookup (ifname); + if (ret >= 0) + return -1; + + vector_set (ripng_enable_if, strdup (ifname)); + + ripng_enable_apply_all(); + + return 1; +} + +/* Delete interface from ripng_enable_if. */ +static int +ripng_enable_if_delete (const char *ifname) +{ + int index; + char *str; + + index = ripng_enable_if_lookup (ifname); + if (index < 0) + return -1; + + str = vector_slot (ripng_enable_if, index); + free (str); + vector_unset (ripng_enable_if, index); + + ripng_enable_apply_all(); + + return 1; +} + +/* Wake up interface. */ +static int +ripng_interface_wakeup (struct thread *t) +{ + struct interface *ifp; + struct ripng_interface *ri; + + /* Get interface. */ + ifp = THREAD_ARG (t); + + ri = ifp->info; + ri->t_wakeup = NULL; + + /* Join to multicast group. */ + if (ripng_multicast_join (ifp) < 0) { + zlog_err ("multicast join failed, interface %s not running", ifp->name); + return 0; + } + + /* Set running flag. */ + ri->running = 1; + + /* Send RIP request to the interface. */ + ripng_request (ifp); + + return 0; +} + +static void +ripng_connect_set (struct interface *ifp, int set) +{ + struct listnode *node, *nnode; + struct connected *connected; + struct prefix_ipv6 address; + + for (ALL_LIST_ELEMENTS (ifp->connected, node, nnode, connected)) + { + struct prefix *p; + p = connected->address; + + if (p->family != AF_INET6) + continue; + + address.family = AF_INET6; + address.prefix = p->u.prefix6; + address.prefixlen = p->prefixlen; + apply_mask_ipv6 (&address); + + if (set) { + /* Check once more wether this prefix is within a "network IF_OR_PREF" one */ + 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, 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, 0); + } + } +} + +/* Check RIPng is enabed on this interface. */ +void +ripng_enable_apply (struct interface *ifp) +{ + int ret; + struct ripng_interface *ri = NULL; + + /* Check interface. */ + if (! if_is_up (ifp)) + return; + + ri = ifp->info; + + /* Is this interface a candidate for RIPng ? */ + ret = ripng_enable_network_lookup_if (ifp); + + /* If the interface is matched. */ + if (ret > 0) + ri->enable_network = 1; + else + ri->enable_network = 0; + + /* Check interface name configuration. */ + ret = ripng_enable_if_lookup (ifp->name); + if (ret >= 0) + ri->enable_interface = 1; + else + ri->enable_interface = 0; + + /* any candidate interface MUST have a link-local IPv6 address */ + if ((! ripng_if_ipv6_lladdress_check (ifp)) && + (ri->enable_network || ri->enable_interface)) { + ri->enable_network = 0; + ri->enable_interface = 0; + zlog_warn("Interface %s does not have any link-local address", + ifp->name); + } + + /* Update running status of the interface. */ + if (ri->enable_network || ri->enable_interface) + { + { + if (IS_RIPNG_DEBUG_EVENT) + zlog_debug ("RIPng turn on %s", ifp->name); + + /* Add interface wake up thread. */ + if (! ri->t_wakeup) + ri->t_wakeup = thread_add_timer (master, ripng_interface_wakeup, + ifp, 1); + + ripng_connect_set (ifp, 1); + } + } + else + { + if (ri->running) + { + /* Might as well clean up the route table as well + * ripng_if_down sets to 0 ri->running, and displays "turn off %s" + **/ + ripng_if_down(ifp); + + ripng_connect_set (ifp, 0); + } + } +} + +/* Set distribute list to all interfaces. */ +static void +ripng_enable_apply_all (void) +{ + struct interface *ifp; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) + ripng_enable_apply (ifp); +} + +/* Clear all network and neighbor configuration */ +void +ripng_clean_network () +{ + unsigned int i; + char *str; + struct route_node *rn; + + /* ripng_enable_network */ + for (rn = route_top (ripng_enable_network); rn; rn = route_next (rn)) + if (rn->info) { + rn->info = NULL; + route_unlock_node(rn); + } + + /* ripng_enable_if */ + for (i = 0; i < vector_active (ripng_enable_if); i++) + if ((str = vector_slot (ripng_enable_if, i)) != NULL) { + free (str); + vector_slot (ripng_enable_if, i) = NULL; + } +} + +/* Vector to store passive-interface name. */ +vector Vripng_passive_interface; + +/* Utility function for looking up passive interface settings. */ +static int +ripng_passive_interface_lookup (const char *ifname) +{ + unsigned int i; + char *str; + + for (i = 0; i < vector_active (Vripng_passive_interface); i++) + if ((str = vector_slot (Vripng_passive_interface, i)) != NULL) + if (strcmp (str, ifname) == 0) + return i; + return -1; +} + +void +ripng_passive_interface_apply (struct interface *ifp) +{ + int ret; + struct ripng_interface *ri; + + ri = ifp->info; + + ret = ripng_passive_interface_lookup (ifp->name); + if (ret < 0) + ri->passive = 0; + else + ri->passive = 1; +} + +static void +ripng_passive_interface_apply_all (void) +{ + struct interface *ifp; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) + ripng_passive_interface_apply (ifp); +} + +/* Passive interface. */ +static int +ripng_passive_interface_set (struct vty *vty, const char *ifname) +{ + if (ripng_passive_interface_lookup (ifname) >= 0) + return CMD_WARNING; + + vector_set (Vripng_passive_interface, strdup (ifname)); + + ripng_passive_interface_apply_all (); + + return CMD_SUCCESS; +} + +static int +ripng_passive_interface_unset (struct vty *vty, const char *ifname) +{ + int i; + char *str; + + i = ripng_passive_interface_lookup (ifname); + if (i < 0) + return CMD_WARNING; + + str = vector_slot (Vripng_passive_interface, i); + free (str); + vector_unset (Vripng_passive_interface, i); + + ripng_passive_interface_apply_all (); + + return CMD_SUCCESS; +} + +/* Free all configured RIP passive-interface settings. */ +void +ripng_passive_interface_clean (void) +{ + unsigned int i; + char *str; + + for (i = 0; i < vector_active (Vripng_passive_interface); i++) + if ((str = vector_slot (Vripng_passive_interface, i)) != NULL) + { + free (str); + vector_slot (Vripng_passive_interface, i) = NULL; + } + ripng_passive_interface_apply_all (); +} + +/* Write RIPng enable network and interface to the vty. */ +int +ripng_network_write (struct vty *vty, int config_mode) +{ + unsigned int i; + const char *ifname; + struct route_node *node; + char buf[BUFSIZ]; + + /* Write enable network. */ + for (node = route_top (ripng_enable_network); node; node = route_next (node)) + if (node->info) + { + struct prefix *p = &node->p; + vty_out (vty, "%s%s/%d%s", + config_mode ? " network " : " ", + inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ), + p->prefixlen, + VTY_NEWLINE); + + } + + /* Write enable interface. */ + for (i = 0; i < vector_active (ripng_enable_if); i++) + if ((ifname = vector_slot (ripng_enable_if, i)) != NULL) + vty_out (vty, "%s%s%s", + config_mode ? " network " : " ", + ifname, + VTY_NEWLINE); + + /* Write passive interface. */ + if (config_mode) + for (i = 0; i < vector_active (Vripng_passive_interface); i++) + if ((ifname = vector_slot (Vripng_passive_interface, i)) != NULL) + vty_out (vty, " passive-interface %s%s", ifname, VTY_NEWLINE); + + return 0; +} + +/* RIPng enable on specified interface or matched network. */ +DEFUN (ripng_network, + ripng_network_cmd, + "network IF_OR_ADDR", + "RIPng enable on specified interface or network.\n" + "Interface or address") +{ + int ret; + struct prefix p; + + ret = str2prefix (argv[0], &p); + + /* Given string is IPv6 network or interface name. */ + if (ret) + ret = ripng_enable_network_add (&p); + else + ret = ripng_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; +} + +/* RIPng enable on specified interface or matched network. */ +DEFUN (no_ripng_network, + no_ripng_network_cmd, + "no network IF_OR_ADDR", + NO_STR + "RIPng enable on specified interface or network.\n" + "Interface or address") +{ + int ret; + struct prefix p; + + ret = str2prefix (argv[0], &p); + + /* Given string is interface name. */ + if (ret) + ret = ripng_enable_network_delete (&p); + else + ret = ripng_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; +} + +DEFUN (ipv6_ripng_split_horizon, + ipv6_ripng_split_horizon_cmd, + "ipv6 ripng split-horizon", + IPV6_STR + "Routing Information Protocol\n" + "Perform split horizon\n") +{ + struct interface *ifp; + struct ripng_interface *ri; + + ifp = vty->index; + ri = ifp->info; + + ri->split_horizon = RIPNG_SPLIT_HORIZON; + return CMD_SUCCESS; +} + +DEFUN (ipv6_ripng_split_horizon_poisoned_reverse, + ipv6_ripng_split_horizon_poisoned_reverse_cmd, + "ipv6 ripng split-horizon poisoned-reverse", + IPV6_STR + "Routing Information Protocol\n" + "Perform split horizon\n" + "With poisoned-reverse\n") +{ + struct interface *ifp; + struct ripng_interface *ri; + + ifp = vty->index; + ri = ifp->info; + + ri->split_horizon = RIPNG_SPLIT_HORIZON_POISONED_REVERSE; + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_ripng_split_horizon, + no_ipv6_ripng_split_horizon_cmd, + "no ipv6 ripng split-horizon", + NO_STR + IPV6_STR + "Routing Information Protocol\n" + "Perform split horizon\n") +{ + struct interface *ifp; + struct ripng_interface *ri; + + ifp = vty->index; + ri = ifp->info; + + ri->split_horizon = RIPNG_NO_SPLIT_HORIZON; + return CMD_SUCCESS; +} + +ALIAS (no_ipv6_ripng_split_horizon, + no_ipv6_ripng_split_horizon_poisoned_reverse_cmd, + "no ipv6 ripng split-horizon poisoned-reverse", + NO_STR + IPV6_STR + "Routing Information Protocol\n" + "Perform split horizon\n" + "With poisoned-reverse\n") + +DEFUN (ripng_passive_interface, + ripng_passive_interface_cmd, + "passive-interface IFNAME", + "Suppress routing updates on an interface\n" + "Interface name\n") +{ + return ripng_passive_interface_set (vty, argv[0]); +} + +DEFUN (no_ripng_passive_interface, + no_ripng_passive_interface_cmd, + "no passive-interface IFNAME", + NO_STR + "Suppress routing updates on an interface\n" + "Interface name\n") +{ + return ripng_passive_interface_unset (vty, argv[0]); +} + +static struct ripng_interface * +ri_new (void) +{ + struct ripng_interface *ri; + ri = XCALLOC (MTYPE_IF, sizeof (struct ripng_interface)); + + /* 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 = RIPNG_SPLIT_HORIZON; + ri->split_horizon = ri->split_horizon_default; + + return ri; +} + +static int +ripng_if_new_hook (struct interface *ifp) +{ + ifp->info = ri_new (); + return 0; +} + +/* Called when interface structure deleted. */ +static int +ripng_if_delete_hook (struct interface *ifp) +{ + XFREE (MTYPE_IF, ifp->info); + ifp->info = NULL; + return 0; +} + +/* Configuration write function for ripngd. */ +static int +interface_config_write (struct vty *vty) +{ + struct listnode *node; + struct interface *ifp; + struct ripng_interface *ri; + int write = 0; + + for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) + { + ri = ifp->info; + + /* Do not display the interface if there is no + * configuration about it. + **/ + if ((!ifp->desc) && + (ri->split_horizon == ri->split_horizon_default)) + continue; + + vty_out (vty, "interface %s%s", ifp->name, + VTY_NEWLINE); + if (ifp->desc) + vty_out (vty, " description %s%s", ifp->desc, + VTY_NEWLINE); + + /* Split horizon. */ + if (ri->split_horizon != ri->split_horizon_default) + { + switch (ri->split_horizon) { + case RIPNG_SPLIT_HORIZON: + vty_out (vty, " ipv6 ripng split-horizon%s", VTY_NEWLINE); + break; + case RIPNG_SPLIT_HORIZON_POISONED_REVERSE: + vty_out (vty, " ipv6 ripng split-horizon poisoned-reverse%s", + VTY_NEWLINE); + break; + case RIPNG_NO_SPLIT_HORIZON: + default: + vty_out (vty, " no ipv6 ripng split-horizon%s", VTY_NEWLINE); + break; + } + } + + vty_out (vty, "!%s", VTY_NEWLINE); + + write++; + } + return write; +} + +/* ripngd's interface node. */ +static struct cmd_node interface_node = +{ + INTERFACE_NODE, + "%s(config-if)# ", + 1 /* VTYSH */ +}; + +/* Initialization of interface. */ +void +ripng_if_init () +{ + /* Interface initialize. */ + if_add_hook (IF_NEW_HOOK, ripng_if_new_hook); + if_add_hook (IF_DELETE_HOOK, ripng_if_delete_hook); + + /* RIPng enable network init. */ + ripng_enable_network = route_table_init (); + + /* RIPng enable interface init. */ + ripng_enable_if = vector_init (1); + + /* RIPng passive interface. */ + Vripng_passive_interface = vector_init (1); + + /* Install interface node. */ + install_node (&interface_node, interface_config_write); + + /* Install commands. */ + 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 (RIPNG_NODE, &ripng_network_cmd); + install_element (RIPNG_NODE, &no_ripng_network_cmd); + install_element (RIPNG_NODE, &ripng_passive_interface_cmd); + install_element (RIPNG_NODE, &no_ripng_passive_interface_cmd); + + install_element (INTERFACE_NODE, &ipv6_ripng_split_horizon_cmd); + install_element (INTERFACE_NODE, &ipv6_ripng_split_horizon_poisoned_reverse_cmd); + install_element (INTERFACE_NODE, &no_ipv6_ripng_split_horizon_cmd); + install_element (INTERFACE_NODE, &no_ipv6_ripng_split_horizon_poisoned_reverse_cmd); +} diff --git a/ripngd/ripng_main.c b/ripngd/ripng_main.c new file mode 100644 index 0000000..1c184e2 --- /dev/null +++ b/ripngd/ripng_main.c @@ -0,0 +1,316 @@ +/* + * RIPngd main routine. + * Copyright (C) 1998, 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 "getopt.h" +#include "vector.h" +#include "vty.h" +#include "command.h" +#include "memory.h" +#include "thread.h" +#include "log.h" +#include "prefix.h" +#include "if.h" +#include "privs.h" +#include "sigevent.h" +#include "vrf.h" + +#include "ripngd/ripngd.h" + +/* Configuration filename and directory. */ +char config_default[] = SYSCONFDIR RIPNG_DEFAULT_CONFIG; +char *config_file = NULL; + +/* RIPngd 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'}, + { "dryrun", no_argument, NULL, 'C'}, + { "help", no_argument, NULL, 'h'}, + { "vty_addr", required_argument, NULL, 'A'}, + { "vty_port", required_argument, NULL, 'P'}, + { "retain", no_argument, NULL, 'r'}, + { "user", required_argument, NULL, 'u'}, + { "group", required_argument, NULL, 'g'}, + { "version", no_argument, NULL, 'v'}, + { 0 } +}; + +/* ripngd privileges */ +zebra_capabilities_t _caps_p [] = +{ + ZCAP_NET_RAW, + ZCAP_BIND +}; + +struct zebra_privs_t ripngd_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 +}; + + +/* RIPngd program name */ + +/* Route retain mode flag. */ +int retain_mode = 0; + +/* RIPng VTY bind address. */ +char *vty_addr = NULL; + +/* RIPng VTY connection port. */ +int vty_port = RIPNG_VTY_PORT; + +/* Master of threads. */ +struct thread_master *master; + +/* Process ID saved for use by init system */ +const char *pid_file = PATH_RIPNGD_PID; + +/* Help information display. */ +static void +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 RIPng.\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\ +-r, --retain When program terminates, retain added route by ripngd.\n\ +-u, --user User to run as\n\ +-g, --group Group to 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\ +\n\ +Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); + } + exit (status); +} + +/* SIGHUP handler. */ +static void +sighup (void) +{ + zlog_info ("SIGHUP received"); + ripng_clean (); + ripng_reset (); + + /* Reload config file. */ + vty_read_config (config_file, config_default); + /* Create VTY's socket */ + vty_serv_sock (vty_addr, vty_port, RIPNG_VTYSH_PATH); + + /* Try to return to normal operation. */ +} + +/* SIGINT handler. */ +static void +sigint (void) +{ + zlog_notice ("Terminating on signal"); + + if (! retain_mode) + ripng_clean (); + + exit (0); +} + +/* SIGUSR1 handler. */ +static void +sigusr1 (void) +{ + zlog_rotate (NULL); +} + +struct quagga_signal_t ripng_signals[] = +{ + { + .signal = SIGHUP, + .handler = &sighup, + }, + { + .signal = SIGUSR1, + .handler = &sigusr1, + }, + { + .signal = SIGINT, + .handler = &sigint, + }, + { + .signal = SIGTERM, + .handler = &sigint, + }, +}; + +/* RIPngd main routine. */ +int +main (int argc, char **argv) +{ + char *p; + int vty_port = RIPNG_VTY_PORT; + int daemon_mode = 0; + char *progname; + struct thread thread; + int dryrun = 0; + + /* Set umask before anything for security */ + umask (0027); + + /* get program name */ + progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]); + + zlog_default = openzlog(progname, ZLOG_RIPNG, + LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON); + + while (1) + { + int opt; + + opt = getopt_long (argc, argv, "df:i:z:hA:P:u:g:vC", longopts, 0); + + if (opt == EOF) + break; + + switch (opt) + { + case 0: + break; + case 'd': + daemon_mode = 1; + break; + case 'f': + config_file = optarg; + break; + case 'A': + vty_addr = optarg; + break; + case 'i': + pid_file = optarg; + break; + case 'z': + zclient_serv_path_set (optarg); + break; + case 'P': + /* Deal with atoi() returning 0 on failure, and ripngd not + listening on ripngd port... */ + if (strcmp(optarg, "0") == 0) + { + vty_port = 0; + break; + } + vty_port = atoi (optarg); + if (vty_port <= 0 || vty_port > 0xffff) + vty_port = RIPNG_VTY_PORT; + break; + case 'r': + retain_mode = 1; + break; + case 'u': + ripngd_privs.user = optarg; + break; + case 'g': + ripngd_privs.group = optarg; + break; + case 'v': + print_version (progname); + exit (0); + break; + case 'C': + dryrun = 1; + break; + case 'h': + usage (progname, 0); + break; + default: + usage (progname, 1); + break; + } + } + + master = thread_master_create (); + + /* Library inits. */ + zprivs_init (&ripngd_privs); + 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 (master); + ripng_peer_init (); + + /* Get configuration file. */ + vty_read_config (config_file, config_default); + + /* Start execution only if not in dry-run mode */ + if(dryrun) + return(0); + + /* Change to the daemon program. */ + if (daemon_mode && daemon (0, 0) < 0) + { + zlog_err("RIPNGd daemon failed: %s", strerror(errno)); + exit (1); + } + + /* Create VTY socket */ + vty_serv_sock (vty_addr, vty_port, RIPNG_VTYSH_PATH); + + /* Process id file create. */ + pid_output (pid_file); + + /* Print banner. */ + zlog_notice ("RIPNGd %s starting: vty@%d", QUAGGA_VERSION, vty_port); + + /* Fetch next active thread. */ + while (thread_fetch (master, &thread)) + thread_call (&thread); + + /* Not reached. */ + return 0; +} diff --git a/ripngd/ripng_nexthop.c b/ripngd/ripng_nexthop.c new file mode 100644 index 0000000..b966af0 --- /dev/null +++ b/ripngd/ripng_nexthop.c @@ -0,0 +1,214 @@ +/* RIPngd Zebra + * Copyright (C) 2002 6WIND + * + * 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. + */ + +/* This file is required in order to support properly the RIPng nexthop + * feature. + */ + +#include + +/* For struct udphdr. */ +#include + +#include "linklist.h" +#include "stream.h" +#include "log.h" +#include "memory.h" +#include "vty.h" +#include "if.h" +#include "prefix.h" + +#include "ripngd/ripngd.h" +#include "ripngd/ripng_debug.h" +#include "ripngd/ripng_nexthop.h" + +#define DEBUG 1 + +#define min(a, b) ((a) < (b) ? (a) : (b)) + +struct ripng_rte_data { + struct prefix_ipv6 *p; + struct ripng_info *rinfo; + struct ripng_aggregate *aggregate; +}; + +void _ripng_rte_del(struct ripng_rte_data *A); +int _ripng_rte_cmp(struct ripng_rte_data *A, struct ripng_rte_data *B); + +#define METRIC_OUT(a) \ + ((a)->rinfo ? (a)->rinfo->metric_out : (a)->aggregate->metric_out) +#define NEXTHOP_OUT_PTR(a) \ + ((a)->rinfo ? &((a)->rinfo->nexthop_out) : &((a)->aggregate->nexthop_out)) +#define TAG_OUT(a) \ + ((a)->rinfo ? (a)->rinfo->tag_out : (a)->aggregate->tag_out) + +struct list * +ripng_rte_new(void) { + struct list *rte; + + rte = list_new(); + rte->cmp = (int (*)(void *, void *)) _ripng_rte_cmp; + rte->del = (void (*)(void *)) _ripng_rte_del; + + return rte; +} + +void +ripng_rte_free(struct list *ripng_rte_list) { + list_delete(ripng_rte_list); +} + +/* Delete RTE */ +void +_ripng_rte_del(struct ripng_rte_data *A) { + XFREE(MTYPE_RIPNG_RTE_DATA, A); +} + +/* Compare RTE: + * return + if A > B + * 0 if A = B + * - if A < B + */ +int +_ripng_rte_cmp(struct ripng_rte_data *A, struct ripng_rte_data *B) { + return addr6_cmp(NEXTHOP_OUT_PTR(A), NEXTHOP_OUT_PTR(B)); +} + +/* Add routing table entry */ +void +ripng_rte_add(struct list *ripng_rte_list, struct prefix_ipv6 *p, + struct ripng_info *rinfo, struct ripng_aggregate *aggregate) { + + struct ripng_rte_data *data; + + /* At least one should not be null */ + assert(!rinfo || !aggregate); + + data = XMALLOC(MTYPE_RIPNG_RTE_DATA, sizeof(*data)); + data->p = p; + data->rinfo = rinfo; + data->aggregate = aggregate; + + listnode_add_sort(ripng_rte_list, data); +} + +/* Send the RTE with the nexthop support + */ +void +ripng_rte_send(struct list *ripng_rte_list, struct interface *ifp, + struct sockaddr_in6 *to) { + + struct ripng_rte_data *data; + struct listnode *node, *nnode; + + struct in6_addr last_nexthop; + struct in6_addr myself_nexthop; + + struct stream *s; + int num; + int mtu; + int rtemax; + int ret; + + /* Most of the time, there is no nexthop */ + memset(&last_nexthop, 0, sizeof(last_nexthop)); + + /* Use myself_nexthop if the nexthop is not a link-local address, because + * we remain a right path without beeing the optimal one. + */ + memset(&myself_nexthop, 0, sizeof(myself_nexthop)); + + /* Output stream get from ripng structre. XXX this should be + interface structure. */ + s = ripng->obuf; + + /* Reset stream and RTE counter. */ + stream_reset (s); + num = 0; + + mtu = ifp->mtu6; + if (mtu < 0) + mtu = IFMINMTU; + + rtemax = (min (mtu, RIPNG_MAX_PACKET_SIZE) - + IPV6_HDRLEN - + sizeof (struct udphdr) - + sizeof (struct ripng_packet) + + sizeof (struct rte)) / sizeof (struct rte); + + for (ALL_LIST_ELEMENTS (ripng_rte_list, node, nnode, data)) { + /* (2.1) Next hop support */ + if (!IPV6_ADDR_SAME(&last_nexthop, NEXTHOP_OUT_PTR(data))) { + + /* A nexthop entry should be at least followed by 1 RTE */ + if (num == (rtemax-1)) { + ret = ripng_send_packet ((caddr_t) STREAM_DATA (s), stream_get_endp (s), + to, 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); + } + + /* Add the nexthop (2.1) */ + + /* If the received next hop address is not a link-local address, + * it should be treated as 0:0:0:0:0:0:0:0. + */ + if (!IN6_IS_ADDR_LINKLOCAL(NEXTHOP_OUT_PTR(data))) + last_nexthop = myself_nexthop; + else + last_nexthop = *NEXTHOP_OUT_PTR(data); + + num = ripng_write_rte(num, s, NULL, &last_nexthop, 0, RIPNG_METRIC_NEXTHOP); + } else { + /* Rewrite the nexthop for each new packet */ + if ((num == 0) && !IPV6_ADDR_SAME(&last_nexthop, &myself_nexthop)) + num = ripng_write_rte(num, s, NULL, &last_nexthop, 0, RIPNG_METRIC_NEXTHOP); + } + num = ripng_write_rte(num, s, data->p, NULL, + TAG_OUT(data), METRIC_OUT(data)); + + if (num == rtemax) { + ret = ripng_send_packet ((caddr_t) STREAM_DATA (s), stream_get_endp (s), + to, 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); + } + } + + /* If unwritten RTE exist, flush it. */ + if (num != 0) { + ret = ripng_send_packet ((caddr_t) STREAM_DATA (s), stream_get_endp (s), + to, ifp); + + if (ret >= 0 && IS_RIPNG_DEBUG_SEND) + ripng_packet_dump ((struct ripng_packet *)STREAM_DATA (s), + stream_get_endp (s), "SEND"); + stream_reset (s); + } +} diff --git a/ripngd/ripng_nexthop.h b/ripngd/ripng_nexthop.h new file mode 100644 index 0000000..19bd32b --- /dev/null +++ b/ripngd/ripng_nexthop.h @@ -0,0 +1,64 @@ +/* RIPng nexthop support + * Copyright (C) 6WIND Vincent Jardin + * + * 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_RIPNG_RIPNG_NEXTHOP_H +#define _ZEBRA_RIPNG_RIPNG_NEXTHOP_H + +#include +#include "linklist.h" +#include "ripngd/ripng_route.h" +#include "ripngd/ripngd.h" + +extern struct list * ripng_rte_new(void); +extern void ripng_rte_free(struct list *ripng_rte_list); +extern void ripng_rte_add(struct list *ripng_rte_list, struct prefix_ipv6 *p, + struct ripng_info *rinfo, + struct ripng_aggregate *aggregate); +extern void ripng_rte_send(struct list *ripng_rte_list, struct interface *ifp, + struct sockaddr_in6 *to); + +/*** + * 1 if A > B + * 0 if A = B + * -1 if A < B + **/ +static inline int +addr6_cmp(struct in6_addr *A, struct in6_addr *B) +{ +#define a(i) A->s6_addr32[i] +#define b(i) B->s6_addr32[i] + + if (a(3) > b(3)) + return 1; + else if ((a(3) == b(3)) && (a(2) > b(2))) + return 1; + else if ((a(3) == b(3)) && (a(2) == b(2)) && (a(1) > b(1))) + return 1; + else if ((a(3) == b(3)) && (a(2) == b(2)) && (a(1) == b(1)) && (a(0) > b(0))) + return 1; + + if ((a(3) == b(3)) && (a(2) == b(2)) && (a(1) == b(1)) && (a(0) == b(0))) + return 0; + + return -1; +} + +#endif /* _ZEBRA_RIPNG_RIPNG_NEXTHOP_H */ diff --git a/ripngd/ripng_offset.c b/ripngd/ripng_offset.c new file mode 100644 index 0000000..5bc2568 --- /dev/null +++ b/ripngd/ripng_offset.c @@ -0,0 +1,421 @@ +/* RIPng offset-list + * 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. + */ + + /* RIPng support by Vincent Jardin + * Copyright (C) 2002 6WIND + */ + +#include + +#include "if.h" +#include "prefix.h" +#include "filter.h" +#include "command.h" +#include "linklist.h" +#include "memory.h" + +#include "ripngd/ripngd.h" + +#define RIPNG_OFFSET_LIST_IN 0 +#define RIPNG_OFFSET_LIST_OUT 1 +#define RIPNG_OFFSET_LIST_MAX 2 + +struct ripng_offset_list +{ + char *ifname; + + struct + { + char *alist_name; + /* struct access_list *alist; */ + int metric; + } direct[RIPNG_OFFSET_LIST_MAX]; +}; + +static struct list *ripng_offset_list_master; + +static int +strcmp_safe (const char *s1, const char *s2) +{ + if (s1 == NULL && s2 == NULL) + return 0; + if (s1 == NULL) + return -1; + if (s2 == NULL) + return 1; + return strcmp (s1, s2); +} + +static struct ripng_offset_list * +ripng_offset_list_new () +{ + struct ripng_offset_list *new; + + new = XCALLOC (MTYPE_RIPNG_OFFSET_LIST, sizeof (struct ripng_offset_list)); + return new; +} + +static void +ripng_offset_list_free (struct ripng_offset_list *offset) +{ + XFREE (MTYPE_RIPNG_OFFSET_LIST, offset); +} + +static struct ripng_offset_list * +ripng_offset_list_lookup (const char *ifname) +{ + struct ripng_offset_list *offset; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS (ripng_offset_list_master, node, nnode, offset)) + { + if (strcmp_safe (offset->ifname, ifname) == 0) + return offset; + } + return NULL; +} + +static struct ripng_offset_list * +ripng_offset_list_get (const char *ifname) +{ + struct ripng_offset_list *offset; + + offset = ripng_offset_list_lookup (ifname); + if (offset) + return offset; + + offset = ripng_offset_list_new (); + if (ifname) + offset->ifname = strdup (ifname); + listnode_add_sort (ripng_offset_list_master, offset); + + return offset; +} + +static int +ripng_offset_list_set (struct vty *vty, const char *alist, + const char *direct_str, const char *metric_str, + const char *ifname) +{ + int direct; + int metric; + struct ripng_offset_list *offset; + + /* Check direction. */ + if (strncmp (direct_str, "i", 1) == 0) + direct = RIPNG_OFFSET_LIST_IN; + else if (strncmp (direct_str, "o", 1) == 0) + direct = RIPNG_OFFSET_LIST_OUT; + else + { + vty_out (vty, "Invalid direction: %s%s", direct_str, VTY_NEWLINE); + return CMD_WARNING; + } + + /* Check metric. */ + metric = atoi (metric_str); + if (metric < 0 || metric > 16) + { + vty_out (vty, "Invalid metric: %s%s", metric_str, VTY_NEWLINE); + return CMD_WARNING; + } + + /* Get offset-list structure with interface name. */ + offset = ripng_offset_list_get (ifname); + + if (offset->direct[direct].alist_name) + free (offset->direct[direct].alist_name); + offset->direct[direct].alist_name = strdup (alist); + offset->direct[direct].metric = metric; + + return CMD_SUCCESS; +} + +static int +ripng_offset_list_unset (struct vty *vty, const char *alist, + const char *direct_str, const char *metric_str, + const char *ifname) +{ + int direct; + int metric; + struct ripng_offset_list *offset; + + /* Check direction. */ + if (strncmp (direct_str, "i", 1) == 0) + direct = RIPNG_OFFSET_LIST_IN; + else if (strncmp (direct_str, "o", 1) == 0) + direct = RIPNG_OFFSET_LIST_OUT; + else + { + vty_out (vty, "Invalid direction: %s%s", direct_str, VTY_NEWLINE); + return CMD_WARNING; + } + + /* Check metric. */ + metric = atoi (metric_str); + if (metric < 0 || metric > 16) + { + vty_out (vty, "Invalid metric: %s%s", metric_str, VTY_NEWLINE); + return CMD_WARNING; + } + + /* Get offset-list structure with interface name. */ + offset = ripng_offset_list_lookup (ifname); + + if (offset) + { + if (offset->direct[direct].alist_name) + free (offset->direct[direct].alist_name); + offset->direct[direct].alist_name = NULL; + + if (offset->direct[RIPNG_OFFSET_LIST_IN].alist_name == NULL && + offset->direct[RIPNG_OFFSET_LIST_OUT].alist_name == NULL) + { + listnode_delete (ripng_offset_list_master, offset); + if (offset->ifname) + free (offset->ifname); + ripng_offset_list_free (offset); + } + } + else + { + vty_out (vty, "Can't find offset-list%s", VTY_NEWLINE); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +#define OFFSET_LIST_IN_NAME(O) ((O)->direct[RIPNG_OFFSET_LIST_IN].alist_name) +#define OFFSET_LIST_IN_METRIC(O) ((O)->direct[RIPNG_OFFSET_LIST_IN].metric) + +#define OFFSET_LIST_OUT_NAME(O) ((O)->direct[RIPNG_OFFSET_LIST_OUT].alist_name) +#define OFFSET_LIST_OUT_METRIC(O) ((O)->direct[RIPNG_OFFSET_LIST_OUT].metric) + +/* If metric is modifed return 1. */ +int +ripng_offset_list_apply_in (struct prefix_ipv6 *p, struct interface *ifp, + u_char *metric) +{ + struct ripng_offset_list *offset; + struct access_list *alist; + + /* Look up offset-list with interface name. */ + offset = ripng_offset_list_lookup (ifp->name); + if (offset && OFFSET_LIST_IN_NAME (offset)) + { + alist = access_list_lookup (AFI_IP6, OFFSET_LIST_IN_NAME (offset)); + + if (alist + && access_list_apply (alist, (struct prefix *)p) == FILTER_PERMIT) + { + *metric += OFFSET_LIST_IN_METRIC (offset); + return 1; + } + return 0; + } + /* Look up offset-list without interface name. */ + offset = ripng_offset_list_lookup (NULL); + if (offset && OFFSET_LIST_IN_NAME (offset)) + { + alist = access_list_lookup (AFI_IP6, OFFSET_LIST_IN_NAME (offset)); + + if (alist + && access_list_apply (alist, (struct prefix *)p) == FILTER_PERMIT) + { + *metric += OFFSET_LIST_IN_METRIC (offset); + return 1; + } + return 0; + } + return 0; +} + +/* If metric is modifed return 1. */ +int +ripng_offset_list_apply_out (struct prefix_ipv6 *p, struct interface *ifp, + u_char *metric) +{ + struct ripng_offset_list *offset; + struct access_list *alist; + + /* Look up offset-list with interface name. */ + offset = ripng_offset_list_lookup (ifp->name); + if (offset && OFFSET_LIST_OUT_NAME (offset)) + { + alist = access_list_lookup (AFI_IP6, OFFSET_LIST_OUT_NAME (offset)); + + if (alist + && access_list_apply (alist, (struct prefix *)p) == FILTER_PERMIT) + { + *metric += OFFSET_LIST_OUT_METRIC (offset); + return 1; + } + return 0; + } + + /* Look up offset-list without interface name. */ + offset = ripng_offset_list_lookup (NULL); + if (offset && OFFSET_LIST_OUT_NAME (offset)) + { + alist = access_list_lookup (AFI_IP6, OFFSET_LIST_OUT_NAME (offset)); + + if (alist + && access_list_apply (alist, (struct prefix *)p) == FILTER_PERMIT) + { + *metric += OFFSET_LIST_OUT_METRIC (offset); + return 1; + } + return 0; + } + return 0; +} + +DEFUN (ripng_offset_list, + ripng_offset_list_cmd, + "offset-list WORD (in|out) <0-16>", + "Modify RIPng metric\n" + "Access-list name\n" + "For incoming updates\n" + "For outgoing updates\n" + "Metric value\n") +{ + return ripng_offset_list_set (vty, argv[0], argv[1], argv[2], NULL); +} + +DEFUN (ripng_offset_list_ifname, + ripng_offset_list_ifname_cmd, + "offset-list WORD (in|out) <0-16> IFNAME", + "Modify RIPng metric\n" + "Access-list name\n" + "For incoming updates\n" + "For outgoing updates\n" + "Metric value\n" + "Interface to match\n") +{ + return ripng_offset_list_set (vty, argv[0], argv[1], argv[2], argv[3]); +} + +DEFUN (no_ripng_offset_list, + no_ripng_offset_list_cmd, + "no offset-list WORD (in|out) <0-16>", + NO_STR + "Modify RIPng metric\n" + "Access-list name\n" + "For incoming updates\n" + "For outgoing updates\n" + "Metric value\n") +{ + return ripng_offset_list_unset (vty, argv[0], argv[1], argv[2], NULL); +} + +DEFUN (no_ripng_offset_list_ifname, + no_ripng_offset_list_ifname_cmd, + "no offset-list WORD (in|out) <0-16> IFNAME", + NO_STR + "Modify RIPng metric\n" + "Access-list name\n" + "For incoming updates\n" + "For outgoing updates\n" + "Metric value\n" + "Interface to match\n") +{ + return ripng_offset_list_unset (vty, argv[0], argv[1], argv[2], argv[3]); +} + +static int +offset_list_cmp (struct ripng_offset_list *o1, struct ripng_offset_list *o2) +{ + return strcmp_safe (o1->ifname, o2->ifname); +} + +static void +offset_list_del (struct ripng_offset_list *offset) +{ + if (OFFSET_LIST_IN_NAME (offset)) + free (OFFSET_LIST_IN_NAME (offset)); + if (OFFSET_LIST_OUT_NAME (offset)) + free (OFFSET_LIST_OUT_NAME (offset)); + if (offset->ifname) + free (offset->ifname); + ripng_offset_list_free (offset); +} + +void +ripng_offset_init (void) +{ + ripng_offset_list_master = list_new (); + ripng_offset_list_master->cmp = (int (*)(void *, void *)) offset_list_cmp; + ripng_offset_list_master->del = (void (*)(void *)) offset_list_del; + + install_element (RIPNG_NODE, &ripng_offset_list_cmd); + install_element (RIPNG_NODE, &ripng_offset_list_ifname_cmd); + install_element (RIPNG_NODE, &no_ripng_offset_list_cmd); + install_element (RIPNG_NODE, &no_ripng_offset_list_ifname_cmd); +} + +void +ripng_offset_clean (void) +{ + list_delete (ripng_offset_list_master); + + ripng_offset_list_master = list_new (); + ripng_offset_list_master->cmp = (int (*)(void *, void *)) offset_list_cmp; + ripng_offset_list_master->del = (void (*)(void *)) offset_list_del; +} + +int +config_write_ripng_offset_list (struct vty *vty) +{ + struct listnode *node, *nnode; + struct ripng_offset_list *offset; + + for (ALL_LIST_ELEMENTS (ripng_offset_list_master, node, nnode, offset)) + { + if (! offset->ifname) + { + if (offset->direct[RIPNG_OFFSET_LIST_IN].alist_name) + vty_out (vty, " offset-list %s in %d%s", + offset->direct[RIPNG_OFFSET_LIST_IN].alist_name, + offset->direct[RIPNG_OFFSET_LIST_IN].metric, + VTY_NEWLINE); + if (offset->direct[RIPNG_OFFSET_LIST_OUT].alist_name) + vty_out (vty, " offset-list %s out %d%s", + offset->direct[RIPNG_OFFSET_LIST_OUT].alist_name, + offset->direct[RIPNG_OFFSET_LIST_OUT].metric, + VTY_NEWLINE); + } + else + { + if (offset->direct[RIPNG_OFFSET_LIST_IN].alist_name) + vty_out (vty, " offset-list %s in %d %s%s", + offset->direct[RIPNG_OFFSET_LIST_IN].alist_name, + offset->direct[RIPNG_OFFSET_LIST_IN].metric, + offset->ifname, VTY_NEWLINE); + if (offset->direct[RIPNG_OFFSET_LIST_OUT].alist_name) + vty_out (vty, " offset-list %s out %d %s%s", + offset->direct[RIPNG_OFFSET_LIST_OUT].alist_name, + offset->direct[RIPNG_OFFSET_LIST_OUT].metric, + offset->ifname, VTY_NEWLINE); + } + } + + return 0; +} diff --git a/ripngd/ripng_peer.c b/ripngd/ripng_peer.c new file mode 100644 index 0000000..b12e146 --- /dev/null +++ b/ripngd/ripng_peer.c @@ -0,0 +1,216 @@ +/* RIPng peer support + * 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. + */ + +/* RIPng support added by Vincent Jardin + * Copyright (C) 2002 6WIND + */ + +#include + +#include "if.h" +#include "prefix.h" +#include "command.h" +#include "linklist.h" +#include "thread.h" +#include "memory.h" + +#include "ripngd/ripngd.h" +#include "ripngd/ripng_nexthop.h" + + +/* Linked list of RIPng peer. */ +struct list *peer_list; + +static struct ripng_peer * +ripng_peer_new (void) +{ + return XCALLOC (MTYPE_RIPNG_PEER, sizeof (struct ripng_peer)); +} + +static void +ripng_peer_free (struct ripng_peer *peer) +{ + XFREE (MTYPE_RIPNG_PEER, peer); +} + +struct ripng_peer * +ripng_peer_lookup (struct in6_addr *addr) +{ + struct ripng_peer *peer; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS (peer_list, node, nnode, peer)) + { + if (IPV6_ADDR_SAME (&peer->addr, addr)) + return peer; + } + return NULL; +} + +struct ripng_peer * +ripng_peer_lookup_next (struct in6_addr *addr) +{ + struct ripng_peer *peer; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS (peer_list, node, nnode, peer)) + { + if (addr6_cmp(&peer->addr, addr) > 0) + return peer; + } + return NULL; +} + +/* RIPng peer is timeout. + * Garbage collector. + **/ +static int +ripng_peer_timeout (struct thread *t) +{ + struct ripng_peer *peer; + + peer = THREAD_ARG (t); + listnode_delete (peer_list, peer); + ripng_peer_free (peer); + + return 0; +} + +/* Get RIPng peer. At the same time update timeout thread. */ +static struct ripng_peer * +ripng_peer_get (struct in6_addr *addr) +{ + struct ripng_peer *peer; + + peer = ripng_peer_lookup (addr); + + if (peer) + { + if (peer->t_timeout) + thread_cancel (peer->t_timeout); + } + else + { + peer = ripng_peer_new (); + peer->addr = *addr; /* XXX */ + listnode_add_sort (peer_list, peer); + } + + /* Update timeout thread. */ + peer->t_timeout = thread_add_timer (master, ripng_peer_timeout, peer, + RIPNG_PEER_TIMER_DEFAULT); + + /* Last update time set. */ + time (&peer->uptime); + + return peer; +} + +void +ripng_peer_update (struct sockaddr_in6 *from, u_char version) +{ + struct ripng_peer *peer; + peer = ripng_peer_get (&from->sin6_addr); + peer->version = version; +} + +void +ripng_peer_bad_route (struct sockaddr_in6 *from) +{ + struct ripng_peer *peer; + peer = ripng_peer_get (&from->sin6_addr); + peer->recv_badroutes++; +} + +void +ripng_peer_bad_packet (struct sockaddr_in6 *from) +{ + struct ripng_peer *peer; + peer = ripng_peer_get (&from->sin6_addr); + peer->recv_badpackets++; +} + +/* Display peer uptime. */ +static char * +ripng_peer_uptime (struct ripng_peer *peer, char *buf, size_t len) +{ + time_t uptime; + struct tm *tm; + + /* If there is no connection has been done before print `never'. */ + if (peer->uptime == 0) + { + snprintf (buf, len, "never "); + return buf; + } + + /* Get current time. */ + uptime = time (NULL); + uptime -= peer->uptime; + tm = gmtime (&uptime); + + /* Making formatted timer strings. */ +#define ONE_DAY_SECOND 60*60*24 +#define ONE_WEEK_SECOND 60*60*24*7 + + if (uptime < ONE_DAY_SECOND) + snprintf (buf, len, "%02d:%02d:%02d", + tm->tm_hour, tm->tm_min, tm->tm_sec); + else if (uptime < ONE_WEEK_SECOND) + snprintf (buf, len, "%dd%02dh%02dm", + tm->tm_yday, tm->tm_hour, tm->tm_min); + else + snprintf (buf, len, "%02dw%dd%02dh", + tm->tm_yday/7, tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour); + return buf; +} + +void +ripng_peer_display (struct vty *vty) +{ + struct ripng_peer *peer; + struct listnode *node, *nnode; +#define RIPNG_UPTIME_LEN 25 + char timebuf[RIPNG_UPTIME_LEN]; + + for (ALL_LIST_ELEMENTS (peer_list, node, nnode, peer)) + { + vty_out (vty, " %s %s%14s %10d %10d %10d %s%s", inet6_ntoa (peer->addr), + VTY_NEWLINE, " ", + peer->recv_badpackets, peer->recv_badroutes, + ZEBRA_RIPNG_DISTANCE_DEFAULT, + ripng_peer_uptime (peer, timebuf, RIPNG_UPTIME_LEN), + VTY_NEWLINE); + } +} + +static int +ripng_peer_list_cmp (struct ripng_peer *p1, struct ripng_peer *p2) +{ + return addr6_cmp(&p1->addr, &p2->addr) > 0; +} + +void +ripng_peer_init () +{ + peer_list = list_new (); + peer_list->cmp = (int (*)(void *, void *)) ripng_peer_list_cmp; +} diff --git a/ripngd/ripng_route.c b/ripngd/ripng_route.c new file mode 100644 index 0000000..f26302e --- /dev/null +++ b/ripngd/ripng_route.c @@ -0,0 +1,181 @@ +/* + * RIPng routes function. + * Copyright (C) 1998 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 "table.h" +#include "memory.h" +#include "if.h" +#include "vty.h" + +#include "ripngd/ripngd.h" +#include "ripngd/ripng_route.h" + +static struct ripng_aggregate * +ripng_aggregate_new () +{ + struct ripng_aggregate *new; + + new = XCALLOC (MTYPE_RIPNG_AGGREGATE, sizeof (struct ripng_aggregate)); + return new; +} + +void +ripng_aggregate_free (struct ripng_aggregate *aggregate) +{ + XFREE (MTYPE_RIPNG_AGGREGATE, aggregate); +} + +/* Aggregate count increment check. */ +void +ripng_aggregate_increment (struct route_node *child, struct ripng_info *rinfo) +{ + struct route_node *np; + struct ripng_aggregate *aggregate; + + for (np = child; np; np = np->parent) + if ((aggregate = np->aggregate) != NULL) + { + aggregate->count++; + rinfo->suppress++; + } +} + +/* Aggregate count decrement check. */ +void +ripng_aggregate_decrement (struct route_node *child, struct ripng_info *rinfo) +{ + struct route_node *np; + struct ripng_aggregate *aggregate; + + for (np = child; np; np = np->parent) + if ((aggregate = np->aggregate) != NULL) + { + aggregate->count--; + rinfo->suppress--; + } +} + +/* 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) +{ + struct route_node *top; + struct route_node *rp; + 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); + + /* Allocate new aggregate. */ + aggregate = ripng_aggregate_new (); + aggregate->metric = 1; + + top->aggregate = aggregate; + + /* Suppress routes match to the aggregate. */ + for (rp = route_lock_node (top); rp; rp = route_next_until (rp, top)) + { + /* Suppress normal route. */ + 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) + { + aggregate->count++; + sub->suppress++; + } + } + + return 0; +} + +/* Delete RIPng static route. */ +int +ripng_aggregate_delete (struct prefix *p) +{ + struct route_node *top; + struct route_node *rp; + 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); + + /* Allocate new aggregate. */ + aggregate = top->aggregate; + + /* Suppress routes match to the aggregate. */ + for (rp = route_lock_node (top); rp; rp = route_next_until (rp, top)) + { + /* Suppress normal route. */ + if ((list = rp->info) != NULL) + for (ALL_LIST_ELEMENTS_RO (list, node, rinfo)) + { + aggregate->count--; + rinfo->suppress--; + } + + if (rp != top && (sub = rp->aggregate) != NULL) + { + aggregate->count--; + sub->suppress--; + } + } + + top->aggregate = NULL; + ripng_aggregate_free (aggregate); + + route_unlock_node (top); + route_unlock_node (top); + + return 0; +} diff --git a/ripngd/ripng_route.h b/ripngd/ripng_route.h new file mode 100644 index 0000000..9ff90aa --- /dev/null +++ b/ripngd/ripng_route.h @@ -0,0 +1,57 @@ +/* + * RIPng daemon + * Copyright (C) 1998 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_RIPNG_ROUTE_H +#define _ZEBRA_RIPNG_ROUTE_H + +struct ripng_aggregate +{ + /* Aggregate route count. */ + unsigned int count; + + /* Suppressed route count. */ + unsigned int suppress; + + /* Metric of this route. */ + u_char metric; + + /* Tag field of RIPng packet.*/ + 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_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 new file mode 100644 index 0000000..c596aec --- /dev/null +++ b/ripngd/ripng_routemap.c @@ -0,0 +1,707 @@ +/* RIPng routemap. + * 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 "memory.h" +#include "prefix.h" +#include "routemap.h" +#include "command.h" +#include "sockunion.h" + +#include "ripngd/ripngd.h" + +struct rip_metric_modifier +{ + enum + { + metric_increment, + metric_decrement, + metric_absolute + } type; + + u_char metric; +}; + + +static int +ripng_route_match_add (struct vty *vty, struct route_map_index *index, + const char *command, const char *arg) +{ + int ret; + + ret = route_map_add_match (index, command, arg); + if (ret) + { + switch (ret) + { + case RMAP_RULE_MISSING: + vty_out (vty, "RIPng Can't find rule.%s", VTY_NEWLINE); + return CMD_WARNING; + case RMAP_COMPILE_ERROR: + vty_out (vty, "RIPng Argument is malformed.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + return CMD_SUCCESS; +} + +static int +ripng_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, "RIPng Can't find rule.%s", VTY_NEWLINE); + return CMD_WARNING; + case RMAP_COMPILE_ERROR: + vty_out (vty, "RIPng Argument is malformed.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + return CMD_SUCCESS; +} + +static int +ripng_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, "RIPng Can't find rule.%s", VTY_NEWLINE); + return CMD_WARNING; + case RMAP_COMPILE_ERROR: + vty_out (vty, "RIPng Argument is malformed.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + return CMD_SUCCESS; +} + +static int +ripng_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, "RIPng Can't find rule.%s", VTY_NEWLINE); + return CMD_WARNING; + case RMAP_COMPILE_ERROR: + 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 +route_match_metric (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + u_int32_t *metric; + struct ripng_info *rinfo; + + if (type == RMAP_RIPNG) + { + metric = rule; + rinfo = object; + + if (rinfo->metric == *metric) + return RMAP_MATCH; + else + return RMAP_NOMATCH; + } + return RMAP_NOMATCH; +} + +/* Route map `match metric' match statement. `arg' is METRIC value */ +static void * +route_match_metric_compile (const char *arg) +{ + u_int32_t *metric; + + metric = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_int32_t)); + *metric = atoi (arg); + + if(*metric > 0) + return metric; + + XFREE (MTYPE_ROUTE_MAP_COMPILED, metric); + return NULL; +} + +/* Free route map's compiled `match metric' value. */ +static void +route_match_metric_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for metric matching. */ +static struct route_map_rule_cmd route_match_metric_cmd = +{ + "metric", + route_match_metric, + 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 +route_match_interface (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct ripng_info *rinfo; + struct interface *ifp; + char *ifname; + + if (type == RMAP_RIPNG) + { + ifname = rule; + ifp = if_lookup_by_name(ifname); + + if (!ifp) + return RMAP_NOMATCH; + + rinfo = object; + + if (rinfo->ifindex == ifp->ifindex) + return RMAP_MATCH; + else + return RMAP_NOMATCH; + } + return RMAP_NOMATCH; +} + +/* Route map `match interface' match statement. `arg' is IFNAME value */ +static void * +route_match_interface_compile (const char *arg) +{ + return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); +} + +static void +route_match_interface_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +static struct route_map_rule_cmd route_match_interface_cmd = +{ + "interface", + route_match_interface, + route_match_interface_compile, + route_match_interface_free +}; + +/* `match tag TAG' */ +/* 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 ripng_info *rinfo; + + if (type == RMAP_RIPNG) + { + tag = rule; + rinfo = object; + + /* The information stored by rinfo is host ordered. */ + if (rinfo->tag == *tag) + return RMAP_MATCH; + else + return RMAP_NOMATCH; + } + return RMAP_NOMATCH; +} + +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 +route_set_metric (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + if (type == RMAP_RIPNG) + { + struct rip_metric_modifier *mod; + struct ripng_info *rinfo; + + mod = rule; + rinfo = object; + + if (mod->type == metric_increment) + rinfo->metric_out += mod->metric; + else if (mod->type == metric_decrement) + rinfo->metric_out-= mod->metric; + else if (mod->type == metric_absolute) + rinfo->metric_out = mod->metric; + + if (rinfo->metric_out < 1) + rinfo->metric_out = 1; + if (rinfo->metric_out > RIPNG_METRIC_INFINITY) + rinfo->metric_out = RIPNG_METRIC_INFINITY; + + rinfo->metric_set = 1; + } + return RMAP_OKAY; +} + +/* set metric compilation. */ +static void * +route_set_metric_compile (const char *arg) +{ + int len; + const char *pnt; + int type; + long metric; + char *endptr = NULL; + struct rip_metric_modifier *mod; + + len = strlen (arg); + pnt = arg; + + if (len == 0) + return NULL; + + /* Examine first character. */ + if (arg[0] == '+') + { + type = metric_increment; + pnt++; + } + else if (arg[0] == '-') + { + type = metric_decrement; + pnt++; + } + else + type = metric_absolute; + + /* Check beginning with digit string. */ + if (*pnt < '0' || *pnt > '9') + return NULL; + + /* Convert string to integer. */ + metric = strtol (pnt, &endptr, 10); + + if (metric == LONG_MAX || *endptr != '\0') + return NULL; + /* Commented out by Hasso Tepper, to avoid problems in vtysh. */ + /* if (metric < 0 || metric > RIPNG_METRIC_INFINITY) */ + if (metric < 0) + return NULL; + + mod = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, + sizeof (struct rip_metric_modifier)); + mod->type = type; + mod->metric = metric; + + return mod; +} + +/* Free route map's compiled `set metric' value. */ +static void +route_set_metric_free (void *rule) +{ + 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, +}; + +/* `set ipv6 next-hop local IP_ADDRESS' */ + +/* Set nexthop to object. ojbect must be pointer to struct attr. */ +static route_map_result_t +route_set_ipv6_nexthop_local (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct in6_addr *address; + struct ripng_info *rinfo; + + if(type == RMAP_RIPNG) + { + /* Fetch routemap's rule information. */ + address = rule; + rinfo = object; + + /* Set next hop value. */ + rinfo->nexthop_out = *address; + } + + return RMAP_OKAY; +} + +/* Route map `ipv6 nexthop local' compile function. Given string is converted + to struct in6_addr structure. */ +static void * +route_set_ipv6_nexthop_local_compile (const char *arg) +{ + int ret; + struct in6_addr *address; + + address = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct in6_addr)); + + ret = inet_pton (AF_INET6, arg, address); + + if (ret == 0) + { + XFREE (MTYPE_ROUTE_MAP_COMPILED, address); + return NULL; + } + + return address; +} + +/* Free route map's compiled `ipv6 nexthop local' value. */ +static void +route_set_ipv6_nexthop_local_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for ipv6 nexthop local set. */ +static struct route_map_rule_cmd route_set_ipv6_nexthop_local_cmd = +{ + "ipv6 next-hop local", + route_set_ipv6_nexthop_local, + route_set_ipv6_nexthop_local_compile, + route_set_ipv6_nexthop_local_free +}; + +/* `set tag TAG' */ + +/* Set tag to object. ojbect must be pointer to struct attr. */ +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 ripng_info *rinfo; + + if(type == RMAP_RIPNG) + { + /* Fetch routemap's rule information. */ + tag = rule; + rinfo = object; + + /* Set next hop value. */ + rinfo->tag_out = *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 +}; + +#define MATCH_STR "Match values from routing table\n" +#define SET_STR "Set values in destination routing protocol\n" + +DEFUN (match_metric, + match_metric_cmd, + "match metric <0-4294967295>", + MATCH_STR + "Match metric of route\n" + "Metric value\n") +{ + return ripng_route_match_add (vty, vty->index, "metric", argv[0]); +} + +DEFUN (no_match_metric, + no_match_metric_cmd, + "no match metric", + NO_STR + MATCH_STR + "Match metric of route\n") +{ + if (argc == 0) + return ripng_route_match_delete (vty, vty->index, "metric", NULL); + + return ripng_route_match_delete (vty, vty->index, "metric", argv[0]); +} + +ALIAS (no_match_metric, + no_match_metric_val_cmd, + "no match metric <0-4294967295>", + NO_STR + MATCH_STR + "Match metric of route\n" + "Metric value\n") + +DEFUN (match_interface, + match_interface_cmd, + "match interface WORD", + MATCH_STR + "Match first hop interface of route\n" + "Interface name\n") +{ + return ripng_route_match_add (vty, vty->index, "interface", argv[0]); +} + +DEFUN (no_match_interface, + no_match_interface_cmd, + "no match interface", + NO_STR + MATCH_STR + "Match first hop interface of route\n") +{ + if (argc == 0) + return ripng_route_match_delete (vty, vty->index, "interface", NULL); + + return ripng_route_match_delete (vty, vty->index, "interface", argv[0]); +} + +ALIAS (no_match_interface, + no_match_interface_val_cmd, + "no match interface WORD", + NO_STR + MATCH_STR + "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" + "Metric value\n") +{ + return ripng_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 ripng_route_match_delete (vty, vty->index, "tag", NULL); + + return ripng_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" + "Metric value\n") + +/* set functions */ + +DEFUN (set_metric, + set_metric_cmd, + "set metric <0-4294967295>", + "Set value\n" + "Metric value for destination routing protocol\n" + "Metric value\n") +{ + return ripng_route_set_add (vty, vty->index, "metric", argv[0]); +} + +DEFUN (no_set_metric, + no_set_metric_cmd, + "no set metric", + NO_STR + SET_STR + "Metric value for destination routing protocol\n") +{ + if (argc == 0) + return ripng_route_set_delete (vty, vty->index, "metric", NULL); + + return ripng_route_set_delete (vty, vty->index, "metric", argv[0]); +} + +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") + +DEFUN (set_ipv6_nexthop_local, + set_ipv6_nexthop_local_cmd, + "set ipv6 next-hop local X:X::X:X", + SET_STR + IPV6_STR + "IPv6 next-hop address\n" + "IPv6 local address\n" + "IPv6 address of next hop\n") +{ + union sockunion su; + int ret; + + ret = str2sockunion (argv[0], &su); + if (ret < 0) + { + vty_out (vty, "%% Malformed next-hop local address%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return ripng_route_set_add (vty, vty->index, "ipv6 next-hop local", argv[0]); +} + +DEFUN (no_set_ipv6_nexthop_local, + no_set_ipv6_nexthop_local_cmd, + "no set ipv6 next-hop local", + NO_STR + SET_STR + IPV6_STR + "IPv6 next-hop address\n" + "IPv6 local address\n") +{ + if (argc == 0) + return ripng_route_set_delete (vty, vty->index, "ipv6 next-hop local", NULL); + + return ripng_route_set_delete (vty, vty->index, "ipv6 next-hop local", argv[0]); +} + +ALIAS (no_set_ipv6_nexthop_local, + no_set_ipv6_nexthop_local_val_cmd, + "no set ipv6 next-hop local X:X::X:X", + NO_STR + SET_STR + IPV6_STR + "IPv6 next-hop address\n" + "IPv6 local address\n" + "IPv6 address of next hop\n") + +DEFUN (set_tag, + set_tag_cmd, + "set tag <1-4294967295>", + SET_STR + "Tag value for routing protocol\n" + "Tag value\n") +{ + return ripng_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) + return ripng_route_set_delete (vty, vty->index, "tag", NULL); + + return ripng_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") + +void +ripng_route_map_reset () +{ + /* XXX ??? */ + ; +} + +void +ripng_route_map_init () +{ + route_map_init (); + route_map_init_vty (); + + route_map_install_match (&route_match_metric_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_ipv6_nexthop_local_cmd); + route_map_install_set (&route_set_tag_cmd); + + 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_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); + install_element (RMAP_NODE, &no_set_metric_val_cmd); + install_element (RMAP_NODE, &set_ipv6_nexthop_local_cmd); + install_element (RMAP_NODE, &no_set_ipv6_nexthop_local_cmd); + install_element (RMAP_NODE, &no_set_ipv6_nexthop_local_val_cmd); + install_element (RMAP_NODE, &set_tag_cmd); + install_element (RMAP_NODE, &no_set_tag_cmd); + install_element (RMAP_NODE, &no_set_tag_val_cmd); +} diff --git a/ripngd/ripng_zebra.c b/ripngd/ripng_zebra.c new file mode 100644 index 0000000..a35bc99 --- /dev/null +++ b/ripngd/ripng_zebra.c @@ -0,0 +1,593 @@ +/* + * RIPngd and zebra interface. + * Copyright (C) 1998, 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 "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; + +/* 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 (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); + SET_FLAG (api.message, ZAPI_MESSAGE_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 = 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_add (struct route_node *rp) +{ + ripng_zebra_ipv6_send (rp, ZEBRA_IPV6_ROUTE_ADD); +} + +/* 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, 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; + memset (&nexthop, 0, sizeof (struct in6_addr)); + + /* Type, flags, message. */ + api.type = stream_getc (s); + api.flags = stream_getc (s); + api.message = stream_getc (s); + + /* IPv6 prefix. */ + memset (&p, 0, sizeof (struct prefix_ipv6)); + p.family = AF_INET6; + plength = stream_getc (s); + p.prefixlen = MIN(IPV6_MAX_PREFIXLEN, plength); + 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); + stream_get (&nexthop, s, 16); + } + 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 (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, api.tag); + else + ripng_redistribute_delete (api.type, RIPNG_ROUTE_REDISTRIBUTE, &p, ifindex); + + return 0; +} + +void +ripng_zclient_reset (void) +{ + zclient_reset (zclient); +} + +static int +ripng_redistribute_unset (int type) +{ + if (! vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT)) + return CMD_SUCCESS; + + vrf_bitmap_set (zclient->redist[type], VRF_DEFAULT); + + if (zclient->sock > 0) + zebra_redistribute_send (ZEBRA_REDISTRIBUTE_DELETE, zclient, type, + VRF_DEFAULT); + + ripng_redistribute_withdraw (type); + + return CMD_SUCCESS; +} + +int +ripng_redistribute_check (int type) +{ + return vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT); +} + +static void +ripng_redistribute_metric_set (int type, int metric) +{ + ripng->route_map[type].metric_config = 1; + ripng->route_map[type].metric = metric; +} + +static int +ripng_redistribute_metric_unset (int type) +{ + ripng->route_map[type].metric_config = 0; + ripng->route_map[type].metric = 0; + return 0; +} + +static void +ripng_redistribute_routemap_set (int type, const char *name) +{ + if (ripng->route_map[type].name) + free (ripng->route_map[type].name); + + ripng->route_map[type].name = strdup (name); + ripng->route_map[type].map = route_map_lookup_by_name (name); +} + +static void +ripng_redistribute_routemap_unset (int type) +{ + if (ripng->route_map[type].name) + free (ripng->route_map[type].name); + + ripng->route_map[type].name = NULL; + ripng->route_map[type].map = NULL; +} + +/* Redistribution types */ +static struct { + int type; + int str_min_len; + const char *str; +} redist_type[] = { + {ZEBRA_ROUTE_KERNEL, 1, "kernel"}, + {ZEBRA_ROUTE_CONNECT, 1, "connected"}, + {ZEBRA_ROUTE_STATIC, 1, "static"}, + {ZEBRA_ROUTE_OSPF6, 1, "ospf6"}, + {ZEBRA_ROUTE_BGP, 2, "bgp"}, + {ZEBRA_ROUTE_BABEL, 2, "babel"}, + {0, 0, NULL} +}; + +void +ripng_redistribute_clean () +{ + int i; + + for (i = 0; redist_type[i].str; i++) + { + 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, + VRF_DEFAULT); + + vrf_bitmap_unset (zclient->redist[redist_type[i].type], VRF_DEFAULT); + + /* Remove the routes from RIPng table. */ + ripng_redistribute_withdraw (redist_type[i].type); + } + } +} + +DEFUN (router_zebra, + router_zebra_cmd, + "router zebra", + "Enable a routing process\n" + "Make connection to zebra daemon\n") +{ + vty->node = ZEBRA_NODE; + zclient->enable = 1; + zclient_start (zclient); + return CMD_SUCCESS; +} + +DEFUN (no_router_zebra, + no_router_zebra_cmd, + "no router zebra", + NO_STR + "Disable a routing process\n" + "Stop connection to zebra daemon\n") +{ + zclient->enable = 0; + zclient_stop (zclient); + return CMD_SUCCESS; +} + +DEFUN (ripng_redistribute_ripng, + ripng_redistribute_ripng_cmd, + "redistribute ripng", + "Redistribute information from another routing protocol\n" + "RIPng route\n") +{ + vrf_bitmap_set (zclient->redist[ZEBRA_ROUTE_RIPNG], VRF_DEFAULT); + return CMD_SUCCESS; +} + +DEFUN (no_ripng_redistribute_ripng, + no_ripng_redistribute_ripng_cmd, + "no redistribute ripng", + NO_STR + "Redistribute information from another routing protocol\n" + "RIPng route\n") +{ + vrf_bitmap_unset (zclient->redist[ZEBRA_ROUTE_RIPNG], VRF_DEFAULT); + return CMD_SUCCESS; +} + +DEFUN (ripng_redistribute_type, + ripng_redistribute_type_cmd, + "redistribute " QUAGGA_REDIST_STR_RIPNGD, + "Redistribute\n" + QUAGGA_REDIST_HELP_STR_RIPNGD) +{ + int type; + + type = proto_redistnum(AFI_IP6, 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, VRF_DEFAULT); + return CMD_SUCCESS; +} + +DEFUN (no_ripng_redistribute_type, + no_ripng_redistribute_type_cmd, + "no redistribute " QUAGGA_REDIST_STR_RIPNGD, + NO_STR + "Redistribute\n" + QUAGGA_REDIST_HELP_STR_RIPNGD) +{ + int type; + + type = proto_redistnum(AFI_IP6, argv[0]); + + if (type < 0) + { + vty_out(vty, "Invalid type %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + ripng_redistribute_metric_unset (type); + ripng_redistribute_routemap_unset (type); + return ripng_redistribute_unset (type); +} + + +DEFUN (ripng_redistribute_type_metric, + ripng_redistribute_type_metric_cmd, + "redistribute " QUAGGA_REDIST_STR_RIPNGD " metric <0-16>", + "Redistribute\n" + QUAGGA_REDIST_HELP_STR_RIPNGD + "Metric\n" + "Metric value\n") +{ + int type; + int metric; + + metric = atoi (argv[1]); + type = proto_redistnum(AFI_IP6, argv[0]); + + if (type < 0) + { + vty_out(vty, "Invalid type %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + ripng_redistribute_metric_set (type, metric); + zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, type, VRF_DEFAULT); + return CMD_SUCCESS; +} + +ALIAS (no_ripng_redistribute_type, + no_ripng_redistribute_type_metric_cmd, + "no redistribute " QUAGGA_REDIST_STR_RIPNGD " metric <0-16>", + NO_STR + "Redistribute\n" + QUAGGA_REDIST_HELP_STR_RIPNGD + "Metric\n" + "Metric value\n") + +DEFUN (ripng_redistribute_type_routemap, + ripng_redistribute_type_routemap_cmd, + "redistribute " QUAGGA_REDIST_STR_RIPNGD " route-map WORD", + "Redistribute\n" + QUAGGA_REDIST_HELP_STR_RIPNGD + "Route map reference\n" + "Pointer to route-map entries\n") +{ + int type; + + type = proto_redistnum(AFI_IP6, argv[0]); + + if (type < 0) + { + vty_out(vty, "Invalid type %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + ripng_redistribute_routemap_set (type, argv[1]); + zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, type, VRF_DEFAULT); + return CMD_SUCCESS; +} + +ALIAS (no_ripng_redistribute_type, + no_ripng_redistribute_type_routemap_cmd, + "no redistribute " QUAGGA_REDIST_STR_RIPNGD " route-map WORD", + NO_STR + "Redistribute\n" + QUAGGA_REDIST_HELP_STR_RIPNGD + "Route map reference\n" + "Pointer to route-map entries\n") + +DEFUN (ripng_redistribute_type_metric_routemap, + ripng_redistribute_type_metric_routemap_cmd, + "redistribute " QUAGGA_REDIST_STR_RIPNGD " metric <0-16> route-map WORD", + "Redistribute\n" + QUAGGA_REDIST_HELP_STR_RIPNGD + "Metric\n" + "Metric value\n" + "Route map reference\n" + "Pointer to route-map entries\n") +{ + int type; + int metric; + + type = proto_redistnum(AFI_IP6, argv[0]); + metric = atoi (argv[1]); + + if (type < 0) + { + vty_out(vty, "Invalid type %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + ripng_redistribute_metric_set (type, metric); + ripng_redistribute_routemap_set (type, argv[2]); + zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, type, VRF_DEFAULT); + return CMD_SUCCESS; +} + +ALIAS (no_ripng_redistribute_type, + no_ripng_redistribute_type_metric_routemap_cmd, + "no redistribute " QUAGGA_REDIST_STR_RIPNGD " metric <0-16> route-map WORD", + NO_STR + "Redistribute\n" + QUAGGA_REDIST_HELP_STR_RIPNGD + "Route map reference\n" + "Pointer to route-map entries\n") + +void +ripng_redistribute_write (struct vty *vty, int config_mode) +{ + int i; + + for (i = 0; i < ZEBRA_ROUTE_MAX; i++) + if (i != zclient->redist_default && + vrf_bitmap_check (zclient->redist[i], VRF_DEFAULT)) + { + if (config_mode) + { + if (ripng->route_map[i].metric_config) + { + if (ripng->route_map[i].name) + vty_out (vty, " redistribute %s metric %d route-map %s%s", + zebra_route_string(i), ripng->route_map[i].metric, + ripng->route_map[i].name, VTY_NEWLINE); + else + vty_out (vty, " redistribute %s metric %d%s", + zebra_route_string(i), ripng->route_map[i].metric, + VTY_NEWLINE); + } + else + { + if (ripng->route_map[i].name) + vty_out (vty, " redistribute %s route-map %s%s", + zebra_route_string(i), ripng->route_map[i].name, + VTY_NEWLINE); + else + vty_out (vty, " redistribute %s%s", zebra_route_string(i), + VTY_NEWLINE); + } + } + else + vty_out (vty, " %s", zebra_route_string(i)); + } +} + +/* RIPng configuration write function. */ +static int +zebra_config_write (struct vty *vty) +{ + if (! zclient->enable) + { + vty_out (vty, "no router zebra%s", VTY_NEWLINE); + return 1; + } + 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); + return 1; + } + return 0; +} + +/* Zebra node structure. */ +static struct cmd_node zebra_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 (struct thread_master *master) +{ + /* Allocate zebra structure. */ + 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; + zclient->interface_delete = ripng_interface_delete; + zclient->interface_address_add = ripng_interface_address_add; + zclient->interface_address_delete = ripng_interface_address_delete; + zclient->ipv6_route_add = ripng_zebra_read_ipv6; + zclient->ipv6_route_delete = ripng_zebra_read_ipv6; + + /* Install zebra node. */ + install_node (&zebra_node, zebra_config_write); + + /* Install command element for zebra node. */ + install_element (CONFIG_NODE, &router_zebra_cmd); + install_element (CONFIG_NODE, &no_router_zebra_cmd); + install_default (ZEBRA_NODE); + install_element (ZEBRA_NODE, &ripng_redistribute_ripng_cmd); + install_element (ZEBRA_NODE, &no_ripng_redistribute_ripng_cmd); + + /* Install command elements to ripng node */ + install_element (RIPNG_NODE, &ripng_redistribute_type_cmd); + install_element (RIPNG_NODE, &ripng_redistribute_type_routemap_cmd); + install_element (RIPNG_NODE, &ripng_redistribute_type_metric_cmd); + install_element (RIPNG_NODE, &ripng_redistribute_type_metric_routemap_cmd); + install_element (RIPNG_NODE, &no_ripng_redistribute_type_cmd); + install_element (RIPNG_NODE, &no_ripng_redistribute_type_routemap_cmd); + install_element (RIPNG_NODE, &no_ripng_redistribute_type_metric_cmd); + install_element (RIPNG_NODE, &no_ripng_redistribute_type_metric_routemap_cmd); +} diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c new file mode 100644 index 0000000..824b3a4 --- /dev/null +++ b/ripngd/ripngd.c @@ -0,0 +1,3092 @@ +/* RIPng daemon + * Copyright (C) 1998, 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 "prefix.h" +#include "filter.h" +#include "log.h" +#include "thread.h" +#include "memory.h" +#include "if.h" +#include "stream.h" +#include "table.h" +#include "command.h" +#include "sockopt.h" +#include "distribute.h" +#include "plist.h" +#include "routemap.h" +#include "if_rmap.h" +#include "privs.h" + +#include "ripngd/ripngd.h" +#include "ripngd/ripng_route.h" +#include "ripngd/ripng_debug.h" +#include "ripngd/ripng_nexthop.h" + +/* RIPng structure which includes many parameters related to RIPng + protocol. If ripng couldn't active or ripng doesn't configured, + ripng->fd must be negative value. */ +struct ripng *ripng = NULL; + +enum +{ + ripng_all_route, + ripng_changed_route, +}; + +extern struct zebra_privs_t ripngd_privs; + +/* Prototypes. */ +void +ripng_output_process (struct interface *, struct sockaddr_in6 *, int); + +int +ripng_triggered_update (struct thread *); + +/* RIPng next hop specification. */ +struct ripng_nexthop +{ + enum ripng_nexthop_type + { + RIPNG_NEXTHOP_UNSPEC, + RIPNG_NEXTHOP_ADDRESS + } flag; + struct in6_addr address; +}; + +static int +ripng_route_rte (struct ripng_info *rinfo) +{ + return (rinfo->type == ZEBRA_ROUTE_RIPNG && rinfo->sub_type == RIPNG_ROUTE_RTE); +} + +/* Allocate new ripng information. */ +struct ripng_info * +ripng_info_new () +{ + struct ripng_info *new; + + new = XCALLOC (MTYPE_RIPNG_ROUTE, sizeof (struct ripng_info)); + return new; +} + +/* Free ripng information. */ +void +ripng_info_free (struct ripng_info *rinfo) +{ + XFREE (MTYPE_RIPNG_ROUTE, rinfo); +} + +/* Create ripng socket. */ +static int +ripng_make_socket (void) +{ + int ret; + int sock; + struct sockaddr_in6 ripaddr; + + sock = socket (AF_INET6, SOCK_DGRAM, 0); + if (sock < 0) + { + zlog (NULL, LOG_ERR, "Can't make ripng socket"); + return sock; + } + + ret = setsockopt_so_recvbuf (sock, 8096); + if (ret < 0) + return ret; + ret = setsockopt_ipv6_pktinfo (sock, 1); + if (ret < 0) + return ret; +#ifdef IPTOS_PREC_INTERNETCONTROL + ret = setsockopt_ipv6_tclass (sock, IPTOS_PREC_INTERNETCONTROL); + if (ret < 0) + return ret; +#endif + ret = setsockopt_ipv6_multicast_hops (sock, 255); + if (ret < 0) + return ret; + ret = setsockopt_ipv6_multicast_loop (sock, 0); + if (ret < 0) + return ret; + ret = setsockopt_ipv6_hoplimit (sock, 1); + if (ret < 0) + return ret; + + memset (&ripaddr, 0, sizeof (ripaddr)); + ripaddr.sin6_family = AF_INET6; +#ifdef SIN6_LEN + ripaddr.sin6_len = sizeof (struct sockaddr_in6); +#endif /* SIN6_LEN */ + ripaddr.sin6_port = htons (RIPNG_PORT_DEFAULT); + + if (ripngd_privs.change (ZPRIVS_RAISE)) + zlog_err ("ripng_make_socket: could not raise privs"); + + ret = bind (sock, (struct sockaddr *) &ripaddr, sizeof (ripaddr)); + if (ret < 0) + { + zlog (NULL, LOG_ERR, "Can't bind ripng socket: %s.", safe_strerror (errno)); + if (ripngd_privs.change (ZPRIVS_LOWER)) + zlog_err ("ripng_make_socket: could not lower privs"); + return ret; + } + if (ripngd_privs.change (ZPRIVS_LOWER)) + zlog_err ("ripng_make_socket: could not lower privs"); + return sock; +} + +/* Send RIPng packet. */ +int +ripng_send_packet (caddr_t buf, int bufsize, struct sockaddr_in6 *to, + struct interface *ifp) +{ + int ret; + struct msghdr msg; + struct iovec iov; + struct cmsghdr *cmsgptr; + char adata [256]; + struct in6_pktinfo *pkt; + struct sockaddr_in6 addr; + + if (IS_RIPNG_DEBUG_SEND) { + if (to) + zlog_debug ("send to %s", inet6_ntoa (to->sin6_addr)); + zlog_debug (" send interface %s", ifp->name); + zlog_debug (" send packet size %d", bufsize); + } + + memset (&addr, 0, sizeof (struct sockaddr_in6)); + addr.sin6_family = AF_INET6; +#ifdef SIN6_LEN + addr.sin6_len = sizeof (struct sockaddr_in6); +#endif /* SIN6_LEN */ + addr.sin6_flowinfo = htonl (RIPNG_PRIORITY_DEFAULT); + + /* When destination is specified. */ + if (to != NULL) + { + addr.sin6_addr = to->sin6_addr; + addr.sin6_port = to->sin6_port; + } + else + { + inet_pton(AF_INET6, RIPNG_GROUP, &addr.sin6_addr); + addr.sin6_port = htons (RIPNG_PORT_DEFAULT); + } + + msg.msg_name = (void *) &addr; + msg.msg_namelen = sizeof (struct sockaddr_in6); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = (void *) adata; + msg.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); + + iov.iov_base = buf; + iov.iov_len = bufsize; + + cmsgptr = (struct cmsghdr *)adata; + cmsgptr->cmsg_len = CMSG_LEN(sizeof (struct in6_pktinfo)); + cmsgptr->cmsg_level = IPPROTO_IPV6; + cmsgptr->cmsg_type = IPV6_PKTINFO; + + pkt = (struct in6_pktinfo *) CMSG_DATA (cmsgptr); + memset (&pkt->ipi6_addr, 0, sizeof (struct in6_addr)); + pkt->ipi6_ifindex = ifp->ifindex; + + ret = sendmsg (ripng->sock, &msg, 0); + + if (ret < 0) { + if (to) + zlog_err ("RIPng send fail on %s to %s: %s", ifp->name, + inet6_ntoa (to->sin6_addr), safe_strerror (errno)); + else + zlog_err ("RIPng send fail on %s: %s", ifp->name, safe_strerror (errno)); + } + + return ret; +} + +/* Receive UDP RIPng packet from socket. */ +static int +ripng_recv_packet (int sock, u_char *buf, int bufsize, + struct sockaddr_in6 *from, ifindex_t *ifindex, + int *hoplimit) +{ + int ret; + struct msghdr msg; + struct iovec iov; + struct cmsghdr *cmsgptr; + 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 */ + char adata[1024]; + + /* Fill in message and iovec. */ + msg.msg_name = (void *) from; + msg.msg_namelen = sizeof (struct sockaddr_in6); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = (void *) adata; + msg.msg_controllen = sizeof adata; + iov.iov_base = buf; + iov.iov_len = bufsize; + + /* If recvmsg fail return minus value. */ + ret = recvmsg (sock, &msg, 0); + if (ret < 0) + return ret; + + for (cmsgptr = ZCMSG_FIRSTHDR(&msg); cmsgptr != NULL; + cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) + { + /* I want interface index which this packet comes from. */ + if (cmsgptr->cmsg_level == IPPROTO_IPV6 && + cmsgptr->cmsg_type == IPV6_PKTINFO) + { + struct in6_pktinfo *ptr; + + ptr = (struct in6_pktinfo *) CMSG_DATA (cmsgptr); + *ifindex = ptr->ipi6_ifindex; + dst = ptr->ipi6_addr; + + if (*ifindex == 0) + zlog_warn ("Interface index returned by IPV6_PKTINFO is zero"); + } + + /* Incoming packet's multicast hop limit. */ + if (cmsgptr->cmsg_level == IPPROTO_IPV6 && + cmsgptr->cmsg_type == IPV6_HOPLIMIT) + { + int *phoplimit = (int *) CMSG_DATA (cmsgptr); + *hoplimit = *phoplimit; + } + } + + /* Hoplimit check shold be done when destination address is + multicast address. */ + if (! IN6_IS_ADDR_MULTICAST (&dst)) + *hoplimit = -1; + + return ret; +} + +/* Dump rip packet */ +void +ripng_packet_dump (struct ripng_packet *packet, int size, const char *sndrcv) +{ + caddr_t lim; + struct rte *rte; + const char *command_str; + + /* Set command string. */ + if (packet->command == RIPNG_REQUEST) + command_str = "request"; + else if (packet->command == RIPNG_RESPONSE) + command_str = "response"; + else + command_str = "unknown"; + + /* Dump packet header. */ + zlog_debug ("%s %s version %d packet size %d", + sndrcv, command_str, packet->version, size); + + /* Dump each routing table entry. */ + rte = packet->rte; + + for (lim = (caddr_t) packet + size; (caddr_t) rte < lim; rte++) + { + if (rte->metric == RIPNG_METRIC_NEXTHOP) + zlog_debug (" nexthop %s/%d", inet6_ntoa (rte->addr), rte->prefixlen); + else + zlog_debug (" %s/%d metric %d tag %d", + inet6_ntoa (rte->addr), rte->prefixlen, + rte->metric, ntohs (rte->tag)); + } +} + +/* RIPng next hop address RTE (Route Table Entry). */ +static void +ripng_nexthop_rte (struct rte *rte, + struct sockaddr_in6 *from, + struct ripng_nexthop *nexthop) +{ + char buf[INET6_BUFSIZ]; + + /* Logging before checking RTE. */ + if (IS_RIPNG_DEBUG_RECV) + zlog_debug ("RIPng nexthop RTE address %s tag %d prefixlen %d", + inet6_ntoa (rte->addr), ntohs (rte->tag), rte->prefixlen); + + /* RFC2080 2.1.1 Next Hop: + The route tag and prefix length in the next hop RTE must be + set to zero on sending and ignored on receiption. */ + if (ntohs (rte->tag) != 0) + zlog_warn ("RIPng nexthop RTE with non zero tag value %d from %s", + ntohs (rte->tag), inet6_ntoa (from->sin6_addr)); + + if (rte->prefixlen != 0) + zlog_warn ("RIPng nexthop RTE with non zero prefixlen value %d from %s", + rte->prefixlen, inet6_ntoa (from->sin6_addr)); + + /* Specifying a value of 0:0:0:0:0:0:0:0 in the prefix field of a + next hop RTE indicates that the next hop address should be the + originator of the RIPng advertisement. An address specified as a + next hop must be a link-local address. */ + if (IN6_IS_ADDR_UNSPECIFIED (&rte->addr)) + { + nexthop->flag = RIPNG_NEXTHOP_UNSPEC; + memset (&nexthop->address, 0, sizeof (struct in6_addr)); + return; + } + + if (IN6_IS_ADDR_LINKLOCAL (&rte->addr)) + { + nexthop->flag = RIPNG_NEXTHOP_ADDRESS; + IPV6_ADDR_COPY (&nexthop->address, &rte->addr); + return; + } + + /* The purpose of the next hop RTE is to eliminate packets being + routed through extra hops in the system. It is particularly useful + when RIPng is not being run on all of the routers on a network. + Note that next hop RTE is "advisory". That is, if the provided + information is ignored, a possibly sub-optimal, but absolutely + valid, route may be taken. If the received next hop address is not + a link-local address, it should be treated as 0:0:0:0:0:0:0:0. */ + zlog_warn ("RIPng nexthop RTE with non link-local address %s from %s", + inet6_ntoa (rte->addr), + inet_ntop (AF_INET6, &from->sin6_addr, buf, INET6_BUFSIZ)); + + nexthop->flag = RIPNG_NEXTHOP_UNSPEC; + memset (&nexthop->address, 0, sizeof (struct in6_addr)); + + return; +} + +/* If ifp has same link-local address then return 1. */ +static int +ripng_lladdr_check (struct interface *ifp, struct in6_addr *addr) +{ + struct listnode *node; + struct connected *connected; + struct prefix *p; + + for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, connected)) + { + p = connected->address; + + if (p->family == AF_INET6 && + IN6_IS_ADDR_LINKLOCAL (&p->u.prefix6) && + IN6_ARE_ADDR_EQUAL (&p->u.prefix6, addr)) + return 1; + } + return 0; +} + +/* RIPng route garbage collect timer. */ +static int +ripng_garbage_collect (struct thread *t) +{ + struct ripng_info *rinfo; + struct route_node *rp; + + rinfo = THREAD_ARG (t); + rinfo->t_garbage_collect = NULL; + + /* Off timeout timer. */ + RIPNG_TIMER_OFF (rinfo->t_timeout); + + /* Get route_node pointer. */ + rp = rinfo->rp; + + /* Unlock route_node. */ + 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); + + return 0; +} + +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 route_node *rp = rinfo_new->rp; + struct ripng_info *rinfo = NULL; + struct list *list = NULL; + + if (rp->info == NULL) + rp->info = list_new (); + list = (struct list *)rp->info; + + /* If ECMP is not allowed and some entry already exists in the list, + * do nothing. */ + if (listcount (list) && !ripng->ecmp) + return NULL; + + rinfo = ripng_info_new (); + memcpy (rinfo, rinfo_new, sizeof (struct ripng_info)); + listnode_add (list, rinfo); + + if (ripng_route_rte (rinfo)) + { + ripng_timeout_update (rinfo); + ripng_zebra_ipv6_add (rp); + } + + ripng_aggregate_increment (rp, rinfo); + + /* 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; +} + +/* 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_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; +} + +/* 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 route_node *rp = rinfo->rp; + struct list *list = (struct list *)rp->info; + + RIPNG_TIMER_OFF (rinfo->t_timeout); + + if (rinfo->metric != RIPNG_METRIC_INFINITY) + ripng_aggregate_decrement (rp, rinfo); + + if (listcount (list) > 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; + } + else + { + assert (rinfo == listgetdata (listhead (list))); + + /* This is the only entry left in the list. We must keep it in + * the list for garbage collection time, with INFINITY metric. */ + + rinfo->metric = 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); + } + + /* 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) + { + RIPNG_TIMER_OFF (rinfo->t_timeout); + RIPNG_TIMER_ON (rinfo->t_timeout, ripng_timeout, ripng->timeout_time); + } +} + +static int +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"; + + /* Input distribute-list filtering. */ + if (ri->list[ripng_distribute]) + { + if (access_list_apply (ri->list[ripng_distribute], + (struct prefix *) p) == FILTER_DENY) + { + if (IS_RIPNG_DEBUG_PACKET) + zlog_debug ("%s/%d filtered by distribute %s", + inet6_ntoa (p->prefix), p->prefixlen, inout); + return -1; + } + } + if (ri->prefix[ripng_distribute]) + { + if (prefix_list_apply (ri->prefix[ripng_distribute], + (struct prefix *) p) == PREFIX_DENY) + { + if (IS_RIPNG_DEBUG_PACKET) + zlog_debug ("%s/%d filtered by prefix-list %s", + inet6_ntoa (p->prefix), p->prefixlen, inout); + return -1; + } + } + + /* 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, + (struct prefix *) p) == FILTER_DENY) + { + if (IS_RIPNG_DEBUG_PACKET) + zlog_debug ("%s/%d filtered by distribute %s", + inet6_ntoa (p->prefix), p->prefixlen, inout); + return -1; + } + } + } + if (dist->prefix[distribute]) + { + 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 %s", + inet6_ntoa (p->prefix), p->prefixlen, inout); + return -1; + } + } + } + } + return 0; +} + +/* Process RIPng route according to RFC2080. */ +static void +ripng_route_process (struct rte *rte, struct sockaddr_in6 *from, + struct ripng_nexthop *ripng_nexthop, + struct interface *ifp) +{ + int ret; + struct prefix_ipv6 p; + struct route_node *rp; + struct ripng_info *rinfo = NULL, newinfo; + struct ripng_interface *ri; + struct in6_addr *nexthop; + int same = 0; + struct list *list = NULL; + struct listnode *node = NULL; + + /* Make prefix structure. */ + memset (&p, 0, sizeof (struct prefix_ipv6)); + p.family = AF_INET6; + /* p.prefix = rte->addr; */ + IPV6_ADDR_COPY (&p.prefix, &rte->addr); + p.prefixlen = rte->prefixlen; + + /* Make sure mask is applied. */ + /* XXX We have to check the prefix is valid or not before call + apply_mask_ipv6. */ + apply_mask_ipv6 (&p); + + /* Apply input filters. */ + ri = ifp->info; + + 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; + + ret = route_map_apply (ri->routemap[RIPNG_FILTER_IN], + (struct prefix *)&p, RMAP_RIPNG, &newinfo); + + if (ret == RMAP_DENYMATCH) + { + if (IS_RIPNG_DEBUG_PACKET) + zlog_debug ("RIPng %s/%d is filtered by route-map in", + inet6_ntoa (p.prefix), p.prefixlen); + return; + } + + /* Get back the object */ + if (ripng_nexthop->flag == RIPNG_NEXTHOP_ADDRESS) { + if (! IPV6_ADDR_SAME(&newinfo.nexthop, &ripng_nexthop->address) ) { + /* the nexthop get changed by the routemap */ + if (IN6_IS_ADDR_LINKLOCAL(&newinfo.nexthop)) + ripng_nexthop->address = newinfo.nexthop; + else + ripng_nexthop->address = in6addr_any; + } + } else { + if (! IPV6_ADDR_SAME(&newinfo.nexthop, &from->sin6_addr) ) { + /* the nexthop get changed by the routemap */ + if (IN6_IS_ADDR_LINKLOCAL(&newinfo.nexthop)) { + ripng_nexthop->flag = RIPNG_NEXTHOP_ADDRESS; + ripng_nexthop->address = newinfo.nexthop; + } + } + } + rte->tag = htons(newinfo.tag_out); /* XXX */ + rte->metric = newinfo.metric_out; /* XXX: the routemap uses the metric_out field */ + } + + /* Once the entry has been validated, update the metric by + * adding the cost of the network on wich the message + * arrived. If the result is greater than infinity, use infinity + * (RFC2453 Sec. 3.9.2) + **/ + + /* Zebra ripngd can handle offset-list in. */ + ret = ripng_offset_list_apply_in (&p, ifp, &rte->metric); + + /* If offset-list does not modify the metric use interface's + * one. */ + if (! ret) + rte->metric += ifp->metric ? ifp->metric : 1; + + if (rte->metric > RIPNG_METRIC_INFINITY) + rte->metric = RIPNG_METRIC_INFINITY; + + /* Set nexthop pointer. */ + if (ripng_nexthop->flag == RIPNG_NEXTHOP_ADDRESS) + nexthop = &ripng_nexthop->address; + else + nexthop = &from->sin6_addr; + + /* Lookup RIPng routing table. */ + rp = route_node_get (ripng->table, (struct prefix *) &p); + + 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) + { + 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) + { + route_unlock_node (rp); + return; + } + } + + if (!rinfo) + { + /* 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 != RIPNG_METRIC_INFINITY) + ripng_ecmp_add (&newinfo); + } + else + { + /* 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 + route, reinitialize the timeout. */ + same = (IN6_ARE_ADDR_EQUAL (&rinfo->from, &from->sin6_addr) + && (rinfo->ifindex == ifp->ifindex)); + + /* 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 + one; do the following actions: */ + if ((same && rinfo->metric != rte->metric) || + rte->metric < rinfo->metric) + { + 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); + } +} + +/* Add redistributed route to RIPng table. */ +void +ripng_redistribute_add (int type, int sub_type, struct prefix_ipv6 *p, + ifindex_t ifindex, struct in6_addr *nexthop, + route_tag_t tag) +{ + struct route_node *rp; + 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; + + rp = route_node_get (ripng->table, (struct prefix *) p); + + 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) { + route_unlock_node (rp); + return; + } + + /* Manually configured RIPng route check. + * They have the precedence on all the other entries. + **/ + if (rinfo->type == ZEBRA_ROUTE_RIPNG + && ((rinfo->sub_type == RIPNG_ROUTE_STATIC) || + (rinfo->sub_type == RIPNG_ROUTE_DEFAULT)) ) { + if (type != ZEBRA_ROUTE_RIPNG || ((sub_type != RIPNG_ROUTE_STATIC) && + (sub_type != RIPNG_ROUTE_DEFAULT))) { + route_unlock_node (rp); + return; + } + } + + rinfo = ripng_ecmp_replace (&newinfo); + route_unlock_node (rp); + } + else + rinfo = ripng_ecmp_add (&newinfo); + + if (IS_RIPNG_DEBUG_EVENT) { + if (!nexthop) + zlog_debug ("Redistribute new prefix %s/%d on the interface %s", + inet6_ntoa(p->prefix), p->prefixlen, + ifindex2ifname(ifindex)); + else + zlog_debug ("Redistribute new prefix %s/%d with nexthop %s on the interface %s", + inet6_ntoa(p->prefix), p->prefixlen, inet6_ntoa(*nexthop), + ifindex2ifname(ifindex)); + } + + ripng_event (RIPNG_TRIGGERED_UPDATE, 0); +} + +/* Delete redistributed route to RIPng table. */ +void +ripng_redistribute_delete (int type, int sub_type, struct prefix_ipv6 *p, + ifindex_t ifindex) +{ + struct route_node *rp; + struct ripng_info *rinfo; + + if (IN6_IS_ADDR_LINKLOCAL (&p->prefix)) + return; + if (IN6_IS_ADDR_LOOPBACK (&p->prefix)) + return; + + rp = route_node_lookup (ripng->table, (struct prefix *) p); + + if (rp) + { + 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); + } +} + +/* Withdraw redistributed route. */ +void +ripng_redistribute_withdraw (int type) +{ + struct route_node *rp; + struct ripng_info *rinfo = NULL; + struct list *list = NULL; + + if (!ripng) + return; + + for (rp = route_top (ripng->table); rp; rp = route_next (rp)) + if ((list = rp->info) != NULL) + { + rinfo = listgetdata (listhead (list)); + if ((rinfo->type == type) + && (rinfo->sub_type != RIPNG_ROUTE_INTERFACE)) + { + /* 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) { + struct prefix_ipv6 *p = (struct prefix_ipv6 *) &rp->p; + + zlog_debug ("Poisone %s/%d on the interface %s [withdraw]", + inet6_ntoa(p->prefix), p->prefixlen, + ifindex2ifname(rinfo->ifindex)); + } + + ripng_event (RIPNG_TRIGGERED_UPDATE, 0); + } + } +} + +/* RIP routing information. */ +static void +ripng_response_process (struct ripng_packet *packet, int size, + struct sockaddr_in6 *from, struct interface *ifp, + int hoplimit) +{ + caddr_t lim; + struct rte *rte; + struct ripng_nexthop nexthop; + + /* RFC2080 2.4.2 Response Messages: + The Response must be ignored if it is not from the RIPng port. */ + if (ntohs (from->sin6_port) != RIPNG_PORT_DEFAULT) + { + zlog_warn ("RIPng packet comes from non RIPng port %d from %s", + ntohs (from->sin6_port), inet6_ntoa (from->sin6_addr)); + ripng_peer_bad_packet (from); + return; + } + + /* The datagram's IPv6 source address should be checked to see + whether the datagram is from a valid neighbor; the source of the + datagram must be a link-local address. */ + if (! IN6_IS_ADDR_LINKLOCAL(&from->sin6_addr)) + { + zlog_warn ("RIPng packet comes from non link local address %s", + inet6_ntoa (from->sin6_addr)); + ripng_peer_bad_packet (from); + return; + } + + /* It is also worth checking to see whether the response is from one + of the router's own addresses. Interfaces on broadcast networks + may receive copies of their own multicasts immediately. If a + router processes its own output as new input, confusion is likely, + and such datagrams must be ignored. */ + if (ripng_lladdr_check (ifp, &from->sin6_addr)) + { + zlog_warn ("RIPng packet comes from my own link local address %s", + inet6_ntoa (from->sin6_addr)); + ripng_peer_bad_packet (from); + return; + } + + /* As an additional check, periodic advertisements must have their + hop counts set to 255, and inbound, multicast packets sent from the + RIPng port (i.e. periodic advertisement or triggered update + packets) must be examined to ensure that the hop count is 255. */ + if (hoplimit >= 0 && hoplimit != 255) + { + zlog_warn ("RIPng packet comes with non 255 hop count %d from %s", + hoplimit, inet6_ntoa (from->sin6_addr)); + ripng_peer_bad_packet (from); + return; + } + + /* Update RIPng peer. */ + ripng_peer_update (from, packet->version); + + /* Reset nexthop. */ + memset (&nexthop, 0, sizeof (struct ripng_nexthop)); + nexthop.flag = RIPNG_NEXTHOP_UNSPEC; + + /* Set RTE pointer. */ + rte = packet->rte; + + for (lim = ((caddr_t) packet) + size; (caddr_t) rte < lim; rte++) + { + /* First of all, we have to check this RTE is next hop RTE or + not. Next hop RTE is completely different with normal RTE so + we need special treatment. */ + if (rte->metric == RIPNG_METRIC_NEXTHOP) + { + ripng_nexthop_rte (rte, from, &nexthop); + continue; + } + + /* RTE information validation. */ + + /* - is the destination prefix valid (e.g., not a multicast + prefix and not a link-local address) A link-local address + should never be present in an RTE. */ + if (IN6_IS_ADDR_MULTICAST (&rte->addr)) + { + zlog_warn ("Destination prefix is a multicast address %s/%d [%d]", + inet6_ntoa (rte->addr), rte->prefixlen, rte->metric); + ripng_peer_bad_route (from); + continue; + } + if (IN6_IS_ADDR_LINKLOCAL (&rte->addr)) + { + zlog_warn ("Destination prefix is a link-local address %s/%d [%d]", + inet6_ntoa (rte->addr), rte->prefixlen, rte->metric); + ripng_peer_bad_route (from); + continue; + } + if (IN6_IS_ADDR_LOOPBACK (&rte->addr)) + { + zlog_warn ("Destination prefix is a loopback address %s/%d [%d]", + inet6_ntoa (rte->addr), rte->prefixlen, rte->metric); + ripng_peer_bad_route (from); + continue; + } + + /* - is the prefix length valid (i.e., between 0 and 128, + inclusive) */ + if (rte->prefixlen > 128) + { + zlog_warn ("Invalid prefix length %s/%d from %s%%%s", + inet6_ntoa (rte->addr), rte->prefixlen, + inet6_ntoa (from->sin6_addr), ifp->name); + ripng_peer_bad_route (from); + continue; + } + + /* - is the metric valid (i.e., between 1 and 16, inclusive) */ + if (! (rte->metric >= 1 && rte->metric <= 16)) + { + zlog_warn ("Invalid metric %d from %s%%%s", rte->metric, + inet6_ntoa (from->sin6_addr), ifp->name); + ripng_peer_bad_route (from); + continue; + } + + /* Vincent: XXX Should we compute the direclty reachable nexthop + * for our RIPng network ? + **/ + + /* Routing table updates. */ + ripng_route_process (rte, from, &nexthop, ifp); + } +} + +/* Response to request message. */ +static void +ripng_request_process (struct ripng_packet *packet,int size, + struct sockaddr_in6 *from, struct interface *ifp) +{ + caddr_t lim; + struct rte *rte; + struct prefix_ipv6 p; + struct route_node *rp; + struct ripng_info *rinfo; + struct ripng_interface *ri; + + /* Does not reponse to the requests on the loopback interfaces */ + if (if_is_loopback (ifp)) + return; + + /* Check RIPng process is enabled on this interface. */ + ri = ifp->info; + if (! ri->running) + return; + + /* When passive interface is specified, suppress responses */ + if (ri->passive) + return; + + /* RIPng peer update. */ + ripng_peer_update (from, packet->version); + + lim = ((caddr_t) packet) + size; + rte = packet->rte; + + /* The Request is processed entry by entry. If there are no + entries, no response is given. */ + if (lim == (caddr_t) rte) + return; + + /* There is one special case. If there is exactly one entry in the + request, and it has a destination prefix of zero, a prefix length + of zero, and a metric of infinity (i.e., 16), then this is a + request to send the entire routing table. In that case, a call + is made to the output process to send the routing table to the + requesting address/port. */ + if (lim == ((caddr_t) (rte + 1)) && + IN6_IS_ADDR_UNSPECIFIED (&rte->addr) && + rte->prefixlen == 0 && + rte->metric == RIPNG_METRIC_INFINITY) + { + /* All route with split horizon */ + ripng_output_process (ifp, from, ripng_all_route); + } + else + { + /* Except for this special case, processing is quite simple. + Examine the list of RTEs in the Request one by one. For each + entry, look up the destination in the router's routing + database and, if there is a route, put that route's metric in + the metric field of the RTE. If there is no explicit route + to the specified destination, put infinity in the metric + field. Once all the entries have been filled in, change the + command from Request to Response and send the datagram back + to the requestor. */ + memset (&p, 0, sizeof (struct prefix_ipv6)); + p.family = AF_INET6; + + for (; ((caddr_t) rte) < lim; rte++) + { + p.prefix = rte->addr; + p.prefixlen = rte->prefixlen; + apply_mask_ipv6 (&p); + + rp = route_node_lookup (ripng->table, (struct prefix *) &p); + + if (rp) + { + rinfo = listgetdata (listhead ((struct list *)rp->info)); + rte->metric = rinfo->metric; + route_unlock_node (rp); + } + else + rte->metric = RIPNG_METRIC_INFINITY; + } + packet->command = RIPNG_RESPONSE; + + ripng_send_packet ((caddr_t) packet, size, from, ifp); + } +} + +/* First entry point of reading RIPng packet. */ +static int +ripng_read (struct thread *thread) +{ + int len; + int sock; + struct sockaddr_in6 from; + struct ripng_packet *packet; + ifindex_t ifindex = 0; + struct interface *ifp; + int hoplimit = -1; + + /* Check ripng is active and alive. */ + assert (ripng != NULL); + assert (ripng->sock >= 0); + + /* Fetch thread data and set read pointer to empty for event + managing. `sock' sould be same as ripng->sock. */ + sock = THREAD_FD (thread); + ripng->t_read = NULL; + + /* Add myself to the next event. */ + ripng_event (RIPNG_READ, sock); + + /* Read RIPng packet. */ + len = ripng_recv_packet (sock, STREAM_DATA (ripng->ibuf), + STREAM_SIZE (ripng->ibuf), &from, &ifindex, + &hoplimit); + if (len < 0) + { + zlog_warn ("RIPng recvfrom failed: %s.", safe_strerror (errno)); + return len; + } + + /* Check RTE boundary. RTE size (Packet length - RIPng header size + (4)) must be multiple size of one RTE size (20). */ + if (((len - 4) % 20) != 0) + { + zlog_warn ("RIPng invalid packet size %d from %s", len, + inet6_ntoa (from.sin6_addr)); + ripng_peer_bad_packet (&from); + return 0; + } + + packet = (struct ripng_packet *) STREAM_DATA (ripng->ibuf); + ifp = if_lookup_by_index (ifindex); + + /* RIPng packet received. */ + if (IS_RIPNG_DEBUG_EVENT) + zlog_debug ("RIPng packet received from %s port %d on %s", + inet6_ntoa (from.sin6_addr), ntohs (from.sin6_port), + ifp ? ifp->name : "unknown"); + + /* Logging before packet checking. */ + if (IS_RIPNG_DEBUG_RECV) + ripng_packet_dump (packet, len, "RECV"); + + /* Packet comes from unknown interface. */ + if (ifp == NULL) + { + zlog_warn ("RIPng packet comes from unknown interface %d", ifindex); + return 0; + } + + /* Packet version mismatch checking. */ + if (packet->version != ripng->version) + { + zlog_warn ("RIPng packet version %d doesn't fit to my version %d", + packet->version, ripng->version); + ripng_peer_bad_packet (&from); + return 0; + } + + /* Process RIPng packet. */ + switch (packet->command) + { + case RIPNG_REQUEST: + ripng_request_process (packet, len, &from, ifp); + break; + case RIPNG_RESPONSE: + ripng_response_process (packet, len, &from, ifp, hoplimit); + break; + default: + zlog_warn ("Invalid RIPng command %d", packet->command); + ripng_peer_bad_packet (&from); + break; + } + return 0; +} + +/* Walk down the RIPng routing table then clear changed flag. */ +static void +ripng_clear_changed_flag (void) +{ + struct route_node *rp; + 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 ((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 + enabled interface. */ +static int +ripng_update (struct thread *t) +{ + struct listnode *node; + struct interface *ifp; + struct ripng_interface *ri; + + /* Clear update timer thread. */ + ripng->t_update = NULL; + + /* Logging update event. */ + if (IS_RIPNG_DEBUG_EVENT) + zlog_debug ("RIPng update timer expired!"); + + /* Supply routes to each interface. */ + for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) + { + ri = ifp->info; + + if (if_is_loopback (ifp) || ! if_is_up (ifp)) + continue; + + if (! ri->running) + continue; + + /* When passive interface is specified, suppress announce to the + interface. */ + if (ri->passive) + continue; + +#if RIPNG_ADVANCED + if (ri->ri_send == RIPNG_SEND_OFF) + { + if (IS_RIPNG_DEBUG_EVENT) + zlog (NULL, LOG_DEBUG, + "[Event] RIPng send to if %d is suppressed by config", + ifp->ifindex); + continue; + } +#endif /* RIPNG_ADVANCED */ + + ripng_output_process (ifp, NULL, ripng_all_route); + } + + /* Triggered updates may be suppressed if a regular update is due by + the time the triggered update would be sent. */ + if (ripng->t_triggered_interval) + { + thread_cancel (ripng->t_triggered_interval); + ripng->t_triggered_interval = NULL; + } + ripng->trigger = 0; + + /* Reset flush event. */ + ripng_event (RIPNG_UPDATE_EVENT, 0); + + return 0; +} + +/* Triggered update interval timer. */ +static int +ripng_triggered_interval (struct thread *t) +{ + ripng->t_triggered_interval = NULL; + + if (ripng->trigger) + { + ripng->trigger = 0; + ripng_triggered_update (t); + } + return 0; +} + +/* Execute triggered update. */ +int +ripng_triggered_update (struct thread *t) +{ + struct listnode *node; + struct interface *ifp; + struct ripng_interface *ri; + int interval; + + ripng->t_triggered_update = NULL; + + /* Cancel interval timer. */ + if (ripng->t_triggered_interval) + { + thread_cancel (ripng->t_triggered_interval); + ripng->t_triggered_interval = NULL; + } + ripng->trigger = 0; + + /* Logging triggered update. */ + if (IS_RIPNG_DEBUG_EVENT) + zlog_debug ("RIPng triggered update!"); + + /* Split Horizon processing is done when generating triggered + updates as well as normal updates (see section 2.6). */ + for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) + { + ri = ifp->info; + + if (if_is_loopback (ifp) || ! if_is_up (ifp)) + continue; + + if (! ri->running) + continue; + + /* When passive interface is specified, suppress announce to the + interface. */ + if (ri->passive) + continue; + + ripng_output_process (ifp, NULL, ripng_changed_route); + } + + /* Once all of the triggered updates have been generated, the route + change flags should be cleared. */ + ripng_clear_changed_flag (); + + /* After a triggered update is sent, a timer should be set for a + random interval between 1 and 5 seconds. If other changes that + would trigger updates occur before the timer expires, a single + update is triggered when the timer expires. */ + interval = (random () % 5) + 1; + + ripng->t_triggered_interval = + thread_add_timer (master, ripng_triggered_interval, NULL, interval); + + return 0; +} + +/* Write routing table entry to the stream and return next index of + the routing table entry in the stream. */ +int +ripng_write_rte (int num, struct stream *s, struct prefix_ipv6 *p, + struct in6_addr *nexthop, u_int16_t tag, u_char metric) +{ + /* RIPng packet header. */ + if (num == 0) + { + stream_putc (s, RIPNG_RESPONSE); + stream_putc (s, RIPNG_V1); + stream_putw (s, 0); + } + + /* Write routing table entry. */ + if (!nexthop) + stream_write (s, (u_char *) &p->prefix, sizeof (struct in6_addr)); + else + stream_write (s, (u_char *) nexthop, sizeof (struct in6_addr)); + stream_putw (s, tag); + if (p) + stream_putc (s, p->prefixlen); + else + stream_putc (s, 0); + stream_putc (s, metric); + + return ++num; +} + +/* Send RESPONSE message to specified destination. */ +void +ripng_output_process (struct interface *ifp, struct sockaddr_in6 *to, + int route_type) +{ + int ret; + struct route_node *rp; + struct ripng_info *rinfo; + struct ripng_interface *ri; + 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) + zlog_debug ("RIPng update routes to neighbor %s", + inet6_ntoa(to->sin6_addr)); + else + zlog_debug ("RIPng update routes on interface %s", ifp->name); + } + + /* Get RIPng interface. */ + ri = ifp->info; + + ripng_rte_list = ripng_rte_new(); + + for (rp = route_top (ripng->table); rp; rp = route_next (rp)) + { + 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. + */ + p = (struct prefix_ipv6 *) &rp->p; + rinfo->metric_out = rinfo->metric; + rinfo->tag_out = rinfo->tag; + memset(&rinfo->nexthop_out, 0, sizeof(rinfo->nexthop_out)); + /* In order to avoid some local loops, + * if the RIPng route has a nexthop via this interface, keep the nexthop, + * otherwise set it to 0. The nexthop should not be propagated + * beyond the local broadcast/multicast area in order + * to avoid an IGP multi-level recursive look-up. + */ + if (rinfo->ifindex == ifp->ifindex) + rinfo->nexthop_out = rinfo->nexthop; + + /* Apply output filters. */ + ret = ripng_filter (RIPNG_FILTER_OUT, p, ri); + if (ret < 0) + continue; + + /* Changed route only output. */ + if (route_type == ripng_changed_route && + (! (rinfo->flags & RIPNG_RTF_CHANGED))) + continue; + + /* Split horizon. */ + if (ri->split_horizon == RIPNG_SPLIT_HORIZON) + { + /* We perform split horizon for RIPng routes. */ + 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; + } + + /* Preparation for route-map. */ + rinfo->metric_set = 0; + /* nexthop_out, + * metric_out + * and tag_out are already initialized. + */ + + /* Interface route-map */ + if (ri->routemap[RIPNG_FILTER_OUT]) + { + int ret; + + ret = route_map_apply (ri->routemap[RIPNG_FILTER_OUT], + (struct prefix *) p, RMAP_RIPNG, + rinfo); + + if (ret == RMAP_DENYMATCH) + { + if (IS_RIPNG_DEBUG_PACKET) + zlog_debug ("RIPng %s/%d is filtered by route-map out", + inet6_ntoa (p->prefix), p->prefixlen); + continue; + } + + } + + /* Redistribute route-map. */ + if (ripng->route_map[rinfo->type].name) + { + int ret; + + ret = route_map_apply (ripng->route_map[rinfo->type].map, + (struct prefix *) p, RMAP_RIPNG, + rinfo); + + if (ret == RMAP_DENYMATCH) + { + if (IS_RIPNG_DEBUG_PACKET) + zlog_debug ("RIPng %s/%d is filtered by route-map", + inet6_ntoa (p->prefix), p->prefixlen); + continue; + } + } + + /* When the route-map does not set metric. */ + if (! rinfo->metric_set) + { + /* If the redistribute metric is set. */ + if (ripng->route_map[rinfo->type].metric_config + && rinfo->metric != RIPNG_METRIC_INFINITY) + { + rinfo->metric_out = ripng->route_map[rinfo->type].metric; + } + else + { + /* If the route is not connected or localy generated + one, use default-metric value */ + if (rinfo->type != ZEBRA_ROUTE_RIPNG + && rinfo->type != ZEBRA_ROUTE_CONNECT + && rinfo->metric != RIPNG_METRIC_INFINITY) + rinfo->metric_out = ripng->default_metric; + } + } + + /* Apply offset-list */ + if (rinfo->metric_out != RIPNG_METRIC_INFINITY) + ripng_offset_list_apply_out (p, ifp, &rinfo->metric_out); + + if (rinfo->metric_out > RIPNG_METRIC_INFINITY) + rinfo->metric_out = RIPNG_METRIC_INFINITY; + + /* Perform split-horizon with poisoned reverse + * for RIPng routes. + **/ + if (ri->split_horizon == RIPNG_SPLIT_HORIZON_POISONED_REVERSE) { + 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 */ + ripng_rte_add(ripng_rte_list, p, rinfo, NULL); + } + + /* Process the aggregated RTE entry */ + if ((aggregate = rp->aggregate) != NULL && + aggregate->count > 0 && + aggregate->suppress == 0) + { + /* If no route-map are applied, the RTE will be these following + * informations. + */ + p = (struct prefix_ipv6 *) &rp->p; + aggregate->metric_set = 0; + aggregate->metric_out = aggregate->metric; + aggregate->tag_out = aggregate->tag; + memset(&aggregate->nexthop_out, 0, sizeof(aggregate->nexthop_out)); + + /* Apply output filters.*/ + ret = ripng_filter (RIPNG_FILTER_OUT, p, ri); + if (ret < 0) + continue; + + /* Interface route-map */ + if (ri->routemap[RIPNG_FILTER_OUT]) + { + int ret; + struct ripng_info newinfo; + + /* let's cast the aggregate structure to ripng_info */ + memset (&newinfo, 0, sizeof (struct ripng_info)); + /* the nexthop is :: */ + newinfo.metric = aggregate->metric; + newinfo.metric_out = aggregate->metric_out; + newinfo.tag = aggregate->tag; + newinfo.tag_out = aggregate->tag_out; + + ret = route_map_apply (ri->routemap[RIPNG_FILTER_OUT], + (struct prefix *) p, RMAP_RIPNG, + &newinfo); + + if (ret == RMAP_DENYMATCH) + { + if (IS_RIPNG_DEBUG_PACKET) + zlog_debug ("RIPng %s/%d is filtered by route-map out", + inet6_ntoa (p->prefix), p->prefixlen); + continue; + } + + aggregate->metric_out = newinfo.metric_out; + aggregate->tag_out = newinfo.tag_out; + if (IN6_IS_ADDR_LINKLOCAL(&newinfo.nexthop_out)) + aggregate->nexthop_out = newinfo.nexthop_out; + } + + /* There is no redistribute routemap for the aggregated RTE */ + + /* Changed route only output. */ + /* XXX, vincent, in order to increase time convergence, + * it should be announced if a child has changed. + */ + if (route_type == ripng_changed_route) + continue; + + /* Apply offset-list */ + if (aggregate->metric_out != RIPNG_METRIC_INFINITY) + ripng_offset_list_apply_out (p, ifp, &aggregate->metric_out); + + if (aggregate->metric_out > RIPNG_METRIC_INFINITY) + aggregate->metric_out = RIPNG_METRIC_INFINITY; + + /* Add RTE to the list */ + ripng_rte_add(ripng_rte_list, p, NULL, aggregate); + } + + } + + /* Flush the list */ + ripng_rte_send(ripng_rte_list, ifp, to); + ripng_rte_free(ripng_rte_list); +} + +/* Create new RIPng instance and set it to global variable. */ +static int +ripng_create (void) +{ + /* ripng should be NULL. */ + assert (ripng == NULL); + + /* Allocaste RIPng instance. */ + ripng = XCALLOC (MTYPE_RIPNG, sizeof (struct ripng)); + + /* Default version and timer values. */ + ripng->version = RIPNG_V1; + ripng->update_time = RIPNG_UPDATE_TIMER_DEFAULT; + ripng->timeout_time = RIPNG_TIMEOUT_TIMER_DEFAULT; + ripng->garbage_time = RIPNG_GARBAGE_TIMER_DEFAULT; + ripng->default_metric = RIPNG_DEFAULT_METRIC_DEFAULT; + + /* Make buffer. */ + ripng->ibuf = stream_new (RIPNG_MAX_PACKET_SIZE * 5); + ripng->obuf = stream_new (RIPNG_MAX_PACKET_SIZE); + + /* Initialize RIPng routig table. */ + ripng->table = route_table_init (); + ripng->route = route_table_init (); + ripng->aggregate = route_table_init (); + + /* Make socket. */ + ripng->sock = ripng_make_socket (); + if (ripng->sock < 0) + return ripng->sock; + + /* Threads. */ + ripng_event (RIPNG_READ, ripng->sock); + ripng_event (RIPNG_UPDATE_EVENT, 1); + + return 0; +} + +/* Send RIPng request to the interface. */ +int +ripng_request (struct interface *ifp) +{ + struct rte *rte; + struct ripng_packet ripng_packet; + + /* In default ripd doesn't send RIP_REQUEST to the loopback interface. */ + if (if_is_loopback(ifp)) + return 0; + + /* If interface is down, don't send RIP packet. */ + if (! if_is_up (ifp)) + return 0; + + if (IS_RIPNG_DEBUG_EVENT) + zlog_debug ("RIPng send request to %s", ifp->name); + + memset (&ripng_packet, 0, sizeof (ripng_packet)); + ripng_packet.command = RIPNG_REQUEST; + ripng_packet.version = RIPNG_V1; + rte = ripng_packet.rte; + rte->metric = RIPNG_METRIC_INFINITY; + + return ripng_send_packet ((caddr_t) &ripng_packet, sizeof (ripng_packet), + NULL, ifp); +} + + +static int +ripng_update_jitter (int time) +{ + return ((random () % (time + 1)) - (time / 2)); +} + +void +ripng_event (enum ripng_event event, int sock) +{ + int jitter = 0; + + switch (event) + { + case RIPNG_READ: + if (!ripng->t_read) + ripng->t_read = thread_add_read (master, ripng_read, NULL, sock); + break; + case RIPNG_UPDATE_EVENT: + if (ripng->t_update) + { + thread_cancel (ripng->t_update); + ripng->t_update = NULL; + } + /* Update timer jitter. */ + jitter = ripng_update_jitter (ripng->update_time); + + ripng->t_update = + thread_add_timer (master, ripng_update, NULL, + sock ? 2 : ripng->update_time + jitter); + break; + case RIPNG_TRIGGERED_UPDATE: + if (ripng->t_triggered_interval) + ripng->trigger = 1; + else if (! ripng->t_triggered_update) + ripng->t_triggered_update = + thread_add_event (master, ripng_triggered_update, NULL, 0); + break; + default: + break; + } +} + + +/* Print out routes update time. */ +static void +ripng_vty_out_uptime (struct vty *vty, struct ripng_info *rinfo) +{ + time_t clock; + struct tm *tm; +#define TIME_BUF 25 + char timebuf [TIME_BUF]; + struct thread *thread; + + if ((thread = rinfo->t_timeout) != NULL) + { + clock = thread_timer_remain_second (thread); + tm = gmtime (&clock); + strftime (timebuf, TIME_BUF, "%M:%S", tm); + vty_out (vty, "%5s", timebuf); + } + else if ((thread = rinfo->t_garbage_collect) != NULL) + { + clock = thread_timer_remain_second (thread); + tm = gmtime (&clock); + strftime (timebuf, TIME_BUF, "%M:%S", tm); + vty_out (vty, "%5s", timebuf); + } +} + +static char * +ripng_route_subtype_print (struct ripng_info *rinfo) +{ + static char str[3]; + memset(str, 0, 3); + + if (rinfo->suppress) + strcat(str, "S"); + + switch (rinfo->sub_type) + { + case RIPNG_ROUTE_RTE: + strcat(str, "n"); + break; + case RIPNG_ROUTE_STATIC: + strcat(str, "s"); + break; + case RIPNG_ROUTE_DEFAULT: + strcat(str, "d"); + break; + case RIPNG_ROUTE_REDISTRIBUTE: + strcat(str, "r"); + break; + case RIPNG_ROUTE_INTERFACE: + strcat(str, "i"); + break; + default: + strcat(str, "?"); + break; + } + + return str; +} + +DEFUN (show_ipv6_ripng, + show_ipv6_ripng_cmd, + "show ipv6 ripng", + SHOW_STR + IPV6_STR + "Show RIPng routes\n") +{ + struct route_node *rp; + struct ripng_info *rinfo; + struct ripng_aggregate *aggregate; + struct prefix_ipv6 *p; + struct list *list = NULL; + struct listnode *listnode = NULL; + int len; + + if (! ripng) + return CMD_SUCCESS; + + /* Header of display. */ + vty_out (vty, "Codes: R - RIPng, C - connected, S - Static, O - OSPF, B - BGP%s" + "Sub-codes:%s" + " (n) - normal, (s) - static, (d) - default, (r) - redistribute,%s" + " (i) - interface, (a/S) - aggregated/Suppressed%s%s" + " Network Next Hop Via Metric Tag Time%s", + VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, + VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + + for (rp = route_top (ripng->table); rp; rp = route_next (rp)) + { + if ((aggregate = rp->aggregate) != NULL) + { + p = (struct prefix_ipv6 *) &rp->p; + +#ifdef DEBUG + len = vty_out (vty, "R(a) %d/%d %s/%d ", + aggregate->count, aggregate->suppress, + inet6_ntoa (p->prefix), p->prefixlen); +#else + len = vty_out (vty, "R(a) %s/%d ", + inet6_ntoa (p->prefix), p->prefixlen); +#endif /* DEBUG */ + vty_out (vty, "%s", VTY_NEWLINE); + vty_out (vty, "%*s", 18, " "); + + vty_out (vty, "%*s", 28, " "); + vty_out (vty, "self %2d %3d%s", aggregate->metric, + aggregate->tag, + VTY_NEWLINE); + } + + if ((list = rp->info) != NULL) + for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo)) + { + p = (struct prefix_ipv6 *) &rp->p; + +#ifdef DEBUG + len = vty_out (vty, "%c(%s) 0/%d %s/%d ", + zebra_route_char(rinfo->type), + ripng_route_subtype_print(rinfo), + rinfo->suppress, + inet6_ntoa (p->prefix), p->prefixlen); +#else + len = vty_out (vty, "%c(%s) %s/%d ", + zebra_route_char(rinfo->type), + ripng_route_subtype_print(rinfo), + inet6_ntoa (p->prefix), p->prefixlen); +#endif /* DEBUG */ + vty_out (vty, "%s", VTY_NEWLINE); + vty_out (vty, "%*s", 18, " "); + len = vty_out (vty, "%s", inet6_ntoa (rinfo->nexthop)); + + len = 28 - len; + if (len > 0) + len = vty_out (vty, "%*s", len, " "); + + /* from */ + if ((rinfo->type == ZEBRA_ROUTE_RIPNG) && + (rinfo->sub_type == RIPNG_ROUTE_RTE)) + { + len = vty_out (vty, "%s", ifindex2ifname(rinfo->ifindex)); + } else if (rinfo->metric == RIPNG_METRIC_INFINITY) + { + len = vty_out (vty, "kill"); + } else + len = vty_out (vty, "self"); + + len = 9 - len; + if (len > 0) + vty_out (vty, "%*s", len, " "); + + vty_out (vty, " %2d %3d ", + rinfo->metric, rinfo->tag); + + /* time */ + if ((rinfo->type == ZEBRA_ROUTE_RIPNG) && + (rinfo->sub_type == RIPNG_ROUTE_RTE)) + { + /* RTE from remote RIP routers */ + ripng_vty_out_uptime (vty, rinfo); + } else if (rinfo->metric == RIPNG_METRIC_INFINITY) + { + /* poisonous reversed routes (gc) */ + ripng_vty_out_uptime (vty, rinfo); + } + + vty_out (vty, "%s", VTY_NEWLINE); + } + } + + return CMD_SUCCESS; +} + +DEFUN (show_ipv6_ripng_status, + show_ipv6_ripng_status_cmd, + "show ipv6 ripng status", + SHOW_STR + IPV6_STR + "Show RIPng routes\n" + "IPv6 routing protocol process parameters and statistics\n") +{ + struct listnode *node; + struct interface *ifp; + + if (! ripng) + return CMD_SUCCESS; + + vty_out (vty, "Routing Protocol is \"RIPng\"%s", VTY_NEWLINE); + vty_out (vty, " Sending updates every %ld seconds with +/-50%%,", + ripng->update_time); + vty_out (vty, " next due in %lu seconds%s", + thread_timer_remain_second (ripng->t_update), + VTY_NEWLINE); + vty_out (vty, " Timeout after %ld seconds,", ripng->timeout_time); + vty_out (vty, " garbage collect after %ld seconds%s", ripng->garbage_time, + VTY_NEWLINE); + + /* Filtering status show. */ + config_show_distribute (vty); + + /* Default metric information. */ + vty_out (vty, " Default redistribution metric is %d%s", + ripng->default_metric, VTY_NEWLINE); + + /* Redistribute information. */ + vty_out (vty, " Redistributing:"); + ripng_redistribute_write (vty, 0); + vty_out (vty, "%s", VTY_NEWLINE); + + vty_out (vty, " Default version control: send version %d,", ripng->version); + vty_out (vty, " receive version %d %s", ripng->version, + VTY_NEWLINE); + + vty_out (vty, " Interface Send Recv%s", VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) + { + struct ripng_interface *ri; + + ri = ifp->info; + + if (ri->enable_network || ri->enable_interface) + { + + vty_out (vty, " %-17s%-3d %-3d%s", ifp->name, + ripng->version, + ripng->version, + VTY_NEWLINE); + } + } + + vty_out (vty, " Routing for Networks:%s", VTY_NEWLINE); + ripng_network_write (vty, 0); + + vty_out (vty, " Routing Information Sources:%s", VTY_NEWLINE); + vty_out (vty, " Gateway BadPackets BadRoutes Distance Last Update%s", VTY_NEWLINE); + ripng_peer_display (vty); + + return CMD_SUCCESS; +} + +DEFUN (router_ripng, + router_ripng_cmd, + "router ripng", + "Enable a routing process\n" + "Make RIPng instance command\n") +{ + int ret; + + vty->node = RIPNG_NODE; + + if (!ripng) + { + ret = ripng_create (); + + /* Notice to user we couldn't create RIPng. */ + if (ret < 0) + { + zlog_warn ("can't create RIPng"); + return CMD_WARNING; + } + } + + return CMD_SUCCESS; +} + +DEFUN (no_router_ripng, + no_router_ripng_cmd, + "no router ripng", + NO_STR + "Enable a routing process\n" + "Make RIPng instance command\n") +{ + if(ripng) + ripng_clean(); + return CMD_SUCCESS; +} + +DEFUN (ripng_route, + ripng_route_cmd, + "route IPV6ADDR", + "Static route setup\n" + "Set static RIPng route announcement\n") +{ + int ret; + struct prefix_ipv6 p; + struct route_node *rp; + + ret = str2prefix_ipv6 (argv[0], (struct prefix_ipv6 *)&p); + if (ret <= 0) + { + vty_out (vty, "Malformed address%s", VTY_NEWLINE); + return CMD_WARNING; + } + apply_mask_ipv6 (&p); + + rp = route_node_get (ripng->route, (struct prefix *) &p); + if (rp->info) + { + vty_out (vty, "There is already same static route.%s", VTY_NEWLINE); + route_unlock_node (rp); + return CMD_WARNING; + } + rp->info = (void *)1; + + ripng_redistribute_add (ZEBRA_ROUTE_RIPNG, RIPNG_ROUTE_STATIC, &p, 0, NULL, 0); + + return CMD_SUCCESS; +} + +DEFUN (no_ripng_route, + no_ripng_route_cmd, + "no route IPV6ADDR", + NO_STR + "Static route setup\n" + "Delete static RIPng route announcement\n") +{ + int ret; + struct prefix_ipv6 p; + struct route_node *rp; + + ret = str2prefix_ipv6 (argv[0], (struct prefix_ipv6 *)&p); + if (ret <= 0) + { + vty_out (vty, "Malformed address%s", VTY_NEWLINE); + return CMD_WARNING; + } + apply_mask_ipv6 (&p); + + rp = route_node_lookup (ripng->route, (struct prefix *) &p); + if (! rp) + { + vty_out (vty, "Can't find static route.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ripng_redistribute_delete (ZEBRA_ROUTE_RIPNG, RIPNG_ROUTE_STATIC, &p, 0); + route_unlock_node (rp); + + rp->info = NULL; + route_unlock_node (rp); + + return CMD_SUCCESS; +} + +DEFUN (ripng_aggregate_address, + ripng_aggregate_address_cmd, + "aggregate-address X:X::X:X/M", + "Set aggregate RIPng route announcement\n" + "Aggregate network\n") +{ + int ret; + struct prefix p; + struct route_node *node; + + ret = str2prefix_ipv6 (argv[0], (struct prefix_ipv6 *)&p); + if (ret <= 0) + { + vty_out (vty, "Malformed address%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Check aggregate alredy exist or not. */ + node = route_node_get (ripng->aggregate, &p); + if (node->info) + { + vty_out (vty, "There is already same aggregate route.%s", VTY_NEWLINE); + route_unlock_node (node); + return CMD_WARNING; + } + node->info = (void *)1; + + ripng_aggregate_add (&p); + + return CMD_SUCCESS; +} + +DEFUN (no_ripng_aggregate_address, + no_ripng_aggregate_address_cmd, + "no aggregate-address X:X::X:X/M", + NO_STR + "Delete aggregate RIPng route announcement\n" + "Aggregate network") +{ + int ret; + struct prefix p; + struct route_node *rn; + + ret = str2prefix_ipv6 (argv[0], (struct prefix_ipv6 *) &p); + if (ret <= 0) + { + vty_out (vty, "Malformed address%s", VTY_NEWLINE); + return CMD_WARNING; + } + + rn = route_node_lookup (ripng->aggregate, &p); + if (! rn) + { + vty_out (vty, "Can't find aggregate route.%s", VTY_NEWLINE); + return CMD_WARNING; + } + route_unlock_node (rn); + rn->info = NULL; + route_unlock_node (rn); + + ripng_aggregate_delete (&p); + + return CMD_SUCCESS; +} + +DEFUN (ripng_default_metric, + ripng_default_metric_cmd, + "default-metric <1-16>", + "Set a metric of redistribute routes\n" + "Default metric\n") +{ + if (ripng) + { + ripng->default_metric = atoi (argv[0]); + } + return CMD_SUCCESS; +} + +DEFUN (no_ripng_default_metric, + no_ripng_default_metric_cmd, + "no default-metric", + NO_STR + "Set a metric of redistribute routes\n" + "Default metric\n") +{ + if (ripng) + { + ripng->default_metric = RIPNG_DEFAULT_METRIC_DEFAULT; + } + return CMD_SUCCESS; +} + +ALIAS (no_ripng_default_metric, + no_ripng_default_metric_val_cmd, + "no default-metric <1-16>", + NO_STR + "Set a metric of redistribute routes\n" + "Default metric\n") + +#if 0 +/* RIPng update timer setup. */ +DEFUN (ripng_update_timer, + ripng_update_timer_cmd, + "update-timer SECOND", + "Set RIPng update timer in seconds\n" + "Seconds\n") +{ + unsigned long update; + char *endptr = NULL; + + update = strtoul (argv[0], &endptr, 10); + if (update == ULONG_MAX || *endptr != '\0') + { + vty_out (vty, "update timer value error%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ripng->update_time = update; + + ripng_event (RIPNG_UPDATE_EVENT, 0); + return CMD_SUCCESS; +} + +DEFUN (no_ripng_update_timer, + no_ripng_update_timer_cmd, + "no update-timer SECOND", + NO_STR + "Unset RIPng update timer in seconds\n" + "Seconds\n") +{ + ripng->update_time = RIPNG_UPDATE_TIMER_DEFAULT; + ripng_event (RIPNG_UPDATE_EVENT, 0); + return CMD_SUCCESS; +} + +/* RIPng timeout timer setup. */ +DEFUN (ripng_timeout_timer, + ripng_timeout_timer_cmd, + "timeout-timer SECOND", + "Set RIPng timeout timer in seconds\n" + "Seconds\n") +{ + unsigned long timeout; + char *endptr = NULL; + + timeout = strtoul (argv[0], &endptr, 10); + if (timeout == ULONG_MAX || *endptr != '\0') + { + vty_out (vty, "timeout timer value error%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ripng->timeout_time = timeout; + + return CMD_SUCCESS; +} + +DEFUN (no_ripng_timeout_timer, + no_ripng_timeout_timer_cmd, + "no timeout-timer SECOND", + NO_STR + "Unset RIPng timeout timer in seconds\n" + "Seconds\n") +{ + ripng->timeout_time = RIPNG_TIMEOUT_TIMER_DEFAULT; + return CMD_SUCCESS; +} + +/* RIPng garbage timer setup. */ +DEFUN (ripng_garbage_timer, + ripng_garbage_timer_cmd, + "garbage-timer SECOND", + "Set RIPng garbage timer in seconds\n" + "Seconds\n") +{ + unsigned long garbage; + char *endptr = NULL; + + garbage = strtoul (argv[0], &endptr, 10); + if (garbage == ULONG_MAX || *endptr != '\0') + { + vty_out (vty, "garbage timer value error%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ripng->garbage_time = garbage; + + return CMD_SUCCESS; +} + +DEFUN (no_ripng_garbage_timer, + no_ripng_garbage_timer_cmd, + "no garbage-timer SECOND", + NO_STR + "Unset RIPng garbage timer in seconds\n" + "Seconds\n") +{ + ripng->garbage_time = RIPNG_GARBAGE_TIMER_DEFAULT; + return CMD_SUCCESS; +} +#endif /* 0 */ + +DEFUN (ripng_timers, + ripng_timers_cmd, + "timers basic <0-65535> <0-65535> <0-65535>", + "RIPng timers setup\n" + "Basic timer\n" + "Routing table update timer value in second. Default is 30.\n" + "Routing information timeout timer. Default is 180.\n" + "Garbage collection timer. Default is 120.\n") +{ + unsigned long update; + unsigned long timeout; + unsigned long garbage; + + VTY_GET_INTEGER_RANGE("update timer", update, argv[0], 0, 65535); + VTY_GET_INTEGER_RANGE("timeout timer", timeout, argv[1], 0, 65535); + VTY_GET_INTEGER_RANGE("garbage timer", garbage, argv[2], 0, 65535); + + /* Set each timer value. */ + ripng->update_time = update; + ripng->timeout_time = timeout; + ripng->garbage_time = garbage; + + /* Reset update timer thread. */ + ripng_event (RIPNG_UPDATE_EVENT, 0); + + return CMD_SUCCESS; +} + +DEFUN (no_ripng_timers, + no_ripng_timers_cmd, + "no timers basic", + NO_STR + "RIPng timers setup\n" + "Basic timer\n") +{ + /* Set each timer value to the default. */ + ripng->update_time = RIPNG_UPDATE_TIMER_DEFAULT; + ripng->timeout_time = RIPNG_TIMEOUT_TIMER_DEFAULT; + ripng->garbage_time = RIPNG_GARBAGE_TIMER_DEFAULT; + + /* Reset update timer thread. */ + ripng_event (RIPNG_UPDATE_EVENT, 0); + + return CMD_SUCCESS; +} + +ALIAS (no_ripng_timers, + no_ripng_timers_val_cmd, + "no timers basic <0-65535> <0-65535> <0-65535>", + NO_STR + "RIPng timers setup\n" + "Basic timer\n" + "Routing table update timer value in second. Default is 30.\n" + "Routing information timeout timer. Default is 180.\n" + "Garbage collection timer. Default is 120.\n") + +DEFUN (show_ipv6_protocols, show_ipv6_protocols_cmd, + "show ipv6 protocols", + SHOW_STR + IPV6_STR + "Routing protocol information") +{ + if (! ripng) + return CMD_SUCCESS; + + vty_out (vty, "Routing Protocol is \"ripng\"%s", VTY_NEWLINE); + + vty_out (vty, "Sending updates every %ld seconds, next due in %d seconds%s", + ripng->update_time, 0, + VTY_NEWLINE); + + vty_out (vty, "Timerout after %ld seconds, garbage correct %ld%s", + ripng->timeout_time, + ripng->garbage_time, + VTY_NEWLINE); + + vty_out (vty, "Outgoing update filter list for all interfaces is not set"); + vty_out (vty, "Incoming update filter list for all interfaces is not set"); + + return CMD_SUCCESS; +} + +/* Please be carefull to use this command. */ +DEFUN (ripng_default_information_originate, + ripng_default_information_originate_cmd, + "default-information originate", + "Default route information\n" + "Distribute default route\n") +{ + struct prefix_ipv6 p; + + if (! ripng ->default_information) { + ripng->default_information = 1; + + str2prefix_ipv6 ("::/0", &p); + ripng_redistribute_add (ZEBRA_ROUTE_RIPNG, RIPNG_ROUTE_DEFAULT, &p, 0, NULL, 0); + } + + return CMD_SUCCESS; +} + +DEFUN (no_ripng_default_information_originate, + no_ripng_default_information_originate_cmd, + "no default-information originate", + NO_STR + "Default route information\n" + "Distribute default route\n") +{ + struct prefix_ipv6 p; + + if (ripng->default_information) { + ripng->default_information = 0; + + str2prefix_ipv6 ("::/0", &p); + ripng_redistribute_delete (ZEBRA_ROUTE_RIPNG, RIPNG_ROUTE_DEFAULT, &p, 0); + } + + 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) +{ + int ripng_network_write (struct vty *, int); + void ripng_redistribute_write (struct vty *, int); + int write = 0; + struct route_node *rp; + + if (ripng) + { + + /* RIPng router. */ + vty_out (vty, "router ripng%s", VTY_NEWLINE); + + if (ripng->default_information) + vty_out (vty, " default-information originate%s", VTY_NEWLINE); + + ripng_network_write (vty, 1); + + /* RIPng default metric configuration */ + if (ripng->default_metric != RIPNG_DEFAULT_METRIC_DEFAULT) + vty_out (vty, " default-metric %d%s", + ripng->default_metric, VTY_NEWLINE); + + ripng_redistribute_write (vty, 1); + + /* RIP offset-list configuration. */ + config_write_ripng_offset_list (vty); + + /* RIPng aggregate routes. */ + for (rp = route_top (ripng->aggregate); rp; rp = route_next (rp)) + if (rp->info != NULL) + vty_out (vty, " aggregate-address %s/%d%s", + inet6_ntoa (rp->p.u.prefix6), + rp->p.prefixlen, + + 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) + vty_out (vty, " route %s/%d%s", inet6_ntoa (rp->p.u.prefix6), + rp->p.prefixlen, + VTY_NEWLINE); + + /* RIPng timers configuration. */ + if (ripng->update_time != RIPNG_UPDATE_TIMER_DEFAULT || + ripng->timeout_time != RIPNG_TIMEOUT_TIMER_DEFAULT || + ripng->garbage_time != RIPNG_GARBAGE_TIMER_DEFAULT) + { + vty_out (vty, " timers basic %ld %ld %ld%s", + ripng->update_time, + ripng->timeout_time, + ripng->garbage_time, + VTY_NEWLINE); + } +#if 0 + if (ripng->update_time != RIPNG_UPDATE_TIMER_DEFAULT) + vty_out (vty, " update-timer %d%s", ripng->update_time, + VTY_NEWLINE); + if (ripng->timeout_time != RIPNG_TIMEOUT_TIMER_DEFAULT) + vty_out (vty, " timeout-timer %d%s", ripng->timeout_time, + VTY_NEWLINE); + if (ripng->garbage_time != RIPNG_GARBAGE_TIMER_DEFAULT) + vty_out (vty, " garbage-timer %d%s", ripng->garbage_time, + VTY_NEWLINE); +#endif /* 0 */ + + write += config_write_distribute (vty); + + write += config_write_if_rmap (vty); + + write++; + } + return write; +} + +/* RIPng node structure. */ +static struct cmd_node cmd_ripng_node = +{ + RIPNG_NODE, + "%s(config-router)# ", + 1, +}; + +static void +ripng_distribute_update (struct distribute *dist) +{ + struct interface *ifp; + struct ripng_interface *ri; + struct access_list *alist; + struct prefix_list *plist; + + if (! dist->ifname) + return; + + ifp = if_lookup_by_name (dist->ifname); + if (ifp == NULL) + return; + + ri = ifp->info; + + if (dist->list[DISTRIBUTE_V6_IN]) + { + alist = access_list_lookup (AFI_IP6, dist->list[DISTRIBUTE_V6_IN]); + if (alist) + ri->list[RIPNG_FILTER_IN] = alist; + else + ri->list[RIPNG_FILTER_IN] = NULL; + } + else + ri->list[RIPNG_FILTER_IN] = NULL; + + if (dist->list[DISTRIBUTE_V6_OUT]) + { + alist = access_list_lookup (AFI_IP6, dist->list[DISTRIBUTE_V6_OUT]); + if (alist) + ri->list[RIPNG_FILTER_OUT] = alist; + else + ri->list[RIPNG_FILTER_OUT] = NULL; + } + else + ri->list[RIPNG_FILTER_OUT] = NULL; + + if (dist->prefix[DISTRIBUTE_V6_IN]) + { + plist = prefix_list_lookup (AFI_IP6, dist->prefix[DISTRIBUTE_V6_IN]); + if (plist) + ri->prefix[RIPNG_FILTER_IN] = plist; + else + ri->prefix[RIPNG_FILTER_IN] = NULL; + } + else + ri->prefix[RIPNG_FILTER_IN] = NULL; + + if (dist->prefix[DISTRIBUTE_V6_OUT]) + { + plist = prefix_list_lookup (AFI_IP6, dist->prefix[DISTRIBUTE_V6_OUT]); + if (plist) + ri->prefix[RIPNG_FILTER_OUT] = plist; + else + ri->prefix[RIPNG_FILTER_OUT] = NULL; + } + else + ri->prefix[RIPNG_FILTER_OUT] = NULL; +} + +void +ripng_distribute_update_interface (struct interface *ifp) +{ + struct distribute *dist; + + dist = distribute_lookup (ifp->name); + if (dist) + ripng_distribute_update (dist); +} + +/* Update all interface's distribute list. */ +static void +ripng_distribute_update_all (struct prefix_list *notused) +{ + struct interface *ifp; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) + ripng_distribute_update_interface (ifp); +} + +static void +ripng_distribute_update_all_wrapper (struct access_list *notused) +{ + ripng_distribute_update_all(NULL); +} + +/* delete all the added ripng routes. */ +void +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 ((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 */ + RIPNG_TIMER_OFF (ripng->t_update); + RIPNG_TIMER_OFF (ripng->t_triggered_update); + RIPNG_TIMER_OFF (ripng->t_triggered_interval); + + /* Cancel the read thread */ + if (ripng->t_read) { + thread_cancel (ripng->t_read); + ripng->t_read = NULL; + } + + /* Close the RIPng socket */ + if (ripng->sock >= 0) { + close(ripng->sock); + ripng->sock = -1; + } + + /* Static RIPng route configuration. */ + for (rp = route_top (ripng->route); rp; rp = route_next (rp)) + if (rp->info) { + rp->info = NULL; + route_unlock_node (rp); + } + + /* RIPng aggregated prefixes */ + for (rp = route_top (ripng->aggregate); rp; rp = route_next (rp)) + if (rp->info) { + rp->info = NULL; + route_unlock_node (rp); + } + + for (i = 0; i < ZEBRA_ROUTE_MAX; i++) + if (ripng->route_map[i].name) + free (ripng->route_map[i].name); + + XFREE (MTYPE_ROUTE_TABLE, ripng->table); + XFREE (MTYPE_ROUTE_TABLE, ripng->route); + XFREE (MTYPE_ROUTE_TABLE, ripng->aggregate); + + XFREE (MTYPE_RIPNG, ripng); + ripng = NULL; + } /* if (ripng) */ + + ripng_clean_network(); + ripng_passive_interface_clean (); + ripng_offset_clean (); + ripng_interface_clean (); + ripng_redistribute_clean (); +} + +/* Reset all values to the default settings. */ +void +ripng_reset () +{ + /* Call ripd related reset functions. */ + ripng_debug_reset (); + ripng_route_map_reset (); + + /* Call library reset functions. */ + vty_reset (); + access_list_reset (); + prefix_list_reset (); + + distribute_list_reset (); + + ripng_interface_reset (); + + ripng_zclient_reset (); +} + +static void +ripng_if_rmap_update (struct if_rmap *if_rmap) +{ + struct interface *ifp; + struct ripng_interface *ri; + struct route_map *rmap; + + ifp = if_lookup_by_name (if_rmap->ifname); + if (ifp == NULL) + return; + + ri = ifp->info; + + if (if_rmap->routemap[IF_RMAP_IN]) + { + rmap = route_map_lookup_by_name (if_rmap->routemap[IF_RMAP_IN]); + if (rmap) + ri->routemap[IF_RMAP_IN] = rmap; + else + ri->routemap[IF_RMAP_IN] = NULL; + } + else + ri->routemap[RIPNG_FILTER_IN] = NULL; + + if (if_rmap->routemap[IF_RMAP_OUT]) + { + rmap = route_map_lookup_by_name (if_rmap->routemap[IF_RMAP_OUT]); + if (rmap) + ri->routemap[IF_RMAP_OUT] = rmap; + else + ri->routemap[IF_RMAP_OUT] = NULL; + } + else + ri->routemap[RIPNG_FILTER_OUT] = NULL; +} + +void +ripng_if_rmap_update_interface (struct interface *ifp) +{ + struct if_rmap *if_rmap; + + if_rmap = if_rmap_lookup (ifp->name); + if (if_rmap) + ripng_if_rmap_update (if_rmap); +} + +static void +ripng_routemap_update_redistribute (void) +{ + int i; + + if (ripng) + { + for (i = 0; i < ZEBRA_ROUTE_MAX; i++) + { + if (ripng->route_map[i].name) + ripng->route_map[i].map = + route_map_lookup_by_name (ripng->route_map[i].name); + } + } +} + +static void +ripng_routemap_update (const char *unused) +{ + struct interface *ifp; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) + ripng_if_rmap_update_interface (ifp); + + ripng_routemap_update_redistribute (); +} + +/* Initialize ripng structure and set commands. */ +void +ripng_init () +{ + /* Randomize. */ + srandom (time (NULL)); + + /* Install RIPNG_NODE. */ + install_node (&cmd_ripng_node, ripng_config_write); + + /* Install ripng commands. */ + install_element (VIEW_NODE, &show_ipv6_ripng_cmd); + install_element (VIEW_NODE, &show_ipv6_ripng_status_cmd); + + install_element (CONFIG_NODE, &router_ripng_cmd); + install_element (CONFIG_NODE, &no_router_ripng_cmd); + + install_default (RIPNG_NODE); + install_element (RIPNG_NODE, &ripng_route_cmd); + install_element (RIPNG_NODE, &no_ripng_route_cmd); + install_element (RIPNG_NODE, &ripng_aggregate_address_cmd); + install_element (RIPNG_NODE, &no_ripng_aggregate_address_cmd); + + install_element (RIPNG_NODE, &ripng_default_metric_cmd); + install_element (RIPNG_NODE, &no_ripng_default_metric_cmd); + install_element (RIPNG_NODE, &no_ripng_default_metric_val_cmd); + + install_element (RIPNG_NODE, &ripng_timers_cmd); + install_element (RIPNG_NODE, &no_ripng_timers_cmd); + install_element (RIPNG_NODE, &no_ripng_timers_val_cmd); +#if 0 + install_element (RIPNG_NODE, &ripng_update_timer_cmd); + install_element (RIPNG_NODE, &no_ripng_update_timer_cmd); + install_element (RIPNG_NODE, &ripng_timeout_timer_cmd); + install_element (RIPNG_NODE, &no_ripng_timeout_timer_cmd); + install_element (RIPNG_NODE, &ripng_garbage_timer_cmd); + install_element (RIPNG_NODE, &no_ripng_garbage_timer_cmd); +#endif /* 0 */ + + 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 (); + + /* Access list install. */ + access_list_init (); + access_list_add_hook (ripng_distribute_update_all_wrapper); + access_list_delete_hook (ripng_distribute_update_all_wrapper); + + /* Prefix list initialize.*/ + prefix_list_init (); + prefix_list_add_hook (ripng_distribute_update_all); + prefix_list_delete_hook (ripng_distribute_update_all); + + /* Distribute list install. */ + distribute_list_init (RIPNG_NODE); + distribute_list_add_hook (ripng_distribute_update); + distribute_list_delete_hook (ripng_distribute_update); + + /* Route-map for interface. */ + ripng_route_map_init (); + ripng_offset_init (); + + route_map_add_hook (ripng_routemap_update); + route_map_delete_hook (ripng_routemap_update); + + if_rmap_init (RIPNG_NODE); + if_rmap_hook_add (ripng_if_rmap_update); + if_rmap_hook_delete (ripng_if_rmap_update); +} diff --git a/ripngd/ripngd.conf.sample b/ripngd/ripngd.conf.sample new file mode 100644 index 0000000..ad673e5 --- /dev/null +++ b/ripngd/ripngd.conf.sample @@ -0,0 +1,22 @@ +! -*- rip -*- +! +! RIPngd sample configuration file +! +! $Id: ripngd.conf.sample,v 1.1 2002/12/13 20:15:30 paul Exp $ +! +hostname ripngd +password zebra +! +! debug ripng events +! debug ripng packet +! +! +router ripng +! network sit1 +! route 3ffe:506::0/32 +! distribute-list local-only out sit1 +! +!ipv6 access-list local-only permit 3ffe:506::0/32 +!ipv6 access-list local-only deny any +! +log stdout diff --git a/ripngd/ripngd.h b/ripngd/ripngd.h new file mode 100644 index 0000000..a0c6a4e --- /dev/null +++ b/ripngd/ripngd.h @@ -0,0 +1,427 @@ +/* + * RIPng related value and structure. + * Copyright (C) 1998 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_RIPNG_RIPNGD_H +#define _ZEBRA_RIPNG_RIPNGD_H + +#include +#include + +/* RIPng version and port number. */ +#define RIPNG_V1 1 +#define RIPNG_PORT_DEFAULT 521 +#define RIPNG_VTY_PORT 2603 +#define RIPNG_MAX_PACKET_SIZE 1500 +#define RIPNG_PRIORITY_DEFAULT 0 + +/* RIPng commands. */ +#define RIPNG_REQUEST 1 +#define RIPNG_RESPONSE 2 + +/* RIPng metric and multicast group address. */ +#define RIPNG_METRIC_INFINITY 16 +#define RIPNG_METRIC_NEXTHOP 0xff +#define RIPNG_GROUP "ff02::9" + +/* RIPng timers. */ +#define RIPNG_UPDATE_TIMER_DEFAULT 30 +#define RIPNG_TIMEOUT_TIMER_DEFAULT 180 +#define RIPNG_GARBAGE_TIMER_DEFAULT 120 + +/* RIPng peer timeout value. */ +#define RIPNG_PEER_TIMER_DEFAULT 180 + +/* Default config file name. */ +#define RIPNG_DEFAULT_CONFIG "ripngd.conf" + +/* RIPng route types. */ +#define RIPNG_ROUTE_RTE 0 +#define RIPNG_ROUTE_STATIC 1 +#define RIPNG_ROUTE_DEFAULT 2 +#define RIPNG_ROUTE_REDISTRIBUTE 3 +#define RIPNG_ROUTE_INTERFACE 4 +#define RIPNG_ROUTE_AGGREGATE 5 + +/* Interface send/receive configuration. */ +#define RIPNG_SEND_UNSPEC 0 +#define RIPNG_SEND_OFF 1 +#define RIPNG_RECEIVE_UNSPEC 0 +#define RIPNG_RECEIVE_OFF 1 + +/* RIP default route's accept/announce methods. */ +#define RIPNG_DEFAULT_ADVERTISE_UNSPEC 0 +#define RIPNG_DEFAULT_ADVERTISE_NONE 1 +#define RIPNG_DEFAULT_ADVERTISE 2 + +#define RIPNG_DEFAULT_ACCEPT_UNSPEC 0 +#define RIPNG_DEFAULT_ACCEPT_NONE 1 +#define RIPNG_DEFAULT_ACCEPT 2 + +/* Default value for "default-metric" command. */ +#define RIPNG_DEFAULT_METRIC_DEFAULT 1 + +/* For max RTE calculation. */ +#ifndef IPV6_HDRLEN +#define IPV6_HDRLEN 40 +#endif /* IPV6_HDRLEN */ + +#ifndef IFMINMTU +#define IFMINMTU 576 +#endif /* IFMINMTU */ + +/* RIPng structure. */ +struct ripng +{ + /* RIPng socket. */ + int sock; + + /* RIPng Parameters.*/ + u_char command; + u_char version; + unsigned long update_time; + unsigned long timeout_time; + unsigned long garbage_time; + int max_mtu; + int default_metric; + int default_information; + + /* Input/output buffer of RIPng. */ + struct stream *ibuf; + struct stream *obuf; + + /* RIPng routing information base. */ + struct route_table *table; + + /* RIPng only static route information. */ + struct route_table *route; + + /* RIPng aggregate route information. */ + struct route_table *aggregate; + + /* RIPng threads. */ + struct thread *t_read; + struct thread *t_write; + struct thread *t_update; + struct thread *t_garbage; + struct thread *t_zebra; + + /* Triggered update hack. */ + int trigger; + struct thread *t_triggered_update; + struct thread *t_triggered_interval; + + /* RIPng ECMP flag */ + unsigned int ecmp; + + /* For redistribute route map. */ + struct + { + char *name; + struct route_map *map; + int metric_config; + u_int32_t metric; + } route_map[ZEBRA_ROUTE_MAX]; +}; + +/* Routing table entry. */ +struct rte +{ + struct in6_addr addr; /* RIPng destination prefix */ + 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 + * ripng_nexthop within ripngd.c */ +}; + +/* RIPNG send packet. */ +struct ripng_packet +{ + u_char command; + u_char version; + u_int16_t zero; + struct rte rte[1]; +}; + +/* Each route's information. */ +struct ripng_info +{ + /* This route's type. Static, ripng or aggregate. */ + u_char type; + + /* Sub type for static route. */ + u_char sub_type; + + /* RIPng specific information */ + struct in6_addr nexthop; + struct in6_addr from; + + /* Which interface does this route come from. */ + ifindex_t ifindex; + + /* Metric of this route. */ + u_char metric; + + /* Tag field of RIPng packet.*/ + u_int16_t tag; + + /* For aggregation. */ + unsigned int suppress; + + /* Flags of RIPng route. */ +#define RIPNG_RTF_FIB 1 +#define RIPNG_RTF_CHANGED 2 + u_char flags; + + /* Garbage collect timer. */ + struct thread *t_timeout; + struct thread *t_garbage_collect; + + /* Route-map features - this variables can be changed. */ + struct in6_addr nexthop_out; + u_char metric_set; + u_char metric_out; + u_int16_t tag_out; + + struct route_node *rp; +}; + +#ifdef notyet +#if 0 +/* RIPng tag structure. */ +struct ripng_tag +{ + /* Tag value. */ + u_int16_t tag; + + /* Port. */ + u_int16_t port; + + /* Multicast group. */ + struct in6_addr maddr; + + /* Table number. */ + int table; + + /* Distance. */ + int distance; + + /* Split horizon. */ + u_char split_horizon; + + /* Poison reverse. */ + u_char poison_reverse; +}; +#endif /* 0 */ +#endif /* not yet */ + +typedef enum { + RIPNG_NO_SPLIT_HORIZON = 0, + RIPNG_SPLIT_HORIZON, + RIPNG_SPLIT_HORIZON_POISONED_REVERSE +} split_horizon_policy_t; + +/* RIPng specific interface configuration. */ +struct ripng_interface +{ + /* RIPng is enabled on this interface. */ + int enable_network; + int enable_interface; + + /* RIPng is running on this interface. */ + int running; + + /* Split horizon flag. */ + split_horizon_policy_t split_horizon; + split_horizon_policy_t split_horizon_default; + + /* For filter type slot. */ +#define RIPNG_FILTER_IN 0 +#define RIPNG_FILTER_OUT 1 +#define RIPNG_FILTER_MAX 2 + + /* Access-list. */ + struct access_list *list[RIPNG_FILTER_MAX]; + + /* Prefix-list. */ + struct prefix_list *prefix[RIPNG_FILTER_MAX]; + + /* Route-map. */ + struct route_map *routemap[RIPNG_FILTER_MAX]; + +#ifdef notyet +#if 0 + /* RIPng tag configuration. */ + struct ripng_tag *rtag; +#endif /* 0 */ +#endif /* notyet */ + + /* Default information originate. */ + u_char default_originate; + + /* Default information only. */ + u_char default_only; + + /* Wake up thread. */ + struct thread *t_wakeup; + + /* Passive interface. */ + int passive; +}; + +/* RIPng peer information. */ +struct ripng_peer +{ + /* Peer address. */ + struct in6_addr addr; + + /* Peer RIPng tag value. */ + int domain; + + /* Last update time. */ + time_t uptime; + + /* Peer RIP version. */ + u_char version; + + /* Statistics. */ + int recv_badpackets; + int recv_badroutes; + + /* Timeout thread. */ + struct thread *t_timeout; +}; + +/* All RIPng events. */ +enum ripng_event +{ + RIPNG_READ, + RIPNG_ZEBRA, + RIPNG_REQUEST_EVENT, + RIPNG_UPDATE_EVENT, + RIPNG_TRIGGERED_UPDATE, +}; + +/* RIPng timer on/off macro. */ +#define RIPNG_TIMER_ON(T,F,V) \ +do { \ + if (!(T)) \ + (T) = thread_add_timer (master, (F), rinfo, (V)); \ +} while (0) + +#define RIPNG_TIMER_OFF(T) \ +do { \ + if (T) \ + { \ + thread_cancel(T); \ + (T) = NULL; \ + } \ +} while (0) + +/* Extern variables. */ +extern struct ripng *ripng; + +extern struct thread_master *master; + +/* Prototypes. */ +extern void ripng_init (void); +extern void ripng_reset (void); +extern void ripng_clean (void); +extern void ripng_clean_network (void); +extern void ripng_interface_clean (void); +extern void ripng_interface_reset (void); +extern void ripng_passive_interface_clean (void); +extern void ripng_if_init (void); +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 (struct thread_master *); +extern void ripng_zclient_start (void); +extern void ripng_zclient_reset (void); +extern void ripng_offset_init (void); + +extern int config_write_ripng_offset_list (struct vty *); + +extern void ripng_peer_init (void); +extern void ripng_peer_update (struct sockaddr_in6 *, u_char); +extern void ripng_peer_bad_route (struct sockaddr_in6 *); +extern void ripng_peer_bad_packet (struct sockaddr_in6 *); +extern void ripng_peer_display (struct vty *); +extern struct ripng_peer *ripng_peer_lookup (struct in6_addr *); +extern struct ripng_peer *ripng_peer_lookup_next (struct in6_addr *); + +extern int ripng_offset_list_apply_in (struct prefix_ipv6 *, + struct interface *, u_char *); +extern int ripng_offset_list_apply_out (struct prefix_ipv6 *, + struct interface *, u_char *); +extern void ripng_offset_clean (void); + +extern struct ripng_info * ripng_info_new (void); +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 *, + ifindex_t, struct in6_addr *, route_tag_t); +extern void ripng_redistribute_delete (int, int, struct prefix_ipv6 *, + 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 route_node *); +extern void ripng_zebra_ipv6_delete (struct route_node *); + +extern void ripng_redistribute_clean (void); +extern int ripng_redistribute_check (int); +extern void ripng_redistribute_write (struct vty *, int); + +extern int ripng_write_rte (int num, struct stream *s, struct prefix_ipv6 *p, + struct in6_addr *nexthop, + u_int16_t tag, u_char metric); +extern int ripng_send_packet (caddr_t buf, int bufsize, + struct sockaddr_in6 *to, struct interface *ifp); + +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, + 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/.gitignore b/solaris/.gitignore new file mode 100644 index 0000000..63afe3f --- /dev/null +++ b/solaris/.gitignore @@ -0,0 +1,22 @@ +Makefile +Makefile.in +?.manifest +*.xml +pkginfo.*.full +pkginfo.tmpl +prototype.daemons +prototype.dev +prototype.doc +prototype.libs +prototype.smf +depend.daemons +depend.dev +depend.doc +depend.libs +depend.smf +quagga.init +*.pkg +*.pkg.gz +*~ +*.loT +*.a \ No newline at end of file diff --git a/solaris/Makefile.am b/solaris/Makefile.am new file mode 100644 index 0000000..dcee240 --- /dev/null +++ b/solaris/Makefile.am @@ -0,0 +1,124 @@ +# Solaris packages automake file + +# XXX This file uses GNU make extensions. + +.PHONY: packages + +# the names of the various subpackages, and some convenient +# derived variables. +pkg_names = daemons dev doc libs smf +pkg_quagga_daemons = zebra bgpd ospfd ospf6d ripd ripngd +pkg_name_rev = @PACKAGE_VERSION@-@CONFDATE@-@target_os@-@target_cpu@ +pkg_depends = $(pkg_names:%=depend.%) +pkg_packages = $(pkg_names:%=@PACKAGE_TARNAME@-%-$(pkg_name_rev).pkg) +pkg_pkginfos = $(pkg_names:%=pkginfo.%.full) +pkg_prototypes = $(pkg_names:%=prototype.%) +pkg_manifests = quagga.xml + +# pkgmk variable substitutions wont grok ${variable} in prototype +# file, so we cant let autoconf generate the file sadly +# wish automake would just provide a template for this +edit = $(SED) \ + -e 's,@prefix\@,$(prefix),g' \ + -e 's,@exec_prefix,$(exec_prefix),g' \ + -e 's,@bindir\@,$(bindir),g' \ + -e 's,@sbindir\@,$(sbindir),g' \ + -e 's,@libexecdir\@,$(libexecdir),g' \ + -e 's,@datadir\@,$(datadir),g' \ + -e 's,@sysconfdir\@,$(sysconfdir),g' \ + -e 's,@sharedstatedir\@,$(sharedstatedir),g' \ + -e 's,@localstatedir\@,$(localstatedir),g' \ + -e 's,@libdir\@,$(libdir),g' \ + -e 's,@includedir\@,$(includedir),g' \ + -e 's,@infodir\@,$(infodir),g' \ + -e 's,@mandir\@,$(mandir),g' \ + -e 's,@enable_user\@,$(enable_user),g' \ + -e 's,@enable_group\@,$(enable_group),g' \ + -e 's,@enable_vty_group\@,$(enable_vty_group),g' \ + -e 's,@quagga_statedir\@,$(quagga_statedir),g' \ + -e 's,[@]PACKAGE_NAME[@],@PACKAGE_NAME@,g' \ + -e 's,[@]PACKAGE_TARNAME[@],@PACKAGE_TARNAME@,g' \ + -e 's,[@]PACKAGE_VERSION[@],@PACKAGE_VERSION@,g' \ + -e 's,[@]PACKAGE_BUGREPORT[@],@PACKAGE_BUGREPORT@,g' \ + -e 's,[@]CONFDATE[@],@CONFDATE@,g' \ + -e 's,[@]target_cpu[@],$(target_cpu),g' \ + -e 's,[@]target_host[@],$(target_host),g' \ + -e 's,[@]target_os[@],$(target_os),g' + +# common options for pkgmk +pkg_make_vars = exec_prefix=@exec_prefix@ prefix=@prefix@ \ + builddir=@builddir@ srcdir=@srcdir@ \ + top_builddir=@top_builddir@ top_srcdir=@top_srcdir@ \ + abs_builddir=@abs_builddir@ abs_srcdir=@abs_srcdir@ \ + abs_top_builddir=@abs_top_builddir@ abs_top_srcdir=@abs_top_srcdir@ + +# pkgmk: write the package to spool in build dir, to avoid root dependencies +pkg_make = pkgmk -o -d @abs_builddir@ \ + -f $< DESTDIR="$(DESTDIR)/" $(pkg_make_vars) + +# pkgtrans: write a pkg file stream, shame we cant pipe directly to it from +# pkgmk.. +pkg_trans = pkgtrans -s @abs_builddir@ "@abs_builddir@/$@" + +# pkgmk can only cope with a single pkginfo, cant 'stack' various +# pkginfo template files and a package specific pkginfo file in the prototype +# Create the package specific template here, and create the full pkginfo +# by cating this and the common pkginfo.tmpl together. +pkginfo.tmpl: $(srcdir)/pkginfo.tmpl.in Makefile + rm -f $@ + $(edit) $< > $@ + +pkginfo.%.tmpl: $(srcdir)/pkginfo.%.tmpl.in Makefile + rm -f $@ + $(edit) $< > $@ + +pkginfo.%.full: pkginfo.%.tmpl pkginfo.tmpl Makefile + cat pkginfo.tmpl pkginfo.$*.tmpl > $@ + +# use 'edit' above to transform prototype.in to pkgmk acceptable prototype +prototype.%: $(srcdir)/prototype.%.in Makefile + rm -f $@ + $(edit) $< > $@ + +# use edit to construct the SMF manifest files +%.xml: $(srcdir)/%.xml.in Makefile + rm -f $@ + $(edit) $< > $@ +# use edit to construct the depend files +depend.%: $(srcdir)/depend.%.in Makefile + rm -f $@ + $(edit) $< > $@ + +# method file (bit like init script) +quagga.init: $(srcdir)/quagga.init.in Makefile + rm -f $@ + $(edit) $< > $@ + +# construct the pkg +@PACKAGE_TARNAME@-%-$(pkg_name_rev).pkg: prototype.% \ + depend.% quagga.init pkginfo.%.full + ($(pkg_make) && \ + $(pkg_trans) "QUAGGA$*") + +%.pkg.gz : %.pkg + (gzip -c $< > $@) + +# pkginfo.package and prototype.package are all built sources +#BUILT_SOURCES = pkginfo.daemons pkginfo.dev pkginfo.doc pkginfo.libs \ +# prototype.daemons prototype.dev prototype.doc prototype.libs +BUILT_SOURCES = $(pkg_pkginfos) pkginfo.tmpl $(pkg_prototypes) \ + $(pkg_manifests) $(pkg_depends) quagga.init + +CLEANFILES = $(BUILT_SOURCES) $(pkg_packages) + +EXTRA_DIST = $(pkg_manifests:%=%.in) $(pkg_prototypes:%=%.in) \ + $(pkg_names:%=pkginfo.%.tmpl.in) $(srcdir)/pkginfo.tmpl.in \ + $(pkg_depends:%=%.in) quagga.init.in README.txt + +pkg-root-install: + (cd $(top_builddir) && \ + $(MAKE) DESTDIR=$(abs_builddir)/quagga-root install) + +packages: $(pkg_packages) + +#nodist_pkgdata_DATA = $(pkg_packages) diff --git a/solaris/README.txt b/solaris/README.txt new file mode 100644 index 0000000..811e9d0 --- /dev/null +++ b/solaris/README.txt @@ -0,0 +1,188 @@ +To build packages for Solaris 10: + +Requirements: +------------- + +- Development environment including gcc (eg as shipped with Solaris 10) + +- The Package tools from Solaris 10 or Solaris Nevada/Express. + +- i.manifest and r.manifest scripts as supplied with Solaris Express + in /usr/sadm/install/scripts/ or from OpenSolaris.org: + + http://cvs.opensolaris.org/source/xref/usr/src/pkgdefs/common_files/i.manifest + http://cvs.opensolaris.org/source/xref/usr/src/pkgdefs/common_files/r.manifest + + i.manifest must be at least version 1.5. Place these scripts in + this directory if you are using Solaris 10 GA (which does not ship with + these scripts), or in the solaris/ directory in the Quagga source. + + +Package creation instructions: +------------------------------ + +1. Configure and build Quagga in the top level build directory as per +normal, eg: + + ./configure --prefix=/usr/local/quagga \ + --localstatedir=/var/run/quagga + --enable-gcc-rdynamic --enable-opaque-lsa --enable-ospf-te \ + --enable-multipath=64 --enable-user=quagga \ + --enable-ospfclient=yes --enable-ospfapi=yes \ + --enable-group=quagga --enable-nssa --enable-opaque-lsa + +You will need /usr/sfw/bin and /usr/ccs/bin in your path. + +2. make install in the top-level build directory, it's a good idea to make +use of DESTDIR to install to an alternate root, eg: + + gmake DESTDIR=/var/tmp/qroot install + +3. In this directory (solaris/), run make packages, specifying DESTDIR if +appropriate, eg: + + gmake DESTDIR=/var/tmp/qroot packages + +This should result in 4 packages being created: + + quagga-libs-...-$ARCH.pkg - QUAGGAlibs + quagga-daemons-...-$ARCH.pkg - QUAGGAdaemons + quagga-doc-...-$ARCH.pkg - QUAGGAdoc + quagga-dev-...-$ARCH.pkg - QUAGGAdev + quagga-smf-...-$ARCH.pkg - QUAGGAsmf + +QUAGGAlibs and QUAGGAdaemons are needed for daemon runtime. QUAGGAsmf +provides the required bits for Solaris 10+ SMF support. + + +Install and post-install configuration notes: +--------------------------------------------- + +- If you specified a user/group which does not exist per default on Solaris + (eg quagga/quagga) you *must* create these before installing these on a + system. The packages do *not* create the users. + +- The configuration files are not created. You must create the configuration + file yourself, either with your complete desired configuration, or else if + you wish to use the telnet interface for further configuration you must + create them containing at least: + + password whatever + + The user which quagga runs as must have write permissions on this file, no + other user should have read permissions, and you would also have to enable + the telnet interface (see below). + +- SMF notes: + + - QUAGGAsmf installs a svc:/network/routing/quagga service, with an + instance for each daemon + + - The state of all instances of quagga service can be inspected with: + + svcs -l svc:/network/routing/quagga + + or typically just with a shortcut of 'quagga': + + svcs -l quagga + + - A specific instance of the quagga service can be inspected by specifying + the daemon name as the instance, ie quagga:: + + svcs -l svc:/network/routing/quagga:zebra + svcs -l svc:/network/routing/quagga:ospfd + + + or typically just with the shortcut of 'quagga:' or even + : + + svcs -l quagga:zebra + svcs -l ospfd + + Eg: + + # # svcs -l ripd + fmri svc:/network/routing/quagga:ripd + name Quagga: ripd, RIPv1/2 IPv4 routing protocol daemon. + enabled true + state online + next_state none + state_time Wed Jun 15 16:21:02 2005 + logfile /var/svc/log/network-routing-quagga:ripd.log + restarter svc:/system/svc/restarter:default + contract_id 93 + dependency require_all/restart svc:/network/routing/quagga:zebra (online) + dependency require_all/restart file://localhost//usr/local/quagga/etc/ripd.conf (online) + dependency require_all/none svc:/system/filesystem/usr:default (online) + dependency require_all/none svc:/network/loopback (online) + + - Configuration of startup options is by way of SMF properties in a + property group named 'quagga'. The defaults should automatically be + inline with how you configured Quagga in Step 1 above. + + - By default the VTY interface is disabled. To change this, see below for + how to set the 'quagga/vty_port' property as appropriate for + /each/ service. Also, the VTY is set to listen only to localhost by + default, you may change the 'quagga/vty_addr' property as appropriate + for both of the 'quagga' service and specific individual instances of + the 'quagga' service (ie quagga:zebra, quagga:ospfd, etc..). + + - Properties belonging to the 'quagga' service are inherited by all + instances. Eg: + + # svcprop -p quagga svc:/network/routing/quagga + quagga/group astring root + quagga/retain boolean false + quagga/user astring root + quagga/vty_addr astring 127.1 + quagga/vty_port integer 0 + + # svcprop -p quagga svc:/network/routing/quagga:ospfd + quagga/retain_routes boolean false + quagga/group astring root + quagga/retain boolean false + quagga/user astring root + quagga/vty_addr astring 127.1 + quagga/vty_port integer 0 + + All instances will inherit these properties, unless the instance itself + overrides these defaults. This also implies one can modify properties of + the 'quagga' service and have them apply to all daemons. + + # svccfg -s svc:/network/routing/quagga \ + setprop quagga/vty_addr = astring: ::1 + + # svcprop -p quagga svc:/network/routing/quagga + quagga/group astring root + quagga/retain boolean false + quagga/user astring root + quagga/vty_port integer 0 + quagga/vty_addr astring ::1 + + # # You *must* refresh instances to have the property change + # # take affect for the 'running snapshot' of service state. + # svcadm refresh quagga:ospfd + + # svcprop -p quagga svc:/network/routing/quagga:ospfd + quagga/retain_routes boolean false + quagga/group astring root + quagga/retain boolean false + quagga/user astring root + quagga/vty_port integer 0 + quagga/vty_addr astring ::1 + + Other daemon-specific options/properties may be available, however they + are not yet honoured/used (eg ospfd/apiserver on svc:/network/ospf). + + - As SMF is dependency aware, restarting network/zebra will restart all the + other daemons. + + - To upgrade from one set of Quagga packages to a newer release, one must + first pkgrm the installed packages. When one pkgrm's QUAGGAsmf all + property configuration will be lost, and any customisations will have to + redone after installing the updated QUAGGAsmf package. + +- These packages are not supported by Sun Microsystems, report bugs via the + usual Quagga channels, ie Bugzilla. Improvements/contributions of course + would be greatly appreciated. + diff --git a/solaris/depend.daemons.in b/solaris/depend.daemons.in new file mode 100644 index 0000000..3430e8c --- /dev/null +++ b/solaris/depend.daemons.in @@ -0,0 +1,8 @@ +P QUAGGAlibs Quagga common runtime libraries + @PACKAGE_VERSION@,REV=@CONFDATE@ +P SUNWcsu Core Solaris, (Usr) +P SUNWcsr Core Solaris Libraries (Root) +P SUNWcnetr Core Solaris Network Infrastructure (Root) +I SUNWzebrar +I SUNWzebrau +I CSWzebra diff --git a/solaris/depend.dev.in b/solaris/depend.dev.in new file mode 100644 index 0000000..8f23482 --- /dev/null +++ b/solaris/depend.dev.in @@ -0,0 +1,2 @@ +P QUAGGAlibs Quagga common runtime libraries + @PACKAGE_VERSION@,REV=@CONFDATE@ diff --git a/solaris/depend.doc.in b/solaris/depend.doc.in new file mode 100644 index 0000000..b337929 --- /dev/null +++ b/solaris/depend.doc.in @@ -0,0 +1 @@ +P SUNWdoc Documentation Tools diff --git a/solaris/depend.libs.in b/solaris/depend.libs.in new file mode 100644 index 0000000..4185977 --- /dev/null +++ b/solaris/depend.libs.in @@ -0,0 +1,5 @@ +P SUNWcslr Core Solaris Libraries (Root) +P SUNWcsl Core Solaris, (Shared Libs) +P SUNWlibmsr Math & Microtasking Libraries (Root) +R QUAGGAdaemons Quagga daemons +R QUAGGAdev diff --git a/solaris/depend.smf.in b/solaris/depend.smf.in new file mode 100644 index 0000000..6d928d2 --- /dev/null +++ b/solaris/depend.smf.in @@ -0,0 +1,8 @@ +P QUAGGAdaemons Quagga daemons + @PACKAGE_VERSION@,REV=@CONFDATE@ +P SUNWcsu Core Solaris, (Usr) +P SUNWcsr Core Solaris Libraries (Root) +P SUNWroute Network Routing daemons/commands (Usr) +I SUNWzebrar +I SUNWzebrau +I CSWzebra diff --git a/solaris/pkginfo.daemons.tmpl.in b/solaris/pkginfo.daemons.tmpl.in new file mode 100644 index 0000000..cab0e3c --- /dev/null +++ b/solaris/pkginfo.daemons.tmpl.in @@ -0,0 +1,2 @@ +PKG="QUAGGAdaemons" +NAME="@PACKAGE_NAME@ - @PACKAGE_NAME@ daemons" diff --git a/solaris/pkginfo.dev.tmpl.in b/solaris/pkginfo.dev.tmpl.in new file mode 100644 index 0000000..9c5d23e --- /dev/null +++ b/solaris/pkginfo.dev.tmpl.in @@ -0,0 +1,3 @@ +PKG=QUAGGAdev +NAME="@PACKAGE_NAME@ - @PACKAGE_NAME@ development files" + diff --git a/solaris/pkginfo.doc.tmpl.in b/solaris/pkginfo.doc.tmpl.in new file mode 100644 index 0000000..809ec77 --- /dev/null +++ b/solaris/pkginfo.doc.tmpl.in @@ -0,0 +1,2 @@ +PKG=QUAGGAdoc +NAME="@PACKAGE_NAME@ - @PACKAGE_NAME@ documentation" diff --git a/solaris/pkginfo.libs.tmpl.in b/solaris/pkginfo.libs.tmpl.in new file mode 100644 index 0000000..42adc6e --- /dev/null +++ b/solaris/pkginfo.libs.tmpl.in @@ -0,0 +1,2 @@ +PKG=QUAGGAlibs +NAME="@PACKAGE_NAME@ - @PACKAGE_NAME@ common runtime libraries" diff --git a/solaris/pkginfo.smf.tmpl.in b/solaris/pkginfo.smf.tmpl.in new file mode 100644 index 0000000..4aa0393 --- /dev/null +++ b/solaris/pkginfo.smf.tmpl.in @@ -0,0 +1,2 @@ +PKG="QUAGGAsmf" +NAME="@PACKAGE_NAME@ - @PACKAGE_NAME@ SMF support" diff --git a/solaris/pkginfo.tmpl.in b/solaris/pkginfo.tmpl.in new file mode 100644 index 0000000..2dd27fd --- /dev/null +++ b/solaris/pkginfo.tmpl.in @@ -0,0 +1,10 @@ +ARCH="@target_cpu@" +CATEGORY="system" +VERSION="@PACKAGE_VERSION@,REV=@CONFDATE@" +VENDOR="http://www.quagga.net/" +HOTLINE="@PACKAGE_BUGREPORT@" +EMAIL=paul@quagga.net +DESC="@PACKAGE_NAME@ Routing Protocols" +MAXINST=1 +CLASSES="none preserve renamenew manifest" +BASEDIR=/ diff --git a/solaris/prototype.daemons.in b/solaris/prototype.daemons.in new file mode 100644 index 0000000..ce65d5e --- /dev/null +++ b/solaris/prototype.daemons.in @@ -0,0 +1,20 @@ +i pkginfo=$abs_builddir/pkginfo.daemons.full +i depend=$abs_builddir/depend.daemons +i copying=$abs_top_srcdir/COPYING +d none @sbindir@=$DESTDIR/@sbindir@ 0755 root bin +f none @sbindir@/zebra=$DESTDIR/@sbindir@/zebra 0755 root bin +f none @sbindir@/bgpd=$DESTDIR/@sbindir@/bgpd 0755 root bin +f none @sbindir@/ripd=$DESTDIR/@sbindir@/ripd 0755 root bin +f none @sbindir@/ripngd=$DESTDIR/@sbindir@/ripngd 0755 root bin +f none @sbindir@/ospfd=$DESTDIR/@sbindir@/ospfd 0755 root bin +f none @sbindir@/ospf6d=$DESTDIR/@sbindir@/ospf6d 0755 root bin +f none @sbindir@/watchquagga=$DESTDIR/@sbindir@/watchquagga 0755 root bin +d none @sysconfdir@=$DESTDIR/@sysconfdir@ 0711 @enable_user@ @enable_group@ +f none @sysconfdir@/zebra.conf.sample=$DESTDIR/@sysconfdir@/zebra.conf.sample 0644 root bin +f none @sysconfdir@/bgpd.conf.sample=$DESTDIR/@sysconfdir@/bgpd.conf.sample 0644 root bin +f none @sysconfdir@/bgpd.conf.sample2=$DESTDIR/@sysconfdir@/bgpd.conf.sample2 0644 root bin +f none @sysconfdir@/ripd.conf.sample=$DESTDIR/@sysconfdir@/ripd.conf.sample 0644 root bin +f none @sysconfdir@/ripngd.conf.sample=$DESTDIR/@sysconfdir@/ripngd.conf.sample 0644 root bin +f none @sysconfdir@/ospfd.conf.sample=$DESTDIR/@sysconfdir@/ospfd.conf.sample 0644 root bin +f none @sysconfdir@/ospf6d.conf.sample=$DESTDIR/@sysconfdir@/ospf6d.conf.sample 0644 root bin +d none @quagga_statedir@=$DESTDIR/@quagga_statedir@ 0711 @enable_user@ @enable_group@ diff --git a/solaris/prototype.dev.in b/solaris/prototype.dev.in new file mode 100644 index 0000000..2ad937b --- /dev/null +++ b/solaris/prototype.dev.in @@ -0,0 +1,57 @@ +i pkginfo=$abs_builddir/pkginfo.dev.full +i depend=$abs_builddir/depend.dev +i copying=$abs_top_srcdir/COPYING +f none @libdir@/libzebra.la=$DESTDIR/@libdir@/libzebra.la 0755 root bin +f none @libdir@/libzebra.a=$DESTDIR/@libdir@/libzebra.a 0644 root bin +f none @libdir@/libospf.la=$DESTDIR/@libdir@/libospf.la 0755 root bin +f none @libdir@/libospf.a=$DESTDIR/@libdir@/libospf.a 0644 root bin +f none @libdir@/libospfapiclient.la=$DESTDIR/@libdir@/libospfapiclient.la 0755 root bin +f none @libdir@/libospfapiclient.a=$DESTDIR/@libdir@/libospfapiclient.a 0644 root bin +d none @includedir@=$DESTDIR/@includedir@ 0755 root bin +d none @includedir@/quagga=$DESTDIR/@includedir@/quagga 0755 root bin +d none @includedir@/quagga/ospfd=$DESTDIR/@includedir@/quagga/ospfd 0755 root bin +f none @includedir@/quagga/ospfd/ospf_api.h=$DESTDIR/@includedir@/quagga/ospfd/ospf_api.h 0644 root bin +f none @includedir@/quagga/ospfd/ospf_asbr.h=$DESTDIR/@includedir@/quagga/ospfd/ospf_asbr.h 0644 root bin +f none @includedir@/quagga/ospfd/ospf_dump.h=$DESTDIR/@includedir@/quagga/ospfd/ospf_dump.h 0644 root bin +f none @includedir@/quagga/ospfd/ospf_lsa.h=$DESTDIR/@includedir@/quagga/ospfd/ospf_lsa.h 0644 root bin +f none @includedir@/quagga/ospfd/ospf_lsdb.h=$DESTDIR/@includedir@/quagga/ospfd/ospf_lsdb.h 0644 root bin +f none @includedir@/quagga/ospfd/ospf_nsm.h=$DESTDIR/@includedir@/quagga/ospfd/ospf_nsm.h 0644 root bin +f none @includedir@/quagga/ospfd/ospf_ism.h=$DESTDIR/@includedir@/quagga/ospfd/ospf_ism.h 0644 root bin +f none @includedir@/quagga/ospfd/ospf_opaque.h=$DESTDIR/@includedir@/quagga/ospfd/ospf_opaque.h 0644 root bin +f none @includedir@/quagga/ospfd/ospfd.h=$DESTDIR/@includedir@/quagga/ospfd/ospfd.h 0644 root bin +f none @includedir@/quagga/buffer.h=$DESTDIR/@includedir@/quagga/buffer.h 0644 root bin +f none @includedir@/quagga/command.h=$DESTDIR/@includedir@/quagga/command.h 0644 root bin +f none @includedir@/quagga/filter.h=$DESTDIR/@includedir@/quagga/filter.h 0644 root bin +f none @includedir@/quagga/getopt.h=$DESTDIR/@includedir@/quagga/getopt.h 0644 root bin +f none @includedir@/quagga/hash.h=$DESTDIR/@includedir@/quagga/hash.h 0644 root bin +f none @includedir@/quagga/if.h=$DESTDIR/@includedir@/quagga/if.h 0644 root bin +f none @includedir@/quagga/linklist.h=$DESTDIR/@includedir@/quagga/linklist.h 0644 root bin +f none @includedir@/quagga/log.h=$DESTDIR/@includedir@/quagga/log.h 0644 root bin +f none @includedir@/quagga/memory.h=$DESTDIR/@includedir@/quagga/memory.h 0644 root bin +f none @includedir@/quagga/network.h=$DESTDIR/@includedir@/quagga/network.h 0644 root bin +f none @includedir@/quagga/prefix.h=$DESTDIR/@includedir@/quagga/prefix.h 0644 root bin +f none @includedir@/quagga/routemap.h=$DESTDIR/@includedir@/quagga/routemap.h 0644 root bin +f none @includedir@/quagga/distribute.h=$DESTDIR/@includedir@/quagga/distribute.h 0644 root bin +f none @includedir@/quagga/sockunion.h=$DESTDIR/@includedir@/quagga/sockunion.h 0644 root bin +f none @includedir@/quagga/str.h=$DESTDIR/@includedir@/quagga/str.h 0644 root bin +f none @includedir@/quagga/stream.h=$DESTDIR/@includedir@/quagga/stream.h 0644 root bin +f none @includedir@/quagga/table.h=$DESTDIR/@includedir@/quagga/table.h 0644 root bin +f none @includedir@/quagga/thread.h=$DESTDIR/@includedir@/quagga/thread.h 0644 root bin +f none @includedir@/quagga/vector.h=$DESTDIR/@includedir@/quagga/vector.h 0644 root bin +f none @includedir@/quagga/version.h=$DESTDIR/@includedir@/quagga/version.h 0644 root bin +f none @includedir@/quagga/vty.h=$DESTDIR/@includedir@/quagga/vty.h 0644 root bin +f none @includedir@/quagga/zebra.h=$DESTDIR/@includedir@/quagga/zebra.h 0644 root bin +f none @includedir@/quagga/plist.h=$DESTDIR/@includedir@/quagga/plist.h 0644 root bin +f none @includedir@/quagga/zclient.h=$DESTDIR/@includedir@/quagga/zclient.h 0644 root bin +f none @includedir@/quagga/sockopt.h=$DESTDIR/@includedir@/quagga/sockopt.h 0644 root bin +f none @includedir@/quagga/smux.h=$DESTDIR/@includedir@/quagga/smux.h 0644 root bin +f none @includedir@/quagga/md5.h=$DESTDIR/@includedir@/quagga/md5.h 0644 root bin +f none @includedir@/quagga/if_rmap.h=$DESTDIR/@includedir@/quagga/if_rmap.h 0644 root bin +f none @includedir@/quagga/keychain.h=$DESTDIR/@includedir@/quagga/keychain.h 0644 root bin +f none @includedir@/quagga/privs.h=$DESTDIR/@includedir@/quagga/privs.h 0644 root bin +f none @includedir@/quagga/sigevent.h=$DESTDIR/@includedir@/quagga/sigevent.h 0644 root bin +f none @includedir@/quagga/pqueue.h=$DESTDIR/@includedir@/quagga/pqueue.h 0644 root bin +f none @includedir@/quagga/jhash.h=$DESTDIR/@includedir@/quagga/jhash.h 0644 root bin +f none @includedir@/quagga/zassert.h=$DESTDIR/@includedir@/quagga/zassert.h 0644 root bin +d none @includedir@/quagga/ospfapi=$DESTDIR/@includedir@/quagga/ospfapi 0755 root bin +f none @includedir@/quagga/ospfapi/ospf_apiclient.h=$DESTDIR/@includedir@/quagga/ospfapi/ospf_apiclient.h 0644 root bin diff --git a/solaris/prototype.doc.in b/solaris/prototype.doc.in new file mode 100644 index 0000000..42b076d --- /dev/null +++ b/solaris/prototype.doc.in @@ -0,0 +1,17 @@ +i pkginfo=$abs_builddir/pkginfo.doc.full +i depend=$abs_builddir/depend.doc +i copying=$abs_top_srcdir/COPYING +d none @infodir@=$DESTDIR/@infodir@ 0755 root bin +#f none @infodir@/dir=$DESTDIR/@infodir@/dir 0644 root bin +f none @infodir@/quagga.info=$DESTDIR/@infodir@/quagga.info 0644 root bin +d none @mandir@=$DESTDIR/@mandir@ 0755 root bin +d none @mandir@/man1=$DESTDIR/@mandir@/man1 0755 root bin +f none @mandir@/man1/vtysh.1=$DESTDIR/@mandir@/man1/vtysh.1 0644 root bin +d none @mandir@/man8=$DESTDIR/@mandir@/man8 0755 root bin +f none @mandir@/man8/bgpd.8=$DESTDIR/@mandir@/man8/bgpd.8 0644 root bin +f none @mandir@/man8/ospf6d.8=$DESTDIR/@mandir@/man8/ospf6d.8 0644 root bin +f none @mandir@/man8/ospfd.8=$DESTDIR/@mandir@/man8/ospfd.8 0644 root bin +f none @mandir@/man8/ripd.8=$DESTDIR/@mandir@/man8/ripd.8 0644 root bin +f none @mandir@/man8/ripngd.8=$DESTDIR/@mandir@/man8/ripngd.8 0644 root bin +f none @mandir@/man8/zebra.8=$DESTDIR/@mandir@/man8/zebra.8 0644 root bin +f none @mandir@/man8/isisd.8=$DESTDIR/@mandir@/man8/isisd.8 0644 root bin diff --git a/solaris/prototype.libs.in b/solaris/prototype.libs.in new file mode 100644 index 0000000..71f4fd5 --- /dev/null +++ b/solaris/prototype.libs.in @@ -0,0 +1,13 @@ +i pkginfo=$abs_builddir/pkginfo.libs.full +i depend=$abs_builddir/depend.libs +i copying=$abs_top_srcdir/COPYING +d none @libdir@=$DESTDIR/@libdir@ 0755 root bin +s none @libdir@/libzebra.so.0=libzebra.so.0.0.0 +f none @libdir@/libzebra.so.0.0.0=$DESTDIR/@libdir@/libzebra.so.0.0.0 0755 root bin +s none @libdir@/libzebra.so=libzebra.so.0.0.0 +s none @libdir@/libospf.so.0=libospf.so.0.0.0 +f none @libdir@/libospf.so.0.0.0=$DESTDIR/@libdir@/libospf.so.0.0.0 0755 root bin +s none @libdir@/libospf.so=libospf.so.0.0.0 +f none @libdir@/libospfapiclient.so.0.0.0=$DESTDIR/@libdir@/libospfapiclient.so.0.0.0 0755 root bin +s none @libdir@/libospfapiclient.so.0=libospfapiclient.so.0.0.0 +s none @libdir@/libospfapiclient.so=libospfapiclient.so.0.0.0 diff --git a/solaris/prototype.smf.in b/solaris/prototype.smf.in new file mode 100644 index 0000000..3c80f39 --- /dev/null +++ b/solaris/prototype.smf.in @@ -0,0 +1,8 @@ +i pkginfo=$abs_builddir/pkginfo.smf.full +i depend=$abs_builddir/depend.smf +i copying=$abs_top_srcdir/COPYING +i i.manifest +i r.manifest +f manifest var/svc/manifest/network/quagga.xml 0444 root bin +#f none var/svc/profile/@PACKAGE_TARNAME@_options.xml=$abs_builddir/options.xml 0755 root sys +f none lib/svc/method/quagga=$abs_builddir/quagga.init 0755 root bin diff --git a/solaris/quagga.init.in b/solaris/quagga.init.in new file mode 100755 index 0000000..ed3350e --- /dev/null +++ b/solaris/quagga.init.in @@ -0,0 +1,280 @@ +#!/sbin/sh +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# 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. +# +# Starts/stops the given daemon + +SMFINCLUDE=/lib/svc/share/smf_include.sh +ROUTEADMINCLUDE=/lib/svc/share/routing_include.sh +GLOBAL_OPTIONS="PAfiug" +DAEMON_PATH=@sbindir@ +USER=@enable_user@ +GROUP=@enable_group@ + +# handle upgrade of daemon-args SMF property to new routeadm properties +# used during upgrade too by routeadm. +# relevant to S10U4+ only. +handle_routeadm_upgrade () { + GLOBAL_OPTIONS="PAfiug" + + daemon_args=`get_daemon_args $SMF_FMRI` + + if [ -n "$daemon_args" ]; then + set_daemon_value_property "$SMF_FMRI" "$daemon_args" \ + "$GLOBAL_OPTIONS" "P" vty_port 0 + set_daemon_value_property "$SMF_FMRI" "$daemon_args" \ + "$GLOBAL_OPTIONS" "A" vty_address + set_daemon_value_property "$SMF_FMRI" "$daemon_args" \ + "$GLOBAL_OPTIONS" "f" config_file + set_daemon_value_property "$SMF_FMRI" "$daemon_args" \ + "$GLOBAL_OPTIONS" "i" pid_file + set_daemon_value_property "$SMF_FMRI" "$daemon_args" \ + "$GLOBAL_OPTIONS" "u" user + set_daemon_value_property "$SMF_FMRI" "$daemon_args" \ + "$GLOBAL_OPTIONS" "g" group + + case "$1" in + zebra) + set_daemon_boolean_property "$SMF_FMRI" "$daemon_args" \ + "${GLOBAL_OPTIONS}b" "b" batch true false + ;; + ripd|ripngd) + set_daemon_boolean_property "$SMF_FMRI" "$daemon_args" \ + "${GLOBAL_OPTIONS}r" "r" retain true false + ;; + bgpd) + set_daemon_boolean_property "$SMF_FMRI" "$daemon_args" \ + "${GLOBAL_OPTIONS}rnp" "r" retain true false + set_daemon_boolean_property "$SMF_FMRI" "$daemon_args" \ + "${GLOBAL_OPTIONS}rnp" "n" no_kernel true false + set_daemon_value_property "$SMF_FMRI" "$daemon_args" \ + "${GLOBAL_OPTIONS}rnp" "p" bgp_port + esac + clear_daemon_args $SMF_FMRI + fi +} + +upgrade_config () { + DAEMON=$1 + # handle upgrade of SUNWzebra to Quagga + if [ -d "/etc/quagga" -a ! -f "/etc/quagga/${DAEMON}.conf" ] ; then + if [ -f "/etc/sfw/zebra/${DAEMON}.conf" ] ; then + cp "/etc/sfw/zebra/${DAEMON}.conf" \ + "/etc/quagga/${DAEMON}.conf.upgrade" \ + || exit $SMF_EXIT_ERR_FATAL + chown "${USER}:${GROUP}" "/etc/quagga/${DAEMON}.conf.upgrade" \ + || exit $SMF_EXIT_ERR_FATAL + chmod 0600 "/etc/quagga/${DAEMON}.conf.upgrade" \ + || exit $SMF_EXIT_ERR_FATAL + mv "/etc/quagga/${DAEMON}.conf.upgrade" "/etc/quagga/${DAEMON}.conf" \ + || exit $SMF_EXIT_ERR_FATAL + fi + fi + + if [ ! -f "/etc/quagga/${DAEMON}.conf" ] ; then + touch "/etc/quagga/${DAEMON}.conf.new" \ + || exit $SMF_EXIT_ERR_FATAL + chown "${USER}:${GROUP}" "/etc/quagga/${DAEMON}.conf.new" \ + || exit $SMF_EXIT_ERR_FATAL + chmod 0600 "/etc/quagga/${DAEMON}.conf.new" \ + || exit $SMF_EXIT_ERR_FATAL + mv "/etc/quagga/${DAEMON}.conf.new" "/etc/quagga/${DAEMON}.conf" \ + || exit $SMF_EXIT_ERR_FATAL + fi +} + +# Relevant to S10+ +quagga_is_globalzone () { + if [ "${QUAGGA_INIT_ZONENAME:=`/sbin/zonename`}" = "global" \ + -o `/sbin/zonename -t` = "exclusive" ]; then + return 0 + else + return 1 + fi +} + +routeadm_daemon_args () { + # globals + args="`get_daemon_option_from_property $SMF_FMRI config_file f`" + args="${args} `get_daemon_option_from_property $SMF_FMRI vty_port P`" + args="${args} `get_daemon_option_from_property $SMF_FMRI vty_address A`" + args="${args} `get_daemon_option_from_property $SMF_FMRI pid_file i`" + + # 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` + if [ "${SMF_USER}" ] ; then + USER="${SMF_USER}" + args="${args} -u ${SMF_USER}" + fi + if [ "${SMF_GROUP}" ] ; then + GROUP="${SMF_GROUP}" + args="${args} -g ${SMF_GROUP}" + fi + + case $1 in + zebra) + args="${args} `get_daemon_option_from_boolean_property $SMF_FMRI batch -b true`" + ;; + ripd|ripngd) + args="${args} `get_daemon_option_from_boolean_property $SMF_FMRI retain -r true`" + ;; + bgpd) + args="${args} `get_daemon_option_from_boolean_property $SMF_FMRI retain -r true`" + args="${args} `get_daemon_option_from_boolean_property $SMF_FMRI no_kernel -n true`" + args="${args} `get_daemon_option_from_property $SMF_FMRI bgp_port p 179`" + ;; + esac + echo ${args} +} + +# 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 + . "$SMFINCLUDE"; + + # source the SMF-routeadm include if present.. + if [ -f "$ROUTEADMINCLUDE" ] ; then + . "$ROUTEADMINCLUDE" + fi +else + # pre-SMF system, fake up any functions and exit codes + # which SMFINCLUDE usually provides. + smf_present () { + return 1 + } + SMF_EXIT_OK=0; + SMF_EXIT_ERR_CONFIG=96; + SMF_EXIT_ERR_FATAL=95; +fi + +# if there's no SMF, set some default DAEMON_ARGS +smf_present || DAEMON_ARGS="" + +usage () { + if smf_present ; then + echo "Usage: $0 "; + else + echo "Usage: $0 "; + fi + echo "The --pid_file argument is implied"; + echo "This help message: $0 "; +} + +# parse arguments, different according to SMF or not. +case $1 in + 'help' | 'usage') + usage + exit $SMF_EXIT_OK + ;; +esac + +if smf_present ; then + QUAGGA_METHOD="start" +else + QUAGGA_METHOD="$1" + shift; +fi + +DAEMON="$1" + +# daemon path must be given +if [ "$DAEMON_PATH/$DAEMON" = "/" ]; then + usage + exit $SMF_EXIT_ERR_FATAL +fi + +# only bgpd is suitable for running in a non-global zone, at this +# time. +case "${DAEMON}" in + bgpd) + ;; + zebra | ospfd | ospf6d | ripd | ripngd ) + quagga_is_globalzone || exit $SMF_EXIT_OK + ;; + *) + usage + exit $SMF_EXIT_ERR_CONFIG; + ;; +esac + +# Older Quagga SMF packages pass daemon args on the commandline +# Newer SMF routeadm model uses properties for each argument +# so we must handle that. +if [ smf_present -a -f "$ROUTEADMINCLUDE" ]; then + handle_routeadm_upgrade $DAEMON; + DAEMON_ARGS=`routeadm_daemon_args`; +else + if [ $# -gt 0 ] ; then + shift + DAEMON_ARGS="$@" + fi +fi + +upgrade_config "$DAEMON" + +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. +if [ ! -d @quagga_statedir@ ] ; then + mkdir -p @quagga_statedir@ + chown @enable_user@:@enable_group@ @quagga_statedir@ + chmod 751 @quagga_statedir@ +fi + +PIDFILE="@quagga_statedir@/${DAEMON}.pid" + +start () { + if [ ! -x "$DAEMON_PATH/$DAEMON" ] ; then + echo "Error, could not find daemon, $DAEMON_PATH/$DAEMON" + exit $SMF_EXIT_ERR_FATAL + fi + eval exec $DAEMON_PATH/$DAEMON $DAEMON_ARGS --pid_file ${PIDFILE} & +} + +stop_by_pidfile () { + if [ -f "${PIDFILE}" ]; then + /usr/bin/kill -TERM `/usr/bin/cat "${PIDFILE}"` + fi +} + +case "$QUAGGA_METHOD" in +'start') + start + ;; +'stop') + stop_by_pidfile + ;; + +*) + usage + exit $SMF_EXIT_ERR_FATAL + ;; +esac + +exit $SMF_EXIT_OK; diff --git a/solaris/quagga.xml.in b/solaris/quagga.xml.in new file mode 100644 index 0000000..60427b0 --- /dev/null +++ b/solaris/quagga.xml.in @@ -0,0 +1,828 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/stamp-h.in b/stamp-h.in new file mode 100644 index 0000000..9788f70 --- /dev/null +++ b/stamp-h.in @@ -0,0 +1 @@ +timestamp diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..3002b27 --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,41 @@ +Makefile +Makefile.in +*.o +tags +TAGS +.deps +.nfs* +*~ +*.loT +*.lo +*.la +*.libs +*.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 +teststream +testnexthopiter +testcommands +test-commands-defun.c +site.exp diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..b13e903 --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,92 @@ +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)/\" + +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_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 +teststream_SOURCES = test-stream.c +heavy_SOURCES = heavy.c main.c +heavywq_SOURCES = heavy-wq.c main.c +heavythread_SOURCES = heavy-thread.c main.c +aspathtest_SOURCES = aspath_test.c +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@ +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 = ../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 new file mode 100644 index 0000000..5a0899e --- /dev/null +++ b/tests/aspath_test.c @@ -0,0 +1,1387 @@ +/* + * 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" +#include "bgpd/bgp_attr.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 + +/* need these to link in libbgp */ +struct zebra_privs_t *bgpd_privs = NULL; +struct thread_master *master = NULL; + +static int failed = 0; + +/* specification for a test - what the results should be */ +struct test_spec +{ + const char *shouldbe; /* the string the path should parse to */ + const char *shouldbe_delete_confed; /* ditto, but once confeds are deleted */ + const unsigned int hops; /* aspath_count_hops result */ + const unsigned int confeds; /* aspath_count_confeds */ + const int private_as; /* whether the private_as check should pass or fail */ +#define NOT_ALL_PRIVATE 0 +#define ALL_PRIVATE 1 + const as_t does_loop; /* an ASN which should trigger loop-check */ + const as_t doesnt_loop; /* one which should not */ + const as_t first; /* the first ASN, if there is one */ +#define NULL_ASN 0 +}; + + +/* test segments to parse and validate, and use for other tests */ +static struct test_segment { + const char *name; + const char *desc; + const u_char asdata[1024]; + int len; + struct test_spec sp; +} test_segments [] = +{ + { /* 0 */ + "seq1", + "seq(8466,3,52737,4096)", + { 0x2,0x4, 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00 }, + 10, + { "8466 3 52737 4096", + "8466 3 52737 4096", + 4, 0, NOT_ALL_PRIVATE, 4096, 4, 8466 }, + }, + { /* 1 */ + "seq2", + "seq(8722) seq(4)", + { 0x2,0x1, 0x22,0x12, + 0x2,0x1, 0x00,0x04 }, + 8, + { "8722 4", + "8722 4", + 2, 0, NOT_ALL_PRIVATE, 4, 5, 8722, }, + }, + { /* 2 */ + "seq3", + "seq(8466,3,52737,4096,8722,4)", + { 0x2,0x6, 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, + 0x22,0x12, 0x00,0x04}, + 14, + { "8466 3 52737 4096 8722 4", + "8466 3 52737 4096 8722 4", + 6, 0, NOT_ALL_PRIVATE, 3, 5, 8466 }, + }, + { /* 3 */ + "seqset", + "seq(8482,51457) set(5204)", + { 0x2,0x2, 0x21,0x22, 0xc9,0x01, + 0x1,0x1, 0x14,0x54 }, + 10, + { "8482 51457 {5204}", + "8482 51457 {5204}", + 3, 0, NOT_ALL_PRIVATE, 5204, 51456, 8482}, + }, + { /* 4 */ + "seqset2", + "seq(8467, 59649) set(4196,48658) set(17322,30745)", + { 0x2,0x2, 0x21,0x13, 0xe9,0x01, + 0x1,0x2, 0x10,0x64, 0xbe,0x12, + 0x1,0x2, 0x43,0xaa, 0x78,0x19 }, + 18, + { "8467 59649 {4196,48658} {17322,30745}", + "8467 59649 {4196,48658} {17322,30745}", + 4, 0, NOT_ALL_PRIVATE, 48658, 1, 8467}, + }, + { /* 5 */ + "multi", + "seq(6435,59408,21665) set(2457,61697,4369), seq(1842,41590,51793)", + { 0x2,0x3, 0x19,0x23, 0xe8,0x10, 0x54,0xa1, + 0x1,0x3, 0x09,0x99, 0xf1,0x01, 0x11,0x11, + 0x2,0x3, 0x07,0x32, 0xa2,0x76, 0xca,0x51 }, + 24, + { "6435 59408 21665 {2457,4369,61697} 1842 41590 51793", + "6435 59408 21665 {2457,4369,61697} 1842 41590 51793", + 7, 0, NOT_ALL_PRIVATE, 51793, 1, 6435 }, + }, + { /* 6 */ + "confed", + "confseq(123,456,789)", + { 0x3,0x3, 0x00,0x7b, 0x01,0xc8, 0x03,0x15 }, + 8, + { "(123 456 789)", + "", + 0, 3, NOT_ALL_PRIVATE, 789, 1, NULL_ASN }, + }, + { /* 7 */ + "confed2", + "confseq(123,456,789) confseq(111,222)", + { 0x3,0x3, 0x00,0x7b, 0x01,0xc8, 0x03,0x15, + 0x3,0x2, 0x00,0x6f, 0x00,0xde }, + 14, + { "(123 456 789) (111 222)", + "", + 0, 5, NOT_ALL_PRIVATE, 111, 1, NULL_ASN }, + }, + { /* 8 */ + "confset", + "confset(456,123,789)", + { 0x4,0x3, 0x01,0xc8, 0x00,0x7b, 0x03,0x15 }, + 8, + { "[123,456,789]", + "[123,456,789]", + 0, 1, NOT_ALL_PRIVATE, 123, 1, NULL_ASN }, + }, + { /* 9 */ + "confmulti", + "confseq(123,456,789) confset(222,111) seq(8722) set(4196,48658)", + { 0x3,0x3, 0x00,0x7b, 0x01,0xc8, 0x03,0x15, + 0x4,0x2, 0x00,0xde, 0x00,0x6f, + 0x2,0x1, 0x22,0x12, + 0x1,0x2, 0x10,0x64, 0xbe,0x12 }, + 24, + { "(123 456 789) [111,222] 8722 {4196,48658}", + "8722 {4196,48658}", + 2, 4, NOT_ALL_PRIVATE, 123, 1, NULL_ASN }, + }, + { /* 10 */ + "seq4", + "seq(8466,2,52737,4096,8722,4)", + { 0x2,0x6, 0x21,0x12, 0x00,0x02, 0xce,0x01, 0x10,0x00, + 0x22,0x12, 0x00,0x04}, + 14, + { "8466 2 52737 4096 8722 4", + "8466 2 52737 4096 8722 4", + 6, 0, NOT_ALL_PRIVATE, 4096, 1, 8466 }, + }, + { /* 11 */ + "tripleseq1", + "seq(8466,2,52737) seq(4096,8722,4) seq(8722)", + { 0x2,0x3, 0x21,0x12, 0x00,0x02, 0xce,0x01, + 0x2,0x3, 0x10,0x00, 0x22,0x12, 0x00,0x04, + 0x2,0x1, 0x22,0x12}, + 20, + { "8466 2 52737 4096 8722 4 8722", + "8466 2 52737 4096 8722 4 8722", + 7, 0, NOT_ALL_PRIVATE, 4096, 1, 8466 }, + }, + { /* 12 */ + "someprivate", + "seq(8466,64512,52737,65535)", + { 0x2,0x4, 0x21,0x12, 0xfc,0x00, 0xce,0x01, 0xff,0xff }, + 10, + { "8466 64512 52737 65535", + "8466 64512 52737 65535", + 4, 0, NOT_ALL_PRIVATE, 65535, 4, 8466 }, + }, + { /* 13 */ + "allprivate", + "seq(65534,64512,64513,65535)", + { 0x2,0x4, 0xff,0xfe, 0xfc,0x00, 0xfc,0x01, 0xff,0xff }, + 10, + { "65534 64512 64513 65535", + "65534 64512 64513 65535", + 4, 0, ALL_PRIVATE, 65534, 4, 65534 }, + }, + { /* 14 */ + "long", + "seq(8466,3,52737,4096,34285,)", + { 0x2,0xfa, 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, + 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x85,0xed, }, + 502, + { "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285", + + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285", + 250, 0, NOT_ALL_PRIVATE, 4096, 4, 8466 }, + }, + { /* 15 */ + "seq1extra", + "seq(8466,3,52737,4096,3456)", + { 0x2,0x5, 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x0d,0x80 }, + 12, + { "8466 3 52737 4096 3456", + "8466 3 52737 4096 3456", + 5, 0, NOT_ALL_PRIVATE, 4096, 4, 8466 }, + }, + { /* 16 */ + "empty", + "", + {}, + 0, + { "", "", 0, 0, 0, 0, 0, 0 }, + }, + { /* 17 */ + "redundantset", + "seq(8466,3,52737,4096,3456) set(7099,8153,8153,8153)", + { 0x2,0x5, 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x0d,0x80, + 0x1,0x4, 0x1b,0xbb, 0x1f,0xd9, 0x1f,0xd9, 0x1f,0xd9 }, + 22, + { + /* We shouldn't ever /generate/ such paths. However, we should + * cope with them fine. + */ + "8466 3 52737 4096 3456 {7099,8153}", + "8466 3 52737 4096 3456 {7099,8153}", + 6, 0, NOT_ALL_PRIVATE, 4096, 4, 8466 }, + }, + { /* 18 */ + "reconcile_lead_asp", + "seq(6435,59408,21665) set(23456,23456,23456), seq(23456,23456,23456)", + { 0x2,0x3, 0x19,0x23, 0xe8,0x10, 0x54,0xa1, + 0x1,0x3, 0x5b,0xa0, 0x5b,0xa0, 0x5b,0xa0, + 0x2,0x3, 0x5b,0xa0, 0x5b,0xa0, 0x5b,0xa0 }, + 24, + { "6435 59408 21665 {23456} 23456 23456 23456", + "6435 59408 21665 {23456} 23456 23456 23456", + 7, 0, NOT_ALL_PRIVATE, 23456, 1, 6435 }, + }, + { /* 19 */ + "reconcile_new_asp", + "set(2457,61697,4369), seq(1842,41591,51793)", + { + 0x1,0x3, 0x09,0x99, 0xf1,0x01, 0x11,0x11, + 0x2,0x3, 0x07,0x32, 0xa2,0x77, 0xca,0x51 }, + 16, + { "{2457,4369,61697} 1842 41591 51793", + "{2457,4369,61697} 1842 41591 51793", + 4, 0, NOT_ALL_PRIVATE, 51793, 1, 2457 }, + }, + { /* 20 */ + "reconcile_confed", + "confseq(123,456,789) confset(456,124,788) seq(6435,59408,21665)" + " set(23456,23456,23456), seq(23456,23456,23456)", + { 0x3,0x3, 0x00,0x7b, 0x01,0xc8, 0x03,0x15, + 0x4,0x3, 0x01,0xc8, 0x00,0x7c, 0x03,0x14, + 0x2,0x3, 0x19,0x23, 0xe8,0x10, 0x54,0xa1, + 0x1,0x3, 0x5b,0xa0, 0x5b,0xa0, 0x5b,0xa0, + 0x2,0x3, 0x5b,0xa0, 0x5b,0xa0, 0x5b,0xa0 }, + 40, + { "(123 456 789) [124,456,788] 6435 59408 21665" + " {23456} 23456 23456 23456", + "6435 59408 21665 {23456} 23456 23456 23456", + 7, 4, NOT_ALL_PRIVATE, 23456, 1, 6435 }, + }, + { /* 21 */ + "reconcile_start_trans", + "seq(23456,23456,23456) seq(6435,59408,21665)", + { 0x2,0x3, 0x5b,0xa0, 0x5b,0xa0, 0x5b,0xa0, + 0x2,0x3, 0x19,0x23, 0xe8,0x10, 0x54,0xa1, }, + 16, + { "23456 23456 23456 6435 59408 21665", + "23456 23456 23456 6435 59408 21665", + 6, 0, NOT_ALL_PRIVATE, 21665, 1, 23456 }, + }, + { /* 22 */ + "reconcile_start_trans4", + "seq(1842,41591,51793) seq(6435,59408,21665)", + { 0x2,0x3, 0x07,0x32, 0xa2,0x77, 0xca,0x51, + 0x2,0x3, 0x19,0x23, 0xe8,0x10, 0x54,0xa1, }, + 16, + { "1842 41591 51793 6435 59408 21665", + "1842 41591 51793 6435 59408 21665", + 6, 0, NOT_ALL_PRIVATE, 41591, 1, 1842 }, + }, + { /* 23 */ + "reconcile_start_trans_error", + "seq(23456,23456,23456) seq(6435,59408)", + { 0x2,0x3, 0x5b,0xa0, 0x5b,0xa0, 0x5b,0xa0, + 0x2,0x2, 0x19,0x23, 0xe8,0x10, }, + 14, + { "23456 23456 23456 6435 59408", + "23456 23456 23456 6435 59408", + 5, 0, NOT_ALL_PRIVATE, 59408, 1, 23456 }, + }, + { /* 24 */ + "redundantset2", + "seq(8466,3,52737,4096,3456) set(7099,8153,8153,8153,7099)", + { 0x2,0x5, 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x0d,0x80, + 0x1,0x5, 0x1b,0xbb, 0x1f,0xd9, 0x1f,0xd9, 0x1f,0xd9, 0x1b,0xbb,}, + 24, + { + /* We should weed out duplicate set members. */ + "8466 3 52737 4096 3456 {7099,8153}", + "8466 3 52737 4096 3456 {7099,8153}", + 6, 0, NOT_ALL_PRIVATE, 4096, 4, 8466 }, + }, + { /* 25 */ + "zero-size overflow", + "#ASNs = 0, data = seq(8466 3 52737 4096 3456)", + { 0x2,0x0, 0x21,0x12, 0x00,0x03, 0xce,0x01, 0x10,0x00, 0x0d,0x80 }, + 12, + { NULL, NULL, + 0, 0, 0, 0, 0, 0 }, + }, + { /* 26 */ + "zero-size overflow + valid segment", + "seq(#AS=0:8466 3 52737),seq(4096 3456)", + { 0x2,0x0, 0x21,0x12, 0x00,0x03, 0xce,0x01, + 0x2,0x2, 0x10,0x00, 0x0d,0x80 }, + 14 + , + { NULL, NULL, + 0, 0, 0, 0, 0, 0 }, + }, + { /* 27 */ + "invalid segment type", + "type=8(4096 3456)", + { 0x8,0x2, 0x10,0x00, 0x0d,0x80 }, + 14 + , + { NULL, NULL, + 0, 0, 0, 0, 0, 0 }, + }, { 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; + const struct test_segment *segment; + const char *shouldbe; /* String it should evaluate to */ + const enum as4 { AS4_DATA, AS2_DATA } + as4; /* whether data should be as4 or not (ie as2) */ + const int result; /* expected result for bgp_attr_parse */ + const int cap; /* capabilities to set for peer */ + const char attrheader [1024]; + size_t len; + const struct test_segment *old_segment; +} aspath_tests [] = +{ + /* 0 */ + { + "basic test", + &test_segments[0], + "8466 3 52737 4096", + AS2_DATA, 0, + 0, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS, + BGP_ATTR_AS_PATH, + 10, + }, + COMMON_ATTR_SIZE + 3, + }, + /* 1 */ + { + "length too short", + &test_segments[0], + "8466 3 52737 4096", + AS2_DATA, -1, + 0, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS, + BGP_ATTR_AS_PATH, + 8, + }, + COMMON_ATTR_SIZE + 3, + }, + /* 2 */ + { + "length too long", + &test_segments[0], + "8466 3 52737 4096", + AS2_DATA, -1, + 0, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS, + BGP_ATTR_AS_PATH, + 12, + }, + COMMON_ATTR_SIZE + 3, + }, + /* 3 */ + { + "incorrect flag", + &test_segments[0], + "8466 3 52737 4096", + AS2_DATA, -1, + 0, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, + BGP_ATTR_AS_PATH, + 10, + }, + COMMON_ATTR_SIZE + 3, + }, + /* 4 */ + { + "as4_path, with as2 format data", + &test_segments[0], + "8466 3 52737 4096", + AS2_DATA, -1, + 0, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, + BGP_ATTR_AS4_PATH, + 10, + }, + COMMON_ATTR_SIZE + 3, + }, + /* 5 */ + { + "as4, with incorrect attr length", + &test_segments[0], + "8466 3 52737 4096", + AS4_DATA, -1, + PEER_CAP_AS4_RCV, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, + BGP_ATTR_AS4_PATH, + 10, + }, + COMMON_ATTR_SIZE + 3, + }, + /* 6 */ + { + "basic 4-byte as-path", + &test_segments[0], + "8466 3 52737 4096", + AS4_DATA, 0, + PEER_CAP_AS4_RCV|PEER_CAP_AS4_ADV, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS, + BGP_ATTR_AS_PATH, + 18, + }, + COMMON_ATTR_SIZE + 3, + }, + /* 7 */ + { + "4b AS_PATH: too short", + &test_segments[0], + "8466 3 52737 4096", + AS4_DATA, -1, + PEER_CAP_AS4_RCV|PEER_CAP_AS4_ADV, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS, + BGP_ATTR_AS_PATH, + 16, + }, + COMMON_ATTR_SIZE + 3, + }, + /* 8 */ + { + "4b AS_PATH: too long", + &test_segments[0], + "8466 3 52737 4096", + AS4_DATA, -1, + PEER_CAP_AS4_RCV|PEER_CAP_AS4_ADV, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS, + BGP_ATTR_AS_PATH, + 20, + }, + COMMON_ATTR_SIZE + 3, + }, + /* 9 */ + { + "4b AS_PATH: too long2", + &test_segments[0], + "8466 3 52737 4096", + AS4_DATA, -1, + PEER_CAP_AS4_RCV|PEER_CAP_AS4_ADV, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS, + BGP_ATTR_AS_PATH, + 22, + }, + COMMON_ATTR_SIZE + 3, + }, + /* 10 */ + { + "4b AS_PATH: bad flags", + &test_segments[0], + "8466 3 52737 4096", + AS4_DATA, -1, + PEER_CAP_AS4_RCV|PEER_CAP_AS4_ADV, + { COMMON_ATTRS, + BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL, + BGP_ATTR_AS_PATH, + 18, + }, + COMMON_ATTR_SIZE + 3, + }, + /* 11 */ + { + "4b AS4_PATH w/o AS_PATH", + &test_segments[6], + NULL, + AS4_DATA, -1, + PEER_CAP_AS4_ADV, + { 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, + }, + COMMON_ATTR_SIZE + 3, + &test_segments[0], + }, + { NULL, NULL, NULL, 0, 0, 0, { 0 }, 0 }, +}; + +/* prepending tests */ +static struct tests { + const struct test_segment *test1; + const struct test_segment *test2; + struct test_spec sp; +} prepend_tests[] = +{ + /* 0 */ + { &test_segments[0], &test_segments[1], + { "8466 3 52737 4096 8722 4", + "8466 3 52737 4096 8722 4", + 6, 0, NOT_ALL_PRIVATE, 4096, 1, 8466 }, + }, + /* 1 */ + { &test_segments[1], &test_segments[3], + { "8722 4 8482 51457 {5204}", + "8722 4 8482 51457 {5204}", + 5, 0, NOT_ALL_PRIVATE, 5204, 1, 8722 } + }, + /* 2 */ + { &test_segments[3], &test_segments[4], + { "8482 51457 {5204} 8467 59649 {4196,48658} {17322,30745}", + "8482 51457 {5204} 8467 59649 {4196,48658} {17322,30745}", + 7, 0, NOT_ALL_PRIVATE, 5204, 1, 8482 }, + }, + /* 3 */ + { &test_segments[4], &test_segments[5], + { "8467 59649 {4196,48658} {17322,30745} 6435 59408 21665" + " {2457,4369,61697} 1842 41590 51793", + "8467 59649 {4196,48658} {17322,30745} 6435 59408 21665" + " {2457,4369,61697} 1842 41590 51793", + 11, 0, NOT_ALL_PRIVATE, 61697, 1, 8467 } + }, + /* 4 */ + { &test_segments[5], &test_segments[6], + { "6435 59408 21665 {2457,4369,61697} 1842 41590 51793", + "6435 59408 21665 {2457,4369,61697} 1842 41590 51793", + 7, 0, NOT_ALL_PRIVATE, 1842, 1, 6435 }, + }, + /* 5 */ + { &test_segments[6], &test_segments[7], + { "(123 456 789) (123 456 789) (111 222)", + "", + 0, 8, NOT_ALL_PRIVATE, 111, 1, 0 } + }, + { &test_segments[7], &test_segments[8], + { "(123 456 789) (111 222) [123,456,789]", + "", + 0, 6, NOT_ALL_PRIVATE, 111, 1, 0 } + }, + { &test_segments[8], &test_segments[9], + { "[123,456,789] (123 456 789) [111,222] 8722 {4196,48658}", + "[123,456,789] (123 456 789) [111,222] 8722 {4196,48658}", + 2, 5, NOT_ALL_PRIVATE, 456, 1, NULL_ASN }, + }, + { &test_segments[9], &test_segments[8], + { "(123 456 789) [111,222] 8722 {4196,48658} [123,456,789]", + "8722 {4196,48658} [123,456,789]", + 2, 5, NOT_ALL_PRIVATE, 48658, 1, NULL_ASN }, + }, + { &test_segments[14], &test_segments[11], + { "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 2 52737 4096 8722 4 8722", + + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 3 52737 4096 34285 8466 3 52737 4096 34285 " + "8466 2 52737 4096 8722 4 8722", + 257, 0, NOT_ALL_PRIVATE, 4096, 1000, 8466 }, + }, + { NULL, NULL, { NULL, 0, 0, 0, 0, 0, 0, } }, +}; + +struct tests reconcile_tests[] = +{ + { &test_segments[18], &test_segments[19], + { "6435 59408 21665 {2457,4369,61697} 1842 41591 51793", + "6435 59408 21665 {2457,4369,61697} 1842 41591 51793", + 7, 0, NOT_ALL_PRIVATE, 51793, 1, 6435 }, + }, + { &test_segments[19], &test_segments[18], + /* AS_PATH (19) has more hops than NEW_AS_PATH, + * so just AS_PATH should be used (though, this practice + * is bad imho). + */ + { "{2457,4369,61697} 1842 41591 51793 6435 59408 21665 {23456} 23456 23456 23456", + "{2457,4369,61697} 1842 41591 51793 6435 59408 21665 {23456} 23456 23456 23456", + 11, 0, NOT_ALL_PRIVATE, 51793, 1, 6435 }, + }, + { &test_segments[20], &test_segments[19], + { "(123 456 789) [124,456,788] 6435 59408 21665" + " {2457,4369,61697} 1842 41591 51793", + "6435 59408 21665 {2457,4369,61697} 1842 41591 51793", + 7, 4, NOT_ALL_PRIVATE, 51793, 1, 6435 }, + }, + { &test_segments[21], &test_segments[22], + { "1842 41591 51793 6435 59408 21665", + "1842 41591 51793 6435 59408 21665", + 6, 0, NOT_ALL_PRIVATE, 51793, 1, 1842 }, + }, + { &test_segments[23], &test_segments[22], + { "23456 23456 23456 6435 59408 1842 41591 51793 6435 59408 21665", + "23456 23456 23456 6435 59408 1842 41591 51793 6435 59408 21665", + 11, 0, NOT_ALL_PRIVATE, 51793, 1, 1842 }, + }, + { NULL, NULL, { NULL, 0, 0, 0, 0, 0, 0, } }, +}; + +struct tests aggregate_tests[] = +{ + { &test_segments[0], &test_segments[2], + { "8466 3 52737 4096 {4,8722}", + "8466 3 52737 4096 {4,8722}", + 5, 0, NOT_ALL_PRIVATE, 4, 1, 8466 }, + }, + { &test_segments[2], &test_segments[0], + { "8466 3 52737 4096 {4,8722}", + "8466 3 52737 4096 {4,8722}", + 5, 0, NOT_ALL_PRIVATE, 8722, 1, 8466 }, + }, + { &test_segments[2], &test_segments[10], + { "8466 {2,3,4,4096,8722,52737}", + "8466 {2,3,4,4096,8722,52737}", + 2, 0, NOT_ALL_PRIVATE, 8722, 5, 8466 }, + }, + { &test_segments[10], &test_segments[2], + { "8466 {2,3,4,4096,8722,52737}", + "8466 {2,3,4,4096,8722,52737}", + 2, 0, NOT_ALL_PRIVATE, 2, 20000, 8466 }, + }, + + { &test_segments[5], &test_segments[18], + { "6435 59408 21665 {1842,2457,4369,23456,41590,51793,61697}", + "6435 59408 21665 {1842,2457,4369,23456,41590,51793,61697}", + 4, 0, NOT_ALL_PRIVATE, 41590, 1, 6435 }, + }, + + { NULL, NULL, { NULL, 0, 0} }, +}; + +struct compare_tests +{ + int test_index1; + int test_index2; +#define CMP_RES_YES 1 +#define CMP_RES_NO 0 + char shouldbe_cmp; + char shouldbe_confed; +} left_compare [] = +{ + { 0, 1, CMP_RES_NO, CMP_RES_NO }, + { 0, 2, CMP_RES_YES, CMP_RES_NO }, + { 0, 11, CMP_RES_YES, CMP_RES_NO }, + { 0, 15, CMP_RES_YES, CMP_RES_NO }, + { 0, 16, CMP_RES_NO, CMP_RES_NO }, + { 1, 11, CMP_RES_NO, CMP_RES_NO }, + { 6, 7, CMP_RES_NO, CMP_RES_YES }, + { 6, 8, CMP_RES_NO, CMP_RES_NO }, + { 7, 8, CMP_RES_NO, CMP_RES_NO }, + { 1, 9, CMP_RES_YES, CMP_RES_NO }, + { 0, 9, CMP_RES_NO, CMP_RES_NO }, + { 3, 9, CMP_RES_NO, CMP_RES_NO }, + { 0, 6, CMP_RES_NO, CMP_RES_NO }, + { 1, 6, CMP_RES_NO, CMP_RES_NO }, + { 0, 8, CMP_RES_NO, CMP_RES_NO }, + { 1, 8, CMP_RES_NO, CMP_RES_NO }, + { 11, 6, CMP_RES_NO, CMP_RES_NO }, + { 11, 7, CMP_RES_NO, CMP_RES_NO }, + { 11, 8, CMP_RES_NO, CMP_RES_NO }, + { 9, 6, CMP_RES_NO, CMP_RES_YES }, + { 9, 7, CMP_RES_NO, CMP_RES_YES }, + { 9, 8, CMP_RES_NO, CMP_RES_NO }, +}; + +/* make an aspath from a data stream */ +static struct aspath * +make_aspath (const u_char *data, size_t len, int use32bit) +{ + struct stream *s = NULL; + struct aspath *as; + + if (len) + { + s = stream_new (len); + stream_put (s, data, len); + } + as = aspath_parse (s, len, use32bit); + + if (s) + stream_free (s); + + return as; +} + +static void +printbytes (const u_char *bytes, int len) +{ + int i = 0; + while (i < len) + { + if (i % 2) + printf ("%02hhx%s", bytes[i], " "); + else + printf ("0x%02hhx", bytes[i]); + i++; + } + printf ("\n"); +} + +/* validate the given aspath */ +static int +validate (struct aspath *as, const struct test_spec *sp) +{ + size_t bytes, bytes4; + int fails = 0; + const u_char *out; + static struct stream *s; + struct aspath *asinout, *asconfeddel, *asstr, *as4; + + if (as == NULL && sp->shouldbe == NULL) + { + printf ("Correctly failed to parse\n"); + return fails; + } + + out = aspath_snmp_pathseg (as, &bytes); + asinout = make_aspath (out, bytes, 0); + + /* Excercise AS4 parsing a bit, with a dogfood test */ + if (!s) + s = stream_new (4096); + bytes4 = aspath_put (s, as, 1); + as4 = make_aspath (STREAM_DATA(s), bytes4, 1); + + asstr = aspath_str2aspath (sp->shouldbe); + + asconfeddel = aspath_delete_confed_seq (aspath_dup (asinout)); + + printf ("got: %s\n", aspath_print(as)); + + /* the parsed path should match the specified 'shouldbe' string. + * We should pass the "eat our own dog food" test, be able to output + * this path and then input it again. Ie the path resulting from: + * + * aspath_parse(aspath_put(as)) + * + * should: + * + * - also match the specified 'shouldbe' value + * - hash to same value as original path + * - have same hops and confed counts as original, and as the + * the specified counts + * + * aspath_str2aspath() and shouldbe should match + * + * We do the same for: + * + * aspath_parse(aspath_put(as,USE32BIT)) + * + * Confederation related tests: + * - aspath_delete_confed_seq(aspath) should match shouldbe_confed + * - aspath_delete_confed_seq should be idempotent. + */ + if (strcmp(aspath_print (as), sp->shouldbe) + /* hash validation */ + || (aspath_key_make (as) != aspath_key_make (asinout)) + /* by string */ + || strcmp(aspath_print (asinout), sp->shouldbe) + /* By 4-byte parsing */ + || strcmp(aspath_print (as4), sp->shouldbe) + /* by various path counts */ + || (aspath_count_hops (as) != sp->hops) + || (aspath_count_confeds (as) != sp->confeds) + || (aspath_count_hops (asinout) != sp->hops) + || (aspath_count_confeds (asinout) != sp->confeds)) + { + failed++; + fails++; + printf ("shouldbe:\n%s\n", sp->shouldbe); + printf ("as4:\n%s\n", aspath_print (as4)); + printf ("hash keys: in: %d out->in: %d\n", + aspath_key_make (as), aspath_key_make (asinout)); + printf ("hops: %d, counted %d %d\n", sp->hops, + aspath_count_hops (as), + aspath_count_hops (asinout) ); + printf ("confeds: %d, counted %d %d\n", sp->confeds, + aspath_count_confeds (as), + aspath_count_confeds (asinout)); + printf ("out->in:\n%s\nbytes: ", aspath_print(asinout)); + printbytes (out, bytes); + } + /* basic confed related tests */ + if ((aspath_print (asconfeddel) == NULL + && sp->shouldbe_delete_confed != NULL) + || (aspath_print (asconfeddel) != NULL + && sp->shouldbe_delete_confed == NULL) + || strcmp(aspath_print (asconfeddel), sp->shouldbe_delete_confed) + /* delete_confed_seq should be idempotent */ + || (aspath_key_make (asconfeddel) + != aspath_key_make (aspath_delete_confed_seq (asconfeddel)))) + { + failed++; + fails++; + printf ("confed_del: %s\n", aspath_print (asconfeddel)); + printf ("should be: %s\n", sp->shouldbe_delete_confed); + } + /* aspath_str2aspath test */ + if ((aspath_print (asstr) == NULL && sp->shouldbe != NULL) + || (aspath_print (asstr) != NULL && sp->shouldbe == NULL) + || strcmp(aspath_print (asstr), sp->shouldbe)) + { + failed++; + fails++; + printf ("asstr: %s\n", aspath_print (asstr)); + } + + /* loop, private and first as checks */ + if ((sp->does_loop && aspath_loop_check (as, sp->does_loop) == 0) + || (sp->doesnt_loop && aspath_loop_check (as, sp->doesnt_loop) != 0) + || (aspath_private_as_check (as) != sp->private_as) + || (aspath_firstas_check (as,sp->first) + && sp->first == 0)) + { + failed++; + fails++; + printf ("firstas: %d, got %d\n", sp->first, + aspath_firstas_check (as,sp->first)); + printf ("loop does: %d %d, doesnt: %d %d\n", + sp->does_loop, aspath_loop_check (as, sp->does_loop), + sp->doesnt_loop, aspath_loop_check (as, sp->doesnt_loop)); + printf ("private check: %d %d\n", sp->private_as, + aspath_private_as_check (as)); + } + aspath_unintern (&asinout); + aspath_unintern (&as4); + + aspath_free (asconfeddel); + aspath_free (asstr); + stream_reset (s); + + return fails; +} + +static void +empty_get_test () +{ + struct aspath *as = aspath_empty_get (); + struct test_spec sp = { "", "", 0, 0, 0, 0, 0, 0 }; + + printf ("empty_get_test, as: %s\n",aspath_print (as)); + if (!validate (as, &sp)) + printf ("%s\n", OK); + else + printf ("%s!\n", FAILED); + + printf ("\n"); + + aspath_free (as); +} + +/* basic parsing test */ +static void +parse_test (struct test_segment *t) +{ + struct aspath *asp; + + printf ("%s: %s\n", t->name, t->desc); + + asp = make_aspath (t->asdata, t->len, 0); + + printf ("aspath: %s\nvalidating...:\n", aspath_print (asp)); + + if (!validate (asp, &t->sp)) + printf (OK "\n"); + else + printf (FAILED "\n"); + + printf ("\n"); + + if (asp) + aspath_unintern (&asp); +} + +/* prepend testing */ +static void +prepend_test (struct tests *t) +{ + struct aspath *asp1, *asp2, *ascratch; + + printf ("prepend %s: %s\n", t->test1->name, t->test1->desc); + printf ("to %s: %s\n", t->test2->name, t->test2->desc); + + asp1 = make_aspath (t->test1->asdata, t->test1->len, 0); + asp2 = make_aspath (t->test2->asdata, t->test2->len, 0); + + ascratch = aspath_dup (asp2); + aspath_unintern (&asp2); + + asp2 = aspath_prepend (asp1, ascratch); + + printf ("aspath: %s\n", aspath_print (asp2)); + + if (!validate (asp2, &t->sp)) + printf ("%s\n", OK); + else + printf ("%s!\n", FAILED); + + printf ("\n"); + aspath_unintern (&asp1); + aspath_free (asp2); +} + +/* empty-prepend testing */ +static void +empty_prepend_test (struct test_segment *t) +{ + struct aspath *asp1, *asp2, *ascratch; + + printf ("empty prepend %s: %s\n", t->name, t->desc); + + asp1 = make_aspath (t->asdata, t->len, 0); + asp2 = aspath_empty (); + + ascratch = aspath_dup (asp2); + aspath_unintern (&asp2); + + asp2 = aspath_prepend (asp1, ascratch); + + printf ("aspath: %s\n", aspath_print (asp2)); + + if (!validate (asp2, &t->sp)) + printf (OK "\n"); + else + printf (FAILED "!\n"); + + printf ("\n"); + if (asp1) + aspath_unintern (&asp1); + aspath_free (asp2); +} + +/* as2+as4 reconciliation testing */ +static void +as4_reconcile_test (struct tests *t) +{ + struct aspath *asp1, *asp2, *ascratch; + + printf ("reconciling %s:\n %s\n", t->test1->name, t->test1->desc); + printf ("with %s:\n %s\n", t->test2->name, t->test2->desc); + + asp1 = make_aspath (t->test1->asdata, t->test1->len, 0); + asp2 = make_aspath (t->test2->asdata, t->test2->len, 0); + + ascratch = aspath_reconcile_as4 (asp1, asp2); + + if (!validate (ascratch, &t->sp)) + printf (OK "\n"); + else + printf (FAILED "!\n"); + + printf ("\n"); + aspath_unintern (&asp1); + aspath_unintern (&asp2); + aspath_free (ascratch); +} + + +/* aggregation testing */ +static void +aggregate_test (struct tests *t) +{ + struct aspath *asp1, *asp2, *ascratch; + + printf ("aggregate %s: %s\n", t->test1->name, t->test1->desc); + printf ("with %s: %s\n", t->test2->name, t->test2->desc); + + asp1 = make_aspath (t->test1->asdata, t->test1->len, 0); + asp2 = make_aspath (t->test2->asdata, t->test2->len, 0); + + ascratch = aspath_aggregate (asp1, asp2); + + if (!validate (ascratch, &t->sp)) + printf (OK "\n"); + else + printf (FAILED "!\n"); + + printf ("\n"); + aspath_unintern (&asp1); + aspath_unintern (&asp2); + aspath_free (ascratch); +/* aspath_unintern (ascratch);*/ +} + +/* cmp_left tests */ +static void +cmp_test () +{ + unsigned int i; +#define CMP_TESTS_MAX \ + (sizeof(left_compare) / sizeof (struct compare_tests)) + + for (i = 0; i < CMP_TESTS_MAX; i++) + { + struct test_segment *t1 = &test_segments[left_compare[i].test_index1]; + struct test_segment *t2 = &test_segments[left_compare[i].test_index2]; + struct aspath *asp1, *asp2; + + printf ("left cmp %s: %s\n", t1->name, t1->desc); + printf ("and %s: %s\n", t2->name, t2->desc); + + asp1 = make_aspath (t1->asdata, t1->len, 0); + asp2 = make_aspath (t2->asdata, t2->len, 0); + + if (aspath_cmp_left (asp1, asp2) != left_compare[i].shouldbe_cmp + || aspath_cmp_left (asp2, asp1) != left_compare[i].shouldbe_cmp + || aspath_cmp_left_confed (asp1, asp2) + != left_compare[i].shouldbe_confed + || aspath_cmp_left_confed (asp2, asp1) + != left_compare[i].shouldbe_confed) + { + failed++; + printf (FAILED "\n"); + printf ("result should be: cmp: %d, confed: %d\n", + left_compare[i].shouldbe_cmp, + left_compare[i].shouldbe_confed); + printf ("got: cmp %d, cmp_confed: %d\n", + aspath_cmp_left (asp1, asp2), + aspath_cmp_left_confed (asp1, asp2)); + printf("path1: %s\npath2: %s\n", aspath_print (asp1), + aspath_print (asp2)); + } + else + printf (OK "\n"); + + printf ("\n"); + aspath_unintern (&asp1); + aspath_unintern (&asp2); + } +} + +static int +handle_attr_test (struct aspath_tests *t) +{ + struct bgp bgp = { 0 }; + struct peer peer = { 0 }; + struct attr attr = { 0 }; + int ret; + int initfail = failed; + struct aspath *asp; + size_t datalen; + + asp = make_aspath (t->segment->asdata, t->segment->len, 0); + + peer.ibuf = stream_new (BGP_MAX_PACKET_SIZE); + peer.obuf = stream_fifo_new (); + peer.bgp = &bgp; + peer.host = (char *)"none"; + peer.fd = -1; + peer.cap = t->cap; + + 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 %zd\n", datalen); + failed++; + } + if (ret != 0) + goto out; + + if (t->shouldbe && attr.aspath == NULL) + { + printf ("aspath is NULL, but should be: %s\n", t->shouldbe); + failed++; + } + if (t->shouldbe && attr.aspath && strcmp (attr.aspath->str, t->shouldbe)) + { + printf ("attr str and 'shouldbe' mismatched!\n" + "attr str: %s\n" + "shouldbe: %s\n", + 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); + if (asp) + aspath_unintern (&asp); + return failed - initfail; +} + +static void +attr_test (struct aspath_tests *t) +{ + printf ("%s\n", t->desc); + printf ("%s\n\n", handle_attr_test (t) ? FAILED : OK); +} + +int +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) + { + printf ("test %u\n", i); + parse_test (&test_segments[i]); + empty_prepend_test (&test_segments[i++]); + } + + i = 0; + while (prepend_tests[i].test1) + { + printf ("prepend test %u\n", i); + prepend_test (&prepend_tests[i++]); + } + + i = 0; + while (aggregate_tests[i].test1) + { + printf ("aggregate test %u\n", i); + aggregate_test (&aggregate_tests[i++]); + } + + i = 0; + + while (reconcile_tests[i].test1) + { + printf ("reconcile test %u\n", i); + as4_reconcile_test (&reconcile_tests[i++]); + } + + i = 0; + + cmp_test(); + + i = 0; + + empty_get_test(); + + i = 0; + + while (aspath_tests[i].desc) + { + printf ("aspath_attr test %d\n", i); + attr_test (&aspath_tests[i++]); + } + + printf ("failures: %d\n", failed); + printf ("aspath count: %ld\n", aspath_count()); + + return (failed + aspath_count()); +} diff --git a/tests/bgp_capability_test.c b/tests/bgp_capability_test.c new file mode 100644 index 0000000..a381351 --- /dev/null +++ b/tests/bgp_capability_test.c @@ -0,0 +1,697 @@ +/* + * 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" +#define VT100_GREEN "\x1b[32m" +#define VT100_YELLOW "\x1b[33m" + + +#define CAPABILITY 0 +#define DYNCAP 1 +#define OPT_PARAM 2 + +/* need these to link in libbgp */ +struct zebra_privs_t *bgpd_privs = NULL; +struct thread_master *master = NULL; + +static int failed = 0; +static int tty = 0; + +/* test segments to parse and validate, and use for other tests */ +static struct test_segment { + const char *name; + const char *desc; + const u_char data[1024]; + int len; +#define SHOULD_PARSE 0 +#define SHOULD_ERR -1 + int parses; /* whether it should parse or not */ + as_t peek_for; /* what peek_for_as4_capability should say */ + + /* AFI/SAFI validation */ + int validate_afi; + afi_t afi; + safi_t safi; +#define VALID_AFI 1 +#define INVALID_AFI 0 + int afi_valid; +} test_segments [] = +{ + /* 0 */ + { "caphdr", + "capability header, and no more", + { CAPABILITY_CODE_REFRESH, 0x0 }, + 2, SHOULD_PARSE, + }, + /* 1 */ + { "nodata", + "header, no data but length says there is", + { 0x1, 0xa }, + 2, SHOULD_ERR, + }, + /* 2 */ + { "padded", + "valid, with padding", + { CAPABILITY_CODE_REFRESH, 0x2, 0x0, 0x0 }, + 4, SHOULD_PARSE, + }, + /* 3 */ + { "minsize", + "violates minsize requirement", + { CAPABILITY_CODE_ORF, 0x2, 0x0, 0x0 }, + 4, SHOULD_ERR, + }, + { NULL, NULL, {0}, 0, 0}, +}; + +static struct test_segment mp_segments[] = +{ + { "MP4", + "MP IP/Uni", + { 0x1, 0x4, 0x0, 0x1, 0x0, 0x1 }, + 6, SHOULD_PARSE, 0, + 1, AFI_IP, SAFI_UNICAST, VALID_AFI, + }, + { "MPv6", + "MP IPv6/Uni", + { 0x1, 0x4, 0x0, 0x2, 0x0, 0x1 }, + 6, SHOULD_PARSE, 0, + 1, AFI_IP6, SAFI_UNICAST, VALID_AFI, + }, + /* 5 */ + { "MP2", + "MP IP/Multicast", + { CAPABILITY_CODE_MP, 0x4, 0x0, 0x1, 0x0, 0x2 }, + 6, SHOULD_PARSE, 0, + 1, AFI_IP, SAFI_MULTICAST, VALID_AFI, + }, + /* 6 */ + { "MP3", + "MP IP6/MPLS-labeled VPN", + { CAPABILITY_CODE_MP, 0x4, 0x0, 0x2, 0x0, 0x80 }, + 6, SHOULD_PARSE, 0, + 1, AFI_IP6, SAFI_MPLS_LABELED_VPN, VALID_AFI, + }, + /* 7 */ + { "MP5", + "MP IP6/MPLS-VPN", + { CAPABILITY_CODE_MP, 0x4, 0x0, 0x2, 0x0, 0x4 }, + 6, SHOULD_PARSE, 0, + 1, AFI_IP6, SAFI_MPLS_VPN, VALID_AFI, + }, + /* 8 */ + { "MP6", + "MP IP4/MPLS-laveled VPN", + { CAPABILITY_CODE_MP, 0x4, 0x0, 0x1, 0x0, 0x80 }, + 6, SHOULD_PARSE, 0, + 1, AFI_IP, SAFI_MPLS_LABELED_VPN, VALID_AFI, + }, + /* 10 */ + { "MP8", + "MP unknown AFI/SAFI", + { CAPABILITY_CODE_MP, 0x4, 0x0, 0xa, 0x0, 0x81 }, + 6, SHOULD_PARSE, 0, + 1, 0xa, 0x81, INVALID_AFI, /* parses, but unknown */ + }, + /* 11 */ + { "MP-short", + "MP IP4/Unicast, length too short (< minimum)", + { CAPABILITY_CODE_MP, 0x2, 0x0, 0x1, 0x0, 0x1 }, + 6, SHOULD_ERR, + }, + /* 12 */ + { "MP-overflow", + "MP IP4/Unicast, length too long", + { CAPABILITY_CODE_MP, 0x6, 0x0, 0x1, 0x0, 0x1 }, + 6, SHOULD_ERR, 0, + 1, AFI_IP, SAFI_UNICAST, VALID_AFI, + }, + { NULL, NULL, {0}, 0, 0} +}; + +static struct test_segment misc_segments[] = +{ + /* 13 */ + { "ORF", + "ORF, simple, single entry, single tuple", + { /* hdr */ CAPABILITY_CODE_ORF, 0x7, + /* mpc */ 0x0, 0x1, 0x0, 0x1, + /* num */ 0x1, + /* tuples */ 0x40, 0x3 + }, + 9, SHOULD_PARSE, + }, + /* 14 */ + { "ORF-many", + "ORF, multi entry/tuple", + { /* hdr */ CAPABILITY_CODE_ORF, 0x21, + /* mpc */ 0x0, 0x1, 0x0, 0x1, + /* num */ 0x3, + /* tuples */ 0x40, ORF_MODE_BOTH, + 0x80, ORF_MODE_RECEIVE, + 0x80, ORF_MODE_SEND, + /* mpc */ 0x0, 0x2, 0x0, 0x1, + /* num */ 0x3, + /* tuples */ 0x40, ORF_MODE_BOTH, + 0x80, ORF_MODE_RECEIVE, + 0x80, ORF_MODE_SEND, + /* mpc */ 0x0, 0x2, 0x0, 0x2, + /* num */ 0x3, + /* tuples */ 0x40, ORF_MODE_RECEIVE, + 0x80, ORF_MODE_SEND, + 0x80, ORF_MODE_BOTH, + }, + 35, SHOULD_PARSE, + }, + /* 15 */ + { "ORFlo", + "ORF, multi entry/tuple, hdr length too short", + { /* hdr */ CAPABILITY_CODE_ORF, 0x15, + /* mpc */ 0x0, 0x1, 0x0, 0x1, + /* num */ 0x3, + /* tuples */ 0x40, 0x3, + 0x80, 0x1, + 0x80, 0x2, + /* mpc */ 0x0, 0x1, 0x0, 0x1, + /* num */ 0x3, + /* tuples */ 0x40, 0x3, + 0x80, 0x1, + 0x80, 0x2, + /* mpc */ 0x0, 0x2, 0x0, 0x2, + /* num */ 0x3, + /* tuples */ 0x40, 0x3, + 0x80, 0x1, + 0x80, 0x2, + }, + 35, SHOULD_ERR, /* It should error on invalid Route-Refresh.. */ + }, + /* 16 */ + { "ORFlu", + "ORF, multi entry/tuple, length too long", + { /* hdr */ 0x3, 0x22, + /* mpc */ 0x0, 0x1, 0x0, 0x1, + /* num */ 0x3, + /* tuples */ 0x40, 0x3, + 0x80, 0x1, + 0x80, 0x2, + /* mpc */ 0x0, 0x2, 0x0, 0x1, + /* num */ 0x3, + /* tuples */ 0x40, 0x3, + 0x80, 0x1, + 0x80, 0x2, + /* mpc */ 0x0, 0x2, 0x0, 0x2, + /* num */ 0x3, + /* tuples */ 0x40, 0x3, + 0x80, 0x1, + 0x80, 0x2, + }, + 35, SHOULD_ERR + }, + /* 17 */ + { "ORFnu", + "ORF, multi entry/tuple, entry number too long", + { /* hdr */ 0x3, 0x21, + /* mpc */ 0x0, 0x1, 0x0, 0x1, + /* num */ 0x3, + /* tuples */ 0x40, 0x3, + 0x80, 0x1, + 0x80, 0x2, + /* mpc */ 0x0, 0x2, 0x0, 0x1, + /* num */ 0x4, + /* tuples */ 0x40, 0x3, + 0x80, 0x1, + 0x80, 0x2, + /* mpc */ 0x0, 0x2, 0x0, 0x2, + /* num */ 0x3, + /* tuples */ 0x40, 0x3, + 0x80, 0x1, + 0x80, 0x2, + }, + 35, SHOULD_PARSE, /* parses, but last few tuples should be gibberish */ + }, + /* 18 */ + { "ORFno", + "ORF, multi entry/tuple, entry number too short", + { /* hdr */ 0x3, 0x21, + /* mpc */ 0x0, 0x1, 0x0, 0x1, + /* num */ 0x3, + /* tuples */ 0x40, 0x3, + 0x80, 0x1, + 0x80, 0x2, + /* mpc */ 0x0, 0x2, 0x0, 0x1, + /* num */ 0x1, + /* tuples */ 0x40, 0x3, + 0x80, 0x1, + 0x80, 0x2, + /* mpc */ 0x0, 0x2, 0x0, 0x2, + /* num */ 0x3, + /* tuples */ 0x40, 0x3, + 0x80, 0x1, + 0x80, 0x2, + }, + 35, SHOULD_PARSE, /* Parses, but should get gibberish afi/safis */ + }, + /* 17 */ + { "ORFpad", + "ORF, multi entry/tuple, padded to align", + { /* hdr */ 0x3, 0x22, + /* mpc */ 0x0, 0x1, 0x0, 0x1, + /* num */ 0x3, + /* tuples */ 0x40, 0x3, + 0x80, 0x1, + 0x80, 0x2, + /* mpc */ 0x0, 0x2, 0x0, 0x1, + /* num */ 0x3, + /* tuples */ 0x40, 0x3, + 0x80, 0x1, + 0x80, 0x2, + /* mpc */ 0x0, 0x2, 0x0, 0x2, + /* num */ 0x3, + /* tuples */ 0x40, 0x3, + 0x80, 0x1, + 0x80, 0x2, + 0x00, + }, + 36, SHOULD_PARSE, + }, + /* 19 */ + { "AS4", + "AS4 capability", + { 0x41, 0x4, 0xab, 0xcd, 0xef, 0x12 }, /* AS: 2882400018 */ + 6, SHOULD_PARSE, 2882400018, + }, + { "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, + /* R-bit, time */ 0xf1, 0x12, + /* afi */ 0x0, 0x1, + /* safi */ 0x1, + /* flags */ 0xf, + /* afi */ 0x0, 0x2, + /* safi */ 0x1, + /* flags */ 0x0, + /* afi */ 0x0, 0x2, + /* safi */ 0x2, + /* flags */ 0x1, + }, + 16, SHOULD_PARSE, + }, + { "GR-short", + "GR capability, but header length too short", + { /* hdr */ 0x40, 0xa, + /* R-bit, time */ 0xf1, 0x12, + /* afi */ 0x0, 0x1, + /* safi */ 0x1, + /* flags */ 0xf, + /* afi */ 0x0, 0x2, + /* safi */ 0x1, + /* flags */ 0x0, + /* afi */ 0x0, 0x2, + /* safi */ 0x2, + /* flags */ 0x1, + }, + 15 /* array is 16 though */, SHOULD_ERR, + }, + { "GR-long", + "GR capability, but header length too long", + { /* hdr */ 0x40, 0xf, + /* R-bit, time */ 0xf1, 0x12, + /* afi */ 0x0, 0x1, + /* safi */ 0x1, + /* flags */ 0xf, + /* afi */ 0x0, 0x2, + /* safi */ 0x1, + /* flags */ 0x0, + /* afi */ 0x0, 0x2, + /* safi */ 0x2, + /* flags */ 0x01, + }, + 16, SHOULD_ERR, + }, + { "GR-trunc", + "GR capability, but truncated", + { /* hdr */ 0x40, 0xf, + /* R-bit, time */ 0xf1, 0x12, + /* afi */ 0x0, 0x1, + /* safi */ 0x1, + /* flags */ 0xf, + /* afi */ 0x0, 0x2, + /* safi */ 0x1, + /* flags */ 0x0, + /* afi */ 0x0, 0x2, + /* safi */ 0x2, + /* flags */ 0x1, + }, + 15, SHOULD_ERR, + }, + { "GR-empty", + "GR capability, but empty.", + { /* hdr */ 0x40, 0x0, + }, + 2, SHOULD_ERR, + }, + { "MP-empty", + "MP capability, but empty.", + { /* hdr */ 0x1, 0x0, + }, + 2, SHOULD_ERR, + }, + { "ORF-empty", + "ORF capability, but empty.", + { /* hdr */ 0x3, 0x0, + }, + 2, SHOULD_ERR, + }, + { "AS4-empty", + "AS4 capability, but empty.", + { /* hdr */ 0x41, 0x0, + }, + 2, SHOULD_ERR, + }, + { "dyn-empty", + "Dynamic capability, but empty.", + { /* hdr */ 0x42, 0x0, + }, + 2, SHOULD_PARSE, + }, + { "dyn-old", + "Dynamic capability (deprecated version)", + { CAPABILITY_CODE_DYNAMIC, 0x0 }, + 2, SHOULD_PARSE, + }, + { NULL, NULL, {0}, 0, 0} +}; + +/* DYNAMIC message */ +struct test_segment dynamic_cap_msgs[] = +{ + { "DynCap", + "Dynamic Capability Message, IP/Multicast", + { 0x0, 0x1, 0x4, 0x0, 0x1, 0x0, 0x2 }, + 7, SHOULD_PARSE, /* horrible alignment, just as with ORF */ + }, + { "DynCapLong", + "Dynamic Capability Message, IP/Multicast, truncated", + { 0x0, 0x1, 0x4, 0x0, 0x1, 0x0, 0x2 }, + 5, SHOULD_ERR, + }, + { "DynCapPadded", + "Dynamic Capability Message, IP/Multicast, padded", + { 0x0, 0x1, 0x4, 0x0, 0x1, 0x0, 0x2, 0x0 }, + 8, SHOULD_ERR, /* No way to tell padding from data.. */ + }, + { "DynCapMPCpadded", + "Dynamic Capability Message, IP/Multicast, cap data padded", + { 0x0, 0x1, 0x5, 0x0, 0x1, 0x0, 0x2, 0x0 }, + 8, SHOULD_PARSE, /* You can though add padding to the capability data */ + }, + { "DynCapMPCoverflow", + "Dynamic Capability Message, IP/Multicast, cap data != length", + { 0x0, 0x1, 0x3, 0x0, 0x1, 0x0, 0x2, 0x0 }, + 8, SHOULD_ERR, + }, + { NULL, NULL, {0}, 0, 0} +}; + +/* Entire Optional-Parameters block */ +struct test_segment opt_params[] = +{ + { "Cap-singlets", + "One capability per Optional-Param", + { 0x02, 0x06, 0x01, 0x04, 0x00, 0x01, 0x00, 0x01, /* MP IPv4/Uni */ + 0x02, 0x06, 0x01, 0x04, 0x00, 0x02, 0x00, 0x01, /* MP IPv6/Uni */ + 0x02, 0x02, 0x80, 0x00, /* RR (old) */ + 0x02, 0x02, 0x02, 0x00, /* RR */ + }, + 24, SHOULD_PARSE, + }, + { "Cap-series", + "Series of capability, one Optional-Param", + { 0x02, 0x10, + 0x01, 0x04, 0x00, 0x01, 0x00, 0x01, /* MP IPv4/Uni */ + 0x01, 0x04, 0x00, 0x02, 0x00, 0x01, /* MP IPv6/Uni */ + 0x80, 0x00, /* RR (old) */ + 0x02, 0x00, /* RR */ + }, + 18, SHOULD_PARSE, + }, + { "AS4more", + "AS4 capability after other caps (singlets)", + { 0x02, 0x06, 0x01, 0x04, 0x00, 0x01, 0x00, 0x01, /* MP IPv4/Uni */ + 0x02, 0x06, 0x01, 0x04, 0x00, 0x02, 0x00, 0x01, /* MP IPv6/Uni */ + 0x02, 0x02, 0x80, 0x00, /* RR (old) */ + 0x02, 0x02, 0x02, 0x00, /* RR */ + 0x02, 0x06, 0x41, 0x04, 0x00, 0x03, 0x00, 0x06 /* AS4: 1996614 */ + }, + 32, SHOULD_PARSE, 196614, + }, + { "AS4series", + "AS4 capability, in series of capabilities", + { 0x02, 0x16, + 0x01, 0x04, 0x00, 0x01, 0x00, 0x01, /* MP IPv4/Uni */ + 0x01, 0x04, 0x00, 0x02, 0x00, 0x01, /* MP IPv6/Uni */ + 0x80, 0x00, /* RR (old) */ + 0x02, 0x00, /* RR */ + 0x41, 0x04, 0x00, 0x03, 0x00, 0x06 /* AS4: 1996614 */ + }, + 24, SHOULD_PARSE, 196614, + }, + { "AS4real", + "AS4 capability, in series of capabilities", + { + 0x02, 0x06, 0x01, 0x04, 0x00, 0x01, 0x00, 0x01, /* MP IPv4/uni */ + 0x02, 0x06, 0x01, 0x04, 0x00, 0x02, 0x00, 0x01, /* MP IPv6/uni */ + 0x02, 0x02, 0x80, 0x00, /* RR old */ + 0x02, 0x02, 0x02, 0x00, /* RR */ + 0x02, 0x06, 0x41, 0x04, 0x00, 0x03, 0x00, 0x06, /* AS4 */ + }, + 32, SHOULD_PARSE, 196614, + }, + { "AS4real2", + "AS4 capability, in series of capabilities", + { + 0x02, 0x06, 0x01, 0x04, 0x00, 0x01, 0x00, 0x01, + 0x02, 0x06, 0x01, 0x04, 0x00, 0x02, 0x00, 0x01, + 0x02, 0x02, 0x80, 0x00, + 0x02, 0x02, 0x02, 0x00, + 0x02, 0x06, 0x41, 0x04, 0x00, 0x00, 0xfc, 0x03, + 0x02, 0x09, 0x82, 0x07, 0x00, 0x01, 0x00, 0x01, 0x01, 0x80, 0x03, + 0x02, 0x09, 0x03, 0x07, 0x00, 0x01, 0x00, 0x01, 0x01, 0x40, 0x03, + 0x02, 0x02, 0x42, 0x00, + }, + 58, SHOULD_PARSE, 64515, + }, + + { NULL, NULL, {0}, 0, 0} +}; + +/* basic parsing test */ +static void +parse_test (struct peer *peer, struct test_segment *t, int type) +{ + int ret; + int capability = 0; + as_t as4 = 0; + int oldfailed = failed; + int len = t->len; +#define RANDOM_FUZZ 35 + + stream_reset (peer->ibuf); + stream_put (peer->ibuf, NULL, RANDOM_FUZZ); + stream_set_getp (peer->ibuf, RANDOM_FUZZ); + + switch (type) + { + case CAPABILITY: + stream_putc (peer->ibuf, BGP_OPEN_OPT_CAP); + stream_putc (peer->ibuf, t->len); + break; + case DYNCAP: +/* for (i = 0; i < BGP_MARKER_SIZE; i++) + stream_putc (peer->, 0xff); + stream_putw (s, 0); + stream_putc (s, BGP_MSG_CAPABILITY);*/ + break; + } + stream_write (peer->ibuf, t->data, t->len); + + printf ("%s: %s\n", t->name, t->desc); + + switch (type) + { + case CAPABILITY: + len += 2; /* to cover the OPT-Param header */ + case OPT_PARAM: + printf ("len: %u\n", len); + /* peek_for_as4 wants getp at capibility*/ + as4 = peek_for_as4_capability (peer, len); + printf ("peek_for_as4: as4 is %u\n", as4); + /* and it should leave getp as it found it */ + assert (stream_get_getp (peer->ibuf) == RANDOM_FUZZ); + + ret = bgp_open_option_parse (peer, len, &capability); + break; + case DYNCAP: + ret = bgp_capability_receive (peer, t->len); + break; + default: + printf ("unknown type %u\n", type); + exit(1); + } + + if (!ret && t->validate_afi) + { + safi_t safi = t->safi; + + if (bgp_afi_safi_valid_indices (t->afi, &safi) != t->afi_valid) + failed++; + + printf ("MP: %u/%u (%u): recv %u, nego %u\n", + t->afi, t->safi, safi, + peer->afc_recv[t->afi][safi], + peer->afc_nego[t->afi][safi]); + + if (t->afi_valid == VALID_AFI) + { + + if (!peer->afc_recv[t->afi][safi]) + failed++; + if (!peer->afc_nego[t->afi][safi]) + failed++; + } + } + + if (as4 != t->peek_for) + { + printf ("as4 %u != %u\n", as4, t->peek_for); + failed++; + } + + printf ("parsed?: %s\n", ret ? "no" : "yes"); + + if (ret != t->parses) + failed++; + + if (tty) + printf ("%s", (failed > oldfailed) ? VT100_RED "failed!" VT100_RESET + : VT100_GREEN "OK" VT100_RESET); + else + printf ("%s", (failed > oldfailed) ? "failed!" : "OK" ); + + if (failed) + printf (" (%u)", failed); + + printf ("\n\n"); +} + +static struct bgp *bgp; +static as_t asn = 100; + +int +main (void) +{ + struct peer *peer; + int i, j; + + conf_bgp_debug_fsm = -1UL; + conf_bgp_debug_events = -1UL; + conf_bgp_debug_packet = -1UL; + conf_bgp_debug_normal = -1UL; + conf_bgp_debug_as4 = -1UL; + term_bgp_debug_fsm = -1UL; + term_bgp_debug_events = -1UL; + term_bgp_debug_packet = -1UL; + term_bgp_debug_normal = -1UL; + term_bgp_debug_as4 = -1UL; + + master = thread_master_create (); + bgp_master_init (); + bgp_option_set (BGP_OPT_NO_LISTEN); + + if (fileno (stdout) >= 0) + tty = isatty (fileno (stdout)); + + if (bgp_get (&bgp, &asn, NULL)) + return -1; + + peer = peer_create_accept (bgp); + peer->host = (char *) "foo"; + + for (i = AFI_IP; i < AFI_MAX; i++) + for (j = SAFI_UNICAST; j < SAFI_MAX; j++) + { + peer->afc[i][j] = 1; + peer->afc_adv[i][j] = 1; + } + + i = 0; + while (mp_segments[i].name) + parse_test (peer, &mp_segments[i++], CAPABILITY); + + /* These tests assume mp_segments tests set at least + * one of the afc_nego's + */ + i = 0; + while (test_segments[i].name) + parse_test (peer, &test_segments[i++], CAPABILITY); + + i = 0; + while (misc_segments[i].name) + parse_test (peer, &misc_segments[i++], CAPABILITY); + + i = 0; + while (opt_params[i].name) + parse_test (peer, &opt_params[i++], OPT_PARAM); + + SET_FLAG (peer->cap, PEER_CAP_DYNAMIC_ADV); + peer->status = Established; + + i = 0; + while (dynamic_cap_msgs[i].name) + parse_test (peer, &dynamic_cap_msgs[i++], DYNCAP); + + printf ("failures: %d\n", failed); + return failed; +} diff --git a/tests/bgp_mp_attr_test.c b/tests/bgp_mp_attr_test.c new file mode 100644 index 0000000..3b1bf14 --- /dev/null +++ b/tests/bgp_mp_attr_test.c @@ -0,0 +1,790 @@ +/* + * 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" +#define VT100_GREEN "\x1b[32m" +#define VT100_YELLOW "\x1b[33m" + + +#define CAPABILITY 0 +#define DYNCAP 1 +#define OPT_PARAM 2 + +/* need these to link in libbgp */ +struct zebra_privs_t *bgpd_privs = NULL; +struct thread_master *master = NULL; + +static int failed = 0; +static int tty = 0; + +/* test segments to parse and validate, and use for other tests */ +static struct test_segment { + const char *name; + const char *desc; + const u_char data[1024]; + int len; +#define SHOULD_PARSE 0 +#define SHOULD_ERR -1 + int parses; /* whether it should parse or not */ + + /* AFI/SAFI validation */ + afi_t afi; + safi_t safi; +#define VALID_AFI 1 +#define INVALID_AFI 0 + int afi_valid; +} mp_reach_segments [] = +{ + { "IPv6", + "IPV6 MP Reach, global nexthop, 1 NLRI", + { + /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST, + /* nexthop bytes */ 16, + /* Nexthop (global) */ 0xff, 0xfe, 0x1, 0x2, + 0xaa, 0xbb, 0xcc, 0xdd, + 0x3, 0x4, 0x5, 0x6, + 0xa1, 0xa2, 0xa3, 0xa4, + /* SNPA (defunct, MBZ) */ 0x0, + /* NLRI tuples */ 32, 0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */ + }, + (4 + 16 + 1 + 5), + SHOULD_PARSE, + AFI_IP6, SAFI_UNICAST, VALID_AFI, + }, + { "IPv6-2", + "IPV6 MP Reach, global nexthop, 2 NLRIs", + { + /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST, + /* nexthop bytes */ 16, + /* Nexthop (global) */ 0xff, 0xfe, 0x1, 0x2, /* ffee:102:... */ + 0xaa, 0xbb, 0xcc, 0xdd, + 0x3, 0x4, 0x5, 0x6, + 0xa1, 0xa2, 0xa3, 0xa4, + /* SNPA (defunct, MBZ) */ 0x0, + /* NLRI tuples */ 32, + 0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */ + 64, + 0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */ + 0x0, 0x2, 0x0, 0x3, + }, + (4 + 16 + 1 + 5 + 9), + SHOULD_PARSE, + AFI_IP6, SAFI_UNICAST, VALID_AFI, + }, + { "IPv6-default", + "IPV6 MP Reach, global nexthop, 2 NLRIs + default", + { + /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST, + /* nexthop bytes */ 16, + /* Nexthop (global) */ 0xff, 0xfe, 0x1, 0x2, + 0xaa, 0xbb, 0xcc, 0xdd, + 0x3, 0x4, 0x5, 0x6, + 0xa1, 0xa2, 0xa3, 0xa4, + /* SNPA (defunct, MBZ) */ 0x0, + /* NLRI tuples */ 32, + 0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */ + 64, + 0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */ + 0x0, 0x2, 0x0, 0x3, + 0x0, /* ::/0 */ + }, + (4 + 16 + 1 + 5 + 9 + 1), + SHOULD_PARSE, + AFI_IP6, SAFI_UNICAST, VALID_AFI, + }, + { "IPv6-lnh", + "IPV6 MP Reach, global+local nexthops, 2 NLRIs + default", + { + /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST, + /* nexthop bytes */ 32, + /* Nexthop (global) */ 0xff, 0xfe, 0x1, 0x2, /* fffe:102:... */ + 0xaa, 0xbb, 0xcc, 0xdd, + 0x3, 0x4, 0x5, 0x6, + 0xa1, 0xa2, 0xa3, 0xa4, + /* Nexthop (local) */ 0xfe, 0x80, 0x0, 0x0, /* fe80::210:2ff:.. */ + 0x0, 0x0, 0x0, 0x0, + 0x2, 0x10, 0x2, 0xff, + 0x1, 0x2, 0x3, 0x4, + /* SNPA (defunct, MBZ) */ 0x0, + /* NLRI tuples */ 32, + 0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */ + 64, + 0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */ + 0x0, 0x2, 0x0, 0x3, + 0x0, /* ::/0 */ + }, + (4 + 32 + 1 + 5 + 9 + 1), + SHOULD_PARSE, + AFI_IP6, SAFI_UNICAST, VALID_AFI, + }, + { "IPv6-nhlen", + "IPV6 MP Reach, inappropriate nexthop length", + { + /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST, + /* nexthop bytes */ 4, + /* Nexthop (global) */ 0xff, 0xfe, 0x1, 0x2, /* fffe:102:... */ + 0xaa, 0xbb, 0xcc, 0xdd, + 0x3, 0x4, 0x5, 0x6, + 0xa1, 0xa2, 0xa3, 0xa4, + /* Nexthop (local) */ 0xfe, 0x80, 0x0, 0x0, /* fe80::210:2ff:.. */ + 0x0, 0x0, 0x0, 0x0, + 0x2, 0x10, 0x2, 0xff, + 0x1, 0x2, 0x3, 0x4, + /* SNPA (defunct, MBZ) */ 0x0, + /* NLRI tuples */ 32, + 0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */ + 64, + 0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */ + 0x0, 0x2, 0x0, 0x3, + 0x0, /* ::/0 */ + }, + (4 + 32 + 1 + 5 + 9 + 1), + SHOULD_ERR, + AFI_IP6, SAFI_UNICAST, VALID_AFI, + }, + { "IPv6-nhlen2", + "IPV6 MP Reach, invalid nexthop length", + { + /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST, + /* nexthop bytes */ 5, + /* Nexthop (global) */ 0xff, 0xfe, 0x1, 0x2, /* fffe:102:... */ + 0xaa, 0xbb, 0xcc, 0xdd, + 0x3, 0x4, 0x5, 0x6, + 0xa1, 0xa2, 0xa3, 0xa4, + /* Nexthop (local) */ 0xfe, 0x80, 0x0, 0x0, /* fe80::210:2ff:.. */ + 0x0, 0x0, 0x0, 0x0, + 0x2, 0x10, 0x2, 0xff, + 0x1, 0x2, 0x3, 0x4, + /* SNPA (defunct, MBZ) */ 0x0, + /* NLRI tuples */ 32, + 0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */ + 64, + 0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */ + 0x0, 0x2, 0x0, 0x3, + 0x0, /* ::/0 */ + }, + (4 + 32 + 1 + 5 + 9 + 1), + SHOULD_ERR, + AFI_IP6, SAFI_UNICAST, VALID_AFI, + }, + { "IPv6-nhlen3", + "IPV6 MP Reach, nexthop length overflow", + { + /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST, + /* nexthop bytes */ 32, + /* Nexthop (global) */ 0xff, 0xfe, 0x1, 0x2, /* fffe:102:... */ + 0xaa, 0xbb, 0xcc, 0xdd, + 0x3, 0x4, 0x5, 0x6, + 0xa1, 0xa2, 0xa3, 0xa4, + }, + (4 + 16), + SHOULD_ERR, + AFI_IP6, SAFI_UNICAST, VALID_AFI, + }, + { "IPv6-nhlen4", + "IPV6 MP Reach, nexthop length short", + { + /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST, + /* nexthop bytes */ 16, + /* Nexthop (global) */ 0xff, 0xfe, 0x1, 0x2, /* fffe:102:... */ + 0xaa, 0xbb, 0xcc, 0xdd, + 0x3, 0x4, 0x5, 0x6, + 0xa1, 0xa2, 0xa3, 0xa4, + /* Nexthop (local) */ 0xfe, 0x80, 0x0, 0x0, /* fe80::210:2ff:.. */ + 0x0, 0x0, 0x0, 0x0, + 0x2, 0x10, 0x2, 0xff, + 0x1, 0x2, 0x3, 0x4, + /* SNPA (defunct, MBZ) */ 0x0, + /* NLRI tuples */ 32, + 0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */ + 64, + 0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */ + 0x0, 0x2, 0x0, 0x3, + 0x0, /* ::/0 */ + }, + (4 + 32 + 1 + 5 + 9 + 1), + SHOULD_ERR, + AFI_IP6, SAFI_UNICAST, VALID_AFI, + }, + { "IPv6-nlri", + "IPV6 MP Reach, NLRI bitlen overflow", + { + /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST, + /* nexthop bytes */ 32, + /* Nexthop (global) */ 0xff, 0xfe, 0x1, 0x2, /* fffe:102:... */ + 0xaa, 0xbb, 0xcc, 0xdd, + 0x3, 0x4, 0x5, 0x6, + 0xa1, 0xa2, 0xa3, 0xa4, + /* Nexthop (local) */ 0xfe, 0x80, 0x0, 0x0, /* fe80::210:2ff:.. */ + 0x0, 0x0, 0x0, 0x0, + 0x2, 0x10, 0x2, 0xff, + 0x1, 0x2, 0x3, 0x4, + /* SNPA (defunct, MBZ) */ 0x0, + /* NLRI tuples */ 120, + 0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */ + 64, + 0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */ + 0x0, 0x2, 0x0, 0x3, + 0, /* ::/0 */ + }, + (4 + 32 + 1 + 5 + 9 + 1), + SHOULD_ERR, + AFI_IP6, SAFI_UNICAST, VALID_AFI, + }, + { "IPv4", + "IPv4 MP Reach, 2 NLRIs + default", + { + /* AFI / SAFI */ 0x0, AFI_IP, SAFI_UNICAST, + /* nexthop bytes */ 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 */ + }, + (4 + 4 + 1 + 3 + 4 + 1), + SHOULD_PARSE, + AFI_IP, SAFI_UNICAST, VALID_AFI, + }, + { "IPv4-nhlen", + "IPv4 MP Reach, nexthop lenth overflow", + { + /* AFI / SAFI */ 0x0, AFI_IP, SAFI_UNICAST, + /* nexthop bytes */ 32, + /* 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 */ + }, + (4 + 4 + 1 + 3 + 4 + 1), + SHOULD_ERR, + AFI_IP, SAFI_UNICAST, VALID_AFI, + }, + { "IPv4-nlrilen", + "IPv4 MP Reach, nlri lenth overflow", + { + /* AFI / SAFI */ 0x0, AFI_IP, SAFI_UNICAST, + /* nexthop bytes */ 4, + /* Nexthop */ 192, 168, 0, 1, + /* SNPA (defunct, MBZ) */ 0x0, + /* NLRI tuples */ 16, 10, 1, /* 10.1/16 */ + 30, 10, + 0, /* 0/0 */ + }, + (4 + 4 + 1 + 3 + 2 + 1), + SHOULD_ERR, + AFI_IP, SAFI_UNICAST, VALID_AFI, + }, + { "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, + /* 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 */ + }, + (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_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", + { + /* AFI / SAFI */ 0x0, 0x2, 0x1, + /* nexthop bytes */ 0x20, + /* Nexthop (global) */ 0x20, 0x01, 0x04, 0x70, + 0x00, 0x01, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, + /* Nexthop (local) */ 0xfe, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x02, 0x0c, 0xdb, 0xff, + 0xfe, 0xfe, 0xeb, 0x00, + /* SNPA (defunct, MBZ) */ 0, + /* NLRI tuples */ /* Should have 0 here for ::/0, but dont */ + }, + 37, + SHOULD_ERR, + AFI_IP6, SAFI_UNICAST, VALID_AFI, + }, + + { NULL, NULL, {0}, 0, 0} +}; + +/* MP_UNREACH_NLRI tests */ +static struct test_segment mp_unreach_segments [] = +{ + { "IPv6-unreach", + "IPV6 MP Unreach, 1 NLRI", + { + /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST, + /* NLRI tuples */ 32, 0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */ + }, + (3 + 5), + SHOULD_PARSE, + AFI_IP6, SAFI_UNICAST, VALID_AFI, + }, + { "IPv6-unreach2", + "IPV6 MP Unreach, 2 NLRIs", + { + /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST, + /* NLRI tuples */ 32, + 0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */ + 64, + 0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */ + 0x0, 0x2, 0x0, 0x3, + }, + (3 + 5 + 9), + SHOULD_PARSE, + AFI_IP6, SAFI_UNICAST, VALID_AFI, + }, + { "IPv6-unreach-default", + "IPV6 MP Unreach, 2 NLRIs + default", + { + /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST, + /* NLRI tuples */ 32, + 0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */ + 64, + 0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */ + 0x0, 0x2, 0x0, 0x3, + 0x0, /* ::/0 */ + }, + (3 + 5 + 9 + 1), + SHOULD_PARSE, + AFI_IP6, SAFI_UNICAST, VALID_AFI, + }, + { "IPv6-unreach-nlri", + "IPV6 MP Unreach, NLRI bitlen overflow", + { + /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST, + /* NLRI tuples */ 120, + 0xff, 0xfe, 0x1, 0x2, /* fffe:102::/32 */ + 64, + 0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */ + 0x0, 0x2, 0x0, 0x3, + 0, /* ::/0 */ + }, + (3 + 5 + 9 + 1), + SHOULD_ERR, + AFI_IP6, SAFI_UNICAST, VALID_AFI, + }, + { "IPv4-unreach", + "IPv4 MP Unreach, 2 NLRIs + default", + { + /* AFI / SAFI */ 0x0, AFI_IP, SAFI_UNICAST, + /* NLRI tuples */ 16, 10, 1, /* 10.1/16 */ + 17, 10, 2, 3, /* 10.2.3/17 */ + 0, /* 0/0 */ + }, + (3 + 3 + 4 + 1), + SHOULD_PARSE, + AFI_IP, SAFI_UNICAST, VALID_AFI, + }, + { "IPv4-unreach-nlrilen", + "IPv4 MP Unreach, nlri length overflow", + { + /* AFI / SAFI */ 0x0, AFI_IP, SAFI_UNICAST, + /* NLRI tuples */ 16, 10, 1, /* 10.1/16 */ + 30, 10, + 0, /* 0/0 */ + }, + (3 + 3 + 2 + 1), + SHOULD_ERR, + AFI_IP, SAFI_UNICAST, VALID_AFI, + }, + { "IPv4-unreach-VPNv4", + "IPv4/MPLS-labeled VPN MP Unreach, RD, 3 NLRIs", + { + /* AFI / SAFI */ 0x0, AFI_IP, SAFI_MPLS_LABELED_VPN, + /* 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 + (1+3+8+2) + (1+3+8+3)), + SHOULD_PARSE, + AFI_IP, SAFI_MPLS_LABELED_VPN, VALID_AFI, + }, + { NULL, NULL, {0}, 0, 0} +}; + +/* 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 +handle_result (struct peer *peer, struct test_segment *t, + int parse_ret, int nlri_ret) +{ + int oldfailed = failed; + + if (!parse_ret) + { + safi_t safi = t->safi; + + if (bgp_afi_safi_valid_indices (t->afi, &safi) != t->afi_valid) + failed++; + + printf ("MP: %u/%u (%u): recv %u, nego %u\n", + t->afi, t->safi, safi, + peer->afc_recv[t->afi][safi], + peer->afc_nego[t->afi][safi]); + } + + 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 ((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); + else + printf ("%s", (failed > oldfailed) ? "failed!" : "OK" ); + + if (failed) + printf (" (%u)", failed); + + 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; + +int +main (void) +{ + struct peer *peer; + int i, j; + + conf_bgp_debug_fsm = -1UL; + conf_bgp_debug_events = -1UL; + conf_bgp_debug_packet = -1UL; + conf_bgp_debug_normal = -1UL; + conf_bgp_debug_as4 = -1UL; + term_bgp_debug_fsm = -1UL; + term_bgp_debug_events = -1UL; + term_bgp_debug_packet = -1UL; + term_bgp_debug_normal = -1UL; + term_bgp_debug_as4 = -1UL; + + 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)); + + if (bgp_get (&bgp, &asn, NULL)) + return -1; + + peer = peer_create_accept (bgp); + peer->host = (char *)"foo"; + peer->status = Established; + + for (i = AFI_IP; i < AFI_MAX; i++) + for (j = SAFI_UNICAST; j < SAFI_MAX; j++) + { + peer->afc[i][j] = 1; + peer->afc_adv[i][j] = 1; + } + + i = 0; + while (mp_reach_segments[i].name) + parse_test (peer, &mp_reach_segments[i++], BGP_ATTR_MP_REACH_NLRI); + + i = 0; + while (mp_unreach_segments[i].name) + parse_test (peer, &mp_unreach_segments[i++], BGP_ATTR_MP_UNREACH_NLRI); + + printf ("failures: %d\n", failed); + return failed; +} diff --git a/tests/bgp_mpath_test.c b/tests/bgp_mpath_test.c new file mode 100644 index 0000000..174d299 --- /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 0000000..5900186 --- /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 0000000..dfecec7 --- /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 0000000..074952f --- /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 0000000..2572623 --- /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 0000000..96a51e3 --- /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 0000000..e6d7305 --- /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 0000000..7135856 --- /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 0000000..8f67515 --- /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 0000000..2f6bcea --- /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 new file mode 100644 index 0000000..23ee405 --- /dev/null +++ b/tests/ecommunity_test.c @@ -0,0 +1,162 @@ +/* + * 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" + +/* need these to link in libbgp */ +struct zebra_privs_t *bgpd_privs = NULL; +struct thread_master *master = NULL; + +static int failed = 0; + +/* specification for a test - what the results should be */ +struct test_spec +{ + const char *shouldbe; /* the string the path should parse to */ +}; + + +/* test segments to parse and validate, and use for other tests */ +static struct test_segment { + const char *name; + const char *desc; + const u_int8_t data[1024]; + int len; + struct test_spec sp; +} test_segments [] = +{ + { /* 0 */ + "ipaddr", + "rt 1.2.3.4:257", + { ECOMMUNITY_ENCODE_IP, ECOMMUNITY_ROUTE_TARGET, + 0x1,0x2,0x3,0x4, 0x1,0x1 }, + 8, + { "rt 1.2.3.4:257" } + }, + { /* 1 */ + "ipaddr-so", + "soo 1.2.3.4:257", + { ECOMMUNITY_ENCODE_IP, ECOMMUNITY_SITE_ORIGIN, + 0x1,0x2,0x3,0x4, 0x1,0x1}, + 8, + { "soo 1.2.3.4:257" } + }, + { /* 2 */ + "asn", + "rt 23456:987654321", + { ECOMMUNITY_ENCODE_AS, ECOMMUNITY_SITE_ORIGIN, + 0x5b,0xa0, 0x3a,0xde,0x68,0xb1 }, + 8, + { "soo 23456:987654321" } + }, + { /* 3 */ + "asn4", + "rt 168450976:4321", + { ECOMMUNITY_ENCODE_AS4, ECOMMUNITY_SITE_ORIGIN, + 0xa,0xa,0x5b,0xa0, 0x10,0xe1 }, + 8, + { "soo 168450976:4321" } + }, + { NULL, NULL, {0}, 0, { NULL } } +}; + + +/* validate the given aspath */ +static int +validate (struct ecommunity *ecom, const struct test_spec *sp) +{ + int fails = 0; + struct ecommunity *etmp; + char *str1, *str2; + + printf ("got:\n %s\n", ecommunity_str (ecom)); + str1 = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_COMMUNITY_LIST); + etmp = ecommunity_str2com (str1, 0, 1); + if (etmp) + str2 = ecommunity_ecom2str (etmp, ECOMMUNITY_FORMAT_COMMUNITY_LIST); + else + str2 = NULL; + + if (strcmp (sp->shouldbe, str1)) + { + failed++; + fails++; + printf ("shouldbe: %s\n%s\n", str1, sp->shouldbe); + } + if (!etmp || strcmp (str1, str2)) + { + failed++; + fails++; + printf ("dogfood: in %s\n" + " in->out %s\n", + str1, + (etmp && str2) ? str2 : "NULL"); + } + ecommunity_free (&etmp); + XFREE (MTYPE_ECOMMUNITY_STR, str1); + XFREE (MTYPE_ECOMMUNITY_STR, str2); + + return fails; +} + +/* basic parsing test */ +static void +parse_test (struct test_segment *t) +{ + struct ecommunity *ecom; + + printf ("%s: %s\n", t->name, t->desc); + + ecom = ecommunity_parse ((u_int8_t *)t->data, t->len); + + printf ("ecom: %s\nvalidating...:\n", ecommunity_str (ecom)); + + if (!validate (ecom, &t->sp)) + printf ("OK\n"); + else + printf ("failed\n"); + + printf ("\n"); + ecommunity_unintern (&ecom); +} + + +int +main (void) +{ + int i = 0; + ecommunity_init(); + while (test_segments[i].name) + parse_test (&test_segments[i++]); + + printf ("failures: %d\n", failed); + //printf ("aspath count: %ld\n", aspath_count()); + return failed; + //return (failed + aspath_count()); +} diff --git a/tests/global-conf.exp b/tests/global-conf.exp new file mode 100644 index 0000000..e69de29 diff --git a/tests/heavy-thread.c b/tests/heavy-thread.c new file mode 100644 index 0000000..c2e71c1 --- /dev/null +++ b/tests/heavy-thread.c @@ -0,0 +1,147 @@ +/* + * $Id: heavy-thread.c,v 1.2 2005/04/25 16:42:24 paul 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. + */ + +/* This programme shows the effects of 'heavy' long-running functions + * on the cooperative threading model, as demonstrated by heavy.c, and how + * they can be mitigated using a background thread. + * + * Run it with a config file containing 'password whatever', telnet to it + * (it defaults to port 4000) and enter the 'clear foo string' command. + * then type whatever and observe that, unlike heavy.c, the vty interface + * remains responsive. + */ +#include +#include + +#include "thread.h" +#include "vty.h" +#include "command.h" +#include "memory.h" +#include "log.h" + +#include "tests.h" + +extern struct thread_master *master; + +enum +{ + ITERS_FIRST = 0, + ITERS_ERR = 100, + ITERS_LATER = 400, + ITERS_PRINT = 10, + ITERS_MAX = 1000, +}; + +struct work_state { + struct vty *vty; + char *str; + int i; +}; + +static void +slow_func (struct vty *vty, const char *str, const int i) +{ + double x = 1; + int j; + + for (j = 0; j < 300; j++) + x += sin(x)*j; + + if ((i % ITERS_LATER) == 0) + printf ("%s: %d, temporary error, save this somehow and do it later..\n", + __func__, i); + + if ((i % ITERS_ERR) == 0) + printf ("%s: hard error\n", __func__); + + if ((i % ITERS_PRINT) == 0) + printf ("%s did %d, x = %g\n", str, i, x); +} + +static int +clear_something (struct thread *thread) +{ + struct work_state *ws = THREAD_ARG(thread); + + /* this could be like iterating through 150k of route_table + * or worse, iterating through a list of peers, to bgp_stop them with + * each having 150k route tables to process... + */ + while (ws->i < ITERS_MAX) + { + slow_func(ws->vty, ws->str, ws->i); + ws->i++; + if (thread_should_yield(thread)) + { + thread_add_background(master, clear_something, ws, 0); + return 0; + } + } + + /* All done! */ + XFREE (MTYPE_TMP, ws->str); + XFREE (MTYPE_TMP, ws); + return 0; +} + +DEFUN (clear_foo, + clear_foo_cmd, + "clear foo .LINE", + "clear command\n" + "arbitrary string\n") +{ + char *str; + struct work_state *ws; + + if (!argc) + { + vty_out (vty, "%% string argument required%s", VTY_NEWLINE); + return CMD_WARNING; + } + + str = argv_concat (argv, argc, 0); + + if ((ws = XMALLOC(MTYPE_TMP, sizeof(*ws))) == NULL) + { + zlog_err ("%s: unable to allocate work_state", __func__); + return CMD_WARNING; + } + + if (!(ws->str = XSTRDUP (MTYPE_TMP, str))) + { + zlog_err ("%s: unable to xstrdup", __func__); + XFREE (MTYPE_TMP, ws); + return CMD_WARNING; + } + + ws->vty = vty; + ws->i = ITERS_FIRST; + + thread_add_background(master, clear_something, ws, 0); + + return CMD_SUCCESS; +} + +void +test_init() +{ + install_element (VIEW_NODE, &clear_foo_cmd); +} diff --git a/tests/heavy-wq.c b/tests/heavy-wq.c new file mode 100644 index 0000000..2f133cc --- /dev/null +++ b/tests/heavy-wq.c @@ -0,0 +1,180 @@ +/* + * 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. + */ + +/* This programme shows the effects of 'heavy' long-running functions + * on the cooperative threading model. + * + * Run it with a config file containing 'password whatever', telnet to it + * (it defaults to port 4000) and enter the 'clear foo string' command. + * then type whatever and observe that the vty interface is unresponsive + * for quite a period of time, due to the clear_something command + * taking a very long time to complete. + */ +#include + +#include "thread.h" +#include "vty.h" +#include "command.h" +#include "memory.h" +#include "log.h" +#include "workqueue.h" +#include + +#include "tests.h" + +extern struct thread_master *master; +static struct work_queue *heavy_wq; + +struct heavy_wq_node +{ + char *str; + int i; +}; + +enum +{ + ITERS_FIRST = 0, + ITERS_ERR = 100, + ITERS_LATER = 400, + ITERS_PRINT = 10, + ITERS_MAX = 1000, +}; + +static void +heavy_wq_add (struct vty *vty, const char *str, int i) +{ + struct heavy_wq_node *hn; + + if ((hn = XCALLOC (MTYPE_PREFIX_LIST, sizeof(struct heavy_wq_node))) == NULL) + { + zlog_err ("%s: unable to allocate hn", __func__); + return; + } + + hn->i = i; + if (!(hn->str = XSTRDUP (MTYPE_PREFIX_LIST_STR, str))) + { + zlog_err ("%s: unable to xstrdup", __func__); + XFREE (MTYPE_PREFIX_LIST, hn); + return; + } + + work_queue_add (heavy_wq, hn); + + return; +} + +static void +slow_func_err (struct work_queue *wq, struct work_queue_item *item) +{ + printf ("%s: running error function\n", __func__); +} + +static void +slow_func_del (struct work_queue *wq, void *data) +{ + struct heavy_wq_node *hn = data; + assert (hn && hn->str); + printf ("%s: %s\n", __func__, hn->str); + XFREE (MTYPE_PREFIX_LIST_STR, hn->str); + hn->str = NULL; + XFREE(MTYPE_PREFIX_LIST, hn); +} + +static wq_item_status +slow_func (struct work_queue *wq, void *data) +{ + struct heavy_wq_node *hn = data; + double x = 1; + int j; + + assert (hn && hn->str); + + for (j = 0; j < 300; j++) + x += sin(x)*j; + + if ((hn->i % ITERS_LATER) == 0) + return WQ_RETRY_LATER; + + if ((hn->i % ITERS_ERR) == 0) + return WQ_RETRY_NOW; + + if ((hn->i % ITERS_PRINT) == 0) + printf ("%s did %d, x = %g\n", hn->str, hn->i, x); + + return WQ_SUCCESS; +} + +static void +clear_something (struct vty *vty, const char *str) +{ + int i; + + /* this could be like iterating through 150k of route_table + * or worse, iterating through a list of peers, to bgp_stop them with + * each having 150k route tables to process... + */ + for (i = ITERS_FIRST; i < ITERS_MAX; i++) + heavy_wq_add (vty, str, i); +} + +DEFUN (clear_foo, + clear_foo_cmd, + "clear foo .LINE", + "clear command\n" + "arbitrary string\n") +{ + char *str; + if (!argc) + { + vty_out (vty, "%% string argument required%s", VTY_NEWLINE); + return CMD_WARNING; + } + + str = argv_concat (argv, argc, 0); + + clear_something (vty, str); + XFREE (MTYPE_TMP, str); + return CMD_SUCCESS; +} + +static int +heavy_wq_init () +{ + if (! (heavy_wq = work_queue_new (master, "heavy_work_queue"))) + { + zlog_err ("%s: could not get new work queue!", __func__); + return -1; + } + + heavy_wq->spec.workfunc = &slow_func; + heavy_wq->spec.errorfunc = &slow_func_err; + heavy_wq->spec.del_item_data = &slow_func_del; + heavy_wq->spec.max_retries = 3; + heavy_wq->spec.hold = 1000; + + return 0; +} + +void +test_init() +{ + install_element (VIEW_NODE, &clear_foo_cmd); + heavy_wq_init(); +} diff --git a/tests/heavy.c b/tests/heavy.c new file mode 100644 index 0000000..9af46c8 --- /dev/null +++ b/tests/heavy.c @@ -0,0 +1,113 @@ +/* + * $Id: heavy.c,v 1.3 2005/04/25 16:42:24 paul 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. + */ + +/* This programme shows the effects of 'heavy' long-running functions + * on the cooperative threading model. + * + * Run it with a config file containing 'password whatever', telnet to it + * (it defaults to port 4000) and enter the 'clear foo string' command. + * then type whatever and observe that the vty interface is unresponsive + * for quite a period of time, due to the clear_something command + * taking a very long time to complete. + */ +#include + +#include "thread.h" +#include "vty.h" +#include "command.h" +#include "memory.h" +#include + +#include "tests.h" + +enum +{ + ITERS_FIRST = 0, + ITERS_ERR = 100, + ITERS_LATER = 400, + ITERS_PRINT = 10, + ITERS_MAX = 1000, +}; + +static void +slow_func (struct vty *vty, const char *str, const int i) +{ + double x = 1; + int j; + + for (j = 0; j < 300; j++) + x += sin(x)*j; + + if ((i % ITERS_LATER) == 0) + printf ("%s: %d, temporary error, save this somehow and do it later..\n", + __func__, i); + + if ((i % ITERS_ERR) == 0) + printf ("%s: hard error\n", __func__); + + if ((i % ITERS_PRINT) == 0) + printf ("%s did %d, x = %g%s", str, i, x, VTY_NEWLINE); +} + +static void +clear_something (struct vty *vty, const char *str) +{ + int i; + + /* this could be like iterating through 150k of route_table + * or worse, iterating through a list of peers, to bgp_stop them with + * each having 150k route tables to process... + */ + for (i = ITERS_FIRST; i < ITERS_MAX; i++) + slow_func (vty, str, i); +} + +DEFUN (clear_foo, + clear_foo_cmd, + "clear foo .LINE", + "clear command\n" + "arbitrary string\n") +{ + char *str; + if (!argc) + { + vty_out (vty, "%% string argument required%s", VTY_NEWLINE); + return CMD_WARNING; + } + + str = argv_concat (argv, argc, 0); + + clear_something (vty, str); + XFREE (MTYPE_TMP, str); + return CMD_SUCCESS; +} + +static void +slow_vty_init() +{ + install_element (VIEW_NODE, &clear_foo_cmd); +} + +void +test_init() +{ + slow_vty_init(); +} diff --git a/tests/lib/bgpd.exp b/tests/lib/bgpd.exp new file mode 100644 index 0000000..e69de29 diff --git a/tests/lib/libzebra.exp b/tests/lib/libzebra.exp new file mode 100644 index 0000000..e69de29 diff --git a/tests/libzebra.tests/Makefile.am b/tests/libzebra.tests/Makefile.am new file mode 100644 index 0000000..4b74e2d --- /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 0000000..5838d4f --- /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 0000000..83531c7 --- /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 0000000..778bd0c --- /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 0000000..d4bfc82 --- /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 0000000..be35a0a --- /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 0000000..ca602e3 --- /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 new file mode 100644 index 0000000..5396c7d --- /dev/null +++ b/tests/main.c @@ -0,0 +1,200 @@ +/* + * $Id: main.c,v 1.1 2005/04/25 16:42:24 paul 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. + */ + +#include + +#include +#include "getopt.h" +#include "thread.h" +#include "vty.h" +#include "command.h" +#include "memory.h" + +extern void test_init(); + +struct thread_master *master; + +struct option longopts[] = +{ + { "daemon", no_argument, NULL, 'd'}, + { "config_file", required_argument, NULL, 'f'}, + { "help", no_argument, NULL, 'h'}, + { "vty_addr", required_argument, NULL, 'A'}, + { "vty_port", required_argument, NULL, 'P'}, + { "version", no_argument, NULL, 'v'}, + { 0 } +}; + +DEFUN (daemon_exit, + daemon_exit_cmd, + "daemon-exit", + "Make the daemon exit\n") +{ + exit(0); +} + +static int timer_count; +static int +test_timer (struct thread *thread) +{ + int *count = THREAD_ARG(thread); + + printf ("run %d of timer\n", (*count)++); + thread_add_timer (master, test_timer, count, 5); + return 0; +} + +static void +test_timer_init() +{ + thread_add_timer (master, test_timer, &timer_count, 10); +} + +static void +test_vty_init() +{ + install_element (VIEW_NODE, &daemon_exit_cmd); +} + +/* Help information display. */ +static void +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 does 'slow' things.\n\n\ +-d, --daemon Runs in daemon mode\n\ +-f, --config_file Set configuration file name\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\ +-h, --help Display this help and exit\n\ +\n\ +Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); + } + exit (status); +} + + +/* main routine. */ +int +main (int argc, char **argv) +{ + char *p; + char *vty_addr = NULL; + int vty_port = 4000; + int daemon_mode = 0; + char *progname; + struct thread thread; + char *config_file = NULL; + + /* Set umask before anything for security */ + umask (0027); + + /* get program name */ + progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]); + + /* master init. */ + master = thread_master_create (); + + while (1) + { + int opt; + + opt = getopt_long (argc, argv, "dhf:A:P:v", longopts, 0); + + if (opt == EOF) + break; + + switch (opt) + { + case 0: + break; + case 'f': + config_file = optarg; + break; + case 'd': + daemon_mode = 1; + break; + case 'A': + vty_addr = optarg; + break; + case 'P': + /* Deal with atoi() returning 0 on failure */ + if (strcmp(optarg, "0") == 0) + { + vty_port = 0; + break; + } + vty_port = atoi (optarg); + vty_port = (vty_port ? vty_port : 4000); + break; + case 'v': + print_version (progname); + exit (0); + break; + case 'h': + usage (progname, 0); + break; + default: + usage (progname, 1); + break; + } + } + + /* Library inits. */ + cmd_init (1); + vty_init (master); + memory_init (); + + /* OSPF vty inits. */ + test_vty_init (); + + /* Change to the daemon program. */ + if (daemon_mode && daemon (0, 0) < 0) + { + fprintf(stderr, "daemon failed: %s", strerror(errno)); + exit (1); + } + + /* Create VTY socket */ + vty_serv_sock (vty_addr, vty_port, "/tmp/.heavy.sock"); + + /* Configuration file read*/ + if (!config_file) + usage (progname, 1); + vty_read_config (config_file, NULL); + + test_timer_init(); + + test_init(); + + /* Fetch next active thread. */ + while (thread_fetch (master, &thread)) + thread_call (&thread); + + /* Not reached. */ + exit (0); +} + diff --git a/tests/prng.c b/tests/prng.c new file mode 100644 index 0000000..bdcfb07 --- /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 0000000..cf0bacc --- /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 0000000..fc9cc3d --- /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 new file mode 100644 index 0000000..e95d6fb --- /dev/null +++ b/tests/test-buffer.c @@ -0,0 +1,59 @@ +/* + * 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 + +struct thread_master *master; + +int +main(int argc, char **argv) +{ + struct buffer *b1, *b2; + int n; + char junk[3]; + char c = 'a'; + + memory_init(); + + if ((argc != 2) || (sscanf(argv[1], "%d%1s", &n, junk) != 1)) + { + fprintf(stderr, "Usage: %s \n", *argv); + return 1; + } + + b1 = buffer_new(0); + b2 = buffer_new(1024); + + while (n-- > 0) + { + buffer_put(b1, &c, 1); + buffer_put(b2, &c, 1); + if (c++ == 'z') + c = 'a'; + buffer_reset(b1); + buffer_reset(b2); + } + buffer_free(b1); + buffer_free(b2); + return 0; +} diff --git a/tests/test-checksum.c b/tests/test-checksum.c new file mode 100644 index 0000000..b6741f3 --- /dev/null +++ b/tests/test-checksum.c @@ -0,0 +1,550 @@ +/* + * 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 + +#include "checksum.h" + +struct thread_master *master; + +struct acc_vals { + int c0; + int c1; +}; + +struct csum_vals { + struct acc_vals a; + int x; + int y; +}; + +static struct csum_vals ospfd_vals, isisd_vals; + +typedef size_t testsz_t; +typedef uint16_t testoff_t; + +/* Fletcher Checksum -- Refer to RFC1008. */ +#define MODX 4102 + +/* The final reduction phase. + * This one should be the original ospfd version + */ +static u_int16_t +reduce_ospfd (struct csum_vals *vals, testsz_t len, testoff_t off) +{ +#define x vals->x +#define y vals->y +#define c0 vals->a.c0 +#define c1 vals->a.c1 + + x = ((len - off - 1) * c0 - c1) % 255; + + if (x <= 0) + x += 255; + y = 510 - c0 - x; + if (y > 255) + y -= 255; + + /* take care endian issue. */ + return htons ((x << 8) + y); +#undef x +#undef y +#undef c0 +#undef c1 +} + +/* slightly different concatenation */ +static u_int16_t +reduce_ospfd1 (struct csum_vals *vals, testsz_t len, testoff_t off) +{ +#define x vals->x +#define y vals->y +#define c0 vals->a.c0 +#define c1 vals->a.c1 + + x = ((len - off - 1) * c0 - c1) % 255; + if (x <= 0) + x += 255; + y = 510 - c0 - x; + if (y > 255) + y -= 255; + + /* take care endian issue. */ + return htons ((x << 8) | (y & 0xff)); +#undef x +#undef y +#undef c0 +#undef c1 +} + +/* original isisd version */ +static u_int16_t +reduce_isisd (struct csum_vals *vals, testsz_t len, testoff_t off) +{ +#define x vals->x +#define y vals->y +#define c0 vals->a.c0 +#define c1 vals->a.c1 + u_int32_t mul; + + mul = (len - off)*(c0); + x = mul - c0 - c1; + y = c1 - mul - 1; + + if (y > 0) + y++; + if (x < 0) + x--; + + x %= 255; + y %= 255; + + if (x == 0) + x = 255; + if (y == 0) + y = 1; + + return htons ((x << 8) | (y & 0xff)); + +#undef x +#undef y +#undef c0 +#undef c1 +} + +/* Is the -1 in y wrong perhaps? */ +static u_int16_t +reduce_isisd_yfix (struct csum_vals *vals, testsz_t len, testoff_t off) +{ +#define x vals->x +#define y vals->y +#define c0 vals->a.c0 +#define c1 vals->a.c1 + u_int32_t mul; + + mul = (len - off)*(c0); + x = mul - c0 - c1; + y = c1 - mul; + + if (y > 0) + y++; + if (x < 0) + x--; + + x %= 255; + y %= 255; + + if (x == 0) + x = 255; + if (y == 0) + y = 1; + + return htons ((x << 8) | (y & 0xff)); + +#undef x +#undef y +#undef c0 +#undef c1 +} + +/* Move the mods yp */ +static u_int16_t +reduce_isisd_mod (struct csum_vals *vals, testsz_t len, testoff_t off) +{ +#define x vals->x +#define y vals->y +#define c0 vals->a.c0 +#define c1 vals->a.c1 + u_int32_t mul; + + mul = (len - off)*(c0); + x = mul - c1 - c0; + y = c1 - mul - 1; + + x %= 255; + y %= 255; + + if (y > 0) + y++; + if (x < 0) + x--; + + if (x == 0) + x = 255; + if (y == 0) + y = 1; + + return htons ((x << 8) | (y & 0xff)); + +#undef x +#undef y +#undef c0 +#undef c1 +} + +/* Move the mods up + fix y */ +static u_int16_t +reduce_isisd_mody (struct csum_vals *vals, testsz_t len, testoff_t off) +{ +#define x vals->x +#define y vals->y +#define c0 vals->a.c0 +#define c1 vals->a.c1 + u_int32_t mul; + + mul = (len - off)*(c0); + x = mul - c0 - c1; + y = c1 - mul; + + x %= 255; + y %= 255; + + if (y > 0) + y++; + if (x < 0) + x--; + + if (x == 0) + x = 255; + if (y == 0) + y = 1; + + return htons ((x << 8) | (y & 0xff)); + +#undef x +#undef y +#undef c0 +#undef c1 +} + +struct reductions_t { + const char *name; + u_int16_t (*f) (struct csum_vals *, testsz_t, testoff_t); +} reducts[] = { + { .name = "ospfd", .f = reduce_ospfd }, + { .name = "ospfd-1", .f = reduce_ospfd1 }, + { .name = "isisd", .f = reduce_isisd }, + { .name = "isisd-yfix", .f = reduce_isisd_yfix }, + { .name = "isisd-mod", .f = reduce_isisd_mod }, + { .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) +{ + u_char *sp, *ep, *p, *q; + int c0 = 0, c1 = 0; + int x, y; + u_int16_t checksum, *csum; + + csum = (u_int16_t *) (buffer + off); + *(csum) = 0; + + sp = buffer; + + for (ep = sp + len; 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; + } + + ospfd_vals.a.c0 = c0; + ospfd_vals.a.c1 = c1; + + //printf ("%s: len %u, off %u, c0 %d, c1 %d\n", + // __func__, len, off, c0, c1); + + x = ((int)(len - off - 1) * (int)c0 - (int)c1) % 255; + + if (x <= 0) + x += 255; + y = 510 - c0 - x; + if (y > 255) + y -= 255; + + ospfd_vals.x = x; + ospfd_vals.y = y; + + buffer[off] = x; + buffer[off + 1] = y; + + /* take care endian issue. */ + checksum = htons ((x << 8) | (y & 0xff)); + + return (checksum); +} + +/* the original, broken isisd checksum */ +static u_int16_t +iso_csum_create (u_char * buffer, testsz_t len, testoff_t off) +{ + + u_int8_t *p; + int x; + int y; + u_int32_t mul; + u_int32_t c0; + u_int32_t c1; + u_int16_t checksum, *csum; + int i, init_len, partial_len; + + checksum = 0; + + csum = (u_int16_t *) (buffer + off); + *(csum) = checksum; + + p = buffer; + c0 = 0; + c1 = 0; + init_len = len; + + while (len != 0) + { + partial_len = MIN(len, MODX); + + for (i = 0; i < partial_len; i++) + { + c0 = c0 + *(p++); + c1 += c0; + } + + c0 = c0 % 255; + c1 = c1 % 255; + + len -= partial_len; + } + + isisd_vals.a.c0 = c0; + isisd_vals.a.c1 = c1; + + mul = (init_len - off) * c0; + + x = mul - c1 - c0; + y = c1 - mul - 1; + + if (y > 0) + y++; + if (x < 0) + x--; + + x %= 255; + y %= 255; + + if (x == 0) + x = 255; + if (y == 0) + y = 1; + + isisd_vals.x = x; + isisd_vals.y = y; + + checksum = htons((x << 8) | (y & 0xFF)); + + *(csum) = checksum; + + /* return the checksum for user usage */ + return checksum; +} + +static int +verify (u_char * buffer, testsz_t len) +{ + u_int8_t *p; + u_int32_t c0; + u_int32_t c1; + int i, partial_len; + + p = buffer; + + c0 = 0; + c1 = 0; + + while (len) + { + partial_len = MIN(len, 5803); + + for (i = 0; i < partial_len; i++) + { + c0 = c0 + *(p++); + c1 += c0; + } + c0 = c0 % 255; + c1 = c1 % 255; + + len -= partial_len; + } + + if (c0 == 0 && c1 == 0) + return 0; + + return 1; +} + +static int /* return checksum in low-order 16 bits */ +in_cksum_optimized(void *parg, int nbytes) +{ + u_short *ptr = parg; + register long sum; /* assumes long == 32 bits */ + register u_short answer; /* assumes u_short == 16 bits */ + register int count; + /* + * Our algorithm is simple, using a 32-bit accumulator (sum), + * we add sequential 16-bit words to it, and at the end, fold back + * all the carry bits from the top 16 bits into the lower 16 bits. + */ + + sum = 0; + count = nbytes >> 1; /* div by 2 */ + for(ptr--; count; --count) + sum += *++ptr; + + if (nbytes & 1) /* Odd */ + sum += *(u_char *)(++ptr); /* one byte only */ + + /* + * Add back carry outs from top 16 bits to low 16 bits. + */ + + sum = (sum >> 16) + (sum & 0xffff); /* add high-16 to low-16 */ + sum += (sum >> 16); /* add carry */ + answer = ~sum; /* ones-complement, then truncate to 16 bits */ + return(answer); +} + + +static int /* return checksum in low-order 16 bits */ +in_cksum_rfc(void *parg, int count) +/* from RFC 1071 */ +{ + u_short *addr = parg; + /* Compute Internet Checksum for "count" bytes + * beginning at location "addr". + */ + register long sum = 0; + + while (count > 1) { + /* This is the inner loop */ + sum += *addr++; + count -= 2; + } + /* Add left-over byte, if any */ + if (count > 0) { + sum += *(u_char *)addr; + } + + /* Fold 32-bit sum to 16 bits */ + while (sum>>16) + sum = (sum & 0xffff) + (sum >> 16); + return ~sum; +} + + +int +main(int argc, char **argv) +{ +/* 60017 65629 702179 */ +#define MAXDATALEN 60017 +#define BUFSIZE MAXDATALEN + sizeof(u_int16_t) + u_char buffer[BUFSIZE]; + int exercise = 0; +#define EXERCISESTEP 257 + + srandom (time (NULL)); + + while (1) { + u_int16_t ospfd, isisd, lib, in_csum, in_csum_res, in_csum_rfc; + int i,j; + + exercise += EXERCISESTEP; + exercise %= MAXDATALEN; + + for (i = 0; i < exercise; i += sizeof (long int)) { + long int rand = random (); + + for (j = sizeof (long int); j > 0; j--) + buffer[i + (sizeof (long int) - j)] = (rand >> (j * 8)) & 0xff; + } + + in_csum = in_cksum(buffer, exercise); + in_csum_res = in_cksum_optimized(buffer, exercise); + in_csum_rfc = in_cksum_rfc(buffer, exercise); + if (in_csum_res != in_csum || in_csum != in_csum_rfc) + printf ("verify: in_chksum failed in_csum:%x, in_csum_res:%x," + "in_csum_rfc %x, len:%d\n", + in_csum, in_csum_res, in_csum_rfc, exercise); + + ospfd = ospfd_checksum (buffer, exercise + sizeof(u_int16_t), exercise); + if (verify (buffer, exercise + sizeof(u_int16_t))) + printf ("verify: ospfd failed\n"); + isisd = iso_csum_create (buffer, exercise + sizeof(u_int16_t), exercise); + if (verify (buffer, exercise + sizeof(u_int16_t))) + printf ("verify: isisd failed\n"); + lib = fletcher_checksum (buffer, exercise + sizeof(u_int16_t), exercise); + if (verify (buffer, exercise + sizeof(u_int16_t))) + printf ("verify: lib failed\n"); + + if (ospfd != lib) { + printf ("Mismatch in values at size %u\n" + "ospfd: 0x%04x\tc0: %d\tc1: %d\tx: %d\ty: %d\n" + "isisd: 0x%04x\tc0: %d\tc1: %d\tx: %d\ty: %d\n" + "lib: 0x%04x\n", + exercise, + ospfd, ospfd_vals.a.c0, ospfd_vals.a.c1, ospfd_vals.x, ospfd_vals.y, + isisd, isisd_vals.a.c0, isisd_vals.a.c1, isisd_vals.x, isisd_vals.y, + lib + ); + + /* Investigate reduction phase discrepencies */ + if (ospfd_vals.a.c0 == isisd_vals.a.c0 + && ospfd_vals.a.c1 == isisd_vals.a.c1) { + printf ("\n"); + for (i = 0; reducts[i].name != NULL; i++) { + ospfd = reducts[i].f (&ospfd_vals, + exercise + sizeof (u_int16_t), + exercise); + printf ("%20s: x: %02x, y %02x, checksum 0x%04x\n", + reducts[i].name, ospfd_vals.x & 0xff, ospfd_vals.y & 0xff, ospfd); + } + } + + printf ("\n u_char testdata [] = {\n "); + for (i = 0; i < exercise; i++) { + printf ("0x%02x,%s", + buffer[i], + (i + 1) % 8 ? " " : "\n "); + } + printf ("\n}\n"); + exit (1); + } + } +} diff --git a/tests/test-cli.c b/tests/test-cli.c new file mode 100644 index 0000000..6fab6d5 --- /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 0000000..18b3b50 --- /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 new file mode 100644 index 0000000..807249e --- /dev/null +++ b/tests/test-memory.c @@ -0,0 +1,122 @@ +/* + * 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 + +/* Memory torture tests + * + * Tests below are generic but comments are focused on interaction with + * Paul's proposed memory 'quick' cache, which may never be included in + * CVS + */ + +struct thread_master *master; + +#if 0 /* set to 1 to use system alloc directly */ +#undef XMALLOC +#undef XCALLOC +#undef XREALLOC +#undef XFREE +#define XMALLOC(T,S) malloc((S)) +#define XCALLOC(T,S) calloc(1, (S)) +#define XREALLOC(T,P,S) realloc((P),(S)) +#define XFREE(T,P) free((P)) +#endif + +#define TIMES 10 + +int +main(int argc, char **argv) +{ + void *a[10]; + int i; + + printf ("malloc x, malloc x, free, malloc x, free free\n\n"); + /* simple case, test cache */ + for (i = 0; i < TIMES; i++) + { + a[0] = XMALLOC (MTYPE_VTY, 1024); + memset (a[0], 1, 1024); + a[1] = XMALLOC (MTYPE_VTY, 1024); + memset (a[1], 1, 1024); + XFREE(MTYPE_VTY, a[0]); /* should go to cache */ + a[0] = XMALLOC (MTYPE_VTY, 1024); /* should be satisfied from cache */ + XFREE(MTYPE_VTY, a[0]); + XFREE(MTYPE_VTY, a[1]); + } + + printf ("malloc x, malloc y, free x, malloc y, free free\n\n"); + /* cache should go invalid, valid, invalid, etc.. */ + for (i = 0; i < TIMES; i++) + { + a[0] = XMALLOC (MTYPE_VTY, 512); + memset (a[0], 1, 512); + a[1] = XMALLOC (MTYPE_VTY, 1024); /* invalidate cache */ + memset (a[1], 1, 1024); + XFREE(MTYPE_VTY, a[0]); + a[0] = XMALLOC (MTYPE_VTY, 1024); + XFREE(MTYPE_VTY, a[0]); + XFREE(MTYPE_VTY, a[1]); + /* cache should become valid again on next request */ + } + + printf ("calloc\n\n"); + /* test calloc */ + for (i = 0; i < TIMES; i++) + { + a[0] = XCALLOC (MTYPE_VTY, 1024); + memset (a[0], 1, 1024); + a[1] = XCALLOC (MTYPE_VTY, 512); /* invalidate cache */ + memset (a[1], 1, 512); + XFREE(MTYPE_VTY, a[1]); + XFREE(MTYPE_VTY, a[0]); + /* alloc == 0, cache can become valid again on next request */ + } + + printf ("calloc and realloc\n\n"); + /* check calloc + realloc */ + for (i = 0; i < TIMES; i++) + { + printf ("calloc a0 1024\n"); + a[0] = XCALLOC (MTYPE_VTY, 1024); + memset (a[0], 1, 1024/2); + + printf ("calloc 1 1024\n"); + a[1] = XCALLOC (MTYPE_VTY, 1024); + memset (a[1], 1, 1024/2); + + printf ("realloc 0 1024\n"); + a[3] = XREALLOC (MTYPE_VTY, a[0], 2048); /* invalidate cache */ + if (a[3] != NULL) + a[0] = a[3]; + memset (a[0], 1, 1024); + + printf ("calloc 2 512\n"); + a[2] = XCALLOC (MTYPE_VTY, 512); + memset (a[2], 1, 512); + + printf ("free 1 0 2\n"); + XFREE(MTYPE_VTY, a[1]); + XFREE(MTYPE_VTY, a[0]); + XFREE(MTYPE_VTY, a[2]); + /* alloc == 0, cache valid next request */ + } + return 0; +} diff --git a/tests/test-nexthop-iter.c b/tests/test-nexthop-iter.c new file mode 100644 index 0000000..2503793 --- /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 new file mode 100644 index 0000000..beae81f --- /dev/null +++ b/tests/test-privs.c @@ -0,0 +1,152 @@ +/* + * $Id: test-privs.c,v 1.1 2005/10/11 03:48:28 paul 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. + */ + +#include + +#include +#include "getopt.h" +#include "privs.h" +#include "memory.h" + +zebra_capabilities_t _caps_p [] = +{ + ZCAP_NET_RAW, + ZCAP_BIND, + ZCAP_NET_ADMIN, + ZCAP_DAC_OVERRIDE, +}; + +struct zebra_privs_t test_privs = +{ +#if defined(QUAGGA_USER) && defined(QUAGGA_GROUP) + .user = QUAGGA_USER, + .group = QUAGGA_GROUP, +#endif +#if defined(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 +}; + +struct option longopts[] = +{ + { "help", no_argument, NULL, 'h'}, + { "user", required_argument, NULL, 'u'}, + { "group", required_argument, NULL, 'g'}, + { 0 } +}; + +/* Help information display. */ +static void +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 does 'slow' things.\n\n\ +-u, --user User to run as\n\ +-g, --group Group to run as\n\ +-h, --help Display this help and exit\n\ +\n\ +Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); + } + exit (status); +} + +struct thread_master *master; +/* main routine. */ +int +main (int argc, char **argv) +{ + char *p; + char *progname; + struct zprivs_ids_t ids; + + /* Set umask before anything for security */ + umask (0027); + + /* get program name */ + progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]); + + while (1) + { + int opt; + + opt = getopt_long (argc, argv, "hu:g:", longopts, 0); + + if (opt == EOF) + break; + + switch (opt) + { + case 0: + break; + case 'u': + test_privs.user = optarg; + break; + case 'g': + test_privs.group = optarg; + break; + case 'h': + usage (progname, 0); + break; + default: + usage (progname, 1); + break; + } + } + + /* Library inits. */ + memory_init (); + zprivs_init (&test_privs); + +#define PRIV_STATE() \ + ((test_privs.current_state() == ZPRIVS_RAISED) ? "Raised" : "Lowered") + + printf ("%s\n", PRIV_STATE()); + test_privs.change(ZPRIVS_RAISE); + + printf ("%s\n", PRIV_STATE()); + test_privs.change(ZPRIVS_LOWER); + + printf ("%s\n", PRIV_STATE()); + zprivs_get_ids (&ids); + + /* terminate privileges */ + zprivs_terminate(&test_privs); + + /* but these should continue to work... */ + printf ("%s\n", PRIV_STATE()); + test_privs.change(ZPRIVS_RAISE); + + printf ("%s\n", PRIV_STATE()); + test_privs.change(ZPRIVS_LOWER); + + printf ("%s\n", PRIV_STATE()); + zprivs_get_ids (&ids); + + printf ("terminating\n"); + return 0; +} diff --git a/tests/test-segv.c b/tests/test-segv.c new file mode 100644 index 0000000..1b851fc --- /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 new file mode 100644 index 0000000..f24da96 --- /dev/null +++ b/tests/test-sig.c @@ -0,0 +1,78 @@ +/* + * 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" + +static void +sighup (void) +{ + printf ("processed hup\n"); +} + +static void +sigusr1 (void) +{ + printf ("processed usr1\n"); +} + +static void +sigusr2 (void) +{ + printf ("processed usr2\n"); +} + +struct quagga_signal_t sigs[] = +{ + { + .signal = SIGHUP, + .handler = &sighup, + }, + { + .signal = SIGUSR1, + .handler = &sigusr1, + }, + { + .signal = SIGUSR2, + .handler = &sigusr2, + } +}; + +struct thread_master *master; +struct thread t; + +int +main (void) +{ + master = thread_master_create (); + signal_init (master, array_size(sigs), sigs); + + zlog_default = openzlog("testsig", 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); + + while (thread_fetch (master, &t)) + thread_call (&t); + + exit (0); +} diff --git a/tests/test-stream.c b/tests/test-stream.c new file mode 100644 index 0000000..7ef6374 --- /dev/null +++ b/tests/test-stream.c @@ -0,0 +1,76 @@ +/* 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 unsigned long long ham = 0xdeadbeefdeadbeef; +struct thread_master *master; + +static void +print_stream (struct stream *s) +{ + size_t getp = stream_get_getp (s); + + printf ("endp: %zu, readable: %zu, writeable: %zu\n", + stream_get_endp (s), + STREAM_READABLE (s), + STREAM_WRITEABLE (s)); + + while (STREAM_READABLE (s)) + { + printf ("0x%x ", *stream_pnt (s)); + stream_forward_getp (s, 1); + } + + printf ("\n"); + + /* put getp back to where it was */ + stream_set_getp (s, getp); +} + +int +main (void) +{ + struct stream *s; + + s = stream_new (1024); + + stream_putc (s, ham); + stream_putw (s, ham); + stream_putl (s, ham); + stream_putq (s, ham); + + print_stream (s); + + stream_resize (s, stream_get_endp (s)); + + print_stream (s); + + 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%" 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 0000000..e523929 --- /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 0000000..ee45ede --- /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 0000000..f4212b9 --- /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 0000000..1515ea2 --- /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 0000000..7fe6279 --- /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 0000000..11483b8 --- /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 0000000..a528e55 --- /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/.gitignore b/tools/.gitignore new file mode 100644 index 0000000..dd5bf7c --- /dev/null +++ b/tools/.gitignore @@ -0,0 +1,6 @@ +.arch-inventory +.arch-ids + +*~ +*.loT + diff --git a/tools/mrlg.txt b/tools/mrlg.txt new file mode 100644 index 0000000..0ebf7ee --- /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 new file mode 100644 index 0000000..c5668e1 --- /dev/null +++ b/tools/multiple-bgpd.sh @@ -0,0 +1,458 @@ +#!/bin/bash + +# Public domain, not copyrighted.. + +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 +# 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 +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)) + + 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 + + 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/zebra.el b/tools/zebra.el new file mode 100644 index 0000000..01ff09f --- /dev/null +++ b/tools/zebra.el @@ -0,0 +1,108 @@ +;; -*- lisp -*- +;;; zebra-mode.el -- major mode for editing zebra configuration file. + +;; Copyright (C) 1998 Kunihiro Ishiguro + +;; Author: 1998 Kunihiro Ishiguro +;; SeonMeyong HEO +;; Maintainer: kunihiro@zebra.org +;; seirios@Matrix.IRI.Co.JP +;; Created: Jan 28 1998 +;; Version: Alpha 0.2 +;; Keywords: zebra bgpd ripd ripngd languages + +;; You can get the latest version of zebra from +;; +;; http://www.zebra.org/ +;; +;; Install this Emacs Lisp code +;; +;; Compile zebra.el +;; % $(EMACS) -batch -f batch-byte-compile zebra.el +;; Install zebra.el,zebra.elc to Emacs-load-path +;; % cp zebra.el zebra.elc $(emacs-load-path) +;; Add .emacs or (site-load.el | site-start.el) +;; (auto-load 'zebra-mode "zebra" nil t) +;; (auto-load 'bgp-mode "zebra" nil t) +;; (auto-load 'rip-mode "zebra" nil t) +;; + +;;; Code: + +;; Set keywords + +(defvar zebra-font-lock-keywords + (list + '("#.*$" . font-lock-comment-face) + '("!.*$" . font-lock-comment-face) + '("no\\|interface" . font-lock-type-face) + '("ip6\\|ip\\|route\\|address" . font-lock-function-name-face) + '("ipforward\\|ipv6forward" . font-lock-keyword-face) + '("hostname\\|password\\|enable\\|logfile\\|no" . font-lock-keyword-face)) + "Default value to highlight in zebra mode.") + +(defvar bgp-font-lock-keywords + (list + '("#.*$" . font-lock-comment-face) + '("!.*$" . font-lock-comment-face) + '("no\\|router" . font-lock-type-face) + '("bgp\\|router-id\\|neighbor\\|network" . font-lock-function-name-face) + '("ebgp\\|multihop\\|next\\|zebra\\|remote-as" . font-lock-keyword-face) + '("hostname\\|password\\|enable\\|logfile\\|no" . font-lock-keyword-face)) + "Default value to highlight in bgp mode.") + +(defvar rip-font-lock-keywords + (list + '("#.*$" . font-lock-comment-face) + '("!.*$" . font-lock-comment-face) + '("no\\|router\\|interface\\|ipv6\\|ip6\\|ip" . font-lock-type-face) + '("ripng\\|rip\\|recive\\|advertize\\|accept" . font-lock-function-name-face) + '("version\\|network" . font-lock-function-name-face) + '("default\\|none\\|zebra" . font-lock-keyword-face) + '("hostname\\|password\\|enable\\|logfile\\|no" . font-lock-keyword-face)) + "Default value to highlight in bgp mode.") + +;; set font-lock-mode + +(defun zebra-font-lock () + (make-local-variable 'font-lock-defaults) + (setq font-lock-defaults '(zebra-font-lock-keywords nil t))) + +(defun bgp-font-lock () + (make-local-variable 'font-lock-defaults) + (setq font-lock-defaults '(bgp-font-lock-keywords nil t))) + +(defun rip-font-lock () + (make-local-variable 'font-lock-defaults) + (setq font-lock-defaults '(rip-font-lock-keywords nil t))) + +;; define Major mode + +(defun major-mode-define () + (interactive) + (progn + (setq comment-start "[#!]" + comment-end "" + comment-start-skip "!+ ") + (run-hooks 'zebra-mode-hook) + (cond + ((string< "20" emacs-version) + (font-lock-mode))))) + +(defun zebra-mode () + (progn + (setq mode-name "zebra") + (zebra-font-lock)) + (major-mode-define)) + +(defun bgp-mode () + (progn + (setq mode-name "bgp") + (bgp-font-lock)) + (major-mode-define)) + +(defun rip-mode () + (progn + (setq mode-name "rip") + (rip-font-lock)) + (major-mode-define)) diff --git a/update-autotools b/update-autotools new file mode 100755 index 0000000..d5db16d --- /dev/null +++ b/update-autotools @@ -0,0 +1,28 @@ +#! /bin/sh +# +# When local system does not have the latest autoconf/automake +# -- Kunihiro Ishiguro +# + +rm -f config.cache Makefile.in aclocal.m4 config.h.in configure +rm -rf config.guess config.sub ltmain.sh +rm -rf autom4te.cache + +echo "This $0 script is deprecated, and will be removed at some stage." +echo "Please use the 'autoreconf' command included with autoconf." + +echo "TOOLS VERIONS:" +for tool in autoheader autoconf libtool libtoolize aclocal automake; do + $tool --version | head -1 +done + +echo "ACLOCAL:" +aclocal -I m4 +echo "AUTOHEADER:" +autoheader +echo "AUTOCONF:" +autoconf +echo "LIBTOOLIZE:" +libtoolize -c +echo "AUTOMAKE" +automake --gnu --add-missing --copy diff --git a/vtysh/.gitignore b/vtysh/.gitignore new file mode 100644 index 0000000..5856eac --- /dev/null +++ b/vtysh/.gitignore @@ -0,0 +1,16 @@ +Makefile +Makefile.in +*.o +vtysh +tags +TAGS +.deps +vtysh_cmd.c +.nfs* +extract.pl +.libs +.arch-inventory +.arch-ids +*~ +*.loT + diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am new file mode 100644 index 0000000..983103f --- /dev/null +++ b/vtysh/Makefile.am @@ -0,0 +1,41 @@ +## Process this file with Automake to create Makefile.in + +AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" + +LIBS = @LIBS@ @CURSES@ @LIBPAM@ + +AM_CFLAGS = $(WERROR) + +bin_PROGRAMS = vtysh + +vtysh_SOURCES = vtysh_main.c vtysh.c vtysh_user.c vtysh_config.c +nodist_vtysh_SOURCES = vtysh_cmd.c +CLEANFILES = vtysh_cmd.c +noinst_HEADERS = vtysh.h vtysh_user.h +vtysh_LDADD = ../lib/libzebra.la @LIBCAP@ @LIBREADLINE@ + +examplesdir = $(exampledir) +dist_examples_DATA = vtysh.conf.sample + +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)/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 \ + $(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 new file mode 100755 index 0000000..6e24b77 --- /dev/null +++ b/vtysh/extract.pl.in @@ -0,0 +1,243 @@ +#! @PERL@ +## +## @configure_input@ +## +## Virtual terminal interface shell command extractor. +## 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. +## + +print < +#include "command.h" +#include "vtysh.h" + +EOF + +$ignore{'"interface IFNAME"'} = "ignore"; +$ignore{'"interface IFNAME " "vrf <0-65535>"'} = "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 bgp " "<1-4294967295>"'} = "ignore"; +$ignore{'"router bgp " "<1-4294967295>" " view WORD"'} = "ignore"; +$ignore{'"router isis WORD"'} = "ignore"; +$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|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"; +$ignore{'"show route-map"'} = "ignore"; +$ignore{'"line vty"'} = "ignore"; +$ignore{'"who"'} = "ignore"; +$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_builddir@/lib -I@top_srcdir@/isisd/topology @CPPFLAGS@ $file |"); + local $/; undef $/; + $line = ; + close (FH); + + @defun = ($line =~ /(?:DEFUN|ALIAS)\s*\((.+?)\);?\s?\s?\n/sg); + @install = ($line =~ /install_element\s*\(\s*[0-9A-Z_]+,\s*&[^;]*;\s*\n/sg); + + # DEFUN process + foreach (@defun) { + my (@defun_array); + @defun_array = split (/,/); + $defun_array[0] = ''; + + + # Actual input command string. + $str = "$defun_array[2]"; + $str =~ s/^\s+//g; + $str =~ s/\s+$//g; + + # Get VTY command structure. This is needed for searching + # install_element() command. + $cmd = "$defun_array[1]"; + $cmd =~ s/^\s+//g; + $cmd =~ s/\s+$//g; + + # $protocol is VTYSH_PROTO format for redirection of user input + 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; + } + + # Append _vtysh to structure then build DEFUN again + $defun_array[1] = $cmd . "_vtysh"; + $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; + } + + # install_element() process + foreach (@install) { + my (@element_array); + @element_array = split (/,/); + + # Install node + $enode = $element_array[0]; + $enode =~ s/^\s+//g; + $enode =~ s/\s+$//g; + ($enode) = ($enode =~ /([0-9A-Z_]+)$/); + + # VTY command structure. + ($ecmd) = ($element_array[1] =~ /&([^\)]+)/); + $ecmd =~ s/^\s+//g; + $ecmd =~ s/\s+$//g; + + # Register $ecmd + if (defined ($cmd2str{$ecmd}) + && ! defined ($ignore{$cmd2str{$ecmd}})) { + my ($key); + $key = $enode . "," . $cmd2str{$ecmd}; + $ocmd{$key} = $ecmd; + $odefun{$key} = $cmd2defun{$ecmd}; + push (@{$oproto{$key}}, $cmd2proto{$ecmd}); + } + } +} + +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 (/,/)); + my ($cmd) = $ocmd{$_}; + $live{$cmd} = $_; +} + +# Output DEFSH +foreach (keys %live) { + my ($proto); + my ($key); + $key = $live{$_}; + $proto = join ("|", @{$oproto{$key}}); + printf "DEFSH ($proto$odefun{$key})\n\n"; +} + +# Output install_element +print < + +#include +#include +#include +#include +#include + +#include +#include + +#include "command.h" +#include "memory.h" +#include "vtysh/vtysh.h" +#include "log.h" +#include "bgpd/bgp_vty.h" +#include "vrf.h" + +/* Struct VTY. */ +struct vty *vty; + +/* VTY shell pager name. */ +char *vtysh_pager_name = NULL; + +/* VTY shell client structure. */ +struct vtysh_client +{ + int fd; + const char *name; + int flag; + const char *path; +} vtysh_client[] = +{ + { .fd = -1, .name = "zebra", .flag = VTYSH_ZEBRA, .path = ZEBRA_VTYSH_PATH}, + { .fd = -1, .name = "ripd", .flag = VTYSH_RIPD, .path = RIP_VTYSH_PATH}, + { .fd = -1, .name = "ripngd", .flag = VTYSH_RIPNGD, .path = RIPNG_VTYSH_PATH}, + { .fd = -1, .name = "ospfd", .flag = VTYSH_OSPFD, .path = OSPF_VTYSH_PATH}, + { .fd = -1, .name = "ospf6d", .flag = VTYSH_OSPF6D, .path = OSPF6_VTYSH_PATH}, + { .fd = -1, .name = "bgpd", .flag = VTYSH_BGPD, .path = BGP_VTYSH_PATH}, + { .fd = -1, .name = "isisd", .flag = VTYSH_ISISD, .path = ISIS_VTYSH_PATH}, + { .fd = -1, .name = "pimd", .flag = VTYSH_PIMD, .path = PIM_VTYSH_PATH}, + { .fd = -1, .name = "nhrpd", .flag = VTYSH_NHRPD, .path = NHRP_VTYSH_PATH}, +}; + + +/* We need direct access to ripd to implement vtysh_exit_ripd_only. */ +static struct vtysh_client *ripd_client = NULL; + + +/* Using integrated config from Quagga.conf. Default is no. */ +int vtysh_writeconfig_integrated = 0; + +extern char config_default[]; + +static void +vclient_close (struct vtysh_client *vclient) +{ + if (vclient->fd >= 0) + { + fprintf(stderr, + "Warning: closing connection to %s because of an I/O error!\n", + vclient->name); + close (vclient->fd); + vclient->fd = -1; + } +} + +/* Return true if str begins with prefix, else return false */ +static int +begins_with(const char *str, const char *prefix) +{ + if (!str || !prefix) + return 0; + size_t lenstr = strlen(str); + size_t lenprefix = strlen(prefix); + if (lenprefix > 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_execute(): " +static int +vtysh_client_execute (struct vtysh_client *vclient, const char *line, FILE *fp) +{ + int ret; + char *buf; + size_t bufsz; + char *pbuf; + size_t left; + char *eoln; + int nbytes; + int i; + int readln; + 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; + } + + /* Allow enough room for buffer to read more than a few pages from socket. */ + bufsz = 5 * getpagesize() + 1; + buf = XMALLOC(MTYPE_TMP, bufsz); + memset(buf, 0, bufsz); + pbuf = buf; + + while (1) + { + if (pbuf >= ((buf + bufsz) -1)) + { + fprintf (stderr, ERR_WHERE_STRING \ + "warning - pbuf beyond buffer end.\n"); + XFREE(MTYPE_TMP, buf); + return CMD_WARNING; + } + + readln = (buf + bufsz) - pbuf - 1; + nbytes = read (vclient->fd, pbuf, readln); + + if (nbytes <= 0) + { + + if (errno == EINTR) + continue; + + fprintf(stderr, ERR_WHERE_STRING "(%u)", errno); + perror(""); + + if (errno == EAGAIN || errno == EIO) + continue; + + vclient_close (vclient); + 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 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; + } + } + + if(!fp) + vtysh_config_parse (buf); + + XFREE(MTYPE_TMP, buf); + return ret; +} + + +void +vtysh_pager_init (void) +{ + char *pager_defined; + + pager_defined = getenv ("VTYSH_PAGER"); + + if (pager_defined) + vtysh_pager_name = strdup (pager_defined); + else + vtysh_pager_name = strdup ("more"); +} + +/* Command execution over the vty interface. */ +static int +vtysh_execute_func (const char *line, int pager) +{ + int ret, cmd_stat; + u_int i; + vector vline; + struct cmd_element *cmd; + FILE *fp = NULL; + int closepager = 0; + int tried = 0; + int saved_ret, saved_node; + + /* Split readline string up into the vector. */ + vline = cmd_make_strvec (line); + + if (vline == NULL) + return CMD_SUCCESS; + + saved_ret = ret = cmd_execute_command (vline, vty, &cmd, 1); + saved_node = vty->node; + + /* If command doesn't succeeded in current node, try to walk up in node tree. + * Changing vty->node is enough to try it just out without actual walkup in + * the vtysh. */ + while (ret != CMD_SUCCESS && ret != CMD_SUCCESS_DAEMON && ret != CMD_WARNING + && vty->node > CONFIG_NODE) + { + vty->node = node_parent(vty->node); + ret = cmd_execute_command (vline, vty, &cmd, 1); + tried++; + } + + vty->node = saved_node; + + /* If command succeeded in any other node than current (tried > 0) we have + * 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_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)) + { + vtysh_execute("exit-address-family"); + } + else if ((saved_node == KEYCHAIN_KEY_NODE) && (tried == 1)) + { + vtysh_execute("exit"); + } + else if (tried) + { + vtysh_execute ("end"); + vtysh_execute ("configure terminal"); + } + } + /* If command didn't succeed in any node, continue with return value from + * first try. */ + else if (tried) + { + ret = saved_ret; + } + + cmd_free_strvec (vline); + + cmd_stat = ret; + switch (ret) + { + case CMD_WARNING: + if (vty->type == VTY_FILE) + fprintf (stdout,"Warning...\n"); + break; + case CMD_ERR_AMBIGUOUS: + fprintf (stdout,"%% Ambiguous command.\n"); + break; + case CMD_ERR_NO_MATCH: + fprintf (stdout,"%% Unknown command.\n"); + break; + case CMD_ERR_INCOMPLETE: + fprintf (stdout,"%% Command incomplete.\n"); + break; + case CMD_SUCCESS_DAEMON: + { + /* FIXME: Don't open pager for exit commands. popen() causes problems + * if exited from vtysh at all. This hack shouldn't cause any problem + * but is really ugly. */ + if (pager && vtysh_pager_name && (strncmp(line, "exit", 4) != 0)) + { + fp = popen (vtysh_pager_name, "w"); + if (fp == NULL) + { + perror ("popen failed for pager"); + fp = stdout; + } + else + closepager=1; + } + else + fp = stdout; + + if (! strcmp(cmd->string,"configure terminal")) + { + for (i = 0; i < array_size(vtysh_client); i++) + { + cmd_stat = vtysh_client_execute(&vtysh_client[i], line, fp); + if (cmd_stat == CMD_WARNING) + break; + } + + if (cmd_stat) + { + line = "end"; + vline = cmd_make_strvec (line); + + if (vline == NULL) + { + if (pager && vtysh_pager_name && fp && closepager) + { + if (pclose (fp) == -1) + { + perror ("pclose failed for pager"); + } + fp = NULL; + } + return CMD_SUCCESS; + } + + ret = cmd_execute_command (vline, vty, &cmd, 1); + cmd_free_strvec (vline); + if (ret != CMD_SUCCESS_DAEMON) + break; + } + else + if (cmd->func) + { + (*cmd->func) (cmd, vty, 0, NULL); + break; + } + } + + cmd_stat = CMD_SUCCESS; + for (i = 0; i < array_size(vtysh_client); i++) + { + if (cmd->daemon & vtysh_client[i].flag) + { + cmd_stat = vtysh_client_execute(&vtysh_client[i], line, fp); + if (cmd_stat != CMD_SUCCESS) + break; + } + } + if (cmd_stat != CMD_SUCCESS) + break; + + if (cmd->func) + (*cmd->func) (cmd, vty, 0, NULL); + } + } + if (pager && vtysh_pager_name && fp && closepager) + { + if (pclose (fp) == -1) + { + perror ("pclose failed for pager"); + } + fp = NULL; + } + return cmd_stat; +} + +int +vtysh_execute_no_pager (const char *line) +{ + return vtysh_execute_func (line, 0); +} + +int +vtysh_execute (const char *line) +{ + return vtysh_execute_func (line, 1); +} + +/* Configration make from file. */ +int +vtysh_config_from_file (struct vty *vty, FILE *fp) +{ + int ret; + struct cmd_element *cmd; + + while (fgets (vty->buf, vty->max, fp)) + { + ret = command_config_read_one_line (vty, &cmd, 1); + + switch (ret) + { + case CMD_WARNING: + if (vty->type == VTY_FILE) + fprintf (stdout,"Warning...\n"); + break; + case CMD_ERR_AMBIGUOUS: + fprintf (stdout,"%% Ambiguous command.\n"); + break; + case CMD_ERR_NO_MATCH: + fprintf (stdout,"%% Unknown command: %s", vty->buf); + break; + case CMD_ERR_INCOMPLETE: + fprintf (stdout,"%% Command incomplete.\n"); + break; + case CMD_SUCCESS_DAEMON: + { + u_int i; + int cmd_stat = CMD_SUCCESS; + + for (i = 0; i < array_size(vtysh_client); i++) + { + if (cmd->daemon & vtysh_client[i].flag) + { + cmd_stat = vtysh_client_execute (&vtysh_client[i], + vty->buf, stdout); + if (cmd_stat != CMD_SUCCESS) + break; + } + } + if (cmd_stat != CMD_SUCCESS) + break; + + if (cmd->func) + (*cmd->func) (cmd, vty, 0, NULL); + } + } + } + return CMD_SUCCESS; +} + +/* We don't care about the point of the cursor when '?' is typed. */ +static int +vtysh_rl_describe (void) +{ + int ret; + unsigned int i; + vector vline; + vector describe; + int width; + struct cmd_token *token; + + vline = cmd_make_strvec (rl_line_buffer); + + /* In case of '> ?'. */ + if (vline == NULL) + { + vline = vector_init (1); + vector_set (vline, NULL); + } + else + if (rl_end && isspace ((int) rl_line_buffer[rl_end - 1])) + vector_set (vline, NULL); + + describe = cmd_describe_command (vline, vty, &ret); + + fprintf (stdout,"\n"); + + /* Ambiguous and no match error. */ + switch (ret) + { + case CMD_ERR_AMBIGUOUS: + cmd_free_strvec (vline); + fprintf (stdout,"%% Ambiguous command.\n"); + rl_on_new_line (); + return 0; + break; + case CMD_ERR_NO_MATCH: + cmd_free_strvec (vline); + fprintf (stdout,"%% There is no matched command.\n"); + rl_on_new_line (); + return 0; + break; + } + + /* Get width of command string. */ + width = 0; + for (i = 0; i < vector_active (describe); i++) + if ((token = vector_slot (describe, i)) != NULL) + { + int len; + + if (token->cmd[0] == '\0') + continue; + + len = strlen (token->cmd); + if (token->cmd[0] == '.') + len--; + + if (width < len) + width = len; + } + + for (i = 0; i < vector_active (describe); i++) + if ((token = vector_slot (describe, i)) != NULL) + { + if (token->cmd[0] == '\0') + continue; + + if (! token->desc) + fprintf (stdout," %-s\n", + token->cmd[0] == '.' ? token->cmd + 1 : token->cmd); + else + fprintf (stdout," %-*s %s\n", + width, + token->cmd[0] == '.' ? token->cmd + 1 : token->cmd, + token->desc); + } + + cmd_free_strvec (vline); + vector_free (describe); + + rl_on_new_line(); + + return 0; +} + +/* Result of cmd_complete_command() call will be stored here + * and used in new_completion() in order to put the space in + * correct places only. */ +int complete_status; + +static char * +command_generator (const char *text, int state) +{ + vector vline; + static char **matched = NULL; + static int index = 0; + + /* First call. */ + if (! state) + { + index = 0; + + if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE) + return NULL; + + vline = cmd_make_strvec (rl_line_buffer); + if (vline == NULL) + return NULL; + + if (rl_end && isspace ((int) rl_line_buffer[rl_end - 1])) + vector_set (vline, NULL); + + matched = cmd_complete_command (vline, vty, &complete_status); + } + + if (matched && matched[index]) + return matched[index++]; + + return NULL; +} + +static char ** +new_completion (char *text, int start, int end) +{ + char **matches; + + matches = rl_completion_matches (text, command_generator); + + if (matches) + { + rl_point = rl_end; + if (complete_status != CMD_COMPLETE_FULL_MATCH) + /* only append a space on full match */ + rl_completion_append_character = '\0'; + } + + return matches; +} + +#if 0 +/* This function is not actually being used. */ +static char ** +vtysh_completion (char *text, int start, int end) +{ + int ret; + vector vline; + char **matched = NULL; + + if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE) + return NULL; + + vline = cmd_make_strvec (rl_line_buffer); + if (vline == NULL) + return NULL; + + /* In case of 'help \t'. */ + if (rl_end && isspace ((int) rl_line_buffer[rl_end - 1])) + vector_set (vline, '\0'); + + matched = cmd_complete_command (vline, vty, &ret); + + cmd_free_strvec (vline); + + return (char **) matched; +} +#endif + +/* Vty node structures. */ +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_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, + "%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)# " +}; + +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. */ +static int +vtysh_end (void) +{ + switch (vty->node) + { + case VIEW_NODE: + case ENABLE_NODE: + /* Nothing to do. */ + break; + default: + vty->node = ENABLE_NODE; + break; + } + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_ALL, + vtysh_end_all, + vtysh_end_all_cmd, + "end", + "End current mode and change to enable mode\n") +{ + return vtysh_end (); +} + +DEFUNSH (VTYSH_BGPD, + router_bgp, + router_bgp_cmd, + "router bgp " CMD_AS_RANGE, + ROUTER_STR + BGP_STR + AS_STR) +{ + vty->node = BGP_NODE; + return CMD_SUCCESS; +} + +ALIAS_SH (VTYSH_BGPD, + router_bgp, + router_bgp_view_cmd, + "router bgp " CMD_AS_RANGE " view WORD", + ROUTER_STR + BGP_STR + AS_STR + "BGP view\n" + "view name\n") + +DEFUNSH (VTYSH_BGPD, + address_family_vpnv4, + address_family_vpnv4_cmd, + "address-family vpnv4", + "Enter Address Family command mode\n" + "Address family\n") +{ + vty->node = BGP_VPNV4_NODE; + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_BGPD, + address_family_vpnv4_unicast, + address_family_vpnv4_unicast_cmd, + "address-family vpnv4 unicast", + "Enter Address Family command mode\n" + "Address family\n" + "Address Family Modifier\n") +{ + vty->node = BGP_VPNV4_NODE; + 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, + "address-family ipv4 unicast", + "Enter Address Family command mode\n" + "Address family\n" + "Address Family Modifier\n") +{ + vty->node = BGP_IPV4_NODE; + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_BGPD, + address_family_ipv4_multicast, + address_family_ipv4_multicast_cmd, + "address-family ipv4 multicast", + "Enter Address Family command mode\n" + "Address family\n" + "Address Family Modifier\n") +{ + vty->node = BGP_IPV4M_NODE; + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_BGPD, + address_family_ipv6, + address_family_ipv6_cmd, + "address-family ipv6", + "Enter Address Family command mode\n" + "Address family\n") +{ + vty->node = BGP_IPV6_NODE; + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_BGPD, + address_family_ipv6_unicast, + address_family_ipv6_unicast_cmd, + "address-family ipv6 unicast", + "Enter Address Family command mode\n" + "Address family\n" + "Address Family Modifier\n") +{ + vty->node = BGP_IPV6_NODE; + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_BGPD, + address_family_ipv6_multicast, + address_family_ipv6_multicast_cmd, + "address-family ipv6 multicast", + "Enter Address Family command mode\n" + "Address family\n" + "Address Family Modifier\n") +{ + vty->node = BGP_IPV6M_NODE; + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_RIPD, + key_chain, + key_chain_cmd, + "key chain WORD", + "Authentication key management\n" + "Key-chain management\n" + "Key-chain name\n") +{ + vty->node = KEYCHAIN_NODE; + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_RIPD, + key, + key_cmd, + "key <0-2147483647>", + "Configure a key\n" + "Key identifier number\n") +{ + vty->node = KEYCHAIN_KEY_NODE; + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_RIPD, + router_rip, + router_rip_cmd, + "router rip", + ROUTER_STR + "RIP") +{ + vty->node = RIP_NODE; + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_RIPNGD, + router_ripng, + router_ripng_cmd, + "router ripng", + ROUTER_STR + "RIPng") +{ + vty->node = RIPNG_NODE; + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_OSPFD, + router_ospf, + router_ospf_cmd, + "router ospf", + "Enable a routing process\n" + "Start OSPF configuration\n") +{ + vty->node = OSPF_NODE; + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_OSPF6D, + router_ospf6, + router_ospf6_cmd, + "router ospf6", + OSPF6_ROUTER_STR + OSPF6_STR) +{ + vty->node = OSPF6_NODE; + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_ISISD, + router_isis, + router_isis_cmd, + "router isis WORD", + ROUTER_STR + "ISO IS-IS\n" + "ISO Routing area tag") +{ + vty->node = ISIS_NODE; + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_RMAP, + route_map, + route_map_cmd, + "route-map WORD (deny|permit) <1-65535>", + "Create route-map or enter route-map command mode\n" + "Route map tag\n" + "Route map denies set operations\n" + "Route map permits set operations\n" + "Sequence to insert to/delete from existing route-map entry\n") +{ + vty->node = RMAP_NODE; + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_ALL, + vtysh_line_vty, + vtysh_line_vty_cmd, + "line vty", + "Configure a terminal line\n" + "Virtual terminal\n") +{ + vty->node = VTY_NODE; + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_ALL, + vtysh_enable, + vtysh_enable_cmd, + "enable", + "Turn on privileged mode command\n") +{ + vty->node = ENABLE_NODE; + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_ALL, + vtysh_disable, + vtysh_disable_cmd, + "disable", + "Turn off privileged mode command\n") +{ + if (vty->node == ENABLE_NODE) + vty->node = VIEW_NODE; + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_ALL, + vtysh_config_terminal, + vtysh_config_terminal_cmd, + "configure terminal", + "Configuration from vty interface\n" + "Configuration terminal\n") +{ + vty->node = CONFIG_NODE; + return CMD_SUCCESS; +} + +static int +vtysh_exit (struct vty *vty) +{ + switch (vty->node) + { + case VIEW_NODE: + case ENABLE_NODE: + exit (0); + break; + case CONFIG_NODE: + vty->node = ENABLE_NODE; + break; + case INTERFACE_NODE: + case ZEBRA_NODE: + case BGP_NODE: + case RIP_NODE: + case RIPNG_NODE: + case OSPF_NODE: + case OSPF6_NODE: + case BABEL_NODE: + case ISIS_NODE: + case MASC_NODE: + case RMAP_NODE: + case VTY_NODE: + case KEYCHAIN_NODE: + vtysh_execute("end"); + vtysh_execute("configure terminal"); + 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: + case BGP_IPV6M_NODE: + vty->node = BGP_NODE; + break; + case KEYCHAIN_KEY_NODE: + vty->node = KEYCHAIN_NODE; + break; + case LINK_PARAMS_NODE: + vty->node = INTERFACE_NODE; + break; + default: + break; + } + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_ALL, + vtysh_exit_all, + vtysh_exit_all_cmd, + "exit", + "Exit current mode and down to previous mode\n") +{ + return vtysh_exit (vty); +} + +ALIAS (vtysh_exit_all, + vtysh_quit_all_cmd, + "quit", + "Exit current mode and down to previous mode\n") + +DEFUNSH (VTYSH_BGPD, + exit_address_family, + exit_address_family_cmd, + "exit-address-family", + "Exit from Address Family configuration mode\n") +{ + 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; + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_ZEBRA, + vtysh_exit_zebra, + vtysh_exit_zebra_cmd, + "exit", + "Exit current mode and down to previous mode\n") +{ + return vtysh_exit (vty); +} + +ALIAS (vtysh_exit_zebra, + vtysh_quit_zebra_cmd, + "quit", + "Exit current mode and down to previous mode\n") + +DEFUNSH (VTYSH_RIPD, + vtysh_exit_ripd, + vtysh_exit_ripd_cmd, + "exit", + "Exit current mode and down to previous mode\n") +{ + return vtysh_exit (vty); +} + +ALIAS (vtysh_exit_ripd, + vtysh_quit_ripd_cmd, + "quit", + "Exit current mode and down to previous mode\n") + +DEFUNSH (VTYSH_RIPNGD, + vtysh_exit_ripngd, + vtysh_exit_ripngd_cmd, + "exit", + "Exit current mode and down to previous mode\n") +{ + return vtysh_exit (vty); +} + +ALIAS (vtysh_exit_ripngd, + vtysh_quit_ripngd_cmd, + "quit", + "Exit current mode and down to previous mode\n") + +DEFUNSH (VTYSH_RMAP, + vtysh_exit_rmap, + vtysh_exit_rmap_cmd, + "exit", + "Exit current mode and down to previous mode\n") +{ + return vtysh_exit (vty); +} + +ALIAS (vtysh_exit_rmap, + vtysh_quit_rmap_cmd, + "quit", + "Exit current mode and down to previous mode\n") + +DEFUNSH (VTYSH_BGPD, + vtysh_exit_bgpd, + vtysh_exit_bgpd_cmd, + "exit", + "Exit current mode and down to previous mode\n") +{ + return vtysh_exit (vty); +} + +ALIAS (vtysh_exit_bgpd, + vtysh_quit_bgpd_cmd, + "quit", + "Exit current mode and down to previous mode\n") + +DEFUNSH (VTYSH_OSPFD, + vtysh_exit_ospfd, + vtysh_exit_ospfd_cmd, + "exit", + "Exit current mode and down to previous mode\n") +{ + return vtysh_exit (vty); +} + +ALIAS (vtysh_exit_ospfd, + vtysh_quit_ospfd_cmd, + "quit", + "Exit current mode and down to previous mode\n") + +DEFUNSH (VTYSH_OSPF6D, + vtysh_exit_ospf6d, + vtysh_exit_ospf6d_cmd, + "exit", + "Exit current mode and down to previous mode\n") +{ + return vtysh_exit (vty); +} + +ALIAS (vtysh_exit_ospf6d, + vtysh_quit_ospf6d_cmd, + "quit", + "Exit current mode and down to previous mode\n") + +DEFUNSH (VTYSH_ISISD, + vtysh_exit_isisd, + vtysh_exit_isisd_cmd, + "exit", + "Exit current mode and down to previous mode\n") +{ + return vtysh_exit (vty); +} + +ALIAS (vtysh_exit_isisd, + vtysh_quit_isisd_cmd, + "quit", + "Exit current mode and down to previous mode\n") + +DEFUNSH (VTYSH_ALL, + vtysh_exit_line_vty, + vtysh_exit_line_vty_cmd, + "exit", + "Exit current mode and down to previous mode\n") +{ + return vtysh_exit (vty); +} + +ALIAS (vtysh_exit_line_vty, + vtysh_quit_line_vty_cmd, + "quit", + "Exit current mode and down to previous mode\n") + +DEFUNSH (VTYSH_INTERFACE, + vtysh_interface, + vtysh_interface_cmd, + "interface IFNAME", + "Select an interface to configure\n" + "Interface's name\n") +{ + vty->node = INTERFACE_NODE; + return CMD_SUCCESS; +} + +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, + interface_desc_cmd, + "description .LINE", + "Interface specific description\n" + "Characters describing this interface\n") + +DEFSH (VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_OSPFD, + no_interface_desc_cmd, + "no description", + 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, + "exit", + "Exit current mode and down to previous mode\n") +{ + return vtysh_exit (vty); +} + +ALIAS (vtysh_exit_interface, + vtysh_quit_interface_cmd, + "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, + "show memory", + SHOW_STR + "Memory statistics\n") +{ + unsigned int i; + int ret = CMD_SUCCESS; + char line[] = "show memory\n"; + + for (i = 0; i < array_size(vtysh_client); i++) + if ( vtysh_client[i].fd >= 0 ) + { + fprintf (stdout, "Memory statistics for %s:\n", + vtysh_client[i].name); + ret = vtysh_client_execute (&vtysh_client[i], line, stdout); + fprintf (stdout,"\n"); + } + + return ret; +} + +/* Logging commands. */ +DEFUN (vtysh_show_logging, + vtysh_show_logging_cmd, + "show logging", + SHOW_STR + "Show current logging configuration\n") +{ + unsigned int i; + int ret = CMD_SUCCESS; + char line[] = "show logging\n"; + + for (i = 0; i < array_size(vtysh_client); i++) + if ( vtysh_client[i].fd >= 0 ) + { + fprintf (stdout,"Logging configuration for %s:\n", + vtysh_client[i].name); + ret = vtysh_client_execute (&vtysh_client[i], line, stdout); + fprintf (stdout,"\n"); + } + + return ret; +} + +DEFUNSH (VTYSH_ALL, + vtysh_log_stdout, + vtysh_log_stdout_cmd, + "log stdout", + "Logging control\n" + "Set stdout logging level\n") +{ + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_ALL, + vtysh_log_stdout_level, + vtysh_log_stdout_level_cmd, + "log stdout "LOG_LEVELS, + "Logging control\n" + "Set stdout logging level\n" + LOG_LEVEL_DESC) +{ + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_ALL, + no_vtysh_log_stdout, + no_vtysh_log_stdout_cmd, + "no log stdout [LEVEL]", + NO_STR + "Logging control\n" + "Cancel logging to stdout\n" + "Logging level\n") +{ + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_ALL, + vtysh_log_file, + vtysh_log_file_cmd, + "log file FILENAME", + "Logging control\n" + "Logging to file\n" + "Logging filename\n") +{ + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_ALL, + vtysh_log_file_level, + vtysh_log_file_level_cmd, + "log file FILENAME "LOG_LEVELS, + "Logging control\n" + "Logging to file\n" + "Logging filename\n" + LOG_LEVEL_DESC) +{ + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_ALL, + no_vtysh_log_file, + no_vtysh_log_file_cmd, + "no log file [FILENAME]", + NO_STR + "Logging control\n" + "Cancel logging to file\n" + "Logging file name\n") +{ + return CMD_SUCCESS; +} + +ALIAS_SH (VTYSH_ALL, + no_vtysh_log_file, + no_vtysh_log_file_level_cmd, + "no log file FILENAME LEVEL", + NO_STR + "Logging control\n" + "Cancel logging to file\n" + "Logging file name\n" + "Logging level\n") + +DEFUNSH (VTYSH_ALL, + vtysh_log_monitor, + vtysh_log_monitor_cmd, + "log monitor", + "Logging control\n" + "Set terminal line (monitor) logging level\n") +{ + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_ALL, + vtysh_log_monitor_level, + vtysh_log_monitor_level_cmd, + "log monitor "LOG_LEVELS, + "Logging control\n" + "Set terminal line (monitor) logging level\n" + LOG_LEVEL_DESC) +{ + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_ALL, + no_vtysh_log_monitor, + no_vtysh_log_monitor_cmd, + "no log monitor [LEVEL]", + NO_STR + "Logging control\n" + "Disable terminal line (monitor) logging\n" + "Logging level\n") +{ + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_ALL, + vtysh_log_syslog, + vtysh_log_syslog_cmd, + "log syslog", + "Logging control\n" + "Set syslog logging level\n") +{ + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_ALL, + vtysh_log_syslog_level, + vtysh_log_syslog_level_cmd, + "log syslog "LOG_LEVELS, + "Logging control\n" + "Set syslog logging level\n" + LOG_LEVEL_DESC) +{ + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_ALL, + no_vtysh_log_syslog, + no_vtysh_log_syslog_cmd, + "no log syslog [LEVEL]", + NO_STR + "Logging control\n" + "Cancel logging to syslog\n" + "Logging level\n") +{ + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_ALL, + vtysh_log_facility, + vtysh_log_facility_cmd, + "log facility "LOG_FACILITIES, + "Logging control\n" + "Facility parameter for syslog messages\n" + LOG_FACILITY_DESC) + +{ + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_ALL, + no_vtysh_log_facility, + no_vtysh_log_facility_cmd, + "no log facility [FACILITY]", + NO_STR + "Logging control\n" + "Reset syslog facility to default (daemon)\n" + "Syslog facility\n") + +{ + return CMD_SUCCESS; +} + +DEFUNSH_DEPRECATED (VTYSH_ALL, + vtysh_log_trap, + vtysh_log_trap_cmd, + "log trap "LOG_LEVELS, + "Logging control\n" + "(Deprecated) Set logging level and default for all destinations\n" + LOG_LEVEL_DESC) + +{ + return CMD_SUCCESS; +} + +DEFUNSH_DEPRECATED (VTYSH_ALL, + no_vtysh_log_trap, + no_vtysh_log_trap_cmd, + "no log trap [LEVEL]", + NO_STR + "Logging control\n" + "Permit all logging information\n" + "Logging level\n") +{ + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_ALL, + vtysh_log_record_priority, + vtysh_log_record_priority_cmd, + "log record-priority", + "Logging control\n" + "Log the priority of the message within the message\n") +{ + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_ALL, + no_vtysh_log_record_priority, + no_vtysh_log_record_priority_cmd, + "no log record-priority", + NO_STR + "Logging control\n" + "Do not log the priority of the message within the message\n") +{ + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_ALL, + vtysh_log_timestamp_precision, + vtysh_log_timestamp_precision_cmd, + "log timestamp precision <0-6>", + "Logging control\n" + "Timestamp configuration\n" + "Set the timestamp precision\n" + "Number of subsecond digits\n") +{ + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_ALL, + no_vtysh_log_timestamp_precision, + no_vtysh_log_timestamp_precision_cmd, + "no log timestamp precision", + NO_STR + "Logging control\n" + "Timestamp configuration\n" + "Reset the timestamp precision to the default value of 0\n") +{ + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_ALL, + vtysh_service_password_encrypt, + vtysh_service_password_encrypt_cmd, + "service password-encryption", + "Set up miscellaneous service\n" + "Enable encrypted passwords\n") +{ + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_ALL, + no_vtysh_service_password_encrypt, + no_vtysh_service_password_encrypt_cmd, + "no service password-encryption", + NO_STR + "Set up miscellaneous service\n" + "Enable encrypted passwords\n") +{ + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_ALL, + vtysh_config_password, + vtysh_password_cmd, + "password (8|) WORD", + "Assign the terminal connection password\n" + "Specifies a HIDDEN password will follow\n" + "dummy string \n" + "The HIDDEN line password string\n") +{ + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_ALL, + vtysh_password_text, + vtysh_password_text_cmd, + "password LINE", + "Assign the terminal connection password\n" + "The UNENCRYPTED (cleartext) line password\n") +{ + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_ALL, + vtysh_config_enable_password, + vtysh_enable_password_cmd, + "enable password (8|) WORD", + "Modify enable password parameters\n" + "Assign the privileged level password\n" + "Specifies a HIDDEN password will follow\n" + "dummy string \n" + "The HIDDEN 'enable' password string\n") +{ + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_ALL, + vtysh_enable_password_text, + vtysh_enable_password_text_cmd, + "enable password LINE", + "Modify enable password parameters\n" + "Assign the privileged level password\n" + "The UNENCRYPTED (cleartext) 'enable' password\n") +{ + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_ALL, + no_vtysh_config_enable_password, + no_vtysh_enable_password_cmd, + "no enable password", + NO_STR + "Modify enable password parameters\n" + "Assign the privileged level password\n") +{ + return CMD_SUCCESS; +} + +DEFUN (vtysh_write_terminal, + vtysh_write_terminal_cmd, + "write terminal", + "Write running configuration to memory, network, or terminal\n" + "Write to terminal\n") +{ + u_int i; + char line[] = "write terminal\n"; + FILE *fp = NULL; + + if (vtysh_pager_name) + { + fp = popen (vtysh_pager_name, "w"); + if (fp == NULL) + { + perror ("popen"); + exit (1); + } + } + else + fp = stdout; + + vty_out (vty, "Building configuration...%s", VTY_NEWLINE); + vty_out (vty, "%sCurrent configuration:%s", VTY_NEWLINE, + VTY_NEWLINE); + vty_out (vty, "!%s", VTY_NEWLINE); + + for (i = 0; i < array_size(vtysh_client); i++) + vtysh_client_execute (&vtysh_client[i], line, NULL); + + /* Integrate vtysh specific configuration. */ + vtysh_config_write (); + + vtysh_config_dump (fp); + + if (vtysh_pager_name && fp) + { + fflush (fp); + if (pclose (fp) == -1) + { + perror ("pclose"); + exit (1); + } + fp = NULL; + } + + vty_out (vty, "end%s", VTY_NEWLINE); + + 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", + "Set up miscellaneous service\n" + "Write configuration into integrated file\n") +{ + vtysh_writeconfig_integrated = 1; + return CMD_SUCCESS; +} + +DEFUN (no_vtysh_integrated_config, + no_vtysh_integrated_config_cmd, + "no service integrated-vtysh-config", + NO_STR + "Set up miscellaneous service\n" + "Write configuration into integrated file\n") +{ + vtysh_writeconfig_integrated = 0; + return CMD_SUCCESS; +} + +static int +write_config_integrated(void) +{ + u_int i; + char line[] = "write terminal\n"; + FILE *fp; + char *integrate_sav = NULL; + + integrate_sav = malloc (strlen (integrate_default) + + strlen (CONF_BACKUP_EXT) + 1); + strcpy (integrate_sav, integrate_default); + strcat (integrate_sav, CONF_BACKUP_EXT); + + fprintf (stdout,"Building Configuration...\n"); + + /* Move current configuration file to backup config file. */ + unlink (integrate_sav); + rename (integrate_default, integrate_sav); + free (integrate_sav); + + fp = fopen (integrate_default, "w"); + if (fp == NULL) + { + fprintf (stdout,"%% Can't open configuration file %s.\n", + integrate_default); + return CMD_SUCCESS; + } + + 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); + + if (chmod (integrate_default, CONFIGFILE_MASK) != 0) + { + fprintf (stdout,"%% Can't chmod configuration file %s: %s (%d)\n", + integrate_default, safe_strerror(errno), errno); + return CMD_WARNING; + } + + fprintf(stdout,"Integrated configuration saved to %s\n",integrate_default); + + fprintf (stdout,"[OK]\n"); + + return CMD_SUCCESS; +} + +DEFUN (vtysh_write_memory, + vtysh_write_memory_cmd, + "write memory", + "Write running configuration to memory, network, or terminal\n" + "Write configuration to the file (same as write file)\n") +{ + int ret = CMD_SUCCESS; + char line[] = "write memory\n"; + u_int i; + + /* If integrated Quagga.conf explicitely set. */ + if (vtysh_writeconfig_integrated) + return write_config_integrated(); + + fprintf (stdout,"Building Configuration...\n"); + + for (i = 0; i < array_size(vtysh_client); i++) + ret = vtysh_client_execute (&vtysh_client[i], line, stdout); + + fprintf (stdout,"[OK]\n"); + + return ret; +} + +ALIAS (vtysh_write_memory, + vtysh_copy_runningconfig_startupconfig_cmd, + "copy running-config startup-config", + "Copy from one file to another\n" + "Copy from current system configuration\n" + "Copy to startup configuration\n") + +ALIAS (vtysh_write_memory, + vtysh_write_file_cmd, + "write file", + "Write running configuration to memory, network, or terminal\n" + "Write configuration to the file (same as write memory)\n") + +ALIAS (vtysh_write_memory, + vtysh_write_cmd, + "write", + "Write running configuration to memory, network, or terminal\n") + +ALIAS (vtysh_write_terminal, + vtysh_show_running_config_cmd, + "show running-config", + 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>", + "Set terminal line parameters\n" + "Set number of lines on a screen\n" + "Number of lines on screen (0 for no pausing)\n") +{ + int lines; + char *endptr = NULL; + char default_pager[10]; + + lines = strtol (argv[0], &endptr, 10); + if (lines < 0 || lines > 512 || *endptr != '\0') + { + vty_out (vty, "length is malformed%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (vtysh_pager_name) + { + free (vtysh_pager_name); + vtysh_pager_name = NULL; + } + + if (lines != 0) + { + snprintf(default_pager, 10, "more -%i", lines); + vtysh_pager_name = strdup (default_pager); + } + + return CMD_SUCCESS; +} + +DEFUN (vtysh_terminal_no_length, + vtysh_terminal_no_length_cmd, + "terminal no length", + "Set terminal line parameters\n" + NO_STR + "Set number of lines on a screen\n") +{ + if (vtysh_pager_name) + { + free (vtysh_pager_name); + vtysh_pager_name = NULL; + } + + vtysh_pager_init(); + return CMD_SUCCESS; +} + +DEFUN (vtysh_show_daemons, + vtysh_show_daemons_cmd, + "show daemons", + SHOW_STR + "Show list of running daemons\n") +{ + u_int 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); + + return CMD_SUCCESS; +} + +/* Execute command in child process. */ +static int +execute_command (const char *command, int argc, const char *arg1, + const char *arg2) +{ + pid_t pid; + int status; + + /* Call fork(). */ + pid = fork (); + + if (pid < 0) + { + /* Failure of fork(). */ + fprintf (stderr, "Can't fork: %s\n", safe_strerror (errno)); + exit (1); + } + else if (pid == 0) + { + /* This is child process. */ + switch (argc) + { + case 0: + execlp (command, command, (const char *)NULL); + break; + case 1: + execlp (command, command, arg1, (const char *)NULL); + break; + case 2: + execlp (command, command, arg1, arg2, (const char *)NULL); + break; + } + + /* When execlp suceed, this part is not executed. */ + fprintf (stderr, "Can't execute %s: %s\n", command, safe_strerror (errno)); + exit (1); + } + else + { + /* This is parent. */ + execute_flag = 1; + wait4 (pid, &status, 0, NULL); + execute_flag = 0; + } + return 0; +} + +DEFUN (vtysh_ping, + vtysh_ping_cmd, + "ping WORD", + "Send echo messages\n" + "Ping destination address or hostname\n") +{ + execute_command ("ping", 1, argv[0], NULL); + return CMD_SUCCESS; +} + +ALIAS (vtysh_ping, + vtysh_ping_ip_cmd, + "ping ip WORD", + "Send echo messages\n" + "IP echo\n" + "Ping destination address or hostname\n") + +DEFUN (vtysh_traceroute, + vtysh_traceroute_cmd, + "traceroute WORD", + "Trace route to destination\n" + "Trace route to destination address or hostname\n") +{ + execute_command ("traceroute", 1, argv[0], NULL); + return CMD_SUCCESS; +} + +ALIAS (vtysh_traceroute, + vtysh_traceroute_ip_cmd, + "traceroute ip WORD", + "Trace route to destination\n" + "IP trace\n" + "Trace route to destination address or hostname\n") + +#ifdef HAVE_IPV6 +DEFUN (vtysh_ping6, + vtysh_ping6_cmd, + "ping ipv6 WORD", + "Send echo messages\n" + "IPv6 echo\n" + "Ping destination address or hostname\n") +{ + execute_command ("ping6", 1, argv[0], NULL); + return CMD_SUCCESS; +} + +DEFUN (vtysh_traceroute6, + vtysh_traceroute6_cmd, + "traceroute ipv6 WORD", + "Trace route to destination\n" + "IPv6 trace\n" + "Trace route to destination address or hostname\n") +{ + execute_command ("traceroute6", 1, argv[0], NULL); + return CMD_SUCCESS; +} +#endif + +DEFUN (vtysh_telnet, + vtysh_telnet_cmd, + "telnet WORD", + "Open a telnet connection\n" + "IP address or hostname of a remote system\n") +{ + execute_command ("telnet", 1, argv[0], NULL); + return CMD_SUCCESS; +} + +DEFUN (vtysh_telnet_port, + vtysh_telnet_port_cmd, + "telnet WORD PORT", + "Open a telnet connection\n" + "IP address or hostname of a remote system\n" + "TCP Port number\n") +{ + execute_command ("telnet", 2, argv[0], argv[1]); + return CMD_SUCCESS; +} + +DEFUN (vtysh_ssh, + vtysh_ssh_cmd, + "ssh WORD", + "Open an ssh connection\n" + "[user@]host\n") +{ + execute_command ("ssh", 1, argv[0], NULL); + return CMD_SUCCESS; +} + +DEFUN (vtysh_start_shell, + vtysh_start_shell_cmd, + "start-shell", + "Start UNIX shell\n") +{ + execute_command ("sh", 0, NULL, NULL); + return CMD_SUCCESS; +} + +DEFUN (vtysh_start_bash, + vtysh_start_bash_cmd, + "start-shell bash", + "Start UNIX shell\n" + "Start bash\n") +{ + execute_command ("bash", 0, NULL, NULL); + return CMD_SUCCESS; +} + +DEFUN (vtysh_start_zsh, + vtysh_start_zsh_cmd, + "start-shell zsh", + "Start UNIX shell\n" + "Start Z shell\n") +{ + execute_command ("zsh", 0, NULL, NULL); + return CMD_SUCCESS; +} + +static void +vtysh_install_default (enum node_type node) +{ + install_element (node, &config_list_cmd); +} + +/* Making connection to protocol daemon. */ +static int +vtysh_connect (struct vtysh_client *vclient) +{ + int ret; + int sock, len; + struct sockaddr_un addr; + struct stat s_stat; + + /* Stat socket to see if we have permission to access it. */ + ret = stat (vclient->path, &s_stat); + if (ret < 0 && errno != ENOENT) + { + fprintf (stderr, "vtysh_connect(%s): stat = %s\n", + vclient->path, safe_strerror(errno)); + exit(1); + } + + if (ret >= 0) + { + if (! S_ISSOCK(s_stat.st_mode)) + { + fprintf (stderr, "vtysh_connect(%s): Not a socket\n", + vclient->path); + exit (1); + } + + } + + sock = socket (AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) + { +#ifdef DEBUG + fprintf(stderr, "vtysh_connect(%s): socket = %s\n", vclient->path, + safe_strerror(errno)); +#endif /* DEBUG */ + return -1; + } + + memset (&addr, 0, sizeof (struct sockaddr_un)); + addr.sun_family = AF_UNIX; + strncpy (addr.sun_path, vclient->path, strlen (vclient->path)); +#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN + len = addr.sun_len = SUN_LEN(&addr); +#else + len = sizeof (addr.sun_family) + strlen (addr.sun_path); +#endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */ + + ret = connect (sock, (struct sockaddr *) &addr, len); + if (ret < 0) + { +#ifdef DEBUG + fprintf(stderr, "vtysh_connect(%s): connect = %s\n", vclient->path, + safe_strerror(errno)); +#endif /* DEBUG */ + close (sock); + return -1; + } + vclient->fd = sock; + + return 0; +} + +int +vtysh_connect_all(const char *daemon_name) +{ + u_int i; + int rc = 0; + int matches = 0; + + for (i = 0; i < array_size(vtysh_client); i++) + { + if (!daemon_name || !strcmp(daemon_name, vtysh_client[i].name)) + { + matches++; + if (vtysh_connect(&vtysh_client[i]) == 0) + rc++; + /* We need direct access to ripd in vtysh_exit_ripd_only. */ + if (vtysh_client[i].flag == VTYSH_RIPD) + ripd_client = &vtysh_client[i]; + } + } + if (!matches) + fprintf(stderr, "Error: no daemons match name %s!\n", daemon_name); + return rc; +} + +/* To disable readline's filename completion. */ +static char * +vtysh_completion_entry_function (const char *ignore, int invoking_key) +{ + return NULL; +} + +void +vtysh_readline_init (void) +{ + /* readline related settings. */ + rl_bind_key ('?', (rl_command_func_t *) vtysh_rl_describe); + rl_completion_entry_function = vtysh_completion_entry_function; + rl_attempted_completion_function = (rl_completion_func_t *)new_completion; +} + +char * +vtysh_prompt (void) +{ + static struct utsname names; + static char buf[100]; + const char*hostname; + extern struct host host; + + hostname = host.name; + + if (!hostname) + { + if (!names.nodename[0]) + uname (&names); + hostname = names.nodename; + } + + snprintf (buf, sizeof buf, cmd_prompt (vty->node), hostname); + + return buf; +} + +void +vtysh_init_vty (void) +{ + /* Make vty structure. */ + vty = vty_new (); + vty->type = VTY_SHELL; + vty->node = VIEW_NODE; + + /* Initialize commands. */ + cmd_init (0); + + /* Install nodes. */ + 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 */ + install_node (&bgp_ipv6_node, NULL); + install_node (&bgp_ipv6m_node, NULL); +/* #endif */ + install_node (&ospf_node, NULL); +/* #ifdef HAVE_IPV6 */ + install_node (&ripng_node, NULL); + install_node (&ospf6_node, NULL); +/* #endif */ + 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); + + vtysh_install_default (VIEW_NODE); + vtysh_install_default (ENABLE_NODE); + vtysh_install_default (CONFIG_NODE); + 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); + vtysh_install_default (BGP_IPV6M_NODE); + vtysh_install_default (OSPF_NODE); + vtysh_install_default (RIPNG_NODE); + vtysh_install_default (OSPF6_NODE); + vtysh_install_default (BABEL_NODE); + vtysh_install_default (ISIS_NODE); + vtysh_install_default (KEYCHAIN_NODE); + vtysh_install_default (KEYCHAIN_KEY_NODE); + vtysh_install_default (VTY_NODE); + + install_element (VIEW_NODE, &vtysh_enable_cmd); + install_element (ENABLE_NODE, &vtysh_config_terminal_cmd); + install_element (ENABLE_NODE, &vtysh_disable_cmd); + + /* "exit" command. */ + install_element (VIEW_NODE, &vtysh_exit_all_cmd); + install_element (VIEW_NODE, &vtysh_quit_all_cmd); + install_element (CONFIG_NODE, &vtysh_exit_all_cmd); + /* install_element (CONFIG_NODE, &vtysh_quit_all_cmd); */ + install_element (ENABLE_NODE, &vtysh_exit_all_cmd); + install_element (ENABLE_NODE, &vtysh_quit_all_cmd); + install_element (RIP_NODE, &vtysh_exit_ripd_cmd); + install_element (RIP_NODE, &vtysh_quit_ripd_cmd); + install_element (RIPNG_NODE, &vtysh_exit_ripngd_cmd); + install_element (RIPNG_NODE, &vtysh_quit_ripngd_cmd); + install_element (OSPF_NODE, &vtysh_exit_ospfd_cmd); + install_element (OSPF_NODE, &vtysh_quit_ospfd_cmd); + install_element (OSPF6_NODE, &vtysh_exit_ospf6d_cmd); + install_element (OSPF6_NODE, &vtysh_quit_ospf6d_cmd); + install_element (BGP_NODE, &vtysh_exit_bgpd_cmd); + 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); + install_element (BGP_IPV4M_NODE, &vtysh_quit_bgpd_cmd); + install_element (BGP_IPV6_NODE, &vtysh_exit_bgpd_cmd); + install_element (BGP_IPV6_NODE, &vtysh_quit_bgpd_cmd); + install_element (BGP_IPV6M_NODE, &vtysh_exit_bgpd_cmd); + install_element (BGP_IPV6M_NODE, &vtysh_quit_bgpd_cmd); + install_element (ISIS_NODE, &vtysh_exit_isisd_cmd); + install_element (ISIS_NODE, &vtysh_quit_isisd_cmd); + install_element (KEYCHAIN_NODE, &vtysh_exit_ripd_cmd); + install_element (KEYCHAIN_NODE, &vtysh_quit_ripd_cmd); + install_element (KEYCHAIN_KEY_NODE, &vtysh_exit_ripd_cmd); + install_element (KEYCHAIN_KEY_NODE, &vtysh_quit_ripd_cmd); + install_element (RMAP_NODE, &vtysh_exit_rmap_cmd); + install_element (RMAP_NODE, &vtysh_quit_rmap_cmd); + install_element (VTY_NODE, &vtysh_exit_line_vty_cmd); + install_element (VTY_NODE, &vtysh_quit_line_vty_cmd); + + /* "end" command. */ + install_element (CONFIG_NODE, &vtysh_end_all_cmd); + install_element (ENABLE_NODE, &vtysh_end_all_cmd); + install_element (RIP_NODE, &vtysh_end_all_cmd); + install_element (RIPNG_NODE, &vtysh_end_all_cmd); + install_element (OSPF_NODE, &vtysh_end_all_cmd); + install_element (OSPF6_NODE, &vtysh_end_all_cmd); + install_element (BABEL_NODE, &vtysh_end_all_cmd); + install_element (BGP_NODE, &vtysh_end_all_cmd); + 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); + install_element (KEYCHAIN_NODE, &vtysh_end_all_cmd); + install_element (KEYCHAIN_KEY_NODE, &vtysh_end_all_cmd); + install_element (RMAP_NODE, &vtysh_end_all_cmd); + install_element (VTY_NODE, &vtysh_end_all_cmd); + + install_element (INTERFACE_NODE, &interface_desc_cmd); + 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 + install_element (CONFIG_NODE, &router_ripng_cmd); +#endif + install_element (CONFIG_NODE, &router_ospf_cmd); +#ifdef HAVE_IPV6 + install_element (CONFIG_NODE, &router_ospf6_cmd); +#endif + 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); + install_element (BGP_IPV6M_NODE, &exit_address_family_cmd); + install_element (CONFIG_NODE, &key_chain_cmd); + install_element (CONFIG_NODE, &route_map_cmd); + install_element (CONFIG_NODE, &vtysh_line_vty_cmd); + install_element (KEYCHAIN_NODE, &key_cmd); + install_element (KEYCHAIN_NODE, &key_chain_cmd); + 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); + + /* "write memory" command. */ + install_element (ENABLE_NODE, &vtysh_write_memory_cmd); + + install_element (VIEW_NODE, &vtysh_terminal_length_cmd); + install_element (ENABLE_NODE, &vtysh_terminal_length_cmd); + install_element (VIEW_NODE, &vtysh_terminal_no_length_cmd); + install_element (ENABLE_NODE, &vtysh_terminal_no_length_cmd); + install_element (VIEW_NODE, &vtysh_show_daemons_cmd); + install_element (ENABLE_NODE, &vtysh_show_daemons_cmd); + + install_element (VIEW_NODE, &vtysh_ping_cmd); + install_element (VIEW_NODE, &vtysh_ping_ip_cmd); + install_element (VIEW_NODE, &vtysh_traceroute_cmd); + install_element (VIEW_NODE, &vtysh_traceroute_ip_cmd); +#ifdef HAVE_IPV6 + install_element (VIEW_NODE, &vtysh_ping6_cmd); + install_element (VIEW_NODE, &vtysh_traceroute6_cmd); +#endif + install_element (VIEW_NODE, &vtysh_telnet_cmd); + install_element (VIEW_NODE, &vtysh_telnet_port_cmd); + install_element (VIEW_NODE, &vtysh_ssh_cmd); + install_element (ENABLE_NODE, &vtysh_ping_cmd); + install_element (ENABLE_NODE, &vtysh_ping_ip_cmd); + install_element (ENABLE_NODE, &vtysh_traceroute_cmd); + install_element (ENABLE_NODE, &vtysh_traceroute_ip_cmd); +#ifdef HAVE_IPV6 + install_element (ENABLE_NODE, &vtysh_ping6_cmd); + install_element (ENABLE_NODE, &vtysh_traceroute6_cmd); +#endif + install_element (ENABLE_NODE, &vtysh_telnet_cmd); + install_element (ENABLE_NODE, &vtysh_telnet_port_cmd); + install_element (ENABLE_NODE, &vtysh_ssh_cmd); + install_element (ENABLE_NODE, &vtysh_start_shell_cmd); + install_element (ENABLE_NODE, &vtysh_start_bash_cmd); + install_element (ENABLE_NODE, &vtysh_start_zsh_cmd); + + 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); + install_element (CONFIG_NODE, &vtysh_log_stdout_cmd); + install_element (CONFIG_NODE, &vtysh_log_stdout_level_cmd); + install_element (CONFIG_NODE, &no_vtysh_log_stdout_cmd); + install_element (CONFIG_NODE, &vtysh_log_file_cmd); + install_element (CONFIG_NODE, &vtysh_log_file_level_cmd); + install_element (CONFIG_NODE, &no_vtysh_log_file_cmd); + install_element (CONFIG_NODE, &no_vtysh_log_file_level_cmd); + install_element (CONFIG_NODE, &vtysh_log_monitor_cmd); + install_element (CONFIG_NODE, &vtysh_log_monitor_level_cmd); + install_element (CONFIG_NODE, &no_vtysh_log_monitor_cmd); + install_element (CONFIG_NODE, &vtysh_log_syslog_cmd); + install_element (CONFIG_NODE, &vtysh_log_syslog_level_cmd); + install_element (CONFIG_NODE, &no_vtysh_log_syslog_cmd); + install_element (CONFIG_NODE, &vtysh_log_trap_cmd); + install_element (CONFIG_NODE, &no_vtysh_log_trap_cmd); + install_element (CONFIG_NODE, &vtysh_log_facility_cmd); + install_element (CONFIG_NODE, &no_vtysh_log_facility_cmd); + install_element (CONFIG_NODE, &vtysh_log_record_priority_cmd); + install_element (CONFIG_NODE, &no_vtysh_log_record_priority_cmd); + install_element (CONFIG_NODE, &vtysh_log_timestamp_precision_cmd); + install_element (CONFIG_NODE, &no_vtysh_log_timestamp_precision_cmd); + + install_element (CONFIG_NODE, &vtysh_service_password_encrypt_cmd); + install_element (CONFIG_NODE, &no_vtysh_service_password_encrypt_cmd); + + install_element (CONFIG_NODE, &vtysh_password_cmd); + install_element (CONFIG_NODE, &vtysh_password_text_cmd); + install_element (CONFIG_NODE, &vtysh_enable_password_cmd); + install_element (CONFIG_NODE, &vtysh_enable_password_text_cmd); + install_element (CONFIG_NODE, &no_vtysh_enable_password_cmd); + +} diff --git a/vtysh/vtysh.conf.sample b/vtysh/vtysh.conf.sample new file mode 100644 index 0000000..4e0a2be --- /dev/null +++ b/vtysh/vtysh.conf.sample @@ -0,0 +1,7 @@ +! +! Sample configuration file for vtysh. +! +!service integrated-vtysh-config +!hostname quagga-router +!username root nopassword +! diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h new file mode 100644 index 0000000..02ff7fd --- /dev/null +++ b/vtysh/vtysh.h @@ -0,0 +1,72 @@ +/* Virtual terminal interface shell. + * 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. + */ + +#ifndef VTYSH_H +#define VTYSH_H + +#define VTYSH_ZEBRA 0x01 +#define VTYSH_RIPD 0x02 +#define VTYSH_RIPNGD 0x04 +#define VTYSH_OSPFD 0x08 +#define VTYSH_OSPF6D 0x10 +#define VTYSH_BGPD 0x20 +#define VTYSH_ISISD 0x40 +#define VTYSH_BABELD 0x80 +#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|VTYSH_PIMD|VTYSH_NHRPD + +/* vtysh local configuration file. */ +#define VTYSH_DEFAULT_CONFIG "vtysh.conf" + +void vtysh_init_vty (void); +void vtysh_init_cmd (void); +extern int vtysh_connect_all (const char *optional_daemon_name); +void vtysh_readline_init (void); +void vtysh_user_init (void); + +int vtysh_execute (const char *); +int vtysh_execute_no_pager (const char *); + +char *vtysh_prompt (void); + +void vtysh_config_write (void); + +int vtysh_config_from_file (struct vty *, FILE *); + +int vtysh_read_config (char *); + +void vtysh_config_parse (char *); + +void vtysh_config_dump (FILE *); + +void vtysh_config_init (void); + +void vtysh_pager_init (void); + +/* Child process execution flag. */ +extern int execute_flag; + +extern struct vty *vty; + +#endif /* VTYSH_H */ diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c new file mode 100644 index 0000000..2834ef4 --- /dev/null +++ b/vtysh/vtysh_config.c @@ -0,0 +1,451 @@ +/* Configuration generator. + 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 "linklist.h" +#include "memory.h" + +#include "vtysh/vtysh.h" + +vector configvec; + +extern int vtysh_writeconfig_integrated; + +struct config +{ + /* Configuration node name. */ + char *name; + + /* Configuration string line. */ + struct list *line; + + /* Configuration can be nest. */ + struct config *config; + + /* Index of this config. */ + u_int32_t index; +}; + +struct list *config_top; + +static int +line_cmp (char *c1, char *c2) +{ + return strcmp (c1, c2); +} + +static void +line_del (char *line) +{ + XFREE (MTYPE_VTYSH_CONFIG_LINE, line); +} + +static struct config * +config_new () +{ + struct config *config; + config = XCALLOC (MTYPE_VTYSH_CONFIG, sizeof (struct config)); + return config; +} + +static int +config_cmp (struct config *c1, struct config *c2) +{ + return strcmp (c1->name, c2->name); +} + +static void +config_del (struct config* config) +{ + list_delete (config->line); + if (config->name) + XFREE (MTYPE_VTYSH_CONFIG_LINE, config->name); + XFREE (MTYPE_VTYSH_CONFIG, config); +} + +static struct config * +config_get (int index, const char *line) +{ + struct config *config; + struct config *config_loop; + struct list *master; + struct listnode *node, *nnode; + + config = config_loop = NULL; + + master = vector_lookup_ensure (configvec, index); + + if (! master) + { + master = list_new (); + master->del = (void (*) (void *))config_del; + master->cmp = (int (*)(void *, void *)) config_cmp; + vector_set_index (configvec, index, master); + } + + for (ALL_LIST_ELEMENTS (master, node, nnode, config_loop)) + { + if (strcmp (config_loop->name, line) == 0) + config = config_loop; + } + + if (! config) + { + config = config_new (); + config->line = list_new (); + config->line->del = (void (*) (void *))line_del; + config->line->cmp = (int (*)(void *, void *)) line_cmp; + config->name = XSTRDUP (MTYPE_VTYSH_CONFIG_LINE, line); + config->index = index; + listnode_add (master, config); + } + return config; +} + +static void +config_add_line (struct list *config, const char *line) +{ + listnode_add (config, XSTRDUP (MTYPE_VTYSH_CONFIG_LINE, line)); +} + +static void +config_add_line_uniq (struct list *config, const char *line) +{ + struct listnode *node, *nnode; + char *pnt; + + for (ALL_LIST_ELEMENTS (config, node, nnode, pnt)) + { + if (strcmp (pnt, line) == 0) + return; + } + listnode_add_sort (config, XSTRDUP (MTYPE_VTYSH_CONFIG_LINE, line)); +} + +static void +vtysh_config_parse_line (const char *line) +{ + char c; + static struct config *config = NULL; + + if (! line) + return; + + c = line[0]; + + if (c == '\0') + return; + + /* printf ("[%s]\n", line); */ + + switch (c) + { + case '!': + case '#': + break; + case ' ': + /* Store line to current configuration. */ + if (config) + { + 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_add_line_uniq (config->line, line); + else + config_add_line (config->line, line); + } + else + config_add_line (config_top, line); + break; + default: + if (strncmp (line, "interface", strlen ("interface")) == 0) + config = config_get (INTERFACE_NODE, line); + else if (strncmp (line, "router-id", strlen ("router-id")) == 0) + config = config_get (ZEBRA_NODE, line); + else if (strncmp (line, "router rip", strlen ("router rip")) == 0) + config = config_get (RIP_NODE, line); + else if (strncmp (line, "router ripng", strlen ("router ripng")) == 0) + config = config_get (RIPNG_NODE, line); + else if (strncmp (line, "router ospf", strlen ("router ospf")) == 0) + 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 bgp", strlen ("router bgp")) == 0) + config = config_get (BGP_NODE, line); + else if (strncmp (line, "router isis", strlen ("router isis")) == 0) + config = config_get (ISIS_NODE, line); + else if (strncmp (line, "router bgp", strlen ("router bgp")) == 0) + config = config_get (BGP_NODE, line); + else if (strncmp (line, "route-map", strlen ("route-map")) == 0) + config = config_get (RMAP_NODE, line); + else if (strncmp (line, "access-list", strlen ("access-list")) == 0) + config = config_get (ACCESS_NODE, line); + else if (strncmp (line, "ipv6 access-list", + strlen ("ipv6 access-list")) == 0) + config = config_get (ACCESS_IPV6_NODE, line); + else if (strncmp (line, "ip prefix-list", + strlen ("ip prefix-list")) == 0) + config = config_get (PREFIX_NODE, line); + else if (strncmp (line, "ipv6 prefix-list", + strlen ("ipv6 prefix-list")) == 0) + config = config_get (PREFIX_IPV6_NODE, line); + else if (strncmp (line, "ip as-path access-list", + strlen ("ip as-path access-list")) == 0) + config = config_get (AS_LIST_NODE, line); + else if (strncmp (line, "ip community-list", + strlen ("ip community-list")) == 0) + config = config_get (COMMUNITY_LIST_NODE, line); + else if (strncmp (line, "ip route", strlen ("ip route")) == 0) + config = config_get (IP_NODE, line); + else if (strncmp (line, "ipv6 route", strlen ("ipv6 route")) == 0) + config = config_get (IP_NODE, line); + else if (strncmp (line, "key", strlen ("key")) == 0) + config = config_get (KEYCHAIN_NODE, line); + else if (strncmp (line, "line", strlen ("line")) == 0) + config = config_get (VTY_NODE, line); + else if ( (strncmp (line, "ipv6 forwarding", + strlen ("ipv6 forwarding")) == 0) + || (strncmp (line, "ip forwarding", + strlen ("ip forwarding")) == 0) ) + config = config_get (FORWARDING_NODE, line); + else if (strncmp (line, "service", strlen ("service")) == 0) + config = config_get (SERVICE_NODE, line); + else if (strncmp (line, "debug", strlen ("debug")) == 0) + config = config_get (DEBUG_NODE, line); + else if (strncmp (line, "password", strlen ("password")) == 0 + || strncmp (line, "enable password", + strlen ("enable password")) == 0) + config = config_get (AAA_NODE, line); + else if (strncmp (line, "ip protocol", strlen ("ip protocol")) == 0) + config = config_get (PROTOCOL_NODE, line); + else + { + if (strncmp (line, "log", strlen ("log")) == 0 + || strncmp (line, "hostname", strlen ("hostname")) == 0 + ) + config_add_line_uniq (config_top, line); + else + config_add_line (config_top, line); + config = NULL; + } + break; + } +} + +void +vtysh_config_parse (char *line) +{ + char *begin; + char *pnt; + + begin = pnt = line; + + while (*pnt != '\0') + { + if (*pnt == '\n') + { + *pnt++ = '\0'; + vtysh_config_parse_line (begin); + begin = pnt; + } + else + { + pnt++; + } + } +} + +/* Macro to check delimiter is needed between each configuration line + * or not. */ +#define NO_DELIMITER(I) \ + ((I) == ACCESS_NODE || (I) == PREFIX_NODE || (I) == IP_NODE \ + || (I) == AS_LIST_NODE || (I) == COMMUNITY_LIST_NODE || \ + (I) == ACCESS_IPV6_NODE || (I) == PREFIX_IPV6_NODE \ + || (I) == SERVICE_NODE || (I) == FORWARDING_NODE || (I) == DEBUG_NODE \ + || (I) == AAA_NODE) + +/* Display configuration to file pointer. */ +void +vtysh_config_dump (FILE *fp) +{ + struct listnode *node, *nnode; + struct listnode *mnode, *mnnode; + struct config *config; + struct list *master; + char *line; + unsigned int i; + + for (ALL_LIST_ELEMENTS (config_top, node, nnode, line)) + { + fprintf (fp, "%s\n", line); + fflush (fp); + } + fprintf (fp, "!\n"); + fflush (fp); + + for (i = 0; i < vector_active (configvec); i++) + if ((master = vector_slot (configvec, i)) != NULL) + { + for (ALL_LIST_ELEMENTS (master, node, nnode, config)) + { + fprintf (fp, "%s\n", config->name); + fflush (fp); + + for (ALL_LIST_ELEMENTS (config->line, mnode, mnnode, line)) + { + fprintf (fp, "%s\n", line); + fflush (fp); + } + if (! NO_DELIMITER (i)) + { + fprintf (fp, "!\n"); + fflush (fp); + } + } + if (NO_DELIMITER (i)) + { + fprintf (fp, "!\n"); + fflush (fp); + } + } + + for (i = 0; i < vector_active (configvec); i++) + if ((master = vector_slot (configvec, i)) != NULL) + { + list_delete (master); + vector_slot (configvec, i) = NULL; + } + list_delete_all_node (config_top); +} + +/* Read up configuration file from file_name. */ +static void +vtysh_read_file (FILE *confp) +{ + int ret; + struct vty *vty; + + vty = vty_new (); + vty->fd = 0; /* stdout */ + vty->type = VTY_TERM; + vty->node = CONFIG_NODE; + + vtysh_execute_no_pager ("enable"); + vtysh_execute_no_pager ("configure terminal"); + + /* Execute configuration file. */ + ret = vtysh_config_from_file (vty, confp); + + vtysh_execute_no_pager ("end"); + vtysh_execute_no_pager ("disable"); + + vty_close (vty); + + if (ret != CMD_SUCCESS) + { + switch (ret) + { + case CMD_ERR_AMBIGUOUS: + fprintf (stderr, "Ambiguous command.\n"); + break; + case CMD_ERR_NO_MATCH: + fprintf (stderr, "There is no such command.\n"); + break; + } + fprintf (stderr, "Error occured during reading below line.\n%s\n", + vty->buf); + exit (1); + } +} + +/* Read up configuration file from config_default_dir. */ +int +vtysh_read_config (char *config_default_dir) +{ + FILE *confp = NULL; + + confp = fopen (config_default_dir, "r"); + if (confp == NULL) + return (1); + + vtysh_read_file (confp); + fclose (confp); + host_config_set (config_default_dir); + + return (0); +} + +/* We don't write vtysh specific into file from vtysh. vtysh.conf should + * be edited by hand. So, we handle only "write terminal" case here and + * integrate vtysh specific conf with conf from daemons. + */ +void +vtysh_config_write () +{ + char line[81]; + extern struct host host; + + if (host.name) + { + sprintf (line, "hostname %s", host.name); + vtysh_config_parse_line(line); + } + if (vtysh_writeconfig_integrated) + vtysh_config_parse_line ("service integrated-vtysh-config"); +} + +void +vtysh_config_init () +{ + config_top = list_new (); + config_top->del = (void (*) (void *))line_del; + configvec = vector_init (1); +} diff --git a/vtysh/vtysh_main.c b/vtysh/vtysh_main.c new file mode 100644 index 0000000..e82771b --- /dev/null +++ b/vtysh/vtysh_main.c @@ -0,0 +1,439 @@ +/* Virtual terminal interface shell. + * 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 +#include +#include +#include + +#include +#include + +#include +#include "getopt.h" +#include "command.h" +#include "memory.h" + +#include "vtysh/vtysh.h" +#include "vtysh/vtysh_user.h" + +/* VTY shell program name. */ +char *progname; + +/* Configuration file name and directory. */ +char config_default[] = SYSCONFDIR VTYSH_DEFAULT_CONFIG; +char history_file[MAXPATHLEN]; + +/* Flag for indicate executing child command. */ +int execute_flag = 0; + +/* For sigsetjmp() & siglongjmp(). */ +static sigjmp_buf jmpbuf; + +/* Flag for avoid recursive siglongjmp() call. */ +static int jmpflag = 0; + +/* A static variable for holding the line. */ +static char *line_read; + +/* Master of threads. */ +struct thread_master *master; + +/* Command logging */ +FILE *logfile; + +/* SIGTSTP handler. This function care user's ^Z input. */ +static void +sigtstp (int sig) +{ + /* Execute "end" command. */ + vtysh_execute ("end"); + + /* Initialize readline. */ + rl_initialize (); + printf ("\n"); + + /* Check jmpflag for duplicate siglongjmp(). */ + if (! jmpflag) + return; + + jmpflag = 0; + + /* Back to main command loop. */ + siglongjmp (jmpbuf, 1); +} + +/* SIGINT handler. This function care user's ^Z input. */ +static void +sigint (int sig) +{ + /* Check this process is not child process. */ + if (! execute_flag) + { + rl_initialize (); + printf ("\n"); + rl_forced_update_display (); + } +} + +/* Signale wrapper for vtysh. We don't use sigevent because + * vtysh doesn't use threads. TODO */ +static void +vtysh_signal_set (int signo, void (*func)(int)) +{ + struct sigaction sig; + struct sigaction osig; + + sig.sa_handler = func; + sigemptyset (&sig.sa_mask); + sig.sa_flags = 0; +#ifdef SA_RESTART + sig.sa_flags |= SA_RESTART; +#endif /* SA_RESTART */ + + sigaction (signo, &sig, &osig); +} + +/* Initialization of signal handles. */ +static void +vtysh_signal_init () +{ + vtysh_signal_set (SIGINT, sigint); + vtysh_signal_set (SIGTSTP, sigtstp); + vtysh_signal_set (SIGPIPE, SIG_IGN); +} + +/* Help information display. */ +static void +usage (int status) +{ + if (status != 0) + fprintf (stderr, "Try `%s --help' for more information.\n", progname); + else + printf ("Usage : %s [OPTION...]\n\n" \ + "Integrated shell for Quagga routing software suite. \n\n" \ + "-b, --boot Execute boot startup configuration\n" \ + "-c, --command Execute argument as command\n" \ + "-d, --daemon Connect only to the specified daemon\n" \ + "-E, --echo Echo prompt and command in -c mode\n" \ + "-C, --dryrun Check configuration for validity and exit\n" \ + "-h, --help Display this help and exit\n\n" \ + "Note that multiple commands may be executed from the command\n" \ + "line by passing multiple -c args, or by embedding linefeed\n" \ + "characters in one or more of the commands.\n\n" \ + "Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); + + exit (status); +} + +/* VTY shell options, we use GNU getopt library. */ +struct option longopts[] = +{ + { "boot", no_argument, NULL, 'b'}, + /* For compatibility with older zebra/quagga versions */ + { "eval", required_argument, NULL, 'e'}, + { "command", required_argument, NULL, 'c'}, + { "daemon", required_argument, NULL, 'd'}, + { "echo", no_argument, NULL, 'E'}, + { "dryrun", no_argument, NULL, 'C'}, + { "help", no_argument, NULL, 'h'}, + { "noerror", no_argument, NULL, 'n'}, + { 0 } +}; + +/* Read a string, and return a pointer to it. Returns NULL on EOF. */ +static char * +vtysh_rl_gets () +{ + HIST_ENTRY *last; + /* If the buffer has already been allocated, return the memory + * to the free pool. */ + if (line_read) + { + free (line_read); + line_read = NULL; + } + + /* Get a line from the user. Change prompt according to node. XXX. */ + line_read = readline (vtysh_prompt ()); + + /* If the line has any text in it, save it on the history. But only if + * last command in history isn't the same one. */ + if (line_read && *line_read) + { + using_history(); + last = previous_history(); + if (!last || strcmp (last->line, line_read) != 0) { + add_history (line_read); + append_history(1,history_file); + } + } + + return (line_read); +} + +static void log_it(const char *line) +{ + time_t t = time(NULL); + struct tm *tmp = localtime(&t); + 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); +} + +/* VTY shell main routine. */ +int +main (int argc, char **argv, char **env) +{ + char *p; + int opt; + int dryrun = 0; + int boot_flag = 0; + const char *daemon_name = NULL; + struct cmd_rec { + const char *line; + struct cmd_rec *next; + } *cmd = NULL; + 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]); + + /* if logging open now */ + if ((p = getenv("VTYSH_LOG")) != NULL) + logfile = fopen(p, "a"); + + /* Option handling. */ + while (1) + { + opt = getopt_long (argc, argv, "be:c:d:nEhC", longopts, 0); + + if (opt == EOF) + break; + + switch (opt) + { + case 0: + break; + case 'b': + boot_flag = 1; + break; + case 'e': + case 'c': + { + struct cmd_rec *cr; + cr = XMALLOC(MTYPE_TMP, sizeof(*cr)); + cr->line = optarg; + cr->next = NULL; + if (tail) + tail->next = cr; + else + cmd = cr; + tail = cr; + } + break; + case 'd': + daemon_name = optarg; + break; + case 'n': + no_error = 1; + break; + case 'E': + echo_command = 1; + break; + case 'C': + dryrun = 1; + break; + case 'h': + usage (0); + break; + default: + usage (1); + break; + } + } + + /* Initialize user input buffer. */ + line_read = NULL; + setlinebuf(stdout); + + /* Signal and others. */ + vtysh_signal_init (); + + /* Make vty structure and register commands. */ + vtysh_init_vty (); + vtysh_init_cmd (); + vtysh_user_init (); + vtysh_config_init (); + + vty_init_vtysh (); + + /* Read vtysh configuration file before connecting to daemons. */ + vtysh_read_config (config_default); + + /* Start execution only if not in dry-run mode */ + if(dryrun) + return(0); + + /* Ignore error messages */ + if (no_error) + freopen("/dev/null", "w", stdout); + + /* Make sure we pass authentication before proceeding. */ + vtysh_auth (); + + /* Do not connect until we have passed authentication. */ + if (vtysh_connect_all (daemon_name) <= 0) + { + fprintf(stderr, "Exiting: failed to connect to any daemons.\n"); + 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) + { + /* Enter into enable node. */ + vtysh_execute ("enable"); + + while (cmd != NULL) + { + int ret; + char *eol; + + while ((eol = strchr(cmd->line, '\n')) != NULL) + { + *eol = '\0'; + + add_history (cmd->line); + append_history (1, history_file); + + if (echo_command) + printf("%s%s\n", vtysh_prompt(), cmd->line); + + if (logfile) + log_it(cmd->line); + + ret = vtysh_execute_no_pager(cmd->line); + if (!no_error && + ! (ret == CMD_SUCCESS || + ret == CMD_SUCCESS_DAEMON || + ret == CMD_WARNING)) + exit(1); + + cmd->line = eol+1; + } + + add_history (cmd->line); + append_history (1, history_file); + + if (echo_command) + printf("%s%s\n", vtysh_prompt(), cmd->line); + + if (logfile) + log_it(cmd->line); + + ret = vtysh_execute_no_pager(cmd->line); + if (!no_error && + ! (ret == CMD_SUCCESS || + ret == CMD_SUCCESS_DAEMON || + ret == CMD_WARNING)) + exit(1); + + { + struct cmd_rec *cr; + cr = cmd; + cmd = cmd->next; + XFREE(0, cr); + } + } + + history_truncate_file(history_file,1000); + exit (0); + } + + /* Boot startup configuration file. */ + if (boot_flag) + { + if (vtysh_read_config (integrate_default)) + { + fprintf (stderr, "Can't open configuration file [%s]\n", + integrate_default); + exit (1); + } + else + exit (0); + } + + vtysh_pager_init (); + + vtysh_readline_init (); + + vty_hello (vty); + + /* Enter into enable node. */ + vtysh_execute ("enable"); + + /* Preparation for longjmp() in sigtstp(). */ + sigsetjmp (jmpbuf, 1); + jmpflag = 1; + + /* Main command loop. */ + while (vtysh_rl_gets ()) + vtysh_execute (line_read); + + history_truncate_file(history_file,1000); + printf ("\n"); + + /* Rest in peace. */ + exit (0); +} diff --git a/vtysh/vtysh_user.c b/vtysh/vtysh_user.c new file mode 100644 index 0000000..584b61f --- /dev/null +++ b/vtysh/vtysh_user.c @@ -0,0 +1,213 @@ +/* User authentication for vtysh. + * 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 + +#include + +#ifdef USE_PAM +#include +#ifdef HAVE_PAM_MISC_H +#include +#endif +#ifdef HAVE_OPENPAM_H +#include +#endif +#endif /* USE_PAM */ + +#include "memory.h" +#include "linklist.h" +#include "command.h" +#include "vtysh_user.h" + +#ifdef USE_PAM +static struct pam_conv conv = +{ + PAM_CONV_FUNC, + NULL +}; + +static int +vtysh_pam (const char *user) +{ + int ret; + pam_handle_t *pamh = NULL; + + /* Start PAM. */ + ret = pam_start(QUAGGA_PROGNAME, user, &conv, &pamh); + /* printf ("ret %d\n", ret); */ + + /* Is user really user? */ + if (ret == PAM_SUCCESS) + ret = pam_authenticate (pamh, 0); + /* printf ("ret %d\n", ret); */ + +#if 0 + /* Permitted access? */ + if (ret == PAM_SUCCESS) + ret = pam_acct_mgmt (pamh, 0); + printf ("ret %d\n", ret); + + if (ret == PAM_AUTHINFO_UNAVAIL) + ret = PAM_SUCCESS; +#endif /* 0 */ + + /* This is where we have been authorized or not. */ +#ifdef DEBUG + if (ret == PAM_SUCCESS) + printf("Authenticated\n"); + else + printf("Not Authenticated\n"); +#endif /* DEBUG */ + + /* close Linux-PAM */ + if (pam_end (pamh, ret) != PAM_SUCCESS) + { + pamh = NULL; + fprintf(stderr, "vtysh_pam: failed to release authenticator\n"); + exit(1); + } + + return ret == PAM_SUCCESS ? 0 : 1; +} +#endif /* USE_PAM */ + +struct vtysh_user +{ + char *name; + u_char nopassword; +}; + +struct list *userlist; + +static struct vtysh_user * +user_new () +{ + return XCALLOC (MTYPE_TMP, sizeof (struct vtysh_user)); +} + +#if 0 +static void +user_free (struct vtysh_user *user) +{ + XFREE (0, user); +} +#endif + +static struct vtysh_user * +user_lookup (const char *name) +{ + struct listnode *node, *nnode; + struct vtysh_user *user; + + for (ALL_LIST_ELEMENTS (userlist, node, nnode, user)) + { + if (strcmp (user->name, name) == 0) + return user; + } + return NULL; +} + +#if 0 +static void +user_config_write () +{ + struct listnode *node, *nnode; + struct vtysh_user *user; + + for (ALL_LIST_ELEMENTS (userlist, node, nnode, user)) + { + if (user->nopassword) + printf (" username %s nopassword\n", user->name); + } +} +#endif + +static struct vtysh_user * +user_get (const char *name) +{ + struct vtysh_user *user; + user = user_lookup (name); + if (user) + return user; + + user = user_new (); + user->name = strdup (name); + listnode_add (userlist, user); + + return user; +} + +DEFUN (username_nopassword, + username_nopassword_cmd, + "username WORD nopassword", + "\n" + "\n" + "\n") +{ + struct vtysh_user *user; + user = user_get (argv[0]); + user->nopassword = 1; + return CMD_SUCCESS; +} + +int +vtysh_auth (void) +{ + struct vtysh_user *user; + struct passwd *passwd; + + 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) + /* Pass through */; + else + { +#ifdef USE_PAM + if (vtysh_pam (passwd->pw_name)) + exit (0); +#endif /* USE_PAM */ + } + return 0; +} + +char * +vtysh_get_home (void) +{ + struct passwd *passwd; + + passwd = getpwuid (getuid ()); + + return passwd ? passwd->pw_dir : NULL; +} + +void +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 new file mode 100644 index 0000000..a6c8b99 --- /dev/null +++ b/vtysh/vtysh_user.h @@ -0,0 +1,30 @@ +/* User authentication for vtysh. + * 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. + */ + +#ifndef _VTYSH_USER_H +#define _VTYSH_USER_H + +int vtysh_auth (void); +void vtysh_user_init (void); + +char *vtysh_get_home (void); + +#endif /* _VTYSH_USER_H */ diff --git a/watchquagga/.gitignore b/watchquagga/.gitignore new file mode 100644 index 0000000..b6226d5 --- /dev/null +++ b/watchquagga/.gitignore @@ -0,0 +1,16 @@ +Makefile +Makefile.in +*.o +watchquagga +tags +TAGS +.deps +.nfs* +*.lo +*.la +*.libs +.arch-inventory +.arch-ids +*~ +*.loT + diff --git a/watchquagga/Makefile.am b/watchquagga/Makefile.am new file mode 100644 index 0000000..1f05f26 --- /dev/null +++ b/watchquagga/Makefile.am @@ -0,0 +1,11 @@ +## Process this file with Automake to create Makefile.in + +AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +DEFS = @DEFS@ -DSTATEDIR=\"$(localstatedir)/\" + +AM_CFLAGS = $(WERROR) + +sbin_PROGRAMS = watchquagga + +watchquagga_SOURCES = watchquagga.c +watchquagga_LDADD = ../lib/libzebra.la @LIBCAP@ diff --git a/watchquagga/watchquagga.c b/watchquagga/watchquagga.c new file mode 100644 index 0000000..9bd7a5f --- /dev/null +++ b/watchquagga/watchquagga.c @@ -0,0 +1,1399 @@ +/* + Monitor status of quagga daemons and restart if necessary. + + Copyright (C) 2004 Andrew J. Schorr + + 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef MIN +#define MIN(X,Y) (((X) <= (Y)) ? (X) : (Y)) +#endif + +/* Macros to help randomize timers. */ +#define JITTER(X) ((random() % ((X)+1))-((X)/2)) +#define FUZZY(X) ((X)+JITTER((X)/20)) + +#define DEFAULT_PERIOD 5 +#define DEFAULT_TIMEOUT 10 +#define DEFAULT_RESTART_TIMEOUT 20 +#define DEFAULT_LOGLEVEL LOG_INFO +#define DEFAULT_MIN_RESTART 60 +#define DEFAULT_MAX_RESTART 600 +#ifdef PATH_WATCHQUAGGA_PID +#define DEFAULT_PIDFILE PATH_WATCHQUAGGA_PID +#else +#define DEFAULT_PIDFILE STATEDIR "/watchquagga.pid" +#endif +#ifdef DAEMON_VTY_DIR +#define VTYDIR DAEMON_VTY_DIR +#else +#define VTYDIR STATEDIR +#endif + +#define PING_TOKEN "PING" + +/* Needs to be global, referenced somewhere inside libzebra. */ +struct thread_master *master; + +typedef enum +{ + MODE_MONITOR = 0, + MODE_GLOBAL_RESTART, + MODE_SEPARATE_RESTART, + MODE_PHASED_ZEBRA_RESTART, + MODE_PHASED_ALL_RESTART +} watch_mode_t; + +static const char *mode_str[] = +{ + "monitor", + "global restart", + "individual daemon restart", + "phased zebra restart", + "phased global restart for any failure", +}; + +typedef enum +{ + PHASE_NONE = 0, + PHASE_STOPS_PENDING, + PHASE_WAITING_DOWN, + PHASE_ZEBRA_RESTART_PENDING, + PHASE_WAITING_ZEBRA_UP +} restart_phase_t; + +static const char *phase_str[] = +{ + "None", + "Stop jobs running", + "Waiting for other daemons to come down", + "Zebra restart job running", + "Waiting for zebra to come up", + "Start jobs running", +}; + +#define PHASE_TIMEOUT (3*gs.restart_timeout) + +struct restart_info +{ + const char *name; + const char *what; + pid_t pid; + struct timeval time; + long interval; + struct thread *t_kill; + int kills; +}; + +static struct global_state +{ + watch_mode_t mode; + restart_phase_t phase; + struct thread *t_phase_hanging; + const char *vtydir; + long period; + long timeout; + long restart_timeout; + long min_restart_interval; + long max_restart_interval; + int do_ping; + struct daemon *daemons; + const char *restart_command; + const char *start_command; + const char *stop_command; + struct restart_info restart; + int unresponsive_restart; + int loglevel; + struct daemon *special; /* points to zebra when doing phased restart */ + int numdaemons; + int numpids; + int numdown; /* # of daemons that are not UP or UNRESPONSIVE */ +} gs = { + .mode = MODE_MONITOR, + .phase = PHASE_NONE, + .vtydir = VTYDIR, + .period = 1000*DEFAULT_PERIOD, + .timeout = DEFAULT_TIMEOUT, + .restart_timeout = DEFAULT_RESTART_TIMEOUT, + .loglevel = DEFAULT_LOGLEVEL, + .min_restart_interval = DEFAULT_MIN_RESTART, + .max_restart_interval = DEFAULT_MAX_RESTART, + .do_ping = 1, +}; + +typedef enum +{ + DAEMON_INIT, + DAEMON_DOWN, + DAEMON_CONNECTING, + DAEMON_UP, + DAEMON_UNRESPONSIVE +} daemon_state_t; + +#define IS_UP(DMN) \ + (((DMN)->state == DAEMON_UP) || ((DMN)->state == DAEMON_UNRESPONSIVE)) + +static const char *state_str[] = +{ + "Init", + "Down", + "Connecting", + "Up", + "Unresponsive", +}; + +struct daemon { + const char *name; + daemon_state_t state; + int fd; + struct timeval echo_sent; + u_int connect_tries; + struct thread *t_wakeup; + struct thread *t_read; + struct thread *t_write; + struct daemon *next; + struct restart_info restart; +}; + +static const struct option longopts[] = +{ + { "daemon", no_argument, NULL, 'd'}, + { "statedir", required_argument, NULL, 'S'}, + { "no-echo", no_argument, NULL, 'e'}, + { "loglevel", required_argument, NULL, 'l'}, + { "interval", required_argument, NULL, 'i'}, + { "timeout", required_argument, NULL, 't'}, + { "restart-timeout", required_argument, NULL, 'T'}, + { "restart", required_argument, NULL, 'r'}, + { "start-command", required_argument, NULL, 's'}, + { "kill-command", required_argument, NULL, 'k'}, + { "restart-all", required_argument, NULL, 'R'}, + { "all-restart", no_argument, NULL, 'a'}, + { "always-all-restart", no_argument, NULL, 'A'}, + { "unresponsive-restart", no_argument, NULL, 'z'}, + { "min-restart-interval", required_argument, NULL, 'm'}, + { "max-restart-interval", required_argument, NULL, 'M'}, + { "pid-file", required_argument, NULL, 'p'}, + { "blank-string", required_argument, NULL, 'b'}, + { "help", no_argument, NULL, 'h'}, + { "version", no_argument, NULL, 'v'}, + { NULL, 0, NULL, 0 } +}; + +static int try_connect(struct daemon *dmn); +static int wakeup_send_echo(struct thread *t_wakeup); +static void try_restart(struct daemon *dmn); +static void phase_check(void); + +static int +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\ +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\ +It then repeatedly sends echo commands over that socket to determine whether\n\ +the daemon is responsive. If the daemon crashes, we will receive an EOF\n\ +on the socket connection and know immediately that the daemon is down.\n\n\ +The daemons to be monitored should be listed on the command line.\n\n\ +This program can run in one of 5 modes:\n\n\ +0. Mode: %s.\n\ + Just monitor and report on status changes. Example:\n\ + %s -d zebra ospfd bgpd\n\n\ +1. Mode: %s.\n\ + Whenever any daemon hangs or crashes, use the given command to restart\n\ + them all. Example:\n\ + %s -dz \\\n\ + -R '/sbin/service zebra restart; /sbin/service ospfd restart' \\\n\ + zebra ospfd\n\n\ +2. Mode: %s.\n\ + When any single daemon hangs or crashes, restart only the daemon that's\n\ + in trouble using the supplied restart command. Example:\n\ + %s -dz -r '/sbin/service %%s restart' zebra ospfd bgpd\n\n\ +3. Mode: %s.\n\ + The same as the previous mode, except that there is special treatment when\n\ + the zebra daemon is in trouble. In that case, a phased restart approach\n\ + is used: 1. stop all other daemons; 2. restart zebra; 3. start the other\n\ + daemons. Example:\n\ + %s -adz -r '/sbin/service %%s restart' \\\n\ + -s '/sbin/service %%s start' \\\n\ + -k '/sbin/service %%s stop' zebra ospfd bgpd\n\n\ +4. Mode: %s.\n\ + This is the same as the previous mode, except that the phased restart\n\ + procedure is used whenever any of the daemons hangs or crashes. Example:\n\ + %s -Adz -r '/sbin/service %%s restart' \\\n\ + -s '/sbin/service %%s start' \\\n\ + -k '/sbin/service %%s stop' zebra ospfd bgpd\n\n\ +As of this writing, it is believed that mode 2 [%s]\n\ +is not safe, and mode 3 [%s] may not be safe with some of the\n\ +routing daemons.\n\n\ +In order to avoid attempting to restart the daemons in a fast loop,\n\ +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", + 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\ +-e, --no-echo Do not ping the daemons to test responsiveness (this\n\ + option is necessary if the daemons do not support the\n\ + echo command)\n\ +-l, --loglevel Set the logging level (default is %d).\n\ + The value should range from %d (LOG_EMERG) to %d (LOG_DEBUG),\n\ + but it can be set higher than %d if extra-verbose debugging\n\ + messages are desired.\n\ +-m, --min-restart-interval\n\ + Set the minimum seconds to wait between invocations of daemon\n\ + restart commands (default is %d).\n\ +-M, --max-restart-interval\n\ + Set the maximum seconds to wait between invocations of daemon\n\ + restart commands (default is %d).\n\ +-i, --interval Set the status polling interval in seconds (default is %d)\n\ +-t, --timeout Set the unresponsiveness timeout in seconds (default is %d)\n\ +-T, --restart-timeout\n\ + Set the restart (kill) timeout in seconds (default is %d).\n\ + If any background jobs are still running after this much\n\ + time has elapsed, they will be killed.\n\ +-r, --restart Supply a Bourne shell command to use to restart a single\n\ + daemon. The command string should include '%%s' where the\n\ + name of the daemon should be substituted.\n\ + Note that -r and -R are incompatible.\n\ +-s, --start-command\n\ + Supply a Bourne shell to command to use to start a single\n\ + daemon. The command string should include '%%s' where the\n\ + name of the daemon should be substituted.\n\ +-k, --kill-command\n\ + Supply a Bourne shell to command to use to stop a single\n\ + daemon. The command string should include '%%s' where the\n\ + name of the daemon should be substituted.\n\ +-R, --restart-all\n\ + When one or more daemons is down, try to restart everything\n\ + using the Bourne shell command supplied as the argument.\n\ + Note that -r and -R are incompatible.\n\ +-z, --unresponsive-restart\n\ + When a daemon is unresponsive, treat it as being down for\n\ + restart purposes.\n\ +-a, --all-restart\n\ + When zebra hangs or crashes, restart all daemons using\n\ + this phased approach: 1. stop all other daemons; 2. restart\n\ + zebra; 3. start other daemons. Requires -r, -s, and -k.\n\ +-A, --always-all-restart\n\ + When any daemon (not just zebra) hangs or crashes, use the\n\ + same phased restart mechanism described above for -a.\n\ + Requires -r, -s, and -k.\n\ +-p, --pid-file Set process identifier file name\n\ + (default is %s).\n\ +-b, --blank-string\n\ + When the supplied argument string is found in any of the\n\ + various shell command arguments (-r, -s, -k, or -R), replace\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", + 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(char *shell_cmd) +{ + pid_t child; + + switch (child = fork()) + { + case -1: + zlog_err("fork failed, cannot run command [%s]: %s", + shell_cmd,safe_strerror(errno)); + return -1; + case 0: + /* Child process. */ + /* Use separate process group so child processes can be killed easily. */ + if (setpgid(0,0) < 0) + zlog_warn("warning: setpgid(0,0) failed: %s",safe_strerror(errno)); + { + 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); + } + default: + /* Parent process: we will reap the child later. */ + zlog_err("Forked background command [pid %d]: %s",(int)child,shell_cmd); + return child; + } +} + +static struct timeval * +time_elapsed(struct timeval *result, const struct timeval *start_time) +{ + gettimeofday(result,NULL); + result->tv_sec -= start_time->tv_sec; + result->tv_usec -= start_time->tv_usec; + while (result->tv_usec < 0) + { + result->tv_usec += 1000000L; + result->tv_sec--; + } + return result; +} + +static int +restart_kill(struct thread *t_kill) +{ + struct restart_info *restart = THREAD_ARG(t_kill); + struct timeval delay; + + 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, (long)delay.tv_sec, + (restart->kills ? SIGKILL : SIGTERM)); + kill(-restart->pid,(restart->kills ? SIGKILL : SIGTERM)); + restart->kills++; + restart->t_kill = thread_add_timer(master,restart_kill,restart, + gs.restart_timeout); + return 0; +} + +static struct restart_info * +find_child(pid_t child) +{ + if (gs.mode == MODE_GLOBAL_RESTART) + { + if (gs.restart.pid == child) + return &gs.restart; + } + else + { + struct daemon *dmn; + for (dmn = gs.daemons; dmn; dmn = dmn->next) + { + if (dmn->restart.pid == child) + return &dmn->restart; + } + } + return NULL; +} + +static void +sigchild(void) +{ + pid_t child; + int status; + const char *name; + const char *what; + struct restart_info *restart; + + switch (child = waitpid(-1,&status,WNOHANG)) + { + case -1: + zlog_err("waitpid failed: %s",safe_strerror(errno)); + return; + case 0: + zlog_warn("SIGCHLD received, but waitpid did not reap a child"); + return; + } + + if ((restart = find_child(child)) != NULL) + { + name = restart->name; + what = restart->what; + restart->pid = 0; + gs.numpids--; + thread_cancel(restart->t_kill); + restart->t_kill = NULL; + /* Update restart time to reflect the time the command completed. */ + gettimeofday(&restart->time,NULL); + } + else + { + zlog_err("waitpid returned status for an unknown child process %d", + (int)child); + name = "(unknown)"; + what = "background"; + } + if (WIFSTOPPED(status)) + zlog_warn("warning: %s %s process %d is stopped", + what,name,(int)child); + else if (WIFSIGNALED(status)) + zlog_warn("%s %s process %d terminated due to signal %d", + what,name,(int)child,WTERMSIG(status)); + else if (WIFEXITED(status)) + { + if (WEXITSTATUS(status) != 0) + zlog_warn("%s %s process %d exited with non-zero status %d", + what,name,(int)child,WEXITSTATUS(status)); + else + zlog_debug("%s %s process %d exited normally",what,name,(int)child); + } + else + zlog_err("cannot interpret %s %s process %d wait status 0x%x", + what,name,(int)child,status); + phase_check(); +} + +static int +run_job(struct restart_info *restart, const char *cmdtype, const char *command, + int force, int update_interval) +{ + struct timeval delay; + + if (gs.loglevel > LOG_DEBUG+1) + zlog_debug("attempting to %s %s",cmdtype,restart->name); + + if (restart->pid) + { + if (gs.loglevel > LOG_DEBUG+1) + zlog_debug("cannot %s %s, previous pid %d still running", + cmdtype,restart->name,(int)restart->pid); + return -1; + } + + /* Note: time_elapsed test must come before the force test, since we need + to make sure that delay is initialized for use below in updating the + restart interval. */ + if ((time_elapsed(&delay,&restart->time)->tv_sec < restart->interval) && + !force) + { + if (gs.loglevel > LOG_DEBUG+1) + zlog_debug("postponing %s %s: " + "elapsed time %ld < retry interval %ld", + cmdtype,restart->name,(long)delay.tv_sec,restart->interval); + return -1; + } + + gettimeofday(&restart->time,NULL); + restart->kills = 0; + { + char cmd[strlen(command)+strlen(restart->name)+1]; + snprintf(cmd,sizeof(cmd),command,restart->name); + if ((restart->pid = run_background(cmd)) > 0) + { + restart->t_kill = thread_add_timer(master,restart_kill,restart, + gs.restart_timeout); + restart->what = cmdtype; + gs.numpids++; + } + else + restart->pid = 0; + } + + /* Calculate the new restart interval. */ + if (update_interval) + { + if (delay.tv_sec > 2*gs.max_restart_interval) + restart->interval = gs.min_restart_interval; + else if ((restart->interval *= 2) > gs.max_restart_interval) + restart->interval = gs.max_restart_interval; + if (gs.loglevel > LOG_DEBUG+1) + zlog_debug("restart %s interval is now %ld", + restart->name,restart->interval); + } + return restart->pid; +} + +#define SET_READ_HANDLER(DMN) \ + (DMN)->t_read = thread_add_read(master,handle_read,(DMN),(DMN)->fd) + +#define SET_WAKEUP_DOWN(DMN) \ + (DMN)->t_wakeup = thread_add_timer_msec(master,wakeup_down,(DMN), \ + FUZZY(gs.period)) + +#define SET_WAKEUP_UNRESPONSIVE(DMN) \ + (DMN)->t_wakeup = thread_add_timer_msec(master,wakeup_unresponsive,(DMN), \ + FUZZY(gs.period)) + +#define SET_WAKEUP_ECHO(DMN) \ + (DMN)->t_wakeup = thread_add_timer_msec(master,wakeup_send_echo,(DMN), \ + FUZZY(gs.period)) + +static int +wakeup_down(struct thread *t_wakeup) +{ + struct daemon *dmn = THREAD_ARG(t_wakeup); + + dmn->t_wakeup = NULL; + if (try_connect(dmn) < 0) + SET_WAKEUP_DOWN(dmn); + if ((dmn->connect_tries > 1) && (dmn->state != DAEMON_UP)) + try_restart(dmn); + return 0; +} + +static int +wakeup_init(struct thread *t_wakeup) +{ + struct daemon *dmn = THREAD_ARG(t_wakeup); + + dmn->t_wakeup = NULL; + if (try_connect(dmn) < 0) + { + SET_WAKEUP_DOWN(dmn); + zlog_err("%s state -> down : initial connection attempt failed", + dmn->name); + dmn->state = DAEMON_DOWN; + } + return 0; +} + +static void +daemon_down(struct daemon *dmn, const char *why) +{ + if (IS_UP(dmn) || (dmn->state == DAEMON_INIT)) + zlog_err("%s state -> down : %s",dmn->name,why); + else if (gs.loglevel > LOG_DEBUG) + zlog_debug("%s still down : %s",dmn->name,why); + if (IS_UP(dmn)) + gs.numdown++; + dmn->state = DAEMON_DOWN; + if (dmn->fd >= 0) + { + close(dmn->fd); + dmn->fd = -1; + } + THREAD_OFF(dmn->t_read); + THREAD_OFF(dmn->t_write); + THREAD_OFF(dmn->t_wakeup); + if (try_connect(dmn) < 0) + SET_WAKEUP_DOWN(dmn); + phase_check(); +} + +static int +handle_read(struct thread *t_read) +{ + struct daemon *dmn = THREAD_ARG(t_read); + static const char resp[sizeof(PING_TOKEN)+4] = PING_TOKEN "\n"; + char buf[sizeof(resp)+100]; + ssize_t rc; + struct timeval delay; + + dmn->t_read = NULL; + if ((rc = read(dmn->fd,buf,sizeof(buf))) < 0) + { + char why[100]; + + if (ERRNO_IO_RETRY(errno)) + { + /* Pretend it never happened. */ + SET_READ_HANDLER(dmn); + return 0; + } + snprintf(why,sizeof(why),"unexpected read error: %s", + safe_strerror(errno)); + daemon_down(dmn,why); + return 0; + } + if (rc == 0) + { + daemon_down(dmn,"read returned EOF"); + return 0; + } + if (!dmn->echo_sent.tv_sec) + { + char why[sizeof(buf)+100]; + snprintf(why,sizeof(why),"unexpected read returns %d bytes: %.*s", + (int)rc,(int)rc,buf); + daemon_down(dmn,why); + return 0; + } + + /* We are expecting an echo response: is there any chance that the + response would not be returned entirely in the first read? That + seems inconceivable... */ + if ((rc != sizeof(resp)) || memcmp(buf,resp,sizeof(resp))) + { + char why[100+sizeof(buf)]; + snprintf(why,sizeof(why),"read returned bad echo response of %d bytes " + "(expecting %u): %.*s", + (int)rc,(u_int)sizeof(resp),(int)rc,buf); + daemon_down(dmn,why); + return 0; + } + + time_elapsed(&delay,&dmn->echo_sent); + dmn->echo_sent.tv_sec = 0; + if (dmn->state == DAEMON_UNRESPONSIVE) + { + if (delay.tv_sec < gs.timeout) + { + dmn->state = DAEMON_UP; + zlog_warn("%s state -> up : echo response received after %ld.%06ld " + "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, + (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, (long)delay.tv_sec, (long)delay.tv_usec); + + SET_READ_HANDLER(dmn); + if (dmn->t_wakeup) + thread_cancel(dmn->t_wakeup); + SET_WAKEUP_ECHO(dmn); + + return 0; +} + +static void +daemon_up(struct daemon *dmn, const char *why) +{ + dmn->state = DAEMON_UP; + gs.numdown--; + dmn->connect_tries = 0; + zlog_notice("%s state -> up : %s",dmn->name,why); + if (gs.do_ping) + SET_WAKEUP_ECHO(dmn); + phase_check(); +} + +static int +check_connect(struct thread *t_write) +{ + struct daemon *dmn = THREAD_ARG(t_write); + int sockerr; + socklen_t reslen = sizeof(sockerr); + + dmn->t_write = NULL; + if (getsockopt(dmn->fd,SOL_SOCKET,SO_ERROR,(char *)&sockerr,&reslen) < 0) + { + zlog_warn("%s: check_connect: getsockopt failed: %s", + dmn->name,safe_strerror(errno)); + daemon_down(dmn,"getsockopt failed checking connection success"); + return 0; + } + if ((reslen == sizeof(sockerr)) && sockerr) + { + char why[100]; + snprintf(why,sizeof(why), + "getsockopt reports that connection attempt failed: %s", + safe_strerror(sockerr)); + daemon_down(dmn,why); + return 0; + } + + daemon_up(dmn,"delayed connect succeeded"); + return 0; +} + +static int +wakeup_connect_hanging(struct thread *t_wakeup) +{ + struct daemon *dmn = THREAD_ARG(t_wakeup); + char why[100]; + + dmn->t_wakeup = NULL; + snprintf(why,sizeof(why),"connection attempt timed out after %ld seconds", + gs.timeout); + daemon_down(dmn,why); + return 0; +} + +/* Making connection to protocol daemon. */ +static int +try_connect(struct daemon *dmn) +{ + int sock; + struct sockaddr_un addr; + socklen_t len; + + if (gs.loglevel > LOG_DEBUG+1) + zlog_debug("%s: attempting to connect",dmn->name); + dmn->connect_tries++; + + memset (&addr, 0, sizeof (struct sockaddr_un)); + addr.sun_family = AF_UNIX; + snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s.vty", + gs.vtydir,dmn->name); +#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN + len = addr.sun_len = SUN_LEN(&addr); +#else + len = sizeof (addr.sun_family) + strlen (addr.sun_path); +#endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */ + + /* Quick check to see if we might succeed before we go to the trouble + of creating a socket. */ + if (access(addr.sun_path, W_OK) < 0) + { + if (errno != ENOENT) + zlog_err("%s: access to socket %s denied: %s", + dmn->name,addr.sun_path,safe_strerror(errno)); + return -1; + } + + if ((sock = socket (AF_UNIX, SOCK_STREAM, 0)) < 0) + { + zlog_err("%s(%s): cannot make socket: %s", + __func__,addr.sun_path, safe_strerror(errno)); + return -1; + } + + if (set_nonblocking(sock) < 0) + { + zlog_err("%s(%s): set_nonblocking(%d) failed", + __func__, addr.sun_path, sock); + close(sock); + return -1; + } + + if (connect (sock, (struct sockaddr *) &addr, len) < 0) + { + if ((errno != EINPROGRESS) && (errno != EWOULDBLOCK)) + { + if (gs.loglevel > LOG_DEBUG) + zlog_debug("%s(%s): connect failed: %s", + __func__,addr.sun_path, safe_strerror(errno)); + close (sock); + return -1; + } + if (gs.loglevel > LOG_DEBUG) + zlog_debug("%s: connection in progress",dmn->name); + dmn->state = DAEMON_CONNECTING; + dmn->fd = sock; + dmn->t_write = thread_add_write(master,check_connect,dmn,dmn->fd); + dmn->t_wakeup = thread_add_timer(master,wakeup_connect_hanging,dmn, + gs.timeout); + SET_READ_HANDLER(dmn); + return 0; + } + + dmn->fd = sock; + SET_READ_HANDLER(dmn); + daemon_up(dmn,"connect succeeded"); + return 1; +} + +static int +phase_hanging(struct thread *t_hanging) +{ + gs.t_phase_hanging = NULL; + zlog_err("Phase [%s] hanging for %ld seconds, aborting phased restart", + phase_str[gs.phase],PHASE_TIMEOUT); + gs.phase = PHASE_NONE; + return 0; +} + +static void +set_phase(restart_phase_t new_phase) +{ + gs.phase = new_phase; + if (gs.t_phase_hanging) + thread_cancel(gs.t_phase_hanging); + gs.t_phase_hanging = thread_add_timer(master,phase_hanging,NULL, + PHASE_TIMEOUT); +} + +static void +phase_check(void) +{ + switch (gs.phase) + { + case PHASE_NONE: + break; + case PHASE_STOPS_PENDING: + if (gs.numpids) + break; + zlog_info("Phased restart: all routing daemon stop jobs have completed."); + set_phase(PHASE_WAITING_DOWN); + /*FALLTHRU*/ + case PHASE_WAITING_DOWN: + if (gs.numdown+IS_UP(gs.special) < gs.numdaemons) + break; + zlog_info("Phased restart: all routing daemons now down."); + run_job(&gs.special->restart,"restart",gs.restart_command,1,1); + set_phase(PHASE_ZEBRA_RESTART_PENDING); + /*FALLTHRU*/ + case PHASE_ZEBRA_RESTART_PENDING: + if (gs.special->restart.pid) + break; + zlog_info("Phased restart: %s restart job completed.",gs.special->name); + set_phase(PHASE_WAITING_ZEBRA_UP); + /*FALLTHRU*/ + case PHASE_WAITING_ZEBRA_UP: + if (!IS_UP(gs.special)) + break; + zlog_info("Phased restart: %s is now up.",gs.special->name); + { + struct daemon *dmn; + for (dmn = gs.daemons; dmn; dmn = dmn->next) + { + if (dmn != gs.special) + run_job(&dmn->restart,"start",gs.start_command,1,0); + } + } + gs.phase = PHASE_NONE; + THREAD_OFF(gs.t_phase_hanging); + zlog_notice("Phased global restart has completed."); + break; + } +} + +static void +try_restart(struct daemon *dmn) +{ + switch (gs.mode) + { + case MODE_MONITOR: + return; + case MODE_GLOBAL_RESTART: + run_job(&gs.restart,"restart",gs.restart_command,0,1); + break; + case MODE_SEPARATE_RESTART: + run_job(&dmn->restart,"restart",gs.restart_command,0,1); + break; + case MODE_PHASED_ZEBRA_RESTART: + if (dmn != gs.special) + { + if ((gs.special->state == DAEMON_UP) && (gs.phase == PHASE_NONE)) + run_job(&dmn->restart,"restart",gs.restart_command,0,1); + else + zlog_debug("%s: postponing restart attempt because master %s daemon " + "not up [%s], or phased restart in progress", + dmn->name,gs.special->name,state_str[gs.special->state]); + break; + } + /*FALLTHRU*/ + case MODE_PHASED_ALL_RESTART: + if ((gs.phase != PHASE_NONE) || gs.numpids) + { + if (gs.loglevel > LOG_DEBUG+1) + zlog_debug("postponing phased global restart: restart already in " + "progress [%s], or outstanding child processes [%d]", + phase_str[gs.phase],gs.numpids); + break; + } + /* Is it too soon for a restart? */ + { + struct timeval delay; + if (time_elapsed(&delay,&gs.special->restart.time)->tv_sec < + gs.special->restart.interval) + { + if (gs.loglevel > LOG_DEBUG+1) + zlog_debug("postponing phased global restart: " + "elapsed time %ld < retry interval %ld", + (long)delay.tv_sec,gs.special->restart.interval); + break; + } + } + zlog_info("Phased restart: stopping all routing daemons."); + /* First step: stop all other daemons. */ + for (dmn = gs.daemons; dmn; dmn = dmn->next) + { + if (dmn != gs.special) + run_job(&dmn->restart,"stop",gs.stop_command,1,1); + } + set_phase(PHASE_STOPS_PENDING); + break; + default: + zlog_err("error: unknown restart mode %d",gs.mode); + break; + } +} + +static int +wakeup_unresponsive(struct thread *t_wakeup) +{ + struct daemon *dmn = THREAD_ARG(t_wakeup); + + dmn->t_wakeup = NULL; + if (dmn->state != DAEMON_UNRESPONSIVE) + zlog_err("%s: no longer unresponsive (now %s), " + "wakeup should have been cancelled!", + dmn->name,state_str[dmn->state]); + else + { + SET_WAKEUP_UNRESPONSIVE(dmn); + try_restart(dmn); + } + return 0; +} + +static int +wakeup_no_answer(struct thread *t_wakeup) +{ + struct daemon *dmn = THREAD_ARG(t_wakeup); + + dmn->t_wakeup = NULL; + dmn->state = DAEMON_UNRESPONSIVE; + zlog_err("%s state -> unresponsive : no response yet to ping " + "sent %ld seconds ago",dmn->name,gs.timeout); + if (gs.unresponsive_restart) + { + SET_WAKEUP_UNRESPONSIVE(dmn); + try_restart(dmn); + } + return 0; +} + +static int +wakeup_send_echo(struct thread *t_wakeup) +{ + static const char echocmd[] = "echo " PING_TOKEN; + ssize_t rc; + struct daemon *dmn = THREAD_ARG(t_wakeup); + + dmn->t_wakeup = NULL; + if (((rc = write(dmn->fd,echocmd,sizeof(echocmd))) < 0) || + ((size_t)rc != sizeof(echocmd))) + { + char why[100+sizeof(echocmd)]; + snprintf(why,sizeof(why),"write '%s' returned %d instead of %u", + echocmd,(int)rc,(u_int)sizeof(echocmd)); + daemon_down(dmn,why); + } + else + { + gettimeofday(&dmn->echo_sent,NULL); + dmn->t_wakeup = thread_add_timer(master,wakeup_no_answer,dmn,gs.timeout); + } + return 0; +} + +static void +sigint(void) +{ + zlog_notice("Terminating on signal"); + exit(0); +} + +static int +valid_command(const char *cmd) +{ + char *p; + + return ((p = strchr(cmd,'%')) != NULL) && (*(p+1) == 's') && !strchr(p+1,'%'); +} + +/* This is an ugly hack to circumvent problems with passing command-line + arguments that contain spaces. The fix is to use a configuration file. */ +static char * +translate_blanks(const char *cmd, const char *blankstr) +{ + char *res; + char *p; + size_t bslen = strlen(blankstr); + + if (!(res = strdup(cmd))) + { + perror("strdup"); + exit(1); + } + while ((p = strstr(res,blankstr)) != NULL) + { + *p = ' '; + if (bslen != 1) + memmove(p+1,p+bslen,strlen(p+bslen)+1); + } + return res; +} + +int +main(int argc, char **argv) +{ + const char *progname; + int opt; + int daemon_mode = 0; + const char *pidfile = DEFAULT_PIDFILE; + const char *special = "zebra"; + const char *blankstr = NULL; + static struct quagga_signal_t my_signals[] = + { + { + .signal = SIGINT, + .handler = sigint, + }, + { + .signal = SIGTERM, + .handler = sigint, + }, + { + .signal = SIGCHLD, + .handler = sigchild, + }, + }; + + if ((progname = strrchr (argv[0], '/')) != NULL) + progname++; + else + progname = argv[0]; + + gs.restart.name = "all"; + while ((opt = getopt_long(argc, argv, "aAb:dek:l:m:M:i:p:r:R:S:s:t:T:zvh", + longopts, 0)) != EOF) + { + switch (opt) + { + case 0: + break; + case 'a': + if ((gs.mode != MODE_MONITOR) && (gs.mode != MODE_SEPARATE_RESTART)) + { + fputs("Ambiguous operating mode selected.\n",stderr); + return usage(progname,1); + } + gs.mode = MODE_PHASED_ZEBRA_RESTART; + break; + case 'A': + if ((gs.mode != MODE_MONITOR) && (gs.mode != MODE_SEPARATE_RESTART)) + { + fputs("Ambiguous operating mode selected.\n",stderr); + return usage(progname,1); + } + gs.mode = MODE_PHASED_ALL_RESTART; + break; + case 'b': + blankstr = optarg; + break; + case 'd': + daemon_mode = 1; + break; + case 'e': + gs.do_ping = 0; + break; + case 'k': + if (!valid_command(optarg)) + { + fprintf(stderr,"Invalid kill command, must contain '%%s': %s\n", + optarg); + return usage(progname,1); + } + gs.stop_command = optarg; + break; + case 'l': + { + char garbage[3]; + if ((sscanf(optarg,"%d%1s",&gs.loglevel,garbage) != 1) || + (gs.loglevel < LOG_EMERG)) + { + fprintf(stderr,"Invalid loglevel argument: %s\n",optarg); + return usage(progname,1); + } + } + break; + case 'm': + { + char garbage[3]; + if ((sscanf(optarg,"%ld%1s", + &gs.min_restart_interval,garbage) != 1) || + (gs.min_restart_interval < 0)) + { + fprintf(stderr,"Invalid min_restart_interval argument: %s\n", + optarg); + return usage(progname,1); + } + } + break; + case 'M': + { + char garbage[3]; + if ((sscanf(optarg,"%ld%1s", + &gs.max_restart_interval,garbage) != 1) || + (gs.max_restart_interval < 0)) + { + fprintf(stderr,"Invalid max_restart_interval argument: %s\n", + optarg); + return usage(progname,1); + } + } + break; + case 'i': + { + char garbage[3]; + int period; + if ((sscanf(optarg,"%d%1s",&period,garbage) != 1) || + (gs.period < 1)) + { + fprintf(stderr,"Invalid interval argument: %s\n",optarg); + return usage(progname,1); + } + gs.period = 1000*period; + } + break; + case 'p': + pidfile = optarg; + break; + case 'r': + if ((gs.mode == MODE_GLOBAL_RESTART) || + (gs.mode == MODE_SEPARATE_RESTART)) + { + fputs("Ambiguous operating mode selected.\n",stderr); + return usage(progname,1); + } + if (!valid_command(optarg)) + { + fprintf(stderr, + "Invalid restart command, must contain '%%s': %s\n", + optarg); + return usage(progname,1); + } + gs.restart_command = optarg; + if (gs.mode == MODE_MONITOR) + gs.mode = MODE_SEPARATE_RESTART; + break; + case 'R': + if (gs.mode != MODE_MONITOR) + { + fputs("Ambiguous operating mode selected.\n",stderr); + return usage(progname,1); + } + if (strchr(optarg,'%')) + { + fprintf(stderr, + "Invalid restart-all arg, must not contain '%%s': %s\n", + optarg); + return usage(progname,1); + } + gs.restart_command = optarg; + gs.mode = MODE_GLOBAL_RESTART; + break; + case 's': + if (!valid_command(optarg)) + { + fprintf(stderr,"Invalid start command, must contain '%%s': %s\n", + optarg); + return usage(progname,1); + } + gs.start_command = optarg; + break; + case 'S': + gs.vtydir = optarg; + break; + case 't': + { + char garbage[3]; + if ((sscanf(optarg,"%ld%1s",&gs.timeout,garbage) != 1) || + (gs.timeout < 1)) + { + fprintf(stderr,"Invalid timeout argument: %s\n",optarg); + return usage(progname,1); + } + } + break; + case 'T': + { + char garbage[3]; + if ((sscanf(optarg,"%ld%1s",&gs.restart_timeout,garbage) != 1) || + (gs.restart_timeout < 1)) + { + fprintf(stderr,"Invalid restart timeout argument: %s\n",optarg); + return usage(progname,1); + } + } + break; + case 'z': + gs.unresponsive_restart = 1; + break; + case 'v': + printf ("%s version %s\n", progname, QUAGGA_VERSION); + puts("Copyright 2004 Andrew J. Schorr"); + return 0; + case 'h': + return usage(progname,0); + default: + fputs("Invalid option.\n",stderr); + return usage(progname,1); + } + } + + if (gs.unresponsive_restart && (gs.mode == MODE_MONITOR)) + { + fputs("Option -z requires a -r or -R restart option.\n",stderr); + return usage(progname,1); + } + switch (gs.mode) + { + case MODE_MONITOR: + if (gs.restart_command || gs.start_command || gs.stop_command) + { + fprintf(stderr,"No kill/(re)start commands needed for %s mode.\n", + mode_str[gs.mode]); + return usage(progname,1); + } + break; + case MODE_GLOBAL_RESTART: + case MODE_SEPARATE_RESTART: + if (!gs.restart_command || gs.start_command || gs.stop_command) + { + fprintf(stderr,"No start/kill commands needed in [%s] mode.\n", + mode_str[gs.mode]); + return usage(progname,1); + } + break; + case MODE_PHASED_ZEBRA_RESTART: + case MODE_PHASED_ALL_RESTART: + if (!gs.restart_command || !gs.start_command || !gs.stop_command) + { + fprintf(stderr, + "Need start, kill, and restart commands in [%s] mode.\n", + mode_str[gs.mode]); + return usage(progname,1); + } + break; + } + + if (blankstr) + { + if (gs.restart_command) + gs.restart_command = translate_blanks(gs.restart_command,blankstr); + if (gs.start_command) + gs.start_command = translate_blanks(gs.start_command,blankstr); + if (gs.stop_command) + gs.stop_command = translate_blanks(gs.stop_command,blankstr); + } + + gs.restart.interval = gs.min_restart_interval; + master = thread_master_create(); + signal_init (master, array_size(my_signals), my_signals); + srandom(time(NULL)); + + { + int i; + struct daemon *tail = NULL; + + for (i = optind; i < argc; i++) + { + struct daemon *dmn; + + if (!(dmn = (struct daemon *)calloc(1,sizeof(*dmn)))) + { + fprintf(stderr,"calloc(1,%u) failed: %s\n", + (u_int)sizeof(*dmn), safe_strerror(errno)); + return 1; + } + dmn->name = dmn->restart.name = argv[i]; + dmn->state = DAEMON_INIT; + gs.numdaemons++; + gs.numdown++; + dmn->fd = -1; + dmn->t_wakeup = thread_add_timer_msec(master,wakeup_init,dmn, + 100+(random() % 900)); + dmn->restart.interval = gs.min_restart_interval; + if (tail) + tail->next = dmn; + else + gs.daemons = dmn; + tail = dmn; + + if (((gs.mode == MODE_PHASED_ZEBRA_RESTART) || + (gs.mode == MODE_PHASED_ALL_RESTART)) && + !strcmp(dmn->name,special)) + gs.special = dmn; + } + } + if (!gs.daemons) + { + fputs("Must specify one or more daemons to monitor.\n",stderr); + return usage(progname,1); + } + if (((gs.mode == MODE_PHASED_ZEBRA_RESTART) || + (gs.mode == MODE_PHASED_ALL_RESTART)) && !gs.special) + { + fprintf(stderr,"In mode [%s], but cannot find master daemon %s\n", + mode_str[gs.mode],special); + return usage(progname,1); + } + if (gs.special && (gs.numdaemons < 2)) + { + fprintf(stderr,"Mode [%s] does not make sense with only 1 daemon " + "to watch.\n",mode_str[gs.mode]); + return usage(progname,1); + } + + zlog_default = openzlog(progname, ZLOG_NONE, + LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON); + zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED); + if (daemon_mode) + { + zlog_set_level(NULL, ZLOG_DEST_SYSLOG, MIN(gs.loglevel,LOG_DEBUG)); + if (daemon (0, 0) < 0) + { + fprintf(stderr, "Watchquagga daemon failed: %s", strerror(errno)); + exit (1); + } + } + else + zlog_set_level(NULL, ZLOG_DEST_STDOUT, MIN(gs.loglevel,LOG_DEBUG)); + + /* Make sure we're not already running. */ + pid_output (pidfile); + + /* Announce which daemons are being monitored. */ + { + struct daemon *dmn; + size_t len = 0; + + for (dmn = gs.daemons; dmn; dmn = dmn->next) + len += strlen(dmn->name)+1; + + { + char buf[len+1]; + char *p = buf; + + for (dmn = gs.daemons; dmn; dmn = dmn->next) + { + if (p != buf) + *p++ = ' '; + strcpy(p,dmn->name); + p += strlen(p); + } + zlog_notice("%s %s watching [%s], mode [%s]", + progname, QUAGGA_VERSION, buf, mode_str[gs.mode]); + } + } + + { + struct thread thread; + + while (thread_fetch (master, &thread)) + thread_call (&thread); + } + + /* Not reached. */ + return 0; +} diff --git a/zebra/.gitignore b/zebra/.gitignore new file mode 100644 index 0000000..df703b6 --- /dev/null +++ b/zebra/.gitignore @@ -0,0 +1,16 @@ +Makefile +Makefile.in +*.o +zebra +zebra.conf +client +testzebra +tags +TAGS +.deps +.nfs* +.libs +.arch-inventory +.arch-ids +*~ +*.loT diff --git a/zebra/GNOME-PRODUCT-ZEBRA-MIB b/zebra/GNOME-PRODUCT-ZEBRA-MIB new file mode 100644 index 0000000..96bcec5 --- /dev/null +++ b/zebra/GNOME-PRODUCT-ZEBRA-MIB @@ -0,0 +1,78 @@ +GNOME-PRODUCT-ZEBRA-MIB DEFINITIONS ::= BEGIN + +IMPORTS + MODULE-IDENTITY, + OBJECT-IDENTITY + FROM SNMPv2-SMI + gnomeProducts + FROM GNOME-SMI; + +zebra MODULE-IDENTITY + LAST-UPDATED "200004250000Z" + ORGANIZATION "GNOME project" + CONTACT-INFO + "GNU Network Object Model Environment project + + see http://www.gnome.org for contact persons of a particular + area or subproject of GNOME. + + Administrative contact for MIB module: + + Jochen Friedrich + Wingertstr. 70/1 + 68809 Neulussheim + Germany + + email: snmp@gnome.org" + DESCRIPTION + "The product registrations for the various zebra subdeamons. + These registrations are guaranteed to be unique and are used + for SMUX registration by default (if not overridden manually)." + ::= { gnomeProducts 2 } + +zserv OBJECT-IDENTITY + STATUS current + DESCRIPTION + "zserv is part of the zebra project which again is a GNU + endorsed internet routing program. + zserv is the main zebra process which implements routing + entries with the kernel and handles routing updates between + other routing protocols." + ::= { zebra 1 } + +bgpd OBJECT-IDENTITY + STATUS current + DESCRIPTION + "bgpd is part of the zebra project which again is a GNU + endorsed internet routing program." + ::= { zebra 2 } + +ripd OBJECT-IDENTITY + STATUS current + DESCRIPTION + "ripd is part of the zebra project which again is a GNU + endorsed internet routing program." + ::= { zebra 3 } + +ripngd OBJECT-IDENTITY + STATUS current + DESCRIPTION + "ripngd is part of the zebra project which again is a GNU + endorsed internet routing program." + ::= { zebra 4 } + +ospfd OBJECT-IDENTITY + STATUS current + DESCRIPTION + "ospfd is part of the zebra project which again is a GNU + endorsed internet routing program." + ::= { zebra 5 } + +ospf6d OBJECT-IDENTITY + STATUS current + DESCRIPTION + "ospf6d is part of the zebra project which again is a GNU + endorsed internet routing program." + ::= { zebra 6 } + +END diff --git a/zebra/GNOME-SMI b/zebra/GNOME-SMI new file mode 100644 index 0000000..164732b --- /dev/null +++ b/zebra/GNOME-SMI @@ -0,0 +1,53 @@ +GNOME-SMI DEFINITIONS ::= BEGIN + +IMPORTS + MODULE-IDENTITY, + OBJECT-IDENTITY, + enterprises + FROM SNMPv2-SMI; + +gnome MODULE-IDENTITY + LAST-UPDATED "9809010000Z" + ORGANIZATION "GNOME project" + CONTACT-INFO + "GNU Network Object Model Environment project + + see http://www.gnome.org for contact persons of a particular + area or subproject of GNOME. + + Administrative contact for MIB module: + + Jochen Friedrich + Wingertstr. 70/1 + 68809 Neulussheim + Germany + + email: snmp@gnome.org" + DESCRIPTION + "The Structure of GNOME." + ::= { enterprises 3317 } -- assigned by IANA + +gnomeProducts OBJECT-IDENTITY + STATUS current + DESCRIPTION + "gnomeProducts is the root OBJECT IDENTIFIER from + which sysObjectID values are assigned." + ::= { gnome 1 } + +gnomeMgmt OBJECT-IDENTITY + STATUS current + DESCRIPTION + "gnomeMgmt defines the subtree for production GNOME related + MIB registrations." + ::= { gnome 2 } + +gnomeTest OBJECT-IDENTITY + STATUS current + DESCRIPTION + "gnomeTest defines the subtree for testing GNOME related + MIB registrations." + ::= { gnome 3 } + +-- more to come if necessary. + +END diff --git a/zebra/Makefile.am b/zebra/Makefile.am new file mode 100644 index 0000000..b23f9f1 --- /dev/null +++ b/zebra/Makefile.am @@ -0,0 +1,76 @@ +include ../common.am + +## Process this file with automake to produce Makefile.in. + +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" +INSTALL_SDATA=@INSTALL@ -m 600 + +LIBCAP = @LIBCAP@ + +ipforward = @IPFORWARD@ +if_method = @IF_METHOD@ +rt_method = @RT_METHOD@ +rtread_method = @RTREAD_METHOD@ +kernel_method = @KERNEL_METHOD@ +ioctl_method = @IOCTL_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 = $(WERROR) + +sbin_PROGRAMS = zebra + +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 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 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 \ + rt_netlink.h zebra_fpm.h zebra_fpm_private.h \ + ioctl_solaris.h zebra_rnh.h + +zebra_LDADD = $(otherobj) ../lib/libzebra.la $(LIBCAP) $(Q_FPM_PB_CLIENT_LDOPTS) + +testzebra_LDADD = ../lib/libzebra.la $(LIBCAP) + +zebra_DEPENDENCIES = $(otherobj) + +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) + +quaggaconfdir = $(sysconfdir) + +examplesdir = $(exampledir) +dist_examples_DATA = zebra.conf.sample diff --git a/zebra/client_main.c b/zebra/client_main.c new file mode 100644 index 0000000..75c8867 --- /dev/null +++ b/zebra/client_main.c @@ -0,0 +1,230 @@ +/* + * $Quagga: $Format:%an, %ai, %h$ $ + * + * GNU Zebra client test main routine. + * 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 "stream.h" +#include "zclient.h" +#include "thread.h" +#include "table.h" +#include "zebra/rib.h" +#include "zebra/zserv.h" + +struct thread *master; + +/* Zebra client structure. */ +struct zclient *zclient = NULL; + +/* Zebra socket. */ +int sock; + +/* IPv4 route add and delete test. */ +void +zebra_test_ipv4 (int command, int type, char *prefix, char *gateway, + u_char distance) +{ + struct zapi_ipv4 api; + struct prefix_ipv4 p; + struct in_addr gate; + struct in_addr *gpnt; + + str2prefix_ipv4 (prefix, &p); + inet_aton (gateway, &gate); + gpnt = &gate; + + api.vrf_id = VRF_DEFAULT; + api.type = type; + api.flags = 0; + + api.message = 0; + SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP); + api.nexthop_num = 1; + api.nexthop = &gpnt; + api.ifindex_num = 0; + if (distance) + { + SET_FLAG (api.message, ZAPI_MESSAGE_DISTANCE); + api.distance = distance; + } + + + switch (command) + { + case ZEBRA_IPV4_ROUTE_ADD: + zapi_ipv4_add (zclient, &p, &api); + break; + case ZEBRA_IPV4_ROUTE_DELETE: + zapi_ipv4_delete (zclient, &p, &api); + break; + } +} + +#ifdef HAVE_IPV6 +/* IPv6 route add and delete test. */ +void +zebra_test_v6 (int sock) +{ + struct prefix_ipv6 p; + struct in6_addr nexthop; + + str2prefix_ipv6 ("3ffe:506::2/128", &p); + inet_pton (AF_INET6, "::1", &nexthop); + + /* zebra_ipv6_add (sock, ZEBRA_ROUTE_STATIC, 0, &p, &nexthop, 1); */ + + sleep (5); + /* zebra_ipv6_delete (sock, ZEBRA_ROUTE_STATIC, 0, &p, &nexthop, 1); */ +} +#endif /* HAVE_IPV6 */ + +/* Print out usage and exit. */ +void +usage_exit () +{ + fprintf (stderr, "Usage: client filename\n"); + exit (1); +} + +struct zebra_info +{ + char *str; + int type; +} zebra_type[] = +{ + { "static", ZEBRA_ROUTE_STATIC }, + { "rip", ZEBRA_ROUTE_RIP }, + { "ripng", ZEBRA_ROUTE_RIPNG }, + { "babel", ZEBRA_ROUTE_BABEL }, + { "ospf", ZEBRA_ROUTE_OSPF }, + { "ospf6", ZEBRA_ROUTE_OSPF6 }, + { "bgp", ZEBRA_ROUTE_BGP }, + { "nhrp", ZEBRA_ROUTE_NHRP }, + { NULL, 0 } +}; + +/* Zebra route simulator. */ +void +zebra_sim (FILE *fp) +{ + char buf[BUFSIZ]; + char distance_str[BUFSIZ]; + u_char distance; + + while (fgets (buf, sizeof buf, fp)) + { + int i; + int ret; + int type; + char str[BUFSIZ], command[BUFSIZ], prefix[BUFSIZ], gateway[BUFSIZ]; + + distance = 0; + + if (*buf == '#') + continue; + + type = ZEBRA_ROUTE_STATIC; + + ret = sscanf (buf, "%s %s %s %s %s\n", command, str, prefix, gateway, + distance_str); + + if (ret == 5) + { + distance = atoi (distance_str); + } + else + { + ret = sscanf (buf, "%s %s %s %s\n", command, str, prefix, gateway); + + if (ret != 4) + continue; + } + + for (i = 0; i < 10; i++) + { + if (!zebra_type[i].str) + break; + if (strcmp (zebra_type[i].str, str) == 0) + { + type = zebra_type[i].type; + break; + } + } + + if (strcmp (command, "add") == 0) + { + zebra_test_ipv4 (ZEBRA_IPV4_ROUTE_ADD, type, prefix, gateway, + distance); + printf ("%s", buf); + continue; + } + + if (strcmp (command, "del") == 0) + { + zebra_test_ipv4 (ZEBRA_IPV4_ROUTE_DELETE, type, prefix, gateway, + distance); + printf ("%s", buf); + continue; + } + } +} + +/* Test zebra client main routine. */ +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 (master); + zclient->enable = 1; +#ifdef HAVE_TCP_ZEBRA + zclient->sock = zclient_socket (); +#else + zclient->sock = zclient_socket_un (ZEBRA_SERV_PATH); +#endif /* HAVE_TCP_ZEBRA */ + + /* Open simulation file. */ + fp = fopen (argv[1], "r"); + if (fp == NULL) + { + fprintf (stderr, "can't open %s\n", argv[1]); + exit (1); + } + + /* Do main work. */ + zebra_sim (fp); + + sleep (100); + + fclose (fp); + close (sock); + + return 0; +} diff --git a/zebra/connected.c b/zebra/connected.c new file mode 100644 index 0000000..1980007 --- /dev/null +++ b/zebra/connected.c @@ -0,0 +1,475 @@ +/* + * Address linked list routine. + * 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 "linklist.h" +#include "if.h" +#include "table.h" +#include "rib.h" +#include "table.h" +#include "log.h" +#include "memory.h" + +#include "zebra/zserv.h" +#include "zebra/redistribute.h" +#include "zebra/interface.h" +#include "zebra/connected.h" +extern struct zebra_t zebrad; + +/* communicate the withdrawal of a connected address */ +static void +connected_withdraw (struct connected *ifc) +{ + if (! ifc) + return; + + /* Update interface address information to protocol daemon. */ + if (CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL)) + { + zebra_interface_address_delete_update (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 + else + connected_down_ipv6 (ifc->ifp, ifc); +#endif + + 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); + connected_free (ifc); + } +} + +static void +connected_announce (struct interface *ifp, struct connected *ifc) +{ + if (!ifc) + return; + + if (!if_is_loopback(ifp) && ifc->address->family == AF_INET) + { + if (ifc->address->prefixlen == 32) + SET_FLAG (ifc->flags, ZEBRA_IFA_UNNUMBERED); + else + UNSET_FLAG (ifc->flags, ZEBRA_IFA_UNNUMBERED); + } + + listnode_add (ifp->connected, ifc); + + /* Update interface address information to protocol daemon. */ + if (ifc->address->family == AF_INET) + if_subnet_add (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); +#endif + } +} + +/* If same interface address is already exist... */ +struct connected * +connected_check (struct interface *ifp, struct prefix *p) +{ + struct connected *ifc; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, ifc)) + if (prefix_same (ifc->address, p)) + return ifc; + + return NULL; +} + +/* Check if two ifc's describe the same address in the same state */ +static int +connected_same (struct connected *ifc1, struct connected *ifc2) +{ + if (ifc1->ifp != ifc2->ifp) + return 0; + + if (ifc1->destination) + if (!ifc2->destination) + return 0; + if (ifc2->destination) + if (!ifc1->destination) + return 0; + + if (ifc1->destination && ifc2->destination) + if (!prefix_same (ifc1->destination, ifc2->destination)) + return 0; + + if (ifc1->flags != ifc2->flags) + return 0; + + if (ifc1->conf != ifc2->conf) + return 0; + + return 1; +} + +/* Handle changes to addresses and send the neccesary announcements + * to clients. */ +static void +connected_update(struct interface *ifp, struct connected *ifc) +{ + struct connected *current; + + /* Check same connected route. */ + if ((current = connected_check (ifp, (struct prefix *) ifc->address))) + { + if (CHECK_FLAG(current->conf, ZEBRA_IFC_CONFIGURED)) + SET_FLAG(ifc->conf, ZEBRA_IFC_CONFIGURED); + + /* Avoid spurious withdraws, this might be just the kernel 'reflecting' + * back an address we have already added. + */ + if (connected_same (current, ifc)) + { + /* nothing to do */ + connected_free (ifc); + 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 */ + } + + /* 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(). */ +void +connected_up_ipv4 (struct interface *ifp, struct connected *ifc) +{ + struct prefix_ipv4 p; + + if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL)) + return; + + PREFIX_COPY_IPV4(&p, CONNECTED_PREFIX(ifc)); + + /* Apply mask to the network. */ + apply_mask_ipv4 (&p); + + /* In case of connected address is 0.0.0.0/0 we treat it tunnel + address. */ + if (prefix_ipv4_any (&p)) + return; + + rib_add_ipv4 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, NULL, ifp->ifindex, + 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 (ifp->vrf_id); +} + +/* Add connected IPv4 route to the interface. */ +void +connected_add_ipv4 (struct interface *ifp, int flags, struct in_addr *addr, + u_char prefixlen, struct in_addr *broad, + const char *label) +{ + struct prefix_ipv4 *p; + struct connected *ifc; + + /* Make connected structure. */ + 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 (); + p->family = AF_INET; + p->prefix = *addr; + p->prefixlen = prefixlen; + ifc->address = (struct prefix *) p; + + /* If there is broadcast or peer address. */ + if (broad) + { + p = prefix_ipv4_new (); + p->family = AF_INET; + p->prefix = *broad; + p->prefixlen = prefixlen; + ifc->destination = (struct prefix *) p; + + /* validate the destination address */ + if (CONNECTED_PEER(ifc)) + { + if (IPV4_ADDR_SAME(addr,broad)) + zlog_warn("warning: interface %s has same local and peer " + "address %s, routing protocols may malfunction", + ifp->name,inet_ntoa(*addr)); + } + else + { + if (broad->s_addr != ipv4_broadcast_addr(addr->s_addr,prefixlen)) + { + char buf[2][INET_ADDRSTRLEN]; + struct in_addr bcalc; + bcalc.s_addr = ipv4_broadcast_addr(addr->s_addr,prefixlen); + zlog_warn("warning: interface %s broadcast addr %s/%d != " + "calculated %s, routing protocols may malfunction", + ifp->name, + inet_ntop (AF_INET, broad, buf[0], sizeof(buf[0])), + prefixlen, + inet_ntop (AF_INET, &bcalc, buf[1], sizeof(buf[1]))); + } + } + + } + else + { + if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_PEER)) + { + zlog_warn("warning: %s called for interface %s " + "with peer flag set, but no peer address supplied", + __func__, ifp->name); + UNSET_FLAG(ifc->flags, ZEBRA_IFA_PEER); + } + + /* no broadcast or destination address was supplied */ + if ((prefixlen == IPV4_MAX_PREFIXLEN) && if_is_pointopoint(ifp)) + zlog_warn("warning: PtP interface %s with addr %s/%d needs a " + "peer address",ifp->name,inet_ntoa(*addr),prefixlen); + } + + /* Label of this address. */ + if (label) + ifc->label = XSTRDUP (MTYPE_CONNECTED_LABEL, label); + + /* 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 +connected_down_ipv4 (struct interface *ifp, struct connected *ifc) +{ + struct prefix_ipv4 p; + + if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL)) + return; + + PREFIX_COPY_IPV4(&p, CONNECTED_PREFIX(ifc)); + + /* Apply mask to the network. */ + apply_mask_ipv4 (&p); + + /* In case of connected address is 0.0.0.0/0 we treat it tunnel + address. */ + if (prefix_ipv4_any (&p)) + 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, ifp->vrf_id, + SAFI_UNICAST); + + rib_delete_ipv4 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, ifp->ifindex, ifp->vrf_id, + SAFI_MULTICAST); + + rib_update (ifp->vrf_id); +} + +/* Delete connected IPv4 route to the interface. */ +void +connected_delete_ipv4 (struct interface *ifp, int flags, struct in_addr *addr, + u_char prefixlen, struct in_addr *broad) +{ + struct prefix_ipv4 p; + struct connected *ifc; + + memset (&p, 0, sizeof (struct prefix_ipv4)); + p.family = AF_INET; + p.prefix = *addr; + p.prefixlen = prefixlen; + + ifc = connected_check (ifp, (struct prefix *) &p); + if (! ifc) + return; + + connected_withdraw (ifc); + + rib_update (ifp->vrf_id); +} + +#ifdef HAVE_IPV6 +void +connected_up_ipv6 (struct interface *ifp, struct connected *ifc) +{ + struct prefix_ipv6 p; + + if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL)) + return; + + PREFIX_COPY_IPV6(&p, CONNECTED_PREFIX(ifc)); + + /* Apply mask to the network. */ + apply_mask_ipv6 (&p); + +#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, ifp->vrf_id, + RT_TABLE_MAIN, ifp->metric, 0, 0, SAFI_UNICAST); + + rib_update (ifp->vrf_id); +} + +/* Add connected IPv6 route to the interface. */ +void +connected_add_ipv6 (struct interface *ifp, int flags, struct in6_addr *addr, + u_char prefixlen, struct in6_addr *broad, + const char *label) +{ + struct prefix_ipv6 *p; + struct connected *ifc; + + /* Make connected structure. */ + 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 (); + p->family = AF_INET6; + IPV6_ADDR_COPY (&p->prefix, addr); + p->prefixlen = prefixlen; + ifc->address = (struct prefix *) p; + + /* If there is broadcast or peer address. */ + if (broad) + { + if (IN6_IS_ADDR_UNSPECIFIED(broad)) + zlog_warn("warning: %s called for interface %s with unspecified " + "destination address; ignoring!", __func__, ifp->name); + else + { + p = prefix_ipv6_new (); + p->family = AF_INET6; + IPV6_ADDR_COPY (&p->prefix, broad); + p->prefixlen = prefixlen; + ifc->destination = (struct prefix *) p; + } + } + if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_PEER) && !ifc->destination) + { + zlog_warn("warning: %s called for interface %s " + "with peer flag set, but no peer address supplied", + __func__, ifp->name); + UNSET_FLAG(ifc->flags, ZEBRA_IFA_PEER); + } + + /* Label of this address. */ + if (label) + ifc->label = XSTRDUP (MTYPE_CONNECTED_LABEL, label); + + /* 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 +connected_down_ipv6 (struct interface *ifp, struct connected *ifc) +{ + struct prefix_ipv6 p; + + if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL)) + return; + + PREFIX_COPY_IPV6(&p, CONNECTED_PREFIX(ifc)); + + apply_mask_ipv6 (&p); + + if (IN6_IS_ADDR_UNSPECIFIED (&p.prefix)) + return; + + rib_delete_ipv6 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, ifp->ifindex, ifp->vrf_id, + SAFI_UNICAST); + + rib_update (ifp->vrf_id); +} + +void +connected_delete_ipv6 (struct interface *ifp, struct in6_addr *address, + u_char prefixlen, struct in6_addr *broad) +{ + struct prefix_ipv6 p; + struct connected *ifc; + + memset (&p, 0, sizeof (struct prefix_ipv6)); + p.family = AF_INET6; + memcpy (&p.prefix, address, sizeof (struct in6_addr)); + p.prefixlen = prefixlen; + + ifc = connected_check (ifp, (struct prefix *) &p); + if (! ifc) + return; + + connected_withdraw (ifc); + + rib_update (ifp->vrf_id); +} +#endif /* HAVE_IPV6 */ diff --git a/zebra/connected.h b/zebra/connected.h new file mode 100644 index 0000000..9595ddb --- /dev/null +++ b/zebra/connected.h @@ -0,0 +1,55 @@ +/* + * Interface's address and mask. + * 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. + */ + +#ifndef _ZEBRA_CONNECTED_H +#define _ZEBRA_CONNECTED_H + +extern struct connected * +connected_check (struct interface *ifp, struct prefix *p); + +extern void +connected_add_ipv4 (struct interface *ifp, int flags, struct in_addr *addr, + u_char prefixlen, struct in_addr *broad, + const char *label); + +extern void +connected_delete_ipv4 (struct interface *ifp, int flags, struct in_addr *addr, + u_char prefixlen, struct in_addr *broad); + +extern void connected_up_ipv4 (struct interface *, struct connected *); +extern void connected_down_ipv4 (struct interface *, struct connected *); + +#ifdef HAVE_IPV6 +extern void +connected_add_ipv6 (struct interface *ifp, int flags, struct in6_addr *address, + u_char prefixlen, struct in6_addr *broad, + const char *label); +extern void +connected_delete_ipv6 (struct interface *ifp, struct in6_addr *address, + u_char prefixlen, struct in6_addr *broad); + +extern void connected_up_ipv6 (struct interface *, struct connected *); +extern void connected_down_ipv6 (struct interface *ifp, struct connected *); + +#endif /* HAVE_IPV6 */ + +#endif /*_ZEBRA_CONNECTED_H */ diff --git a/zebra/debug.c b/zebra/debug.c new file mode 100644 index 0000000..7d023ce --- /dev/null +++ b/zebra/debug.c @@ -0,0 +1,413 @@ +/* + * Zebra debug related function + * 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 "command.h" +#include "debug.h" + +/* For debug statement. */ +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 + "Debugging information\n" + "Zebra configuration\n") +{ + vty_out (vty, "Zebra debugging status:%s", VTY_NEWLINE); + + if (IS_ZEBRA_DEBUG_EVENT) + vty_out (vty, " Zebra event debugging is on%s", VTY_NEWLINE); + + if (IS_ZEBRA_DEBUG_PACKET) + { + if (IS_ZEBRA_DEBUG_SEND && IS_ZEBRA_DEBUG_RECV) + { + vty_out (vty, " Zebra packet%s debugging is on%s", + IS_ZEBRA_DEBUG_DETAIL ? " detail" : "", + VTY_NEWLINE); + } + else + { + if (IS_ZEBRA_DEBUG_SEND) + vty_out (vty, " Zebra packet send%s debugging is on%s", + IS_ZEBRA_DEBUG_DETAIL ? " detail" : "", + VTY_NEWLINE); + else + vty_out (vty, " Zebra packet receive%s debugging is on%s", + IS_ZEBRA_DEBUG_DETAIL ? " detail" : "", + VTY_NEWLINE); + } + } + + if (IS_ZEBRA_DEBUG_KERNEL) + vty_out (vty, " Zebra kernel debugging is on%s", VTY_NEWLINE); + + if (IS_ZEBRA_DEBUG_RIB) + vty_out (vty, " Zebra RIB debugging is on%s", VTY_NEWLINE); + 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; +} + +DEFUN (debug_zebra_events, + debug_zebra_events_cmd, + "debug zebra events", + DEBUG_STR + "Zebra configuration\n" + "Debug option set for zebra events\n") +{ + zebra_debug_event = ZEBRA_DEBUG_EVENT; + 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", + DEBUG_STR + "Zebra configuration\n" + "Debug option set for zebra packet\n") +{ + zebra_debug_packet = ZEBRA_DEBUG_PACKET; + 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|detail)", + DEBUG_STR + "Zebra configuration\n" + "Debug option set for zebra packet\n" + "Debug option set for receive packet\n" + "Debug option set for send packet\n") +{ + zebra_debug_packet = ZEBRA_DEBUG_PACKET; + if (strncmp ("send", argv[0], strlen (argv[0])) == 0) + SET_FLAG(zebra_debug_packet, ZEBRA_DEBUG_SEND); + if (strncmp ("recv", argv[0], strlen (argv[0])) == 0) + 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; +} + +DEFUN (debug_zebra_packet_detail, + debug_zebra_packet_detail_cmd, + "debug zebra packet (recv|send) detail", + DEBUG_STR + "Zebra configuration\n" + "Debug option set for zebra packet\n" + "Debug option set for receive packet\n" + "Debug option set for send packet\n" + "Debug option set detailed information\n") +{ + zebra_debug_packet = ZEBRA_DEBUG_PACKET; + if (strncmp ("send", argv[0], strlen (argv[0])) == 0) + SET_FLAG(zebra_debug_packet, ZEBRA_DEBUG_SEND); + if (strncmp ("recv", argv[0], strlen (argv[0])) == 0) + SET_FLAG(zebra_debug_packet, ZEBRA_DEBUG_RECV); + SET_FLAG(zebra_debug_packet, ZEBRA_DEBUG_DETAIL); + return CMD_SUCCESS; +} + +DEFUN (debug_zebra_kernel, + debug_zebra_kernel_cmd, + "debug zebra kernel", + DEBUG_STR + "Zebra configuration\n" + "Debug option set for zebra between kernel interface\n") +{ + zebra_debug_kernel = ZEBRA_DEBUG_KERNEL; + return CMD_SUCCESS; +} + +DEFUN (debug_zebra_rib, + debug_zebra_rib_cmd, + "debug zebra rib", + DEBUG_STR + "Zebra configuration\n" + "Debug RIB events\n") +{ + SET_FLAG (zebra_debug_rib, ZEBRA_DEBUG_RIB); + return CMD_SUCCESS; +} + +DEFUN (debug_zebra_rib_q, + debug_zebra_rib_q_cmd, + "debug zebra rib queue", + DEBUG_STR + "Zebra configuration\n" + "Debug RIB events\n" + "Debug RIB queueing\n") +{ + SET_FLAG (zebra_debug_rib, ZEBRA_DEBUG_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", + NO_STR + DEBUG_STR + "Zebra configuration\n" + "Debug option set for zebra events\n") +{ + zebra_debug_event = 0; + 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", + NO_STR + DEBUG_STR + "Zebra configuration\n" + "Debug option set for zebra packet\n") +{ + zebra_debug_packet = 0; + return CMD_SUCCESS; +} + +DEFUN (no_debug_zebra_packet_direct, + no_debug_zebra_packet_direct_cmd, + "no debug zebra packet (recv|send)", + NO_STR + DEBUG_STR + "Zebra configuration\n" + "Debug option set for zebra packet\n" + "Debug option set for receive packet\n" + "Debug option set for send packet\n") +{ + if (strncmp ("send", argv[0], strlen (argv[0])) == 0) + UNSET_FLAG(zebra_debug_packet, ZEBRA_DEBUG_SEND); + if (strncmp ("recv", argv[0], strlen (argv[0])) == 0) + UNSET_FLAG(zebra_debug_packet, ZEBRA_DEBUG_RECV); + return CMD_SUCCESS; +} + +DEFUN (no_debug_zebra_kernel, + no_debug_zebra_kernel_cmd, + "no debug zebra kernel", + NO_STR + DEBUG_STR + "Zebra configuration\n" + "Debug option set for zebra between kernel interface\n") +{ + zebra_debug_kernel = 0; + return CMD_SUCCESS; +} + +DEFUN (no_debug_zebra_rib, + no_debug_zebra_rib_cmd, + "no debug zebra rib", + NO_STR + DEBUG_STR + "Zebra configuration\n" + "Debug zebra RIB\n") +{ + zebra_debug_rib = 0; + return CMD_SUCCESS; +} + +DEFUN (no_debug_zebra_rib_q, + no_debug_zebra_rib_q_cmd, + "no debug zebra rib queue", + NO_STR + DEBUG_STR + "Zebra configuration\n" + "Debug zebra RIB\n" + "Debug RIB queueing\n") +{ + UNSET_FLAG (zebra_debug_rib, ZEBRA_DEBUG_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 = +{ + DEBUG_NODE, + "", /* Debug node has no interface. */ + 1 +}; + +static int +config_write_debug (struct vty *vty) +{ + int write = 0; + + if (IS_ZEBRA_DEBUG_EVENT) + { + vty_out (vty, "debug zebra events%s", VTY_NEWLINE); + write++; + } + if (IS_ZEBRA_DEBUG_PACKET) + { + if (IS_ZEBRA_DEBUG_SEND && IS_ZEBRA_DEBUG_RECV) + { + vty_out (vty, "debug zebra packet%s%s", + IS_ZEBRA_DEBUG_DETAIL ? " detail" : "", + VTY_NEWLINE); + write++; + } + else + { + if (IS_ZEBRA_DEBUG_SEND) + vty_out (vty, "debug zebra packet send%s%s", + IS_ZEBRA_DEBUG_DETAIL ? " detail" : "", + VTY_NEWLINE); + else + vty_out (vty, "debug zebra packet recv%s%s", + IS_ZEBRA_DEBUG_DETAIL ? " detail" : "", + VTY_NEWLINE); + write++; + } + } + if (IS_ZEBRA_DEBUG_KERNEL) + { + vty_out (vty, "debug zebra kernel%s", VTY_NEWLINE); + write++; + } + if (IS_ZEBRA_DEBUG_RIB) + { + vty_out (vty, "debug zebra rib%s", VTY_NEWLINE); + write++; + } + if (IS_ZEBRA_DEBUG_RIB_Q) + { + 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; +} + +void +zebra_debug_init (void) +{ + zebra_debug_event = 0; + 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, &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 new file mode 100644 index 0000000..0fb4dd9 --- /dev/null +++ b/zebra/debug.h @@ -0,0 +1,67 @@ +/* + * Zebra debug related function + * 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 _ZEBRA_DEBUG_H +#define _ZEBRA_DEBUG_H + +/* Debug flags. */ +#define ZEBRA_DEBUG_EVENT 0x01 + +#define ZEBRA_DEBUG_PACKET 0x01 +#define ZEBRA_DEBUG_SEND 0x20 +#define ZEBRA_DEBUG_RECV 0x40 +#define ZEBRA_DEBUG_DETAIL 0x80 + +#define ZEBRA_DEBUG_KERNEL 0x01 + +#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) + +#define IS_ZEBRA_DEBUG_PACKET (zebra_debug_packet & ZEBRA_DEBUG_PACKET) +#define IS_ZEBRA_DEBUG_SEND (zebra_debug_packet & ZEBRA_DEBUG_SEND) +#define IS_ZEBRA_DEBUG_RECV (zebra_debug_packet & ZEBRA_DEBUG_RECV) +#define IS_ZEBRA_DEBUG_DETAIL (zebra_debug_packet & ZEBRA_DEBUG_DETAIL) + +#define IS_ZEBRA_DEBUG_KERNEL (zebra_debug_kernel & ZEBRA_DEBUG_KERNEL) + +#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); + +#endif /* _ZEBRA_DEBUG_H */ diff --git a/zebra/if_ioctl.c b/zebra/if_ioctl.c new file mode 100644 index 0000000..99328a1 --- /dev/null +++ b/zebra/if_ioctl.c @@ -0,0 +1,475 @@ +/* + * Interface looking up 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 "if.h" +#include "sockunion.h" +#include "prefix.h" +#include "ioctl.h" +#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 +interface_list_ioctl (void) +{ + int ret; + int sock; +#define IFNUM_BASE 32 + int ifnum; + struct ifreq *ifreq; + struct ifconf ifconf; + struct interface *ifp; + int n; + int lastlen; + + /* Normally SIOCGIFCONF works with AF_INET socket. */ + sock = socket (AF_INET, SOCK_DGRAM, 0); + if (sock < 0) + { + zlog_warn ("Can't make AF_INET socket stream: %s", safe_strerror (errno)); + return -1; + } + + /* Set initial ifreq count. This will be double when SIOCGIFCONF + fail. Solaris has SIOCGIFNUM. */ +#ifdef SIOCGIFNUM + ret = ioctl (sock, SIOCGIFNUM, &ifnum); + if (ret < 0) + ifnum = IFNUM_BASE; + else + ifnum++; +#else + ifnum = IFNUM_BASE; +#endif /* SIOCGIFNUM */ + + ifconf.ifc_buf = NULL; + + lastlen = 0; + /* Loop until SIOCGIFCONF success. */ + for (;;) + { + ifconf.ifc_len = sizeof (struct ifreq) * ifnum; + ifconf.ifc_buf = XREALLOC(MTYPE_TMP, ifconf.ifc_buf, ifconf.ifc_len); + + ret = ioctl(sock, SIOCGIFCONF, &ifconf); + + if (ret < 0) + { + zlog_warn ("SIOCGIFCONF: %s", safe_strerror(errno)); + goto end; + } + /* Repeatedly get info til buffer fails to grow. */ + if (ifconf.ifc_len > lastlen) + { + lastlen = ifconf.ifc_len; + ifnum += 10; + continue; + } + /* Success. */ + break; + } + + /* Allocate interface. */ + ifreq = ifconf.ifc_req; + +#ifdef OPEN_BSD + for (n = 0; n < ifconf.ifc_len; ) + { + int size; + + ifreq = (struct ifreq *)((caddr_t) ifconf.ifc_req + n); + ifp = if_get_by_name_len(ifreq->ifr_name, + strnlen(ifreq->ifr_name, + sizeof(ifreq->ifr_name))); + if_add_update (ifp); + size = ifreq->ifr_addr.sa_len; + if (size < sizeof (ifreq->ifr_addr)) + size = sizeof (ifreq->ifr_addr); + size += sizeof (ifreq->ifr_name); + n += size; + } +#else + for (n = 0; n < ifconf.ifc_len; n += sizeof(struct ifreq)) + { + ifp = if_get_by_name_len(ifreq->ifr_name, + strnlen(ifreq->ifr_name, + sizeof(ifreq->ifr_name))); + if_add_update (ifp); + ifreq++; + } +#endif /* OPEN_BSD */ + + end: + close (sock); + XFREE (MTYPE_TMP, ifconf.ifc_buf); + + return ret; +} + +/* Get interface's index by ioctl. */ +static int +if_get_index (struct interface *ifp) +{ +#if defined(HAVE_IF_NAMETOINDEX) + /* Modern systems should have if_nametoindex(3). */ + ifp->ifindex = if_nametoindex(ifp->name); +#elif defined(SIOCGIFINDEX) && !defined(HAVE_BROKEN_ALIASES) + /* Fall-back for older linuxes. */ + int ret; + struct ifreq ifreq; + static int if_fake_index; + + ifreq_set_name (&ifreq, ifp); + + ret = if_ioctl (SIOCGIFINDEX, (caddr_t) &ifreq); + if (ret < 0) + { + /* Linux 2.0.X does not have interface index. */ + ifp->ifindex = if_fake_index++; + return ifp->ifindex; + } + + /* OK we got interface index. */ +#ifdef ifr_ifindex + ifp->ifindex = ifreq.ifr_ifindex; +#else + ifp->ifindex = ifreq.ifr_index; +#endif + +#else +/* Linux 2.2.X does not provide individual interface index + for aliases and we know it. For others issue a warning. */ +#if !defined(HAVE_BROKEN_ALIASES) +#warning "Using if_fake_index. You may want to add appropriate" +#warning "mapping from ifname to ifindex for your system..." +#endif + /* This branch probably won't provide usable results, but anyway... */ + static int if_fake_index = 1; + ifp->ifindex = if_fake_index++; +#endif + + return ifp->ifindex; +} + +#ifdef SIOCGIFHWADDR +static int +if_get_hwaddr (struct interface *ifp) +{ + int ret; + struct ifreq ifreq; + int i; + + strncpy (ifreq.ifr_name, ifp->name, IFNAMSIZ); + ifreq.ifr_addr.sa_family = AF_INET; + + /* Fetch Hardware address if available. */ + ret = if_ioctl (SIOCGIFHWADDR, (caddr_t) &ifreq); + if (ret < 0) + ifp->hw_addr_len = 0; + else + { + memcpy (ifp->hw_addr, ifreq.ifr_hwaddr.sa_data, 6); + + for (i = 0; i < 6; i++) + if (ifp->hw_addr[i] != 0) + break; + + if (i == 6) + ifp->hw_addr_len = 0; + else + ifp->hw_addr_len = 6; + } + return 0; +} +#endif /* SIOCGIFHWADDR */ + +#ifdef HAVE_GETIFADDRS +#include + +static int +if_getaddrs (void) +{ + int ret; + struct ifaddrs *ifap; + struct ifaddrs *ifapfree; + struct interface *ifp; + int prefixlen; + + ret = getifaddrs (&ifap); + if (ret != 0) + { + zlog_err ("getifaddrs(): %s", safe_strerror (errno)); + return -1; + } + + for (ifapfree = ifap; ifap; ifap = ifap->ifa_next) + { + if (ifap->ifa_addr == NULL) + { + zlog_err ("%s: nonsensical ifaddr with NULL ifa_addr, ifname %s", + __func__, (ifap->ifa_name ? ifap->ifa_name : "(null)")); + continue; + } + + ifp = if_lookup_by_name (ifap->ifa_name); + if (ifp == NULL) + { + zlog_err ("if_getaddrs(): Can't lookup interface %s\n", + ifap->ifa_name); + continue; + } + + if (ifap->ifa_addr->sa_family == AF_INET) + { + struct sockaddr_in *addr; + struct sockaddr_in *mask; + struct sockaddr_in *dest; + struct in_addr *dest_pnt; + int flags = 0; + + addr = (struct sockaddr_in *) ifap->ifa_addr; + mask = (struct sockaddr_in *) ifap->ifa_netmask; + prefixlen = ip_masklen (mask->sin_addr); + + dest_pnt = NULL; + + if (ifap->ifa_dstaddr && + !IPV4_ADDR_SAME(&addr->sin_addr, + &((struct sockaddr_in *) + ifap->ifa_dstaddr)->sin_addr)) + { + dest = (struct sockaddr_in *) ifap->ifa_dstaddr; + dest_pnt = &dest->sin_addr; + flags = ZEBRA_IFA_PEER; + } + else if (ifap->ifa_broadaddr && + !IPV4_ADDR_SAME(&addr->sin_addr, + &((struct sockaddr_in *) + ifap->ifa_broadaddr)->sin_addr)) + { + dest = (struct sockaddr_in *) ifap->ifa_broadaddr; + dest_pnt = &dest->sin_addr; + } + + connected_add_ipv4 (ifp, flags, &addr->sin_addr, + prefixlen, dest_pnt, NULL); + } +#ifdef HAVE_IPV6 + if (ifap->ifa_addr->sa_family == AF_INET6) + { + struct sockaddr_in6 *addr; + struct sockaddr_in6 *mask; + struct sockaddr_in6 *dest; + struct in6_addr *dest_pnt; + int flags = 0; + + addr = (struct sockaddr_in6 *) ifap->ifa_addr; + mask = (struct sockaddr_in6 *) ifap->ifa_netmask; + prefixlen = ip6_masklen (mask->sin6_addr); + + dest_pnt = NULL; + + if (ifap->ifa_dstaddr && + !IPV6_ADDR_SAME(&addr->sin6_addr, + &((struct sockaddr_in6 *) + ifap->ifa_dstaddr)->sin6_addr)) + { + dest = (struct sockaddr_in6 *) ifap->ifa_dstaddr; + dest_pnt = &dest->sin6_addr; + flags = ZEBRA_IFA_PEER; + } + else if (ifap->ifa_broadaddr && + !IPV6_ADDR_SAME(&addr->sin6_addr, + &((struct sockaddr_in6 *) + ifap->ifa_broadaddr)->sin6_addr)) + { + dest = (struct sockaddr_in6 *) ifap->ifa_broadaddr; + dest_pnt = &dest->sin6_addr; + } + +#if defined(KAME) + if (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr)) + { + addr->sin6_scope_id = + ntohs(*(u_int16_t *)&addr->sin6_addr.s6_addr[2]); + addr->sin6_addr.s6_addr[2] = addr->sin6_addr.s6_addr[3] = 0; + } +#endif + + connected_add_ipv6 (ifp, flags, &addr->sin6_addr, prefixlen, + dest_pnt, NULL); + } +#endif /* HAVE_IPV6 */ + } + + freeifaddrs (ifapfree); + + return 0; +} +#else /* HAVE_GETIFADDRS */ +/* Interface address lookup by ioctl. This function only looks up + IPv4 address. */ +int +if_get_addr (struct interface *ifp) +{ + int ret; + struct ifreq ifreq; + struct sockaddr_in addr; + struct sockaddr_in mask; + struct sockaddr_in dest; + struct in_addr *dest_pnt; + u_char prefixlen; + int flags = 0; + + /* Interface's name and address family. */ + strncpy (ifreq.ifr_name, ifp->name, IFNAMSIZ); + ifreq.ifr_addr.sa_family = AF_INET; + + /* Interface's address. */ + ret = if_ioctl (SIOCGIFADDR, (caddr_t) &ifreq); + if (ret < 0) + { + if (errno != EADDRNOTAVAIL) + { + zlog_warn ("SIOCGIFADDR fail: %s", safe_strerror (errno)); + return ret; + } + return 0; + } + memcpy (&addr, &ifreq.ifr_addr, sizeof (struct sockaddr_in)); + + /* Interface's network mask. */ + ret = if_ioctl (SIOCGIFNETMASK, (caddr_t) &ifreq); + if (ret < 0) + { + if (errno != EADDRNOTAVAIL) + { + zlog_warn ("SIOCGIFNETMASK fail: %s", safe_strerror (errno)); + return ret; + } + return 0; + } +#ifdef ifr_netmask + memcpy (&mask, &ifreq.ifr_netmask, sizeof (struct sockaddr_in)); +#else + memcpy (&mask, &ifreq.ifr_addr, sizeof (struct sockaddr_in)); +#endif /* ifr_netmask */ + prefixlen = ip_masklen (mask.sin_addr); + + /* Point to point or borad cast address pointer init. */ + dest_pnt = NULL; + + ret = if_ioctl (SIOCGIFDSTADDR, (caddr_t) &ifreq); + if (ret < 0) + { + if (errno != EADDRNOTAVAIL) + zlog_warn ("SIOCGIFDSTADDR fail: %s", safe_strerror (errno)); + } + else if (!IPV4_ADDR_SAME(&addr.sin_addr, &ifreq.ifr_dstaddr.sin_addr)) + { + memcpy (&dest, &ifreq.ifr_dstaddr, sizeof (struct sockaddr_in)); + dest_pnt = &dest.sin_addr; + flags = ZEBRA_IFA_PEER; + } + if (!dest_pnt) + { + ret = if_ioctl (SIOCGIFBRDADDR, (caddr_t) &ifreq); + if (ret < 0) + { + if (errno != EADDRNOTAVAIL) + zlog_warn ("SIOCGIFBRDADDR fail: %s", safe_strerror (errno)); + } + else if (!IPV4_ADDR_SAME(&addr.sin_addr, &ifreq.ifr_broadaddr.sin_addr)) + { + memcpy (&dest, &ifreq.ifr_broadaddr, sizeof (struct sockaddr_in)); + dest_pnt = &dest.sin_addr; + } + } + + + /* Set address to the interface. */ + connected_add_ipv4 (ifp, flags, &addr.sin_addr, prefixlen, dest_pnt, NULL); + + return 0; +} +#endif /* HAVE_GETIFADDRS */ + +/* Fetch interface information via ioctl(). */ +static void +interface_info_ioctl () +{ + struct listnode *node, *nnode; + struct interface *ifp; + + for (ALL_LIST_ELEMENTS (iflist, node, nnode, ifp)) + { + if_get_index (ifp); +#ifdef SIOCGIFHWADDR + if_get_hwaddr (ifp); +#endif /* SIOCGIFHWADDR */ + if_get_flags (ifp); +#ifndef HAVE_GETIFADDRS + if_get_addr (ifp); +#endif /* ! HAVE_GETIFADDRS */ + if_get_mtu (ifp); + if_get_metric (ifp); + } +} + +/* Lookup all interface information. */ +void +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 + interface_list_proc (); +#endif /* HAVE_PROC_NET_DEV */ + interface_list_ioctl (); + + /* After listing is done, get index, address, flags and other + interface's information. */ + interface_info_ioctl (); + +#ifdef HAVE_GETIFADDRS + if_getaddrs (); +#endif /* HAVE_GETIFADDRS */ + +#if defined(HAVE_IPV6) && defined(HAVE_PROC_NET_IF_INET6) + /* Linux provides interface's IPv6 address via + /proc/net/if_inet6. */ + ifaddr_proc_ipv6 (); +#endif /* HAVE_IPV6 && HAVE_PROC_NET_IF_INET6 */ +} diff --git a/zebra/if_ioctl_solaris.c b/zebra/if_ioctl_solaris.c new file mode 100644 index 0000000..b399812 --- /dev/null +++ b/zebra/if_ioctl_solaris.c @@ -0,0 +1,385 @@ +/* + * Interface looking up by ioctl () on Solaris. + * 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 "if.h" +#include "sockunion.h" +#include "prefix.h" +#include "ioctl.h" +#include "connected.h" +#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" + +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; + +static int +interface_list_ioctl (int af) +{ + int ret; + int sock; +#define IFNUM_BASE 32 + struct lifnum lifn; + int ifnum; + struct lifreq *lifreq; + struct lifconf lifconf; + struct interface *ifp; + int n; + int save_errno; + size_t needed, lastneeded = 0; + char *buf = NULL; + + if (zserv_privs.change(ZPRIVS_RAISE)) + zlog (NULL, LOG_ERR, "Can't raise privileges"); + + sock = socket (af, SOCK_DGRAM, 0); + if (sock < 0) + { + zlog_warn ("Can't make %s socket stream: %s", + (af == AF_INET ? "AF_INET" : "AF_INET6"), safe_strerror (errno)); + + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog (NULL, LOG_ERR, "Can't lower privileges"); + + return -1; + } + +calculate_lifc_len: /* must hold privileges to enter here */ + lifn.lifn_family = af; + lifn.lifn_flags = LIFC_NOXMIT; /* we want NOXMIT interfaces too */ + ret = ioctl (sock, SIOCGLIFNUM, &lifn); + save_errno = errno; + + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog (NULL, LOG_ERR, "Can't lower privileges"); + + if (ret < 0) + { + zlog_warn ("interface_list_ioctl: SIOCGLIFNUM failed %s", + safe_strerror (save_errno)); + close (sock); + return -1; + } + ifnum = lifn.lifn_count; + + /* + * When calculating the buffer size needed, add a small number + * of interfaces to those we counted. We do this to capture + * the interface status of potential interfaces which may have + * been plumbed between the SIOCGLIFNUM and the SIOCGLIFCONF. + */ + needed = (ifnum + 4) * sizeof (struct lifreq); + if (needed > lastneeded || needed < lastneeded / 2) + { + if (buf != NULL) + XFREE (MTYPE_TMP, buf); + if ((buf = XMALLOC (MTYPE_TMP, needed)) == NULL) + { + zlog_warn ("interface_list_ioctl: malloc failed"); + close (sock); + return -1; + } + } + lastneeded = needed; + + lifconf.lifc_family = af; + lifconf.lifc_flags = LIFC_NOXMIT; + lifconf.lifc_len = needed; + lifconf.lifc_buf = buf; + + if (zserv_privs.change(ZPRIVS_RAISE)) + zlog (NULL, LOG_ERR, "Can't raise privileges"); + + ret = ioctl (sock, SIOCGLIFCONF, &lifconf); + + if (ret < 0) + { + if (errno == EINVAL) + goto calculate_lifc_len; /* deliberately hold privileges */ + + zlog_warn ("SIOCGLIFCONF: %s", safe_strerror (errno)); + + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog (NULL, LOG_ERR, "Can't lower privileges"); + + goto end; + } + + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog (NULL, LOG_ERR, "Can't lower privileges"); + + /* Allocate interface. */ + lifreq = lifconf.lifc_req; + + for (n = 0; n < lifconf.lifc_len; n += sizeof (struct lifreq)) + { + /* we treat Solaris logical interfaces as addresses, because that is + * how PF_ROUTE on Solaris treats them. Hence we can not directly use + * the lifreq_name to get the ifp. We need to normalise the name + * before attempting get. + * + * Solaris logical interface names are in the form of: + * : + */ + unsigned int normallen = 0; + uint64_t lifflags; + + /* We should exclude ~IFF_UP interfaces, as we'll find out about them + * coming up later through RTM_NEWADDR message on the route socket. + */ + if (if_get_flags_direct (lifreq->lifr_name, &lifflags, + lifreq->lifr_addr.ss_family) + || !CHECK_FLAG (lifflags, IFF_UP)) + { + lifreq++; + continue; + } + + /* Find the normalised name */ + while ( (normallen < sizeof(lifreq->lifr_name)) + && ( *(lifreq->lifr_name + normallen) != '\0') + && ( *(lifreq->lifr_name + normallen) != ':') ) + normallen++; + + ifp = if_get_by_name_len(lifreq->lifr_name, normallen); + + if (lifreq->lifr_addr.ss_family == AF_INET) + ifp->flags |= IFF_IPV4; + + if (lifreq->lifr_addr.ss_family == AF_INET6) + { +#ifdef HAVE_IPV6 + ifp->flags |= IFF_IPV6; +#else + lifreq++; + continue; +#endif /* HAVE_IPV6 */ + } + + if_add_update (ifp); + + interface_info_ioctl (ifp); + + /* If a logical interface pass the full name so it can be + * as a label on the address + */ + if ( *(lifreq->lifr_name + normallen) != '\0') + if_get_addr (ifp, (struct sockaddr *) &lifreq->lifr_addr, + lifreq->lifr_name); + else + if_get_addr (ifp, (struct sockaddr *) &lifreq->lifr_addr, NULL); + + /* Poke the interface flags. Lets IFF_UP mangling kick in */ + if_flags_update (ifp, ifp->flags); + + lifreq++; + } + +end: + close (sock); + XFREE (MTYPE_TMP, lifconf.lifc_buf); + return ret; +} + +/* Get interface's index by ioctl. */ +static int +if_get_index (struct interface *ifp) +{ + int ret; + struct lifreq lifreq; + + lifreq_set_name (&lifreq, ifp->name); + + if (ifp->flags & IFF_IPV4) + ret = AF_IOCTL (AF_INET, SIOCGLIFINDEX, (caddr_t) & lifreq); + else if (ifp->flags & IFF_IPV6) + ret = AF_IOCTL (AF_INET6, SIOCGLIFINDEX, (caddr_t) & lifreq); + else + ret = -1; + + if (ret < 0) + { + zlog_warn ("SIOCGLIFINDEX(%s) failed", ifp->name); + return ret; + } + + /* OK we got interface index. */ +#ifdef ifr_ifindex + ifp->ifindex = lifreq.lifr_ifindex; +#else + ifp->ifindex = lifreq.lifr_index; +#endif + return ifp->ifindex; + +} + + +/* Interface address lookup by ioctl. This function only looks up + IPv4 address. */ +#define ADDRLEN(sa) (((sa)->sa_family == AF_INET ? \ + sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6))) + +#define SIN(s) ((struct sockaddr_in *)(s)) +#define SIN6(s) ((struct sockaddr_in6 *)(s)) + +/* Retrieve address information for the given ifp */ +static int +if_get_addr (struct interface *ifp, struct sockaddr *addr, const char *label) +{ + int ret; + struct lifreq lifreq; + struct sockaddr_storage mask, dest; + char *dest_pnt = NULL; + u_char prefixlen = 0; + afi_t af; + int flags = 0; + + /* Interface's name and address family. + * We need to use the logical interface name / label, if we've been + * given one, in order to get the right address + */ + strncpy (lifreq.lifr_name, (label ? label : ifp->name), IFNAMSIZ); + + /* Interface's address. */ + memcpy (&lifreq.lifr_addr, addr, ADDRLEN (addr)); + af = addr->sa_family; + + /* Point to point or broad cast address pointer init. */ + dest_pnt = NULL; + + if (AF_IOCTL (af, SIOCGLIFDSTADDR, (caddr_t) & lifreq) >= 0) + { + memcpy (&dest, &lifreq.lifr_dstaddr, ADDRLEN (addr)); + if (af == AF_INET) + dest_pnt = (char *) &(SIN (&dest)->sin_addr); + else + dest_pnt = (char *) &(SIN6 (&dest)->sin6_addr); + flags = ZEBRA_IFA_PEER; + } + + if (af == AF_INET) + { + ret = if_ioctl (SIOCGLIFNETMASK, (caddr_t) & lifreq); + + if (ret < 0) + { + if (errno != EADDRNOTAVAIL) + { + zlog_warn ("SIOCGLIFNETMASK (%s) fail: %s", ifp->name, + safe_strerror (errno)); + return ret; + } + return 0; + } + memcpy (&mask, &lifreq.lifr_addr, ADDRLEN (addr)); + + prefixlen = ip_masklen (SIN (&mask)->sin_addr); + if (!dest_pnt && (if_ioctl (SIOCGLIFBRDADDR, (caddr_t) & lifreq) >= 0)) + { + memcpy (&dest, &lifreq.lifr_broadaddr, sizeof (struct sockaddr_in)); + dest_pnt = (char *) &SIN (&dest)->sin_addr; + } + } +#ifdef HAVE_IPV6 + else if (af == AF_INET6) + { + if (if_ioctl_ipv6 (SIOCGLIFSUBNET, (caddr_t) & lifreq) < 0) + { + if (ifp->flags & IFF_POINTOPOINT) + prefixlen = IPV6_MAX_BITLEN; + else + zlog_warn ("SIOCGLIFSUBNET (%s) fail: %s", + ifp->name, safe_strerror (errno)); + } + else + { + prefixlen = lifreq.lifr_addrlen; + } + } +#endif /* HAVE_IPV6 */ + + /* Set address to the interface. */ + if (af == AF_INET) + connected_add_ipv4 (ifp, flags, &SIN (addr)->sin_addr, prefixlen, + (struct in_addr *) dest_pnt, label); +#ifdef HAVE_IPV6 + else if (af == AF_INET6) + connected_add_ipv6 (ifp, flags, &SIN6 (addr)->sin6_addr, prefixlen, + (struct in6_addr *) dest_pnt, label); +#endif /* HAVE_IPV6 */ + + return 0; +} + +/* Fetch interface information via ioctl(). */ +static void +interface_info_ioctl (struct interface *ifp) +{ + if_get_index (ifp); + if_get_flags (ifp); + if_get_mtu (ifp); + if_get_metric (ifp); +} + +/* Lookup all interface information. */ +void +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); +} + +struct connected * +if_lookup_linklocal (struct interface *ifp) +{ +#ifdef HAVE_IPV6 + struct listnode *node; + struct connected *ifc; + + if (ifp == NULL) + return NULL; + + for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) + { + if ((ifc->address->family == AF_INET6) && + (IN6_IS_ADDR_LINKLOCAL (&ifc->address->u.prefix6))) + return ifc; + } +#endif /* HAVE_IPV6 */ + + return NULL; +} diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c new file mode 100644 index 0000000..245b7b2 --- /dev/null +++ b/zebra/if_netlink.c @@ -0,0 +1,33 @@ +/* + * Interface looking up by netlink. + * Copyright (C) 1998 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/zserv.h" +#include "rt_netlink.h" + +/* Interface information read by netlink. */ +void +interface_list (struct zebra_vrf *zvrf) +{ + interface_lookup_netlink (zvrf); +} diff --git a/zebra/if_sysctl.c b/zebra/if_sysctl.c new file mode 100644 index 0000000..bb48f61 --- /dev/null +++ b/zebra/if_sysctl.c @@ -0,0 +1,158 @@ +/* + * Get interface's address and mask information by sysctl() function. + * 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 "if.h" +#include "sockunion.h" +#include "prefix.h" +#include "connected.h" +#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) +{ + caddr_t ref, buf, end; + size_t bufsiz; + struct if_msghdr *ifm; + struct interface *ifp; + +#define MIBSIZ 6 + int mib[MIBSIZ] = + { + CTL_NET, + PF_ROUTE, + 0, + 0, /* AF_INET & AF_INET6 */ + NET_RT_IFLIST, + 0 + }; + + /* Query buffer size. */ + if (sysctl (mib, MIBSIZ, NULL, &bufsiz, NULL, 0) < 0) + { + zlog_warn ("sysctl() error by %s", safe_strerror (errno)); + return; + } + + /* We free this memory at the end of this function. */ + ref = buf = XMALLOC (MTYPE_TMP, bufsiz); + + /* Fetch interface informations into allocated buffer. */ + if (sysctl (mib, MIBSIZ, buf, &bufsiz, NULL, 0) < 0) + { + zlog (NULL, LOG_WARNING, "sysctl error by %s", safe_strerror (errno)); + return; + } + + /* Parse both interfaces and addresses. */ + for (end = buf + bufsiz; buf < end; buf += ifm->ifm_msglen) + { + ifm = (struct if_msghdr *) buf; + if (ifm->ifm_type == RTM_IFINFO) + { + ifp = if_lookup_by_index (ifm->ifm_index); + if (ifp) + ifp->stats = ifm->ifm_data; + } + } + + /* Free sysctl buffer. */ + XFREE (MTYPE_TMP, ref); + + return; +} + +/* Interface listing up function using sysctl(). */ +void +interface_list (struct zebra_vrf *zvrf) +{ + caddr_t ref, buf, end; + size_t bufsiz; + struct if_msghdr *ifm; + +#define MIBSIZ 6 + int mib[MIBSIZ] = + { + CTL_NET, + PF_ROUTE, + 0, + 0, /* AF_INET & AF_INET6 */ + NET_RT_IFLIST, + 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) + { + zlog (NULL, LOG_WARNING, "sysctl() error by %s", safe_strerror (errno)); + return; + } + + /* We free this memory at the end of this function. */ + ref = buf = XMALLOC (MTYPE_TMP, bufsiz); + + /* Fetch interface informations into allocated buffer. */ + if (sysctl (mib, MIBSIZ, buf, &bufsiz, NULL, 0) < 0) + { + zlog (NULL, LOG_WARNING, "sysctl error by %s", safe_strerror (errno)); + return; + } + + /* Parse both interfaces and addresses. */ + for (end = buf + bufsiz; buf < end; buf += ifm->ifm_msglen) + { + ifm = (struct if_msghdr *) buf; + + switch (ifm->ifm_type) + { + case RTM_IFINFO: + ifm_read (ifm); + break; + case RTM_NEWADDR: + ifam_read ((struct ifa_msghdr *) ifm); + break; + default: + zlog_info ("interfaces_list(): unexpected message type"); + XFREE (MTYPE_TMP, ref); + return; + break; + } + } + + /* Free sysctl buffer. */ + XFREE (MTYPE_TMP, ref); +} diff --git a/zebra/interface.c b/zebra/interface.c new file mode 100644 index 0000000..f8b946f --- /dev/null +++ b/zebra/interface.c @@ -0,0 +1,2817 @@ +/* + * Interface function. + * Copyright (C) 1997, 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 "vty.h" +#include "sockunion.h" +#include "prefix.h" +#include "command.h" +#include "memory.h" +#include "ioctl.h" +#include "connected.h" +#include "log.h" +#include "zclient.h" +#include "vrf.h" +#include "command.h" + +#include "zebra/interface.h" +#include "zebra/rtadv.h" +#include "zebra/rib.h" +#include "zebra/zserv.h" +#include "zebra/redistribute.h" +#include "zebra/debug.h" +#include "zebra/irdp.h" + +#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 /* 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 +if_zebra_new_hook (struct interface *ifp) +{ + struct zebra_if *zebra_if; + + zebra_if = XCALLOC (MTYPE_TMP, sizeof (struct zebra_if)); + + zebra_if->multicast = IF_ZEBRA_MULTICAST_UNSPEC; + zebra_if->shutdown = IF_ZEBRA_SHUTDOWN_OFF; + + 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; + + rtadv = &zebra_if->rtadv; + + rtadv->AdvSendAdvertisements = 0; + rtadv->MaxRtrAdvInterval = RTADV_MAX_RTR_ADV_INTERVAL; + rtadv->MinRtrAdvInterval = RTADV_MIN_RTR_ADV_INTERVAL; + rtadv->AdvIntervalTimer = 0; + rtadv->AdvManagedFlag = 0; + rtadv->AdvOtherConfigFlag = 0; + rtadv->AdvHomeAgentFlag = 0; + rtadv->AdvLinkMTU = 0; + rtadv->AdvReachableTime = 0; + rtadv->AdvRetransTimer = 0; + rtadv->AdvCurHopLimit = 0; + rtadv->AdvDefaultLifetime = -1; /* derive from MaxRtrAdvInterval */ + rtadv->HomeAgentPreference = 0; + rtadv->HomeAgentLifetime = -1; /* derive from AdvDefaultLifetime */ + rtadv->AdvIntervalOption = 0; + rtadv->DefaultPreference = RTADV_PREF_MEDIUM; + + rtadv->AdvPrefixList = list_new (); + } +#endif /* HAVE_RTADV */ + + /* Initialize installed address chains tree. */ + zebra_if->ipv4_subnets = route_table_init (); + + ifp->info = zebra_if; + return 0; +} + +/* Called when interface is deleted. */ +static int +if_zebra_delete_hook (struct interface *ifp) +{ + struct zebra_if *zebra_if; + + if (ifp->info) + { + zebra_if = ifp->info; + + /* Free installed address chains tree. */ + if (zebra_if->ipv4_subnets) + route_table_finish (zebra_if->ipv4_subnets); + + XFREE (MTYPE_TMP, zebra_if); + } + + return 0; +} + +/* Tie an interface address to its derived subnet list of addresses. */ +int +if_subnet_add (struct interface *ifp, struct connected *ifc) +{ + struct route_node *rn; + struct zebra_if *zebra_if; + struct prefix cp; + struct list *addr_list; + + assert (ifp && ifp->info && ifc); + zebra_if = ifp->info; + + /* Get address derived subnet node and associated address list, while marking + address secondary attribute appropriately. */ + cp = *ifc->address; + apply_mask (&cp); + rn = route_node_get (zebra_if->ipv4_subnets, &cp); + + if ((addr_list = rn->info)) + SET_FLAG (ifc->flags, ZEBRA_IFA_SECONDARY); + else + { + UNSET_FLAG (ifc->flags, ZEBRA_IFA_SECONDARY); + rn->info = addr_list = list_new (); + route_lock_node (rn); + } + + /* Tie address at the tail of address list. */ + listnode_add (addr_list, ifc); + + /* Return list element count. */ + return (addr_list->count); +} + +/* Untie an interface address from its derived subnet list of addresses. */ +int +if_subnet_delete (struct interface *ifp, struct connected *ifc) +{ + struct route_node *rn; + struct zebra_if *zebra_if; + struct list *addr_list; + + assert (ifp && ifp->info && ifc); + zebra_if = ifp->info; + + /* Get address derived subnet node. */ + rn = route_node_lookup (zebra_if->ipv4_subnets, ifc->address); + if (! (rn && rn->info)) + { + 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); + + /* Return list element count, if not empty. */ + if (addr_list->count) + { + /* If deleted address is primary, mark subsequent one as such and distribute. */ + if (! CHECK_FLAG (ifc->flags, ZEBRA_IFA_SECONDARY)) + { + 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); + } + + return addr_list->count; + } + + /* Otherwise, free list and route node. */ + list_free (addr_list); + rn->info = NULL; + route_unlock_node (rn); + + return 0; +} + +/* if_flags_mangle: A place for hacks that require mangling + * or tweaking the interface flags. + * + * ******************** Solaris flags hacks ************************** + * + * Solaris IFF_UP flag reflects only the primary interface as the + * routing socket only sends IFINFO for the primary interface. Hence + * ~IFF_UP does not per se imply all the logical interfaces are also + * down - which we only know of as addresses. Instead we must determine + * whether the interface really is up or not according to how many + * addresses are still attached. (Solaris always sends RTM_DELADDR if + * an interface, logical or not, goes ~IFF_UP). + * + * Ie, we mangle IFF_UP to *additionally* reflect whether or not there + * are addresses left in struct connected, not just the actual underlying + * IFF_UP flag. + * + * We must hence remember the real state of IFF_UP, which we do in + * struct zebra_if.primary_state. + * + * Setting IFF_UP within zebra to administratively shutdown the + * interface will affect only the primary interface/address on Solaris. + ************************End Solaris flags hacks *********************** + */ +static void +if_flags_mangle (struct interface *ifp, uint64_t *newflags) +{ +#ifdef SUNOS_5 + struct zebra_if *zif = ifp->info; + + zif->primary_state = *newflags & (IFF_UP & 0xff); + + if (CHECK_FLAG (zif->primary_state, IFF_UP) + || listcount(ifp->connected) > 0) + SET_FLAG (*newflags, IFF_UP); + else + UNSET_FLAG (*newflags, IFF_UP); +#endif /* SUNOS_5 */ +} + +/* Update the flags field of the ifp with the new flag set provided. + * Take whatever actions are required for any changes in flags we care + * about. + * + * newflags should be the raw value, as obtained from the OS. + */ +void +if_flags_update (struct interface *ifp, uint64_t newflags) +{ + if_flags_mangle (ifp, &newflags); + + if (if_is_operative (ifp)) + { + /* operative -> inoperative? */ + ifp->flags = newflags; + if (!if_is_operative (ifp)) + if_down (ifp); + } + else + { + /* inoperative -> operative? */ + ifp->flags = newflags; + if (if_is_operative (ifp)) + if_up (ifp); + } +} + +/* Wake up configured address if it is not in current kernel + address. */ +static void +if_addr_wakeup (struct interface *ifp) +{ + struct listnode *node, *nnode; + struct connected *ifc; + struct prefix *p; + int ret; + + for (ALL_LIST_ELEMENTS (ifp->connected, node, nnode, ifc)) + { + p = ifc->address; + + if (CHECK_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED) + && ! CHECK_FLAG (ifc->conf, ZEBRA_IFC_QUEUED)) + { + /* Address check. */ + if (p->family == AF_INET) + { + if (! if_is_up (ifp)) + { + /* 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); + } + + ret = if_set_prefix (ifp, ifc); + if (ret < 0) + { + zlog_warn ("Can't set interface's address: %s", + safe_strerror(errno)); + continue; + } + + 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)) + { + /* See long comment above */ + if_set_flags (ifp, IFF_UP | IFF_RUNNING); + if_refresh (ifp); + } + + ret = if_prefix_add_ipv6 (ifp, ifc); + if (ret < 0) + { + zlog_warn ("Can't set interface's address: %s", + safe_strerror(errno)); + continue; + } + + 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) +{ + 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) + if_unset_flags (ifp, IFF_MULTICAST); + + zebra_interface_add_update (ifp); + + if (! CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE)) + { + 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 vrf %u index %d becomes active.", + ifp->name, ifp->vrf_id, ifp->ifindex); + } + else + { + if (IS_ZEBRA_DEBUG_KERNEL) + 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); + } +} + +/* Handle an interface delete event */ +void +if_delete_update (struct interface *ifp) +{ + struct connected *ifc; + struct prefix *p; + struct route_node *rn; + struct zebra_if *zebra_if; + + zebra_if = ifp->info; + + if (if_is_up(ifp)) + { + zlog_err ("interface %s vrf %u index %d is still up while being deleted.", + ifp->name, ifp->vrf_id, ifp->ifindex); + return; + } + + /* Mark interface as inactive */ + UNSET_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE); + + if (IS_ZEBRA_DEBUG_KERNEL) + 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) + { + struct listnode *node; + struct listnode *last = NULL; + + while ((node = (last ? last->next : listhead (ifp->connected)))) + { + ifc = listgetdata (node); + p = ifc->address; + + if (p->family == AF_INET + && (rn = route_node_lookup (zebra_if->ipv4_subnets, p))) + { + struct listnode *anode; + struct listnode *next; + struct listnode *first; + struct list *addr_list; + + route_unlock_node (rn); + addr_list = (struct list *) rn->info; + + /* Remove addresses, secondaries first. */ + first = listhead (addr_list); + for (anode = first->next; anode || first; anode = next) + { + if (!anode) + { + anode = first; + first = NULL; + } + next = anode->next; + + 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); + route_unlock_node (rn); + + /* Remove from interface address list (unconditionally). */ + if (!CHECK_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED)) + { + listnode_delete (ifp->connected, ifc); + connected_free (ifc); + } + else + last = node; + } + + /* Free chain list and respective route node. */ + list_delete (addr_list); + rn->info = NULL; + route_unlock_node (rn); + } +#ifdef HAVE_IPV6 + else if (p->family == AF_INET6) + { + connected_down_ipv6 (ifp, ifc); + + 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; + else + { + listnode_delete (ifp->connected, ifc); + connected_free (ifc); + } + } +#endif /* HAVE_IPV6 */ + else + { + last = node; + } + } + } + zebra_interface_delete_update (ifp); + + /* Update ifindex after distributing the delete message. This is in + case any client needs to have the old value of ifindex available + while processing the deletion. Each client daemon is responsible + for setting ifindex to IFINDEX_INTERNAL after processing the + interface deletion message. */ + ifp->ifindex = IFINDEX_INTERNAL; +} + +/* Interface is up. */ +void +if_up (struct interface *ifp) +{ + struct listnode *node; + struct listnode *next; + struct connected *ifc; + struct prefix *p; + + if_count_up(ifp->info); + + /* Notify the protocol daemons. */ + zebra_interface_up_update (ifp); + + /* Install connected routes to the kernel. */ + if (ifp->connected) + { + for (ALL_LIST_ELEMENTS (ifp->connected, node, next, ifc)) + { + p = ifc->address; + + if (p->family == AF_INET) + connected_up_ipv4 (ifp, ifc); +#ifdef HAVE_IPV6 + else if (p->family == AF_INET6) + connected_up_ipv6 (ifp, ifc); +#endif /* HAVE_IPV6 */ + } + } + + /* Examine all static routes. */ + rib_update (ifp->vrf_id); +} + +/* Interface goes down. We have to manage different behavior of based + OS. */ +void +if_down (struct interface *ifp) +{ + struct listnode *node; + 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); + + /* Delete connected routes from the kernel. */ + if (ifp->connected) + { + for (ALL_LIST_ELEMENTS (ifp->connected, node, next, ifc)) + { + p = ifc->address; + + if (p->family == AF_INET) + connected_down_ipv4 (ifp, ifc); +#ifdef HAVE_IPV6 + else if (p->family == AF_INET6) + connected_down_ipv6 (ifp, ifc); +#endif /* HAVE_IPV6 */ + } + } + + /* Examine all static routes which direct to the interface. */ + rib_update (ifp->vrf_id); +} + +void +if_refresh (struct interface *ifp) +{ + if_get_flags (ifp); +} + +/* Output prefix string to vty. */ +static int +prefix_vty_out (struct vty *vty, struct prefix *p) +{ + char str[INET6_ADDRSTRLEN]; + + inet_ntop (p->family, &p->u.prefix, str, sizeof (str)); + vty_out (vty, "%s", str); + return strlen (str); +} + +/* Dump if address information to vty. */ +static void +connected_dump_vty (struct vty *vty, struct connected *connected) +{ + struct prefix *p; + + /* Print interface address. */ + p = connected->address; + vty_out (vty, " %s ", prefix_family_str (p)); + prefix_vty_out (vty, p); + vty_out (vty, "/%d", p->prefixlen); + + /* If there is destination address, print it. */ + if (connected->destination) + { + vty_out (vty, (CONNECTED_PEER(connected) ? " peer " : " broadcast ")); + prefix_vty_out (vty, connected->destination); + } + + 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); +} + +#if defined (HAVE_RTADV) +/* Dump interface ND information to vty. */ +static void +nd_dump_vty (struct vty *vty, struct interface *ifp) +{ + struct zebra_if *zif; + struct rtadvconf *rtadv; + int interval; + + zif = (struct zebra_if *) ifp->info; + rtadv = &zif->rtadv; + + if (rtadv->AdvSendAdvertisements) + { + vty_out (vty, " ND advertised reachable time is %d milliseconds%s", + rtadv->AdvReachableTime, VTY_NEWLINE); + vty_out (vty, " ND advertised retransmit interval is %d milliseconds%s", + rtadv->AdvRetransTimer, VTY_NEWLINE); + interval = rtadv->MaxRtrAdvInterval; + if (interval % 1000) + vty_out (vty, " ND router advertisements are sent every " + "%d milliseconds%s", interval, + VTY_NEWLINE); + else + vty_out (vty, " ND router advertisements are sent every " + "%d seconds%s", interval / 1000, + VTY_NEWLINE); + if (rtadv->AdvDefaultLifetime != -1) + vty_out (vty, " ND router advertisements live for %d seconds%s", + rtadv->AdvDefaultLifetime, VTY_NEWLINE); + else + vty_out (vty, " ND router advertisements lifetime tracks ra-interval%s", + VTY_NEWLINE); + vty_out (vty, " ND router advertisement default router preference is " + "%s%s", rtadv_pref_strs[rtadv->DefaultPreference], + VTY_NEWLINE); + if (rtadv->AdvManagedFlag) + vty_out (vty, " Hosts use DHCP to obtain routable addresses.%s", + VTY_NEWLINE); + else + vty_out (vty, " Hosts use stateless autoconfig for addresses.%s", + VTY_NEWLINE); + if (rtadv->AdvHomeAgentFlag) + { + vty_out (vty, " ND router advertisements with " + "Home Agent flag bit set.%s", + VTY_NEWLINE); + if (rtadv->HomeAgentLifetime != -1) + vty_out (vty, " Home Agent lifetime is %u seconds%s", + rtadv->HomeAgentLifetime, VTY_NEWLINE); + else + vty_out (vty, " Home Agent lifetime tracks ra-lifetime%s", + VTY_NEWLINE); + vty_out (vty, " Home Agent preference is %u%s", + rtadv->HomeAgentPreference, VTY_NEWLINE); + } + if (rtadv->AdvIntervalOption) + vty_out (vty, " ND router advertisements with Adv. Interval option.%s", + VTY_NEWLINE); + } +} +#endif /* HAVE_RTADV */ + +/* Interface's information print out to vty interface. */ +static void +if_dump_vty (struct vty *vty, struct interface *ifp) +{ + struct connected *connected; + struct listnode *node; + struct route_node *rn; + struct zebra_if *zebra_if; + + zebra_if = ifp->info; + + vty_out (vty, "Interface %s is ", ifp->name); + if (if_is_up(ifp)) { + vty_out (vty, "up, line protocol "); + + if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION)) { + if (if_is_running(ifp)) + vty_out (vty, "is up%s", VTY_NEWLINE); + else + vty_out (vty, "is down%s", VTY_NEWLINE); + } else { + vty_out (vty, "detection is disabled%s", VTY_NEWLINE); + } + } else { + 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); + if (ifp->ifindex == IFINDEX_INTERNAL) + { + vty_out(vty, " pseudo interface%s", VTY_NEWLINE); + return; + } + else if (! CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE)) + { + vty_out(vty, " index %d inactive interface%s", + ifp->ifindex, + VTY_NEWLINE); + return; + } + + vty_out (vty, " index %d metric %d mtu %d ", + ifp->ifindex, ifp->metric, ifp->mtu); +#ifdef HAVE_IPV6 + if (ifp->mtu6 != ifp->mtu) + vty_out (vty, "mtu6 %d ", ifp->mtu6); +#endif + vty_out (vty, "%s flags: %s%s", VTY_NEWLINE, + if_flag_dump (ifp->flags), VTY_NEWLINE); + + /* Hardware address. */ + vty_out (vty, " Type: %s%s", if_link_type_str (ifp->ll_type), VTY_NEWLINE); + if (ifp->hw_addr_len != 0) + { + int i; + + vty_out (vty, " HWaddr: "); + for (i = 0; i < ifp->hw_addr_len; i++) + vty_out (vty, "%s%02x", i == 0 ? "" : ":", ifp->hw_addr[i]); + vty_out (vty, "%s", VTY_NEWLINE); + } + + /* Bandwidth in kbps */ + if (ifp->bandwidth != 0) + { + vty_out(vty, " bandwidth %u kbps", ifp->bandwidth); + vty_out(vty, "%s", VTY_NEWLINE); + } + + for (rn = route_top (zebra_if->ipv4_subnets); rn; rn = route_next (rn)) + { + if (! rn->info) + continue; + + for (ALL_LIST_ELEMENTS_RO ((struct list *)rn->info, node, connected)) + connected_dump_vty (vty, connected); + } + + for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, connected)) + { + if (CHECK_FLAG (connected->conf, ZEBRA_IFC_REAL) && + (connected->address->family == AF_INET6)) + connected_dump_vty (vty, connected); + } + + 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 /* HAVE_RTADV */ + +#ifdef HAVE_PROC_NET_DEV + /* Statistics print out using proc file system. */ + vty_out (vty, " %lu input packets (%lu multicast), %lu bytes, " + "%lu dropped%s", + ifp->stats.rx_packets, ifp->stats.rx_multicast, + ifp->stats.rx_bytes, ifp->stats.rx_dropped, VTY_NEWLINE); + + vty_out (vty, " %lu input errors, %lu length, %lu overrun," + " %lu CRC, %lu frame%s", + ifp->stats.rx_errors, ifp->stats.rx_length_errors, + ifp->stats.rx_over_errors, ifp->stats.rx_crc_errors, + ifp->stats.rx_frame_errors, VTY_NEWLINE); + + vty_out (vty, " %lu fifo, %lu missed%s", ifp->stats.rx_fifo_errors, + ifp->stats.rx_missed_errors, VTY_NEWLINE); + + vty_out (vty, " %lu output packets, %lu bytes, %lu dropped%s", + ifp->stats.tx_packets, ifp->stats.tx_bytes, + ifp->stats.tx_dropped, VTY_NEWLINE); + + vty_out (vty, " %lu output errors, %lu aborted, %lu carrier," + " %lu fifo, %lu heartbeat%s", + ifp->stats.tx_errors, ifp->stats.tx_aborted_errors, + ifp->stats.tx_carrier_errors, ifp->stats.tx_fifo_errors, + ifp->stats.tx_heartbeat_errors, VTY_NEWLINE); + + vty_out (vty, " %lu window, %lu collisions%s", + ifp->stats.tx_window_errors, ifp->stats.collisions, VTY_NEWLINE); +#endif /* HAVE_PROC_NET_DEV */ + +#ifdef HAVE_NET_RT_IFLIST +#if defined (__bsdi__) || defined (__NetBSD__) + /* Statistics print out using sysctl (). */ + 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," + " multicast packets %lu%s", + ifp->stats.ifi_ipackets, ifp->stats.ifi_ibytes, + ifp->stats.ifi_iqdrops, ifp->stats.ifi_imcasts, + VTY_NEWLINE); + + vty_out (vty, " input errors %lu%s", + ifp->stats.ifi_ierrors, VTY_NEWLINE); + + vty_out (vty, " output packets %lu, bytes %lu, multicast packets %lu%s", + ifp->stats.ifi_opackets, ifp->stats.ifi_obytes, + ifp->stats.ifi_omcasts, VTY_NEWLINE); + + vty_out (vty, " output errors %lu%s", + ifp->stats.ifi_oerrors, VTY_NEWLINE); + + vty_out (vty, " collisions %lu%s", + ifp->stats.ifi_collisions, VTY_NEWLINE); +#endif /* __bsdi__ || __NetBSD__ */ +#endif /* HAVE_NET_RT_IFLIST */ +} + +/* Wrapper hook point for zebra daemon so that ifindex can be set + * DEFUN macro not used as extract.pl HAS to ignore this + * See also interface_cmd in lib/if.c + */ +DEFUN_NOSH (zebra_interface, + zebra_interface_cmd, + "interface IFNAME", + "Select an interface to configure\n" + "Interface's name\n") +{ + int ret; + struct interface *ifp; + + /* Call lib interface() */ + if ((ret = interface_cmd.func (self, vty, argc, argv)) != CMD_SUCCESS) + return ret; + + ifp = vty->index; + + if (ifp->ifindex == IFINDEX_INTERNAL) + /* Is this really necessary? Shouldn't status be initialized to 0 + in that case? */ + UNSET_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE); + + 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, + "%s(config-if)# ", + 1 +}; + +/* Show all interfaces to vty. */ +DEFUN (show_interface, show_interface_cmd, + "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" + 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. */ + ifstat_update_proc (); +#endif /* HAVE_PROC_NET_DEV */ +#ifdef HAVE_NET_RT_IFLIST + ifstat_update_sysctl (); +#endif /* HAVE_NET_RT_IFLIST */ + + if (argc > 1) + VTY_GET_INTEGER ("VRF ID", vrf_id, argv[1]); + + /* Specified interface print. */ + ifp = if_lookup_by_name_vrf (argv[0], vrf_id); + 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; +} + +ALIAS (show_interface_name, + show_interface_name_vrf_cmd, + "show interface IFNAME " VRF_CMD_STR, + SHOW_STR + "Interface status and configuration\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 (vrf_iflist (vrf_id), node, ifp)) + { + int len; + + len = vty_out (vty, "%s", ifp->name); + vty_out (vty, "%*s", (16 - len), " "); + + if (if_is_up(ifp)) + { + vty_out (vty, "up "); + if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION)) + { + if (if_is_running(ifp)) + vty_out (vty, "up "); + else + vty_out (vty, "down "); + } + else + { + vty_out (vty, "unknown "); + } + } + else + { + vty_out (vty, "down down "); + } + + if (ifp->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; +} + +DEFUN (multicast, + multicast_cmd, + "multicast", + "Set multicast flag to interface\n") +{ + int ret; + struct interface *ifp; + struct zebra_if *if_data; + + ifp = (struct interface *) vty->index; + if (CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE)) + { + ret = if_set_flags (ifp, IFF_MULTICAST); + if (ret < 0) + { + vty_out (vty, "Can't set multicast flag%s", VTY_NEWLINE); + return CMD_WARNING; + } + if_refresh (ifp); + } + if_data = ifp->info; + if_data->multicast = IF_ZEBRA_MULTICAST_ON; + + return CMD_SUCCESS; +} + +DEFUN (no_multicast, + no_multicast_cmd, + "no multicast", + NO_STR + "Unset multicast flag to interface\n") +{ + int ret; + struct interface *ifp; + struct zebra_if *if_data; + + ifp = (struct interface *) vty->index; + if (CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE)) + { + ret = if_unset_flags (ifp, IFF_MULTICAST); + if (ret < 0) + { + vty_out (vty, "Can't unset multicast flag%s", VTY_NEWLINE); + return CMD_WARNING; + } + if_refresh (ifp); + } + if_data = ifp->info; + if_data->multicast = IF_ZEBRA_MULTICAST_OFF; + + 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 [default]", + "Enable link detection on interface\n" + "Leave link-detect to the default\n") +{ + struct interface *ifp; + struct zebra_if *zif; + + ifp = (struct interface *) vty->index; + 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! */ + + return CMD_SUCCESS; +} + + +DEFUN (no_linkdetect, + no_linkdetect_cmd, + "no link-detect", + NO_STR + "Disable link detection on interface\n") +{ + struct interface *ifp; + struct zebra_if *zif; + + ifp = (struct interface *) vty->index; + zif = ifp->info; + assert (zif != NULL); + + zif->linkdetect = IF_LINKDETECT_OFF; + if_zebra_linkdetect_set (ifp); + + /* FIXME: see linkdetect_cmd */ + + return CMD_SUCCESS; +} + +DEFUN (shutdown_if, + shutdown_if_cmd, + "shutdown", + "Shutdown the selected interface\n") +{ + int ret; + struct interface *ifp; + struct zebra_if *if_data; + + ifp = (struct interface *) vty->index; + if (ifp->ifindex != IFINDEX_INTERNAL) + { + 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_data = ifp->info; + if_data->shutdown = IF_ZEBRA_SHUTDOWN_ON; + + return CMD_SUCCESS; +} + +DEFUN (no_shutdown_if, + no_shutdown_if_cmd, + "no shutdown", + NO_STR + "Shutdown the selected interface\n") +{ + int ret; + struct interface *ifp; + struct zebra_if *if_data; + + ifp = (struct interface *) vty->index; + + if (ifp->ifindex != IFINDEX_INTERNAL) + { + 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_data = ifp->info; + if_data->shutdown = IF_ZEBRA_SHUTDOWN_OFF; + + return CMD_SUCCESS; +} + +DEFUN (bandwidth_if, + bandwidth_if_cmd, + "bandwidth <1-10000000>", + "Set bandwidth informational parameter\n" + "Bandwidth in kilobits\n") +{ + struct interface *ifp; + unsigned int bandwidth; + + ifp = (struct interface *) vty->index; + bandwidth = strtol(argv[0], NULL, 10); + + /* bandwidth range is <1-10000000> */ + if (bandwidth < 1 || bandwidth > 10000000) + { + vty_out (vty, "Bandwidth is invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ifp->bandwidth = bandwidth; + + /* force protocols to recalculate routes due to cost change */ + if (if_is_operative (ifp)) + zebra_interface_up_update (ifp); + + return CMD_SUCCESS; +} + +DEFUN (no_bandwidth_if, + no_bandwidth_if_cmd, + "no bandwidth", + NO_STR + "Set bandwidth informational parameter\n") +{ + struct interface *ifp; + + ifp = (struct interface *) vty->index; + + ifp->bandwidth = 0; + + /* force protocols to recalculate routes due to cost change */ + if (if_is_operative (ifp)) + zebra_interface_up_update (ifp); + + return CMD_SUCCESS; +} + +ALIAS (no_bandwidth_if, + no_bandwidth_if_val_cmd, + "no bandwidth <1-10000000>", + NO_STR + "Set bandwidth informational parameter\n" + "Bandwidth in kilobits\n") + +struct cmd_node link_params_node = +{ + 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) + { + vty_out (vty, "%% Malformed address %s", VTY_NEWLINE); + return CMD_WARNING; + } + + ifc = connected_check (ifp, (struct prefix *) &cp); + if (! ifc) + { + ifc = connected_new (); + ifc->ifp = ifp; + + /* Address. */ + p = prefix_ipv4_new (); + *p = cp; + ifc->address = (struct prefix *) p; + + /* Broadcast. */ + if (p->prefixlen <= IPV4_MAX_PREFIXLEN-2) + { + p = prefix_ipv4_new (); + *p = cp; + p->prefix.s_addr = ipv4_broadcast_addr(p->prefix.s_addr,p->prefixlen); + ifc->destination = (struct prefix *) p; + } + + /* Label. */ + if (label) + ifc->label = XSTRDUP (MTYPE_CONNECTED_LABEL, label); + + /* Add to linked list. */ + listnode_add (ifp->connected, ifc); + } + + /* This address is configured from zebra. */ + if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED)) + SET_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED); + + /* In case of this route need to install kernel. */ + 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)) + { + if_set_flags (ifp, IFF_UP | IFF_RUNNING); + if_refresh (ifp); + } + + ret = if_set_prefix (ifp, ifc); + if (ret < 0) + { + vty_out (vty, "%% Can't set interface IP address: %s.%s", + safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + 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; +} + +static int +ip_address_uninstall (struct vty *vty, struct interface *ifp, + const char *addr_str, const char *peer_str, + const char *label) +{ + struct prefix_ipv4 cp; + struct connected *ifc; + int ret; + + /* Convert to prefix structure. */ + ret = str2prefix_ipv4 (addr_str, &cp); + if (ret <= 0) + { + vty_out (vty, "%% Malformed address %s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Check current interface address. */ + ifc = connected_check (ifp, (struct prefix *) &cp); + if (! ifc) + { + vty_out (vty, "%% Can't find address%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* This is not configured address. */ + 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_QUEUED) + || ! CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE)) + { + listnode_delete (ifp->connected, ifc); + connected_free (ifc); + return CMD_WARNING; + } + + /* This is real route. */ + ret = if_unset_prefix (ifp, ifc); + if (ret < 0) + { + vty_out (vty, "%% Can't unset interface IP address: %s.%s", + safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + 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; +} + +DEFUN (ip_address, + ip_address_cmd, + "ip address A.B.C.D/M", + "Interface Internet Protocol config commands\n" + "Set the IP address of an interface\n" + "IP address (e.g. 10.0.0.1/8)\n") +{ + return ip_address_install (vty, vty->index, argv[0], NULL, NULL); +} + +DEFUN (no_ip_address, + no_ip_address_cmd, + "no ip address A.B.C.D/M", + NO_STR + "Interface Internet Protocol config commands\n" + "Set the IP address of an interface\n" + "IP Address (e.g. 10.0.0.1/8)") +{ + return ip_address_uninstall (vty, vty->index, argv[0], NULL, NULL); +} + +#ifdef HAVE_NETLINK +DEFUN (ip_address_label, + ip_address_label_cmd, + "ip address A.B.C.D/M label LINE", + "Interface Internet Protocol config commands\n" + "Set the IP address of an interface\n" + "IP address (e.g. 10.0.0.1/8)\n" + "Label of this address\n" + "Label\n") +{ + return ip_address_install (vty, vty->index, argv[0], NULL, argv[1]); +} + +DEFUN (no_ip_address_label, + no_ip_address_label_cmd, + "no ip address A.B.C.D/M label LINE", + NO_STR + "Interface Internet Protocol config commands\n" + "Set the IP address of an interface\n" + "IP address (e.g. 10.0.0.1/8)\n" + "Label of this address\n" + "Label\n") +{ + return ip_address_uninstall (vty, vty->index, argv[0], NULL, argv[1]); +} +#endif /* HAVE_NETLINK */ + +#ifdef HAVE_IPV6 +static int +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) + { + vty_out (vty, "%% Malformed address %s", VTY_NEWLINE); + return CMD_WARNING; + } + + ifc = connected_check (ifp, (struct prefix *) &cp); + if (! ifc) + { + ifc = connected_new (); + ifc->ifp = ifp; + + /* Address. */ + p = prefix_ipv6_new (); + *p = cp; + ifc->address = (struct prefix *) p; + + /* Secondary. */ + if (secondary) + SET_FLAG (ifc->flags, ZEBRA_IFA_SECONDARY); + + /* Label. */ + if (label) + ifc->label = XSTRDUP (MTYPE_CONNECTED_LABEL, label); + + /* Add to linked list. */ + listnode_add (ifp->connected, ifc); + } + + /* This address is configured from zebra. */ + if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED)) + SET_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED); + + /* In case of this route need to install kernel. */ + 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)) + { + if_set_flags (ifp, IFF_UP | IFF_RUNNING); + if_refresh (ifp); + } + + ret = if_prefix_add_ipv6 (ifp, ifc); + + if (ret < 0) + { + vty_out (vty, "%% Can't set interface IP address: %s.%s", + safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + 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; +} + +static int +ipv6_address_uninstall (struct vty *vty, struct interface *ifp, + const char *addr_str, const char *peer_str, + const char *label, int secondry) +{ + struct prefix_ipv6 cp; + struct connected *ifc; + int ret; + + /* Convert to prefix structure. */ + ret = str2prefix_ipv6 (addr_str, &cp); + if (ret <= 0) + { + vty_out (vty, "%% Malformed address %s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Check current interface address. */ + ifc = connected_check (ifp, (struct prefix *) &cp); + if (! ifc) + { + vty_out (vty, "%% Can't find address%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* This is not configured address. */ + 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_QUEUED) + || ! CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE)) + { + listnode_delete (ifp->connected, ifc); + connected_free (ifc); + return CMD_WARNING; + } + + /* This is real route. */ + ret = if_prefix_delete_ipv6 (ifp, ifc); + if (ret < 0) + { + vty_out (vty, "%% Can't unset interface IP address: %s.%s", + safe_strerror(errno), VTY_NEWLINE); + return CMD_WARNING; + } + + UNSET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED); + /* This information will be propagated to the zclients when the + * kernel notification is received. */ + return CMD_SUCCESS; +} + +DEFUN (ipv6_address, + ipv6_address_cmd, + "ipv6 address X:X::X:X/M", + "Interface IPv6 config commands\n" + "Set the IP address of an interface\n" + "IPv6 address (e.g. 3ffe:506::1/48)\n") +{ + return ipv6_address_install (vty, vty->index, argv[0], NULL, NULL, 0); +} + +DEFUN (no_ipv6_address, + no_ipv6_address_cmd, + "no ipv6 address X:X::X:X/M", + NO_STR + "Interface IPv6 config commands\n" + "Set the IP address of an interface\n" + "IPv6 address (e.g. 3ffe:506::1/48)\n") +{ + return ipv6_address_uninstall (vty, vty->index, argv[0], NULL, NULL, 0); +} +#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 (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; + struct connected *ifc; + struct prefix *p; + + if_data = ifp->info; + + 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, + VTY_NEWLINE); + + /* Assign bandwidth here to avoid unnecessary interface flap + while processing config script */ + if (ifp->bandwidth != 0) + vty_out(vty, " bandwidth %u%s", ifp->bandwidth, 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", + p->family == AF_INET ? "" : "v6", + prefix2str (p, buf, sizeof(buf))); + + if (ifc->label) + vty_out (vty, " label %s", ifc->label); + + vty_out (vty, "%s", VTY_NEWLINE); + } + } + + if (if_data) + { + if (if_data->multicast != IF_ZEBRA_MULTICAST_UNSPEC) + vty_out (vty, " %smulticast%s", + if_data->multicast == IF_ZEBRA_MULTICAST_ON ? "" : "no ", + VTY_NEWLINE); + } + +#if defined (HAVE_RTADV) + rtadv_config_write (vty, ifp); +#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_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 (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); + install_element (INTERFACE_NODE, &multicast_cmd); + install_element (INTERFACE_NODE, &no_multicast_cmd); + install_element (INTERFACE_NODE, &linkdetect_cmd); + install_element (INTERFACE_NODE, &no_linkdetect_cmd); + install_element (INTERFACE_NODE, &shutdown_if_cmd); + install_element (INTERFACE_NODE, &no_shutdown_if_cmd); + install_element (INTERFACE_NODE, &bandwidth_if_cmd); + install_element (INTERFACE_NODE, &no_bandwidth_if_cmd); + install_element (INTERFACE_NODE, &no_bandwidth_if_val_cmd); + install_element (INTERFACE_NODE, &ip_address_cmd); + install_element (INTERFACE_NODE, &no_ip_address_cmd); +#ifdef HAVE_IPV6 + install_element (INTERFACE_NODE, &ipv6_address_cmd); + install_element (INTERFACE_NODE, &no_ipv6_address_cmd); +#endif /* HAVE_IPV6 */ +#ifdef HAVE_NETLINK + 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 new file mode 100644 index 0000000..223caf8 --- /dev/null +++ b/zebra/interface.h @@ -0,0 +1,264 @@ +/* Interface function 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. + */ + +#ifndef _ZEBRA_INTERFACE_H +#define _ZEBRA_INTERFACE_H + +#include "redistribute.h" +#include "event_counter.h" + +#ifdef HAVE_IRDP +#include "zebra/irdp.h" +#endif + +/* For interface multicast configuration. */ +#define IF_ZEBRA_MULTICAST_UNSPEC 0 +#define IF_ZEBRA_MULTICAST_ON 1 +#define IF_ZEBRA_MULTICAST_OFF 2 + +/* For interface shutdown configuration. */ +#define IF_ZEBRA_SHUTDOWN_OFF 0 +#define IF_ZEBRA_SHUTDOWN_ON 1 + +/* 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; +}; + +#if defined (HAVE_RTADV) +/* Router advertisement parameter. From RFC4861, RFC6275 and RFC4191. */ +struct rtadvconf +{ + /* A flag indicating whether or not the router sends periodic Router + Advertisements and responds to Router Solicitations. + Default: FALSE */ + int AdvSendAdvertisements; + + /* The maximum time allowed between sending unsolicited multicast + Router Advertisements from the interface, in milliseconds. + MUST be no less than 70 ms [RFC6275 7.5] and no greater + than 1800000 ms [RFC4861 6.2.1]. + + Default: 600000 milliseconds */ + int MaxRtrAdvInterval; +#define RTADV_MAX_RTR_ADV_INTERVAL 600000 + + /* The minimum time allowed between sending unsolicited multicast + Router Advertisements from the interface, in milliseconds. + MUST be no less than 30 ms [RFC6275 7.5]. + MUST be no greater than .75 * MaxRtrAdvInterval. + + Default: 0.33 * MaxRtrAdvInterval */ + int MinRtrAdvInterval; /* This field is currently unused. */ +#define RTADV_MIN_RTR_ADV_INTERVAL (0.33 * RTADV_MAX_RTR_ADV_INTERVAL) + + /* Unsolicited Router Advertisements' interval timer. */ + int AdvIntervalTimer; + + /* The TRUE/FALSE value to be placed in the "Managed address + configuration" flag field in the Router Advertisement. See + [ADDRCONF]. + + Default: FALSE */ + int AdvManagedFlag; + + + /* The TRUE/FALSE value to be placed in the "Other stateful + configuration" flag field in the Router Advertisement. See + [ADDRCONF]. + + Default: FALSE */ + int AdvOtherConfigFlag; + + /* The value to be placed in MTU options sent by the router. A + value of zero indicates that no MTU options are sent. + + Default: 0 */ + int AdvLinkMTU; + + + /* The value to be placed in the Reachable Time field in the Router + Advertisement messages sent by the router. The value zero means + unspecified (by this router). MUST be no greater than 3,600,000 + milliseconds (1 hour). + + Default: 0 */ + u_int32_t AdvReachableTime; +#define RTADV_MAX_REACHABLE_TIME 3600000 + + + /* The value to be placed in the Retrans Timer field in the Router + Advertisement messages sent by the router. The value zero means + unspecified (by this router). + + Default: 0 */ + int AdvRetransTimer; + + /* The default value to be placed in the Cur Hop Limit field in the + Router Advertisement messages sent by the router. The value + should be set to that current diameter of the Internet. The + value zero means unspecified (by this router). + + Default: The value specified in the "Assigned Numbers" RFC + [ASSIGNED] that was in effect at the time of implementation. */ + int AdvCurHopLimit; + + /* The value to be placed in the Router Lifetime field of Router + Advertisements sent from the interface, in seconds. MUST be + either zero or between MaxRtrAdvInterval and 9000 seconds. A + value of zero indicates that the router is not to be used as a + default router. + + Default: 3 * MaxRtrAdvInterval */ + int AdvDefaultLifetime; +#define RTADV_MAX_RTRLIFETIME 9000 /* 2.5 hours */ + + /* A list of prefixes to be placed in Prefix Information options in + Router Advertisement messages sent from the interface. + + Default: all prefixes that the router advertises via routing + protocols as being on-link for the interface from which the + advertisement is sent. The link-local prefix SHOULD NOT be + included in the list of advertised prefixes. */ + struct list *AdvPrefixList; + + /* The TRUE/FALSE value to be placed in the "Home agent" + flag field in the Router Advertisement. See [RFC6275 7.1]. + + Default: FALSE */ + int AdvHomeAgentFlag; +#ifndef ND_RA_FLAG_HOME_AGENT +#define ND_RA_FLAG_HOME_AGENT 0x20 +#endif + + /* The value to be placed in Home Agent Information option if Home + Flag is set. + Default: 0 */ + int HomeAgentPreference; + + /* The value to be placed in Home Agent Information option if Home + Flag is set. Lifetime (seconds) MUST not be greater than 18.2 + hours. + The value 0 has special meaning: use of AdvDefaultLifetime value. + + Default: 0 */ + int HomeAgentLifetime; +#define RTADV_MAX_HALIFETIME 65520 /* 18.2 hours */ + + /* The TRUE/FALSE value to insert or not an Advertisement Interval + option. See [RFC 6275 7.3] + + Default: FALSE */ + int AdvIntervalOption; + + /* The value to be placed in the Default Router Preference field of + a router advertisement. See [RFC 4191 2.1 & 2.2] + + Default: 0 (medium) */ + int DefaultPreference; +#define RTADV_PREF_MEDIUM 0x0 /* Per RFC4191. */ +}; + +#endif /* HAVE_RTADV */ + +/* `zebra' daemon local interface structure. */ +struct zebra_if +{ + /* Shutdown configuration. */ + u_char shutdown; + + /* Multicast configuration. */ + u_char multicast; + + /* 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; + + /* Information about up/down changes */ + struct event_counter up_events; + struct event_counter down_events; + +#if defined(HAVE_RTADV) + struct rtadvconf rtadv; +#endif /* RTADV */ + +#ifdef HAVE_IRDP + 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 + * down (but primary still plumbed) and primary having gone + * ~IFF_UP, and all addresses gone. + */ + u_char primary_state; +#endif /* SUNOS_5 */ +}; + +extern void if_delete_update (struct interface *ifp); +extern void if_add_update (struct interface *ifp); +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 *); + +#ifdef HAVE_PROC_NET_DEV +extern void ifstat_update_proc (void); +#endif /* HAVE_PROC_NET_DEV */ +#ifdef HAVE_NET_RT_IFLIST +extern void ifstat_update_sysctl (void); + +#endif /* HAVE_NET_RT_IFLIST */ +#ifdef HAVE_PROC_NET_DEV +extern int interface_list_proc (void); +#endif /* HAVE_PROC_NET_DEV */ +#ifdef HAVE_PROC_NET_IF_INET6 +extern int ifaddr_proc_ipv6 (void); +#endif /* HAVE_PROC_NET_IF_INET6 */ + +#endif /* _ZEBRA_INTERFACE_H */ diff --git a/zebra/ioctl.c b/zebra/ioctl.c new file mode 100644 index 0000000..e1ee429 --- /dev/null +++ b/zebra/ioctl.c @@ -0,0 +1,596 @@ +/* + * Common ioctl functions. + * 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 "linklist.h" +#include "if.h" +#include "prefix.h" +#include "ioctl.h" +#include "log.h" +#include "privs.h" + +#include "zebra/rib.h" +#include "zebra/rt.h" +#include "zebra/interface.h" + +#ifdef HAVE_BSD_LINK_DETECT +#include +#endif /* HAVE_BSD_LINK_DETECT*/ + +extern struct zebra_privs_t zserv_privs; + +/* clear and set interface name string */ +void +ifreq_set_name (struct ifreq *ifreq, struct interface *ifp) +{ + strncpy (ifreq->ifr_name, ifp->name, IFNAMSIZ); +} + +/* call ioctl system call */ +int +if_ioctl (u_long request, caddr_t buffer) +{ + int sock; + int ret; + int err = 0; + + if (zserv_privs.change(ZPRIVS_RAISE)) + zlog (NULL, LOG_ERR, "Can't raise privileges"); + sock = socket (AF_INET, SOCK_DGRAM, 0); + if (sock < 0) + { + int save_errno = errno; + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog (NULL, LOG_ERR, "Can't lower privileges"); + zlog_err("Cannot create UDP socket: %s", safe_strerror(save_errno)); + exit (1); + } + if ((ret = ioctl (sock, request, buffer)) < 0) + err = errno; + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog (NULL, LOG_ERR, "Can't lower privileges"); + close (sock); + + if (ret < 0) + { + errno = err; + return ret; + } + return 0; +} + +#ifdef HAVE_IPV6 +static int +if_ioctl_ipv6 (u_long request, caddr_t buffer) +{ + int sock; + int ret; + int err = 0; + + if (zserv_privs.change(ZPRIVS_RAISE)) + zlog (NULL, LOG_ERR, "Can't raise privileges"); + sock = socket (AF_INET6, SOCK_DGRAM, 0); + if (sock < 0) + { + int save_errno = errno; + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog (NULL, LOG_ERR, "Can't lower privileges"); + zlog_err("Cannot create IPv6 datagram socket: %s", + safe_strerror(save_errno)); + exit (1); + } + + if ((ret = ioctl (sock, request, buffer)) < 0) + err = errno; + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog (NULL, LOG_ERR, "Can't lower privileges"); + close (sock); + + if (ret < 0) + { + errno = err; + return ret; + } + return 0; +} +#endif /* HAVE_IPV6 */ + +/* + * get interface metric + * -- if value is not avaliable set -1 + */ +void +if_get_metric (struct interface *ifp) +{ +#ifdef SIOCGIFMETRIC + struct ifreq ifreq; + + ifreq_set_name (&ifreq, ifp); + + if (if_ioctl (SIOCGIFMETRIC, (caddr_t) &ifreq) < 0) + return; + ifp->metric = ifreq.ifr_metric; + if (ifp->metric == 0) + ifp->metric = 1; +#else /* SIOCGIFMETRIC */ + ifp->metric = -1; +#endif /* SIOCGIFMETRIC */ +} + +/* get interface MTU */ +void +if_get_mtu (struct interface *ifp) +{ + struct ifreq ifreq; + + ifreq_set_name (&ifreq, ifp); + +#if defined(SIOCGIFMTU) + if (if_ioctl (SIOCGIFMTU, (caddr_t) & ifreq) < 0) + { + zlog_info ("Can't lookup mtu by ioctl(SIOCGIFMTU)"); + ifp->mtu6 = ifp->mtu = -1; + return; + } + +#ifdef SUNOS_5 + ifp->mtu6 = ifp->mtu = ifreq.ifr_metric; +#else + ifp->mtu6 = ifp->mtu = ifreq.ifr_mtu; +#endif /* SUNOS_5 */ + + /* propogate */ + zebra_interface_up_update(ifp); + +#else + zlog (NULL, LOG_INFO, "Can't lookup mtu on this system"); + ifp->mtu6 = ifp->mtu = -1; +#endif +} + +#ifdef HAVE_NETLINK +/* Interface address setting via netlink interface. */ +int +if_set_prefix (struct interface *ifp, struct connected *ifc) +{ + return kernel_address_add_ipv4 (ifp, ifc); +} + +/* Interface address is removed using netlink interface. */ +int +if_unset_prefix (struct interface *ifp, struct connected *ifc) +{ + return kernel_address_delete_ipv4 (ifp, ifc); +} +#else /* ! HAVE_NETLINK */ +#ifdef HAVE_STRUCT_IFALIASREQ +/* Set up interface's IP address, netmask (and broadcas? ). *BSD may + has ifaliasreq structure. */ +int +if_set_prefix (struct interface *ifp, struct connected *ifc) +{ + int ret; + struct ifaliasreq addreq; + struct sockaddr_in addr; + struct sockaddr_in mask; + struct prefix_ipv4 *p; + + p = (struct prefix_ipv4 *) ifc->address; + + memset (&addreq, 0, sizeof addreq); + strncpy ((char *)&addreq.ifra_name, ifp->name, sizeof addreq.ifra_name); + + memset (&addr, 0, sizeof (struct sockaddr_in)); + addr.sin_addr = p->prefix; + addr.sin_family = p->family; +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + addr.sin_len = sizeof (struct sockaddr_in); +#endif + memcpy (&addreq.ifra_addr, &addr, sizeof (struct sockaddr_in)); + + memset (&mask, 0, sizeof (struct sockaddr_in)); + masklen2ip (p->prefixlen, &mask.sin_addr); + mask.sin_family = p->family; +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + mask.sin_len = sizeof (struct sockaddr_in); +#endif + memcpy (&addreq.ifra_mask, &mask, sizeof (struct sockaddr_in)); + + ret = if_ioctl (SIOCAIFADDR, (caddr_t) &addreq); + if (ret < 0) + return ret; + return 0; +} + +/* Set up interface's IP address, netmask (and broadcas? ). *BSD may + has ifaliasreq structure. */ +int +if_unset_prefix (struct interface *ifp, struct connected *ifc) +{ + int ret; + struct ifaliasreq addreq; + struct sockaddr_in addr; + struct sockaddr_in mask; + struct prefix_ipv4 *p; + + p = (struct prefix_ipv4 *)ifc->address; + + memset (&addreq, 0, sizeof addreq); + strncpy ((char *)&addreq.ifra_name, ifp->name, sizeof addreq.ifra_name); + + memset (&addr, 0, sizeof (struct sockaddr_in)); + addr.sin_addr = p->prefix; + addr.sin_family = p->family; +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + addr.sin_len = sizeof (struct sockaddr_in); +#endif + memcpy (&addreq.ifra_addr, &addr, sizeof (struct sockaddr_in)); + + memset (&mask, 0, sizeof (struct sockaddr_in)); + masklen2ip (p->prefixlen, &mask.sin_addr); + mask.sin_family = p->family; +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + mask.sin_len = sizeof (struct sockaddr_in); +#endif + memcpy (&addreq.ifra_mask, &mask, sizeof (struct sockaddr_in)); + + ret = if_ioctl (SIOCDIFADDR, (caddr_t) &addreq); + if (ret < 0) + return ret; + return 0; +} +#else +/* Set up interface's address, netmask (and broadcas? ). Linux or + Solaris uses ifname:number semantics to set IP address aliases. */ +int +if_set_prefix (struct interface *ifp, struct connected *ifc) +{ + int ret; + struct ifreq ifreq; + struct sockaddr_in addr; + struct sockaddr_in broad; + struct sockaddr_in mask; + struct prefix_ipv4 ifaddr; + struct prefix_ipv4 *p; + + p = (struct prefix_ipv4 *) ifc->address; + + ifaddr = *p; + + ifreq_set_name (&ifreq, ifp); + + addr.sin_addr = p->prefix; + addr.sin_family = p->family; + memcpy (&ifreq.ifr_addr, &addr, sizeof (struct sockaddr_in)); + ret = if_ioctl (SIOCSIFADDR, (caddr_t) &ifreq); + if (ret < 0) + return ret; + + /* We need mask for make broadcast addr. */ + masklen2ip (p->prefixlen, &mask.sin_addr); + + if (if_is_broadcast (ifp)) + { + apply_mask_ipv4 (&ifaddr); + addr.sin_addr = ifaddr.prefix; + + broad.sin_addr.s_addr = (addr.sin_addr.s_addr | ~mask.sin_addr.s_addr); + broad.sin_family = p->family; + + memcpy (&ifreq.ifr_broadaddr, &broad, sizeof (struct sockaddr_in)); + ret = if_ioctl (SIOCSIFBRDADDR, (caddr_t) &ifreq); + if (ret < 0) + return ret; + } + + mask.sin_family = p->family; +#ifdef SUNOS_5 + memcpy (&mask, &ifreq.ifr_addr, sizeof (mask)); +#else + memcpy (&ifreq.ifr_netmask, &mask, sizeof (struct sockaddr_in)); +#endif /* SUNOS5 */ + ret = if_ioctl (SIOCSIFNETMASK, (caddr_t) &ifreq); + if (ret < 0) + return ret; + + return 0; +} + +/* Set up interface's address, netmask (and broadcas? ). Linux or + Solaris uses ifname:number semantics to set IP address aliases. */ +int +if_unset_prefix (struct interface *ifp, struct connected *ifc) +{ + int ret; + struct ifreq ifreq; + struct sockaddr_in addr; + struct prefix_ipv4 *p; + + p = (struct prefix_ipv4 *) ifc->address; + + ifreq_set_name (&ifreq, ifp); + + memset (&addr, 0, sizeof (struct sockaddr_in)); + addr.sin_family = p->family; + memcpy (&ifreq.ifr_addr, &addr, sizeof (struct sockaddr_in)); + ret = if_ioctl (SIOCSIFADDR, (caddr_t) &ifreq); + if (ret < 0) + return ret; + + return 0; +} +#endif /* HAVE_STRUCT_IFALIASREQ */ +#endif /* HAVE_NETLINK */ + +/* get interface flags */ +void +if_get_flags (struct interface *ifp) +{ + int ret; + struct ifreq ifreq; +#ifdef HAVE_BSD_LINK_DETECT + struct ifmediareq ifmr; +#endif /* HAVE_BSD_LINK_DETECT */ + + ifreq_set_name (&ifreq, ifp); + + ret = if_ioctl (SIOCGIFFLAGS, (caddr_t) &ifreq); + if (ret < 0) + { + zlog_err("if_ioctl(SIOCGIFFLAGS) failed: %s", safe_strerror(errno)); + return; + } +#ifdef HAVE_BSD_LINK_DETECT /* Detect BSD link-state at start-up */ + + /* Per-default, IFF_RUNNING is held high, unless link-detect says + * otherwise - we abuse IFF_RUNNING inside zebra as a link-state flag, + * following practice on Linux and Solaris kernels + */ + SET_FLAG(ifreq.ifr_flags, IFF_RUNNING); + + if (CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_LINKDETECTION)) + { + (void) memset(&ifmr, 0, sizeof(ifmr)); + strncpy (ifmr.ifm_name, ifp->name, IFNAMSIZ); + + /* Seems not all interfaces implement this ioctl */ + if (if_ioctl(SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) + zlog_err("if_ioctl(SIOCGIFMEDIA) failed: %s", safe_strerror(errno)); + else if (ifmr.ifm_status & IFM_AVALID) /* Link state is valid */ + { + if (ifmr.ifm_status & IFM_ACTIVE) + SET_FLAG(ifreq.ifr_flags, IFF_RUNNING); + else + UNSET_FLAG(ifreq.ifr_flags, IFF_RUNNING); + } + } +#endif /* HAVE_BSD_LINK_DETECT */ + + if_flags_update (ifp, (ifreq.ifr_flags & 0x0000ffff)); +} + +/* Set interface flags */ +int +if_set_flags (struct interface *ifp, uint64_t flags) +{ + int ret; + struct ifreq ifreq; + + memset (&ifreq, 0, sizeof(struct ifreq)); + ifreq_set_name (&ifreq, ifp); + + ifreq.ifr_flags = ifp->flags; + ifreq.ifr_flags |= flags; + + ret = if_ioctl (SIOCSIFFLAGS, (caddr_t) &ifreq); + + if (ret < 0) + { + zlog_info ("can't set interface flags"); + return ret; + } + return 0; +} + +/* Unset interface's flag. */ +int +if_unset_flags (struct interface *ifp, uint64_t flags) +{ + int ret; + struct ifreq ifreq; + + memset (&ifreq, 0, sizeof(struct ifreq)); + ifreq_set_name (&ifreq, ifp); + + ifreq.ifr_flags = ifp->flags; + ifreq.ifr_flags &= ~flags; + + ret = if_ioctl (SIOCSIFFLAGS, (caddr_t) &ifreq); + + if (ret < 0) + { + zlog_info ("can't unset interface flags"); + return ret; + } + return 0; +} + +#ifdef HAVE_IPV6 + +#ifdef LINUX_IPV6 +#ifndef _LINUX_IN6_H +/* linux/include/net/ipv6.h */ +struct in6_ifreq +{ + struct in6_addr ifr6_addr; + u_int32_t ifr6_prefixlen; + int ifr6_ifindex; +}; +#endif /* _LINUX_IN6_H */ + +/* Interface's address add/delete functions. */ +int +if_prefix_add_ipv6 (struct interface *ifp, struct connected *ifc) +{ + int ret; + struct prefix_ipv6 *p; + struct in6_ifreq ifreq; + + p = (struct prefix_ipv6 *) ifc->address; + + memset (&ifreq, 0, sizeof (struct in6_ifreq)); + + memcpy (&ifreq.ifr6_addr, &p->prefix, sizeof (struct in6_addr)); + ifreq.ifr6_ifindex = ifp->ifindex; + ifreq.ifr6_prefixlen = p->prefixlen; + + ret = if_ioctl_ipv6 (SIOCSIFADDR, (caddr_t) &ifreq); + + return ret; +} + +int +if_prefix_delete_ipv6 (struct interface *ifp, struct connected *ifc) +{ + int ret; + struct prefix_ipv6 *p; + struct in6_ifreq ifreq; + + p = (struct prefix_ipv6 *) ifc->address; + + memset (&ifreq, 0, sizeof (struct in6_ifreq)); + + memcpy (&ifreq.ifr6_addr, &p->prefix, sizeof (struct in6_addr)); + ifreq.ifr6_ifindex = ifp->ifindex; + ifreq.ifr6_prefixlen = p->prefixlen; + + ret = if_ioctl_ipv6 (SIOCDIFADDR, (caddr_t) &ifreq); + + return ret; +} +#else /* LINUX_IPV6 */ +#ifdef HAVE_STRUCT_IN6_ALIASREQ +#ifndef ND6_INFINITE_LIFETIME +#define ND6_INFINITE_LIFETIME 0xffffffffL +#endif /* ND6_INFINITE_LIFETIME */ +int +if_prefix_add_ipv6 (struct interface *ifp, struct connected *ifc) +{ + int ret; + struct in6_aliasreq addreq; + struct sockaddr_in6 addr; + struct sockaddr_in6 mask; + struct prefix_ipv6 *p; + + p = (struct prefix_ipv6 * ) ifc->address; + + memset (&addreq, 0, sizeof addreq); + strncpy ((char *)&addreq.ifra_name, ifp->name, sizeof addreq.ifra_name); + + memset (&addr, 0, sizeof (struct sockaddr_in6)); + addr.sin6_addr = p->prefix; + addr.sin6_family = p->family; +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + addr.sin6_len = sizeof (struct sockaddr_in6); +#endif + memcpy (&addreq.ifra_addr, &addr, sizeof (struct sockaddr_in6)); + + memset (&mask, 0, sizeof (struct sockaddr_in6)); + masklen2ip6 (p->prefixlen, &mask.sin6_addr); + mask.sin6_family = p->family; +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + mask.sin6_len = sizeof (struct sockaddr_in6); +#endif + memcpy (&addreq.ifra_prefixmask, &mask, sizeof (struct sockaddr_in6)); + + addreq.ifra_lifetime.ia6t_vltime = 0xffffffff; + addreq.ifra_lifetime.ia6t_pltime = 0xffffffff; + +#ifdef HAVE_STRUCT_IF6_ALIASREQ_IFRA_LIFETIME + addreq.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME; + addreq.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME; +#endif + + ret = if_ioctl_ipv6 (SIOCAIFADDR_IN6, (caddr_t) &addreq); + if (ret < 0) + return ret; + return 0; +} + +int +if_prefix_delete_ipv6 (struct interface *ifp, struct connected *ifc) +{ + int ret; + struct in6_aliasreq addreq; + struct sockaddr_in6 addr; + struct sockaddr_in6 mask; + struct prefix_ipv6 *p; + + p = (struct prefix_ipv6 *) ifc->address; + + memset (&addreq, 0, sizeof addreq); + strncpy ((char *)&addreq.ifra_name, ifp->name, sizeof addreq.ifra_name); + + memset (&addr, 0, sizeof (struct sockaddr_in6)); + addr.sin6_addr = p->prefix; + addr.sin6_family = p->family; +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + addr.sin6_len = sizeof (struct sockaddr_in6); +#endif + memcpy (&addreq.ifra_addr, &addr, sizeof (struct sockaddr_in6)); + + memset (&mask, 0, sizeof (struct sockaddr_in6)); + masklen2ip6 (p->prefixlen, &mask.sin6_addr); + mask.sin6_family = p->family; +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + mask.sin6_len = sizeof (struct sockaddr_in6); +#endif + memcpy (&addreq.ifra_prefixmask, &mask, sizeof (struct sockaddr_in6)); + +#ifdef HAVE_STRUCT_IF6_ALIASREQ_IFRA_LIFETIME + addreq.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME; + addreq.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME; +#endif + + ret = if_ioctl_ipv6 (SIOCDIFADDR_IN6, (caddr_t) &addreq); + if (ret < 0) + return ret; + return 0; +} +#else +int +if_prefix_add_ipv6 (struct interface *ifp, struct connected *ifc) +{ + return 0; +} + +int +if_prefix_delete_ipv6 (struct interface *ifp, struct connected *ifc) +{ + return 0; +} +#endif /* HAVE_STRUCT_IN6_ALIASREQ */ + +#endif /* LINUX_IPV6 */ + +#endif /* HAVE_IPV6 */ diff --git a/zebra/ioctl.h b/zebra/ioctl.h new file mode 100644 index 0000000..fee9b72 --- /dev/null +++ b/zebra/ioctl.h @@ -0,0 +1,58 @@ +/* + * Common ioctl functions. + * Copyright (C) 1998 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_IOCTL_H +#define _ZEBRA_IOCTL_H + +/* Prototypes. */ +extern void ifreq_set_name (struct ifreq *, struct interface *); +extern int if_ioctl (u_long, caddr_t); + +extern int if_set_flags (struct interface *, uint64_t); +extern int if_unset_flags (struct interface *, uint64_t); +extern void if_get_flags (struct interface *); + +extern int if_set_prefix (struct interface *, struct connected *); +extern int if_unset_prefix (struct interface *, struct connected *); + +extern void if_get_metric (struct interface *); +extern void if_get_mtu (struct interface *); + +#ifdef HAVE_IPV6 +extern int if_prefix_add_ipv6 (struct interface *, struct connected *); +extern int if_prefix_delete_ipv6 (struct interface *, struct connected *); +#endif /* HAVE_IPV6 */ + +#ifdef SOLARIS_IPV6 +extern int if_ioctl_ipv6(u_long, caddr_t); +extern struct connected *if_lookup_linklocal( struct interface *); + +#define AF_IOCTL(af, request, buffer) \ + ((af) == AF_INET? if_ioctl(request, buffer) : \ + if_ioctl_ipv6(request, buffer)) +#else /* SOLARIS_IPV6 */ + +#define AF_IOCTL(af, request, buffer) if_ioctl(request, buffer) + +#endif /* SOLARIS_IPV6 */ + +#endif /* _ZEBRA_IOCTL_H */ diff --git a/zebra/ioctl_null.c b/zebra/ioctl_null.c new file mode 100644 index 0000000..5a8be99 --- /dev/null +++ b/zebra/ioctl_null.c @@ -0,0 +1,78 @@ +/* + * 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/rt.h" +#include "zebra/ioctl.h" + +void ifreq_set_name (struct ifreq *a, struct interface *b) { return; } + +int if_set_prefix (struct interface *a, struct connected *b) +{ + kernel_address_add_ipv4 (a, b); + return 0; +} + +int if_unset_prefix (struct interface *a, struct connected *b) +{ + kernel_address_delete_ipv4 (a, b); + return 0; +} + +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 +struct connected *if_lookup_linklocal(struct interface *a) { return 0; } + +#define AF_IOCTL(af, request, buffer) \ + ((af) == AF_INET? if_ioctl(request, buffer) : \ + if_ioctl_ipv6(request, buffer)) +#else /* SOLARIS_IPV6 */ + +#define AF_IOCTL(af, request, buffer) if_ioctl(request, buffer) + +#endif /* SOLARIS_IPV6 */ diff --git a/zebra/ioctl_solaris.c b/zebra/ioctl_solaris.c new file mode 100644 index 0000000..b5bf1cc --- /dev/null +++ b/zebra/ioctl_solaris.c @@ -0,0 +1,435 @@ +/* + * Common ioctl functions for Solaris. + * 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 "linklist.h" +#include "if.h" +#include "prefix.h" +#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; + +/* clear and set interface name string */ +void +lifreq_set_name (struct lifreq *lifreq, const char *ifname) +{ + strncpy (lifreq->lifr_name, ifname, IFNAMSIZ); +} + +/* call ioctl system call */ +int +if_ioctl (u_long request, caddr_t buffer) +{ + int sock; + int ret; + int err; + + if (zserv_privs.change(ZPRIVS_RAISE)) + zlog (NULL, LOG_ERR, "Can't raise privileges"); + + sock = socket (AF_INET, SOCK_DGRAM, 0); + if (sock < 0) + { + int save_errno = errno; + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog (NULL, LOG_ERR, "Can't lower privileges"); + zlog_err("Cannot create UDP socket: %s", safe_strerror(save_errno)); + exit (1); + } + + if ((ret = ioctl (sock, request, buffer)) < 0) + err = errno; + + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog (NULL, LOG_ERR, "Can't lower privileges"); + + close (sock); + + if (ret < 0) + { + errno = err; + return ret; + } + return 0; +} + + +int +if_ioctl_ipv6 (u_long request, caddr_t buffer) +{ +#ifdef HAVE_IPV6 + int sock; + int ret; + int err; + + if (zserv_privs.change(ZPRIVS_RAISE)) + zlog (NULL, LOG_ERR, "Can't raise privileges"); + + sock = socket (AF_INET6, SOCK_DGRAM, 0); + if (sock < 0) + { + int save_errno = errno; + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog (NULL, LOG_ERR, "Can't lower privileges"); + zlog_err("Cannot create IPv6 datagram socket: %s", + safe_strerror(save_errno)); + exit (1); + } + + if ((ret = ioctl (sock, request, buffer)) < 0) + err = errno; + + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog (NULL, LOG_ERR, "Can't lower privileges"); + + close (sock); + + if (ret < 0) + { + errno = err; + return ret; + } +#endif /* HAVE_IPV6 */ + + return 0; +} + +/* + * get interface metric + * -- if value is not avaliable set -1 + */ +void +if_get_metric (struct interface *ifp) +{ + struct lifreq lifreq; + int ret; + + lifreq_set_name (&lifreq, ifp->name); + + if (ifp->flags & IFF_IPV4) + ret = AF_IOCTL (AF_INET, SIOCGLIFMETRIC, (caddr_t) & lifreq); +#ifdef SOLARIS_IPV6 + else if (ifp->flags & IFF_IPV6) + ret = AF_IOCTL (AF_INET6, SIOCGLIFMETRIC, (caddr_t) & lifreq); +#endif /* SOLARIS_IPV6 */ + else + ret = -1; + + if (ret < 0) + return; + + ifp->metric = lifreq.lifr_metric; + + if (ifp->metric == 0) + ifp->metric = 1; +} + +/* get interface MTU */ +void +if_get_mtu (struct interface *ifp) +{ + struct lifreq lifreq; + int ret; + u_char changed = 0; + + if (ifp->flags & IFF_IPV4) + { + lifreq_set_name (&lifreq, ifp->name); + ret = AF_IOCTL (AF_INET, SIOCGLIFMTU, (caddr_t) & lifreq); + if (ret < 0) + { + zlog_info ("Can't lookup mtu on %s by ioctl(SIOCGLIFMTU)", + ifp->name); + ifp->mtu = -1; + } + else + { + ifp->mtu = lifreq.lifr_metric; + changed = 1; + } + } + +#ifdef HAVE_IPV6 + if (ifp->flags & IFF_IPV6) + { + memset(&lifreq, 0, sizeof(lifreq)); + lifreq_set_name (&lifreq, ifp->name); + + ret = AF_IOCTL (AF_INET6, SIOCGLIFMTU, (caddr_t) & lifreq); + if (ret < 0) + { + zlog_info ("Can't lookup mtu6 on %s by ioctl(SIOCGIFMTU)", ifp->name); + ifp->mtu6 = -1; + } + else + { + ifp->mtu6 = lifreq.lifr_metric; + changed = 1; + } + } +#endif /* HAVE_IPV6 */ + + if (changed) + zebra_interface_up_update(ifp); +} + +/* Set up interface's address, netmask (and broadcast? ). + Solaris uses ifname:number semantics to set IP address aliases. */ +int +if_set_prefix (struct interface *ifp, struct connected *ifc) +{ + int ret; + struct ifreq ifreq; + struct sockaddr_in addr; + struct sockaddr_in broad; + struct sockaddr_in mask; + struct prefix_ipv4 ifaddr; + struct prefix_ipv4 *p; + + p = (struct prefix_ipv4 *) ifc->address; + + ifaddr = *p; + + strncpy (ifreq.ifr_name, ifp->name, IFNAMSIZ); + + addr.sin_addr = p->prefix; + addr.sin_family = p->family; + memcpy (&ifreq.ifr_addr, &addr, sizeof (struct sockaddr_in)); + + ret = if_ioctl (SIOCSIFADDR, (caddr_t) & ifreq); + + if (ret < 0) + return ret; + + /* We need mask for make broadcast addr. */ + masklen2ip (p->prefixlen, &mask.sin_addr); + + if (if_is_broadcast (ifp)) + { + apply_mask_ipv4 (&ifaddr); + addr.sin_addr = ifaddr.prefix; + + broad.sin_addr.s_addr = (addr.sin_addr.s_addr | ~mask.sin_addr.s_addr); + broad.sin_family = p->family; + + memcpy (&ifreq.ifr_broadaddr, &broad, sizeof (struct sockaddr_in)); + ret = if_ioctl (SIOCSIFBRDADDR, (caddr_t) & ifreq); + if (ret < 0) + return ret; + } + + mask.sin_family = p->family; +#ifdef SUNOS_5 + memcpy (&mask, &ifreq.ifr_addr, sizeof (mask)); +#else + memcpy (&ifreq.ifr_netmask, &mask, sizeof (struct sockaddr_in)); +#endif /* SUNOS_5 */ + ret = if_ioctl (SIOCSIFNETMASK, (caddr_t) & ifreq); + + return ((ret < 0) ? ret : 0); +} + +/* Set up interface's address, netmask (and broadcast). + Solaris uses ifname:number semantics to set IP address aliases. */ +int +if_unset_prefix (struct interface *ifp, struct connected *ifc) +{ + int ret; + struct ifreq ifreq; + struct sockaddr_in addr; + struct prefix_ipv4 *p; + + p = (struct prefix_ipv4 *) ifc->address; + + strncpy (ifreq.ifr_name, ifp->name, IFNAMSIZ); + + memset (&addr, 0, sizeof (struct sockaddr_in)); + addr.sin_family = p->family; + memcpy (&ifreq.ifr_addr, &addr, sizeof (struct sockaddr_in)); + + ret = if_ioctl (SIOCSIFADDR, (caddr_t) & ifreq); + + if (ret < 0) + return ret; + + return 0; +} + +/* Get just the flags for the given name. + * Used by the normal 'if_get_flags' function, as well + * as the bootup interface-list code, which has to peek at per-address + * flags in order to figure out which ones should be ignored.. + */ +int +if_get_flags_direct (const char *ifname, uint64_t *flags, unsigned int af) +{ + struct lifreq lifreq; + int ret; + + lifreq_set_name (&lifreq, ifname); + + ret = AF_IOCTL (af, SIOCGLIFFLAGS, (caddr_t) &lifreq); + + if (ret) + zlog_debug ("%s: ifname %s, error %s (%d)", + __func__, ifname, safe_strerror (errno), errno); + + *flags = lifreq.lifr_flags; + + return ret; +} + +/* get interface flags */ +void +if_get_flags (struct interface *ifp) +{ + int ret4 = 0, ret6 = 0; + uint64_t newflags = 0; + uint64_t tmpflags; + + if (ifp->flags & IFF_IPV4) + { + ret4 = if_get_flags_direct (ifp->name, &tmpflags, AF_INET); + + if (!ret4) + newflags |= tmpflags; + else if (errno == ENXIO) + { + /* it's gone */ + UNSET_FLAG (ifp->flags, IFF_UP); + if_flags_update (ifp, ifp->flags); + } + } + + if (ifp->flags & IFF_IPV6) + { + ret6 = if_get_flags_direct (ifp->name, &tmpflags, AF_INET6); + + if (!ret6) + newflags |= tmpflags; + else if (errno == ENXIO) + { + /* it's gone */ + UNSET_FLAG (ifp->flags, IFF_UP); + if_flags_update (ifp, ifp->flags); + } + } + + /* only update flags if one of above succeeded */ + if ( !(ret4 && ret6) ) + if_flags_update (ifp, newflags); +} + +/* Set interface flags */ +int +if_set_flags (struct interface *ifp, uint64_t flags) +{ + int ret; + struct lifreq lifreq; + + lifreq_set_name (&lifreq, ifp->name); + + lifreq.lifr_flags = ifp->flags; + lifreq.lifr_flags |= flags; + + if (ifp->flags & IFF_IPV4) + ret = AF_IOCTL (AF_INET, SIOCSLIFFLAGS, (caddr_t) & lifreq); + else if (ifp->flags & IFF_IPV6) + ret = AF_IOCTL (AF_INET6, SIOCSLIFFLAGS, (caddr_t) & lifreq); + else + ret = -1; + + if (ret < 0) + zlog_info ("can't set interface flags on %s: %s", ifp->name, + safe_strerror (errno)); + else + ret = 0; + + return ret; +} + +/* Unset interface's flag. */ +int +if_unset_flags (struct interface *ifp, uint64_t flags) +{ + int ret; + struct lifreq lifreq; + + lifreq_set_name (&lifreq, ifp->name); + + lifreq.lifr_flags = ifp->flags; + lifreq.lifr_flags &= ~flags; + + if (ifp->flags & IFF_IPV4) + ret = AF_IOCTL (AF_INET, SIOCSLIFFLAGS, (caddr_t) & lifreq); + else if (ifp->flags & IFF_IPV6) + ret = AF_IOCTL (AF_INET6, SIOCSLIFFLAGS, (caddr_t) & lifreq); + else + ret = -1; + + if (ret < 0) + zlog_info ("can't unset interface flags"); + else + ret = 0; + + return ret; +} + +#ifdef HAVE_IPV6 + +/* Interface's address add/delete functions. */ +int +if_prefix_add_ipv6 (struct interface *ifp, struct connected *ifc) +{ + char addrbuf[PREFIX_STRLEN]; + + zlog_warn ("Can't set %s on interface %s", + prefix2str(ifc->address, addrbuf, sizeof(addrbuf)), + ifp->name); + + return 0; + +} + +int +if_prefix_delete_ipv6 (struct interface *ifp, struct connected *ifc) +{ + char addrbuf[PREFIX_STRLEN]; + + zlog_warn ("Can't delete %s on interface %s", + prefix2str(ifc->address, addrbuf, sizeof(addrbuf)), + ifp->name); + + return 0; + +} + +#endif /* HAVE_IPV6 */ diff --git a/zebra/ioctl_solaris.h b/zebra/ioctl_solaris.h new file mode 100644 index 0000000..188986b --- /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.h b/zebra/ipforward.h new file mode 100644 index 0000000..8a935c1 --- /dev/null +++ b/zebra/ipforward.h @@ -0,0 +1,35 @@ +/* IP forward settings. + * 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 _ZEBRA_IPFORWARD_H +#define _ZEBRA_IPFORWARD_H + +extern int ipforward (void); +extern int ipforward_on (void); +extern int ipforward_off (void); + +#ifdef HAVE_IPV6 +extern int ipforward_ipv6 (void); +extern int ipforward_ipv6_on (void); +extern int ipforward_ipv6_off (void); +#endif /* HAVE_IPV6 */ + +#endif /* _ZEBRA_IPFORWARD_H */ diff --git a/zebra/ipforward_proc.c b/zebra/ipforward_proc.c new file mode 100644 index 0000000..2876ede --- /dev/null +++ b/zebra/ipforward_proc.c @@ -0,0 +1,200 @@ +/* + * Fetch ipforward value by reading /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 "log.h" +#include "privs.h" + +#include "zebra/ipforward.h" + +extern struct zebra_privs_t zserv_privs; + +char proc_net_snmp[] = "/proc/net/snmp"; + +static void +dropline (FILE *fp) +{ + int c; + + while ((c = getc (fp)) != '\n') + ; +} + +int +ipforward (void) +{ + FILE *fp; + int ipforwarding = 0; + char buf[10]; + + fp = fopen (proc_net_snmp, "r"); + + if (fp == NULL) + return -1; + + /* We don't care about the first line. */ + dropline (fp); + + /* Get ip_statistics.IpForwarding : + 1 => ip forwarding enabled + 2 => ip forwarding off. */ + if (fgets (buf, 6, fp)) + sscanf (buf, "Ip: %d", &ipforwarding); + + fclose(fp); + + if (ipforwarding == 1) + return 1; + + return 0; +} + +/* char proc_ipv4_forwarding[] = "/proc/sys/net/ipv4/conf/all/forwarding"; */ +char proc_ipv4_forwarding[] = "/proc/sys/net/ipv4/ip_forward"; + +int +ipforward_on (void) +{ + FILE *fp; + + if ( zserv_privs.change(ZPRIVS_RAISE) ) + zlog_err ("Can't raise privileges, %s", safe_strerror (errno) ); + + fp = fopen (proc_ipv4_forwarding, "w"); + + if (fp == NULL) { + if ( zserv_privs.change(ZPRIVS_LOWER) ) + zlog_err ("Can't lower privileges, %s", safe_strerror (errno)); + return -1; + } + + fprintf (fp, "1\n"); + + fclose (fp); + + if ( zserv_privs.change(ZPRIVS_LOWER) ) + zlog_err ("Can't lower privileges, %s", safe_strerror (errno)); + + return ipforward (); +} + +int +ipforward_off (void) +{ + FILE *fp; + + if ( zserv_privs.change(ZPRIVS_RAISE) ) + zlog_err ("Can't raise privileges, %s", safe_strerror (errno)); + + fp = fopen (proc_ipv4_forwarding, "w"); + + if (fp == NULL) { + if ( zserv_privs.change(ZPRIVS_LOWER) ) + zlog_err ("Can't lower privileges, %s", safe_strerror (errno)); + return -1; + } + + fprintf (fp, "0\n"); + + fclose (fp); + + if ( zserv_privs.change(ZPRIVS_LOWER) ) + zlog_err ("Can't lower privileges, %s", safe_strerror (errno)); + + return ipforward (); +} +#ifdef HAVE_IPV6 + +char proc_ipv6_forwarding[] = "/proc/sys/net/ipv6/conf/all/forwarding"; + +int +ipforward_ipv6 (void) +{ + FILE *fp; + char buf[5]; + int ipforwarding = 0; + + fp = fopen (proc_ipv6_forwarding, "r"); + + if (fp == NULL) + return -1; + + if (fgets (buf, 2, fp)) + sscanf (buf, "%d", &ipforwarding); + + fclose (fp); + return ipforwarding; +} + +int +ipforward_ipv6_on (void) +{ + FILE *fp; + + if ( zserv_privs.change(ZPRIVS_RAISE) ) + zlog_err ("Can't raise privileges, %s", safe_strerror (errno)); + + fp = fopen (proc_ipv6_forwarding, "w"); + + if (fp == NULL) { + if ( zserv_privs.change(ZPRIVS_LOWER) ) + zlog_err ("Can't lower privileges, %s", safe_strerror (errno)); + return -1; + } + + fprintf (fp, "1\n"); + + fclose (fp); + + if ( zserv_privs.change(ZPRIVS_LOWER) ) + zlog_err ("Can't lower privileges, %s", safe_strerror (errno)); + + return ipforward_ipv6 (); +} + +int +ipforward_ipv6_off (void) +{ + FILE *fp; + + if ( zserv_privs.change(ZPRIVS_RAISE) ) + zlog_err ("Can't raise privileges, %s", safe_strerror (errno)); + + fp = fopen (proc_ipv6_forwarding, "w"); + + if (fp == NULL) { + if ( zserv_privs.change(ZPRIVS_LOWER) ) + zlog_err ("Can't lower privileges, %s", safe_strerror (errno)); + return -1; + } + + fprintf (fp, "0\n"); + + fclose (fp); + + if ( zserv_privs.change(ZPRIVS_LOWER) ) + zlog_err ("Can't lower privileges, %s", safe_strerror (errno)); + + return ipforward_ipv6 (); +} +#endif /* HAVE_IPV6 */ diff --git a/zebra/ipforward_solaris.c b/zebra/ipforward_solaris.c new file mode 100644 index 0000000..4aa1b79 --- /dev/null +++ b/zebra/ipforward_solaris.c @@ -0,0 +1,165 @@ +/* + * ipforward value get function for solaris. + * 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 "log.h" +#include "prefix.h" + +#include "privs.h" +#include "zebra/ipforward.h" + +/* +** Solaris should define IP_DEV_NAME in , but we'll save +** configure.in changes for another day. We can use the same device +** for both IPv4 and IPv6. +*/ +/* #include */ +#ifndef IP_DEV_NAME +#define IP_DEV_NAME "/dev/ip" +#endif + + +extern struct zebra_privs_t zserv_privs; + +/* This is a limited ndd style function that operates one integer +** value only. Errors return -1. ND_SET commands return 0 on +** success. ND_GET commands return the value on success (which could +** be -1 and be confused for an error). The parameter is the string +** name of the parameter being referenced. +*/ + +static int +solaris_nd(const int cmd, const char* parameter, const int value) +{ +#define ND_BUFFER_SIZE 1024 + int fd; + char nd_buf[ND_BUFFER_SIZE]; + struct strioctl strioctl; + const char* device = IP_DEV_NAME; + int retval; + memset(nd_buf, '\0', ND_BUFFER_SIZE); + /* + ** ND_SET takes a NULL delimited list of strings further terminated + ** buy a NULL. ND_GET returns a list in a similar layout, although + ** here we only use the first result. + */ + if (cmd == ND_SET) + snprintf(nd_buf, ND_BUFFER_SIZE, "%s%c%d%c", parameter, '\0', value,'\0'); + else if (cmd == ND_GET) + snprintf(nd_buf, ND_BUFFER_SIZE, "%s", parameter); + else { + zlog_err("internal error - inappropriate command given to " + "solaris_nd()%s:%d", __FILE__, __LINE__); + return -1; + } + + strioctl.ic_cmd = cmd; + strioctl.ic_timout = 0; + strioctl.ic_len = ND_BUFFER_SIZE; + strioctl.ic_dp = nd_buf; + + if ( zserv_privs.change (ZPRIVS_RAISE) ) + zlog_err ("solaris_nd: Can't raise privileges"); + if ((fd = open (device, O_RDWR)) < 0) + { + zlog_warn("failed to open device %s - %s", device, safe_strerror(errno)); + if ( zserv_privs.change (ZPRIVS_LOWER) ) + zlog_err ("solaris_nd: Can't lower privileges"); + return -1; + } + if (ioctl (fd, I_STR, &strioctl) < 0) + { + int save_errno = errno; + if ( zserv_privs.change (ZPRIVS_LOWER) ) + zlog_err ("solaris_nd: Can't lower privileges"); + close (fd); + zlog_warn("ioctl I_STR failed on device %s - %s", + device, safe_strerror(save_errno)); + return -1; + } + close(fd); + if ( zserv_privs.change (ZPRIVS_LOWER) ) + zlog_err ("solaris_nd: Can't lower privileges"); + + if (cmd == ND_GET) + { + errno = 0; + retval = atoi(nd_buf); + if (errno) + { + zlog_warn("failed to convert returned value to integer - %s", + safe_strerror(errno)); + retval = -1; + } + } + else + { + retval = 0; + } + return retval; +} + +static int +solaris_nd_set(const char* parameter, const int value) { + return solaris_nd(ND_SET, parameter, value); +} +static int +solaris_nd_get(const char* parameter) { + return solaris_nd(ND_GET, parameter, 0); +} +int +ipforward(void) +{ + return solaris_nd_get("ip_forwarding"); +} + +int +ipforward_on (void) +{ + (void) solaris_nd_set("ip_forwarding", 1); + return ipforward(); +} + +int +ipforward_off (void) +{ + (void) solaris_nd_set("ip_forwarding", 0); + return ipforward(); +} +#ifdef HAVE_IPV6 +int ipforward_ipv6(void) +{ + return solaris_nd_get("ip6_forwarding"); +} +int +ipforward_ipv6_on (void) +{ + (void) solaris_nd_set("ip6_forwarding", 1); + return ipforward_ipv6(); +} +int +ipforward_ipv6_off (void) +{ + (void) solaris_nd_set("ip6_forwarding", 0); + return ipforward_ipv6(); +} +#endif /* HAVE_IPV6 */ diff --git a/zebra/ipforward_sysctl.c b/zebra/ipforward_sysctl.c new file mode 100644 index 0000000..57ed185 --- /dev/null +++ b/zebra/ipforward_sysctl.c @@ -0,0 +1,176 @@ +/* IP forward control by sysctl function. + * Copyright (C) 1997, 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 "privs.h" +#include "zebra/ipforward.h" + +#include "log.h" + +#define MIB_SIZ 4 + +extern struct zebra_privs_t zserv_privs; + +/* IPv4 forwarding control MIB. */ +int mib[MIB_SIZ] = +{ + CTL_NET, + PF_INET, + IPPROTO_IP, + IPCTL_FORWARDING +}; + +int +ipforward (void) +{ + size_t len; + int ipforwarding = 0; + + len = sizeof ipforwarding; + if (sysctl (mib, MIB_SIZ, &ipforwarding, &len, 0, 0) < 0) + { + zlog_warn ("Can't get ipforwarding value"); + return -1; + } + return ipforwarding; +} + +int +ipforward_on (void) +{ + size_t len; + int ipforwarding = 1; + + len = sizeof ipforwarding; + if (zserv_privs.change(ZPRIVS_RAISE)) + zlog (NULL, LOG_ERR, "Can't raise privileges"); + if (sysctl (mib, MIB_SIZ, NULL, NULL, &ipforwarding, len) < 0) + { + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog (NULL, LOG_ERR, "Can't lower privileges"); + zlog_warn ("Can't set ipforwarding on"); + return -1; + } + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog (NULL, LOG_ERR, "Can't lower privileges"); + return ipforwarding; +} + +int +ipforward_off (void) +{ + size_t len; + int ipforwarding = 0; + + len = sizeof ipforwarding; + if (zserv_privs.change(ZPRIVS_RAISE)) + zlog (NULL, LOG_ERR, "Can't raise privileges"); + if (sysctl (mib, MIB_SIZ, NULL, NULL, &ipforwarding, len) < 0) + { + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog (NULL, LOG_ERR, "Can't lower privileges"); + zlog_warn ("Can't set ipforwarding on"); + return -1; + } + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog (NULL, LOG_ERR, "Can't lower privileges"); + return ipforwarding; +} + +#ifdef HAVE_IPV6 + +/* IPv6 forwarding control MIB. */ +int mib_ipv6[MIB_SIZ] = +{ + CTL_NET, + PF_INET6, +#if defined(KAME) + IPPROTO_IPV6, + IPV6CTL_FORWARDING +#else /* NOT KAME */ + IPPROTO_IP, + IP6CTL_FORWARDING +#endif /* KAME */ +}; + +int +ipforward_ipv6 (void) +{ + size_t len; + int ip6forwarding = 0; + + len = sizeof ip6forwarding; + if (zserv_privs.change(ZPRIVS_RAISE)) + zlog (NULL, LOG_ERR, "Can't raise privileges"); + if (sysctl (mib_ipv6, MIB_SIZ, &ip6forwarding, &len, 0, 0) < 0) + { + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog (NULL, LOG_ERR, "Can't lower privileges"); + zlog_warn ("can't get ip6forwarding value"); + return -1; + } + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog (NULL, LOG_ERR, "Can't lower privileges"); + return ip6forwarding; +} + +int +ipforward_ipv6_on (void) +{ + size_t len; + int ip6forwarding = 1; + + len = sizeof ip6forwarding; + if (zserv_privs.change(ZPRIVS_RAISE)) + zlog (NULL, LOG_ERR, "Can't raise privileges"); + if (sysctl (mib_ipv6, MIB_SIZ, NULL, NULL, &ip6forwarding, len) < 0) + { + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog (NULL, LOG_ERR, "Can't lower privileges"); + zlog_warn ("can't get ip6forwarding value"); + return -1; + } + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog (NULL, LOG_ERR, "Can't lower privileges"); + return ip6forwarding; +} + +int +ipforward_ipv6_off (void) +{ + size_t len; + int ip6forwarding = 0; + + len = sizeof ip6forwarding; + if (zserv_privs.change(ZPRIVS_RAISE)) + zlog (NULL, LOG_ERR, "Can't raise privileges"); + if (sysctl (mib_ipv6, MIB_SIZ, NULL, NULL, &ip6forwarding, len) < 0) + { + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog (NULL, LOG_ERR, "Can't lower privileges"); + zlog_warn ("can't get ip6forwarding value"); + return -1; + } + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog (NULL, LOG_ERR, "Can't lower privileges"); + return ip6forwarding; +} +#endif /* HAVE_IPV6 */ diff --git a/zebra/irdp.h b/zebra/irdp.h new file mode 100644 index 0000000..9ce55e5 --- /dev/null +++ b/zebra/irdp.h @@ -0,0 +1,157 @@ +/* ICMP Router Discovery Messages + * Copyright (C) 1997, 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. + */ + +/* + * This file is modified and completed for the Zebra IRDP implementation + * by Robert Olsson, Swedish University of Agricultural Sciences + */ + +#ifndef _IRDP_H +#define _IRDP_H + +#include "lib/vty.h" + +#define TRUE 1 +#define FALSE 0 + +/* ICMP Messages */ +#ifndef ICMP_ROUTERADVERT +#define ICMP_ROUTERADVERT 9 +#endif /* ICMP_ROUTERADVERT */ + +#ifndef ICMP_ROUTERSOLICIT +#define ICMP_ROUTERSOLICIT 10 +#endif /* ICMP_ROUTERSOLICT */ + +/* Multicast groups */ +#ifndef INADDR_ALLHOSTS_GROUP +#define INADDR_ALLHOSTS_GROUP 0xe0000001U /* 224.0.0.1 */ +#endif /* INADDR_ALLHOSTS_GROUP */ + +#ifndef INADDR_ALLRTRS_GROUP +#define INADDR_ALLRTRS_GROUP 0xe0000002U /* 224.0.0.2 */ +#endif /* INADDR_ALLRTRS_GROUP */ + +/* Default irdp packet interval */ +#define IRDP_DEFAULT_INTERVAL 300 + +/* Router constants from RFC1256 */ +#define MAX_INITIAL_ADVERT_INTERVAL 16 +#define MAX_INITIAL_ADVERTISEMENTS 3 +#define MAX_RESPONSE_DELAY 2 + +#define IRDP_MAXADVERTINTERVAL 600 +#define IRDP_MINADVERTINTERVAL 450 /* 0.75*600 */ +#define IRDP_LIFETIME 1350 /* 3*450 */ +#define IRDP_PREFERENCE 0 + +#define ICMP_MINLEN 8 + +#define IRDP_LAST_ADVERT_MESSAGES 2 /* The last adverts with Holdtime 0 */ + +#define IRDP_RX_BUF 1500 + +/* + Comments comes from RFC1256 ICMP Router Discovery Messages. + + The IP destination address to be used for multicast Router + Advertisements sent from the interface. The only permissible + values are the all-systems multicast address, 224.0.0.1, or the + limited-broadcast address, 255.255.255.255. (The all-systems + address is preferred wherever possible, i.e., on any link where + all listening hosts support IP multicast.) + + Default: 224.0.0.1 if the router supports IP multicast on the + interface, else 255.255.255.255 + + The maximum time allowed between sending multicast Router + Advertisements from the interface, in seconds. Must be no less + than 4 seconds and no greater than 1800 seconds. + + Default: 600 seconds + + The minimum time allowed between sending unsolicited multicast + Router Advertisements from the interface, in seconds. Must be no + less than 3 seconds and no greater than MaxAdvertisementInterval. + + Default: 0.75 * MaxAdvertisementInterval + + The value to be placed in the Lifetime field of Router + Advertisements sent from the interface, in seconds. Must be no + less than MaxAdvertisementInterval and no greater than 9000 + seconds. + + Default: 3 * MaxAdvertisementInterval + + The preferability of the address as a default router address, + relative to other router addresses on the same subnet. A 32-bit, + signed, twos-complement integer, with higher values meaning more + preferable. The minimum value (hex 80000000) is used to indicate + that the address, even though it may be advertised, is not to be + used by neighboring hosts as a default router address. + + Default: 0 +*/ + +struct irdp_interface +{ + unsigned long MaxAdvertInterval; + unsigned long MinAdvertInterval; + unsigned long Preference; + + u_int32_t flags; + +#define IF_ACTIVE (1<<0) /* ICMP Active */ +#define IF_BROADCAST (1<<1) /* 255.255.255.255 */ +#define IF_SOLICIT (1<<2) /* Solicit active */ +#define IF_DEBUG_MESSAGES (1<<3) +#define IF_DEBUG_PACKET (1<<4) +#define IF_DEBUG_MISC (1<<5) +#define IF_SHUTDOWN (1<<6) + + struct interface *ifp; + struct thread *t_advertise; + unsigned long irdp_sent; + u_int16_t Lifetime; + + struct list *AdvPrefList; + +}; + +struct Adv +{ + struct in_addr ip; + int pref; +}; + +extern void irdp_init(void); +extern int irdp_sock_init(void); +extern void irdp_finish(void); +extern void irdp_config_write (struct vty *, struct interface *); +extern int irdp_send_thread(struct thread *t_advert); +extern void irdp_advert_off(struct interface *ifp); +extern void process_solicit (struct interface *ifp); +extern int irdp_read_raw(struct thread *r); +extern void send_packet(struct interface *ifp, struct stream *s, + u_int32_t dst, struct prefix *p, u_int32_t ttl); + + +#endif /* _IRDP_H */ diff --git a/zebra/irdp_interface.c b/zebra/irdp_interface.c new file mode 100644 index 0000000..43c63a8 --- /dev/null +++ b/zebra/irdp_interface.c @@ -0,0 +1,783 @@ +/* + * + * Copyright (C) 2000 Robert Olsson. + * Swedish University of Agricultural Sciences + * + * 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. + */ + +/* + * This work includes work with the following copywrite: + * + * Copyright (C) 1997, 2000 Kunihiro Ishiguro + * + */ + +/* + * Thanks to Jens Låås at Swedish University of Agricultural Sciences + * for reviewing and tests. + */ + + +#include + +#ifdef HAVE_IRDP + +#include "if.h" +#include "vty.h" +#include "sockunion.h" +#include "prefix.h" +#include "command.h" +#include "memory.h" +#include "stream.h" +#include "ioctl.h" +#include "connected.h" +#include "log.h" +#include "zclient.h" +#include "thread.h" +#include "zebra/interface.h" +#include "zebra/rtadv.h" +#include "zebra/rib.h" +#include "zebra/zserv.h" +#include "zebra/redistribute.h" +#include "zebra/irdp.h" +#include +#include "if.h" +#include "sockunion.h" +#include "log.h" + + +/* Master of threads. */ +extern struct zebra_t zebrad; + +extern int irdp_sock; + +static const char * +inet_2a(u_int32_t a, char *b) +{ + sprintf(b, "%u.%u.%u.%u", + (a ) & 0xFF, + (a>> 8) & 0xFF, + (a>>16) & 0xFF, + (a>>24) & 0xFF); + return b; +} + + +static struct prefix * +irdp_get_prefix(struct interface *ifp) +{ + struct listnode *node; + struct connected *ifc; + + if (ifp->connected) + for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, ifc)) + return ifc->address; + + return NULL; +} + +/* Join to the add/leave multicast group. */ +static int +if_group (struct interface *ifp, + int sock, + u_int32_t group, + int add_leave) +{ + struct ip_mreq m; + struct prefix *p; + int ret; + char b1[INET_ADDRSTRLEN]; + + memset (&m, 0, sizeof (m)); + m.imr_multiaddr.s_addr = htonl (group); + p = irdp_get_prefix(ifp); + + if(!p) { + zlog_warn ("IRDP: can't get address for %s", ifp->name); + return 1; + } + + m.imr_interface = p->u.prefix4; + + ret = setsockopt (sock, IPPROTO_IP, add_leave, + (char *) &m, sizeof (struct ip_mreq)); + if (ret < 0) + zlog_warn ("IRDP: %s can't setsockopt %s: %s", + add_leave == IP_ADD_MEMBERSHIP? "join group":"leave group", + inet_2a(group, b1), + safe_strerror (errno)); + + return ret; +} + +static int +if_add_group (struct interface *ifp) +{ + struct zebra_if *zi= ifp->info; + struct irdp_interface *irdp = &zi->irdp; + int ret; + char b1[INET_ADDRSTRLEN]; + + ret = if_group (ifp, irdp_sock, INADDR_ALLRTRS_GROUP, IP_ADD_MEMBERSHIP); + if (ret < 0) { + return ret; + } + + if(irdp->flags & IF_DEBUG_MISC ) + zlog_debug("IRDP: Adding group %s for %s", + inet_2a(htonl(INADDR_ALLRTRS_GROUP), b1), + ifp->name); + return 0; +} + +static int +if_drop_group (struct interface *ifp) +{ + struct zebra_if *zi= ifp->info; + struct irdp_interface *irdp = &zi->irdp; + int ret; + char b1[INET_ADDRSTRLEN]; + + ret = if_group (ifp, irdp_sock, INADDR_ALLRTRS_GROUP, IP_DROP_MEMBERSHIP); + if (ret < 0) + return ret; + + if(irdp->flags & IF_DEBUG_MISC) + zlog_debug("IRDP: Leaving group %s for %s", + inet_2a(htonl(INADDR_ALLRTRS_GROUP), b1), + ifp->name); + return 0; +} + +static void +if_set_defaults(struct interface *ifp) +{ + struct zebra_if *zi=ifp->info; + struct irdp_interface *irdp=&zi->irdp; + + irdp->MaxAdvertInterval = IRDP_MAXADVERTINTERVAL; + irdp->MinAdvertInterval = IRDP_MINADVERTINTERVAL; + irdp->Preference = IRDP_PREFERENCE; + irdp->Lifetime = IRDP_LIFETIME; +} + + +static struct Adv *Adv_new (void) +{ + return XCALLOC (MTYPE_TMP, sizeof (struct Adv)); +} + +static void +Adv_free (struct Adv *adv) +{ + XFREE (MTYPE_TMP, adv); +} + +static void +irdp_if_start(struct interface *ifp, int multicast, int set_defaults) +{ + struct zebra_if *zi= ifp->info; + struct irdp_interface *irdp = &zi->irdp; + struct listnode *node; + struct connected *ifc; + u_int32_t timer, seed; + + if (irdp->flags & IF_ACTIVE ) { + zlog_warn("IRDP: Interface is already active %s", ifp->name); + return; + } + if ((irdp_sock < 0) && ((irdp_sock = irdp_sock_init()) < 0)) { + zlog_warn("IRDP: Cannot activate interface %s (cannot create " + "IRDP socket)", ifp->name); + return; + } + irdp->flags |= IF_ACTIVE; + + if(!multicast) + irdp->flags |= IF_BROADCAST; + + if_add_update(ifp); + + if (! (ifp->flags & IFF_UP)) { + zlog_warn("IRDP: Interface is down %s", ifp->name); + } + + /* Shall we cancel if_start if if_add_group fails? */ + + if( multicast) { + if_add_group(ifp); + + if (! (ifp->flags & (IFF_MULTICAST|IFF_ALLMULTI))) { + zlog_warn("IRDP: Interface not multicast enabled %s", ifp->name); + } + } + + if(set_defaults) + if_set_defaults(ifp); + + irdp->irdp_sent = 0; + + /* The spec suggests this for randomness */ + + seed = 0; + if( ifp->connected) + for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, ifc)) + { + seed = ifc->address->u.prefix4.s_addr; + break; + } + + srandom(seed); + timer = (random () % IRDP_DEFAULT_INTERVAL) + 1; + + irdp->AdvPrefList = list_new(); + irdp->AdvPrefList->del = (void (*)(void *)) Adv_free; /* Destructor */ + + + /* And this for startup. Speed limit from 1991 :-). But it's OK*/ + + if(irdp->irdp_sent < MAX_INITIAL_ADVERTISEMENTS && + timer > MAX_INITIAL_ADVERT_INTERVAL ) + timer= MAX_INITIAL_ADVERT_INTERVAL; + + + if(irdp->flags & IF_DEBUG_MISC) + zlog_debug("IRDP: Init timer for %s set to %u", + ifp->name, + timer); + + irdp->t_advertise = thread_add_timer(zebrad.master, + irdp_send_thread, + ifp, + timer); +} + +static void +irdp_if_stop(struct interface *ifp) +{ + struct zebra_if *zi=ifp->info; + struct irdp_interface *irdp=&zi->irdp; + + if (irdp == NULL) { + zlog_warn ("Interface %s structure is NULL", ifp->name); + return; + } + + if (! (irdp->flags & IF_ACTIVE )) { + zlog_warn("Interface is not active %s", ifp->name); + return; + } + + if(! (irdp->flags & IF_BROADCAST)) + if_drop_group(ifp); + + irdp_advert_off(ifp); + + list_delete(irdp->AdvPrefList); + irdp->AdvPrefList=NULL; + + irdp->flags = 0; +} + + +static void +irdp_if_shutdown(struct interface *ifp) +{ + struct zebra_if *zi= ifp->info; + struct irdp_interface *irdp = &zi->irdp; + + if (irdp->flags & IF_SHUTDOWN ) { + zlog_warn("IRDP: Interface is already shutdown %s", ifp->name); + return; + } + + irdp->flags |= IF_SHUTDOWN; + irdp->flags &= ~IF_ACTIVE; + + if(! (irdp->flags & IF_BROADCAST)) + if_drop_group(ifp); + + /* Tell the hosts we are out of service */ + irdp_advert_off(ifp); +} + +static void +irdp_if_no_shutdown(struct interface *ifp) +{ + struct zebra_if *zi= ifp->info; + struct irdp_interface *irdp = &zi->irdp; + + if (! (irdp->flags & IF_SHUTDOWN )) { + zlog_warn("IRDP: Interface is not shutdown %s", ifp->name); + return; + } + + irdp->flags &= ~IF_SHUTDOWN; + + irdp_if_start(ifp, irdp->flags & IF_BROADCAST? FALSE : TRUE, FALSE); + +} + + +/* Write configuration to user */ + +void irdp_config_write (struct vty *vty, struct interface *ifp) +{ + struct zebra_if *zi=ifp->info; + struct irdp_interface *irdp=&zi->irdp; + struct Adv *adv; + struct listnode *node; + char b1[INET_ADDRSTRLEN]; + + if(irdp->flags & IF_ACTIVE || irdp->flags & IF_SHUTDOWN) { + + if( irdp->flags & IF_SHUTDOWN) + vty_out (vty, " ip irdp shutdown %s", VTY_NEWLINE); + + if( irdp->flags & IF_BROADCAST) + vty_out (vty, " ip irdp broadcast%s", VTY_NEWLINE); + else + vty_out (vty, " ip irdp multicast%s", VTY_NEWLINE); + + vty_out (vty, " ip irdp preference %ld%s", + irdp->Preference, VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO (irdp->AdvPrefList, node, adv)) + vty_out (vty, " ip irdp address %s preference %d%s", + inet_2a(adv->ip.s_addr, b1), + adv->pref, + VTY_NEWLINE); + + vty_out (vty, " ip irdp holdtime %d%s", + irdp->Lifetime, VTY_NEWLINE); + + vty_out (vty, " ip irdp minadvertinterval %ld%s", + irdp->MinAdvertInterval, VTY_NEWLINE); + + vty_out (vty, " ip irdp maxadvertinterval %ld%s", + irdp->MaxAdvertInterval, VTY_NEWLINE); + + } +} + + +DEFUN (ip_irdp_multicast, + ip_irdp_multicast_cmd, + "ip irdp multicast", + IP_STR + "ICMP Router discovery on this interface using multicast\n") +{ + struct interface *ifp; + + ifp = (struct interface *) vty->index; + if(!ifp) { + return CMD_WARNING; + } + + irdp_if_start(ifp, TRUE, TRUE); + return CMD_SUCCESS; +} + +DEFUN (ip_irdp_broadcast, + ip_irdp_broadcast_cmd, + "ip irdp broadcast", + IP_STR + "ICMP Router discovery on this interface using broadcast\n") +{ + struct interface *ifp; + + ifp = (struct interface *) vty->index; + if(!ifp) { + return CMD_WARNING; + } + + irdp_if_start(ifp, FALSE, TRUE); + return CMD_SUCCESS; +} + +DEFUN (no_ip_irdp, + no_ip_irdp_cmd, + "no ip irdp", + NO_STR + IP_STR + "Disable ICMP Router discovery on this interface\n") +{ + struct interface *ifp; + + ifp = (struct interface *) vty->index; + if(!ifp) { + return CMD_WARNING; + } + + irdp_if_stop(ifp); + return CMD_SUCCESS; +} + +DEFUN (ip_irdp_shutdown, + ip_irdp_shutdown_cmd, + "ip irdp shutdown", + IP_STR + "ICMP Router discovery shutdown on this interface\n") +{ + struct interface *ifp; + + ifp = (struct interface *) vty->index; + if(!ifp) { + return CMD_WARNING; + } + + irdp_if_shutdown(ifp); + return CMD_SUCCESS; +} + +DEFUN (no_ip_irdp_shutdown, + no_ip_irdp_shutdown_cmd, + "no ip irdp shutdown", + NO_STR + IP_STR + "ICMP Router discovery no shutdown on this interface\n") +{ + struct interface *ifp; + + ifp = (struct interface *) vty->index; + if(!ifp) { + return CMD_WARNING; + } + + irdp_if_no_shutdown(ifp); + return CMD_SUCCESS; +} + +DEFUN (ip_irdp_holdtime, + ip_irdp_holdtime_cmd, + "ip irdp holdtime <0-9000>", + IP_STR + "ICMP Router discovery on this interface\n" + "Set holdtime value\n" + "Holdtime value in seconds. Default is 1800 seconds\n") +{ + struct interface *ifp; + struct zebra_if *zi; + struct irdp_interface *irdp; + ifp = (struct interface *) vty->index; + if(!ifp) { + return CMD_WARNING; + } + + zi=ifp->info; + irdp=&zi->irdp; + + irdp->Lifetime = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN (ip_irdp_minadvertinterval, + ip_irdp_minadvertinterval_cmd, + "ip irdp minadvertinterval <3-1800>", + IP_STR + "ICMP Router discovery on this interface\n" + "Set minimum time between advertisement\n" + "Minimum advertisement interval in seconds\n") +{ + struct interface *ifp; + struct zebra_if *zi; + struct irdp_interface *irdp; + ifp = (struct interface *) vty->index; + if(!ifp) { + return CMD_WARNING; + } + + zi=ifp->info; + irdp=&zi->irdp; + + if( (unsigned) atoi(argv[0]) <= irdp->MaxAdvertInterval) { + irdp->MinAdvertInterval = atoi(argv[0]); + + return CMD_SUCCESS; + } + + vty_out (vty, "ICMP warning maxadvertinterval is greater or equal than minadvertinterval%s", + VTY_NEWLINE); + + vty_out (vty, "Please correct!%s", + VTY_NEWLINE); + return CMD_WARNING; +} + +DEFUN (ip_irdp_maxadvertinterval, + ip_irdp_maxadvertinterval_cmd, + "ip irdp maxadvertinterval <4-1800>", + IP_STR + "ICMP Router discovery on this interface\n" + "Set maximum time between advertisement\n" + "Maximum advertisement interval in seconds\n") +{ + struct interface *ifp; + struct zebra_if *zi; + struct irdp_interface *irdp; + ifp = (struct interface *) vty->index; + if(!ifp) { + return CMD_WARNING; + } + + zi=ifp->info; + irdp=&zi->irdp; + + + if( irdp->MinAdvertInterval <= (unsigned) atoi(argv[0]) ) { + irdp->MaxAdvertInterval = atoi(argv[0]); + + return CMD_SUCCESS; + } + + vty_out (vty, "ICMP warning maxadvertinterval is greater or equal than minadvertinterval%s", + VTY_NEWLINE); + + vty_out (vty, "Please correct!%s", + VTY_NEWLINE); + return CMD_WARNING; +} + +/* DEFUN needs to be fixed for negative ranages... + * "ip irdp preference <-2147483648-2147483647>", + * Be positive for now. :-) + */ + +DEFUN (ip_irdp_preference, + ip_irdp_preference_cmd, + "ip irdp preference <0-2147483647>", + IP_STR + "ICMP Router discovery on this interface\n" + "Set default preference level for this interface\n" + "Preference level\n") +{ + struct interface *ifp; + struct zebra_if *zi; + struct irdp_interface *irdp; + ifp = (struct interface *) vty->index; + if(!ifp) { + return CMD_WARNING; + } + + zi=ifp->info; + irdp=&zi->irdp; + + irdp->Preference = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN (ip_irdp_address_preference, + ip_irdp_address_preference_cmd, + "ip irdp address A.B.C.D preference <0-2147483647>", + IP_STR + "Alter ICMP Router discovery preference this interface\n" + "Specify IRDP non-default preference to advertise\n" + "Set IRDP address for advertise\n" + "Preference level\n") +{ + struct listnode *node; + struct in_addr ip; + int pref; + int ret; + struct interface *ifp; + struct zebra_if *zi; + struct irdp_interface *irdp; + struct Adv *adv; + + ifp = (struct interface *) vty->index; + if(!ifp) { + return CMD_WARNING; + } + + zi=ifp->info; + irdp=&zi->irdp; + + ret = inet_aton(argv[0], &ip); + if(!ret) return CMD_WARNING; + + pref = atoi(argv[1]); + + for (ALL_LIST_ELEMENTS_RO (irdp->AdvPrefList, node, adv)) + if(adv->ip.s_addr == ip.s_addr) + return CMD_SUCCESS; + + adv = Adv_new(); + adv->ip = ip; + adv->pref = pref; + listnode_add(irdp->AdvPrefList, adv); + + return CMD_SUCCESS; + +} + +DEFUN (no_ip_irdp_address_preference, + no_ip_irdp_address_preference_cmd, + "no ip irdp address A.B.C.D preference <0-2147483647>", + NO_STR + IP_STR + "Alter ICMP Router discovery preference this interface\n" + "Removes IRDP non-default preference\n" + "Select IRDP address\n" + "Old preference level\n") +{ + struct listnode *node, *nnode; + struct in_addr ip; + int ret; + struct interface *ifp; + struct zebra_if *zi; + struct irdp_interface *irdp; + struct Adv *adv; + + ifp = (struct interface *) vty->index; + if(!ifp) { + return CMD_WARNING; + } + + zi=ifp->info; + irdp=&zi->irdp; + + ret = inet_aton(argv[0], &ip); + if (!ret) + return CMD_WARNING; + + for (ALL_LIST_ELEMENTS (irdp->AdvPrefList, node, nnode, adv)) + { + if(adv->ip.s_addr == ip.s_addr ) + { + listnode_delete(irdp->AdvPrefList, adv); + break; + } + } + + return CMD_SUCCESS; +} + +DEFUN (ip_irdp_debug_messages, + ip_irdp_debug_messages_cmd, + "ip irdp debug messages", + IP_STR + "ICMP Router discovery debug Averts. and Solicits (short)\n") +{ + struct interface *ifp; + struct zebra_if *zi; + struct irdp_interface *irdp; + ifp = (struct interface *) vty->index; + if(!ifp) { + return CMD_WARNING; + } + + zi=ifp->info; + irdp=&zi->irdp; + + irdp->flags |= IF_DEBUG_MESSAGES; + + return CMD_SUCCESS; +} + +DEFUN (ip_irdp_debug_misc, + ip_irdp_debug_misc_cmd, + "ip irdp debug misc", + IP_STR + "ICMP Router discovery debug Averts. and Solicits (short)\n") +{ + struct interface *ifp; + struct zebra_if *zi; + struct irdp_interface *irdp; + ifp = (struct interface *) vty->index; + if(!ifp) { + return CMD_WARNING; + } + + zi=ifp->info; + irdp=&zi->irdp; + + irdp->flags |= IF_DEBUG_MISC; + + return CMD_SUCCESS; +} + +DEFUN (ip_irdp_debug_packet, + ip_irdp_debug_packet_cmd, + "ip irdp debug packet", + IP_STR + "ICMP Router discovery debug Averts. and Solicits (short)\n") +{ + struct interface *ifp; + struct zebra_if *zi; + struct irdp_interface *irdp; + ifp = (struct interface *) vty->index; + if(!ifp) { + return CMD_WARNING; + } + + zi=ifp->info; + irdp=&zi->irdp; + + irdp->flags |= IF_DEBUG_PACKET; + + return CMD_SUCCESS; +} + + +DEFUN (ip_irdp_debug_disable, + ip_irdp_debug_disable_cmd, + "ip irdp debug disable", + IP_STR + "ICMP Router discovery debug Averts. and Solicits (short)\n") +{ + struct interface *ifp; + struct zebra_if *zi; + struct irdp_interface *irdp; + ifp = (struct interface *) vty->index; + if(!ifp) { + return CMD_WARNING; + } + + zi=ifp->info; + irdp=&zi->irdp; + + irdp->flags &= ~IF_DEBUG_PACKET; + irdp->flags &= ~IF_DEBUG_MESSAGES; + irdp->flags &= ~IF_DEBUG_MISC; + + return CMD_SUCCESS; +} + +void +irdp_init () +{ + install_element (INTERFACE_NODE, &ip_irdp_broadcast_cmd); + install_element (INTERFACE_NODE, &ip_irdp_multicast_cmd); + install_element (INTERFACE_NODE, &no_ip_irdp_cmd); + install_element (INTERFACE_NODE, &ip_irdp_shutdown_cmd); + install_element (INTERFACE_NODE, &no_ip_irdp_shutdown_cmd); + install_element (INTERFACE_NODE, &ip_irdp_holdtime_cmd); + install_element (INTERFACE_NODE, &ip_irdp_maxadvertinterval_cmd); + install_element (INTERFACE_NODE, &ip_irdp_minadvertinterval_cmd); + install_element (INTERFACE_NODE, &ip_irdp_preference_cmd); + install_element (INTERFACE_NODE, &ip_irdp_address_preference_cmd); + install_element (INTERFACE_NODE, &no_ip_irdp_address_preference_cmd); + + install_element (INTERFACE_NODE, &ip_irdp_debug_messages_cmd); + install_element (INTERFACE_NODE, &ip_irdp_debug_misc_cmd); + install_element (INTERFACE_NODE, &ip_irdp_debug_packet_cmd); + install_element (INTERFACE_NODE, &ip_irdp_debug_disable_cmd); +} + +#endif /* HAVE_IRDP */ diff --git a/zebra/irdp_main.c b/zebra/irdp_main.c new file mode 100644 index 0000000..cf78a54 --- /dev/null +++ b/zebra/irdp_main.c @@ -0,0 +1,334 @@ +/* + * + * Copyright (C) 2000 Robert Olsson. + * Swedish University of Agricultural Sciences + * + * 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. + */ + +/* + * This work includes work with the following copywrite: + * + * Copyright (C) 1997, 2000 Kunihiro Ishiguro + * + */ + +/* + * Thanks to Jens Låås at Swedish University of Agricultural Sciences + * for reviewing and tests. + */ + + +#include + +#ifdef HAVE_IRDP + +#include "if.h" +#include "vty.h" +#include "sockunion.h" +#include "sockopt.h" +#include "prefix.h" +#include "command.h" +#include "memory.h" +#include "stream.h" +#include "ioctl.h" +#include "connected.h" +#include "log.h" +#include "zclient.h" +#include "thread.h" +#include "privs.h" +#include "zebra/interface.h" +#include "zebra/rtadv.h" +#include "zebra/rib.h" +#include "zebra/zserv.h" +#include "zebra/redistribute.h" +#include "zebra/irdp.h" +#include + +#include "checksum.h" +#include "if.h" +#include "sockunion.h" +#include "log.h" + +/* GLOBAL VARS */ + +extern struct zebra_privs_t zserv_privs; + +/* Master of threads. */ +extern struct zebra_t zebrad; +struct thread *t_irdp_raw; + +/* Timer interval of irdp. */ +int irdp_timer_interval = IRDP_DEFAULT_INTERVAL; + +int +irdp_sock_init (void) +{ + int ret, i; + int save_errno; + int sock; + + if ( zserv_privs.change (ZPRIVS_RAISE) ) + zlog_err ("irdp_sock_init: could not raise privs, %s", + safe_strerror (errno) ); + + sock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP); + save_errno = errno; + + if ( zserv_privs.change (ZPRIVS_LOWER) ) + zlog_err ("irdp_sock_init: could not lower privs, %s", + safe_strerror (errno) ); + + if (sock < 0) { + zlog_warn ("IRDP: can't create irdp socket %s", safe_strerror(save_errno)); + return sock; + }; + + i = 1; + ret = setsockopt (sock, IPPROTO_IP, IP_TTL, + (void *) &i, sizeof (i)); + if (ret < 0) { + zlog_warn ("IRDP: can't do irdp sockopt %s", safe_strerror(errno)); + close(sock); + return ret; + }; + + ret = setsockopt_ifindex (AF_INET, sock, 1); + if (ret < 0) { + zlog_warn ("IRDP: can't do irdp sockopt %s", safe_strerror(errno)); + close(sock); + return ret; + }; + + t_irdp_raw = thread_add_read (zebrad.master, irdp_read_raw, NULL, sock); + + return sock; +} + + +static int +get_pref(struct irdp_interface *irdp, struct prefix *p) +{ + struct listnode *node; + struct Adv *adv; + + /* Use default preference or use the override pref */ + + if( irdp->AdvPrefList == NULL ) + return irdp->Preference; + + for (ALL_LIST_ELEMENTS_RO (irdp->AdvPrefList, node, adv)) + if( p->u.prefix4.s_addr == adv->ip.s_addr ) + return adv->pref; + + return irdp->Preference; +} + +/* Make ICMP Router Advertisement Message. */ +static int +make_advertisement_packet (struct interface *ifp, + struct prefix *p, + struct stream *s) +{ + struct zebra_if *zi=ifp->info; + struct irdp_interface *irdp=&zi->irdp; + int size; + int pref; + u_int16_t checksum; + + pref = get_pref(irdp, p); + + stream_putc (s, ICMP_ROUTERADVERT); /* Type. */ + stream_putc (s, 0); /* Code. */ + stream_putw (s, 0); /* Checksum. */ + stream_putc (s, 1); /* Num address. */ + stream_putc (s, 2); /* Address Entry Size. */ + + if(irdp->flags & IF_SHUTDOWN) + stream_putw (s, 0); + else + stream_putw (s, irdp->Lifetime); + + stream_putl (s, htonl(p->u.prefix4.s_addr)); /* Router address. */ + stream_putl (s, pref); + + /* in_cksum return network byte order value */ + size = 16; + checksum = in_cksum (s->data, size); + stream_putw_at (s, 2, htons(checksum)); + + return size; +} + +static void +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; + + if (! (ifp->flags & IFF_UP)) return; + + if (irdp->flags & IF_BROADCAST) + dst =INADDR_BROADCAST ; + else + dst = htonl(INADDR_ALLHOSTS_GROUP); + + if(irdp->flags & IF_DEBUG_MESSAGES) + zlog_debug("IRDP: TX Advert on %s %s Holdtime=%d Preference=%d", + ifp->name, + prefix2str(p, buf, sizeof buf), + irdp->flags & IF_SHUTDOWN? 0 : irdp->Lifetime, + get_pref(irdp, p)); + + send_packet (ifp, s, dst, p, ttl); +} + +static void irdp_advertisement (struct interface *ifp, struct prefix *p) +{ + struct stream *s; + s = stream_new (128); + make_advertisement_packet (ifp, p, s); + irdp_send(ifp, p, s); + stream_free (s); +} + +int irdp_send_thread(struct thread *t_advert) +{ + u_int32_t timer, tmp; + struct interface *ifp = THREAD_ARG (t_advert); + struct zebra_if *zi=ifp->info; + struct irdp_interface *irdp=&zi->irdp; + struct prefix *p; + struct listnode *node, *nnode; + struct connected *ifc; + + irdp->flags &= ~IF_SOLICIT; + + if(ifp->connected) + for (ALL_LIST_ELEMENTS (ifp->connected, node, nnode, ifc)) + { + p = ifc->address; + + if (p->family != AF_INET) + continue; + + irdp_advertisement(ifp, p); + irdp->irdp_sent++; + } + + tmp = irdp->MaxAdvertInterval-irdp->MinAdvertInterval; + timer = (random () % tmp ) + 1; + timer = irdp->MinAdvertInterval + timer; + + if(irdp->irdp_sent < MAX_INITIAL_ADVERTISEMENTS && + timer > MAX_INITIAL_ADVERT_INTERVAL ) + timer= MAX_INITIAL_ADVERT_INTERVAL; + + if(irdp->flags & IF_DEBUG_MISC) + zlog_debug("IRDP: New timer for %s set to %u\n", ifp->name, timer); + + irdp->t_advertise = thread_add_timer(zebrad.master, irdp_send_thread, ifp, timer); + return 0; +} + +void irdp_advert_off(struct interface *ifp) +{ + struct zebra_if *zi=ifp->info; + struct irdp_interface *irdp=&zi->irdp; + struct listnode *node, *nnode; + int i; + struct connected *ifc; + struct prefix *p; + + if(irdp->t_advertise) thread_cancel(irdp->t_advertise); + irdp->t_advertise = NULL; + + if(ifp->connected) + for (ALL_LIST_ELEMENTS (ifp->connected, node, nnode, ifc)) + { + p = ifc->address; + + /* Output some packets with Lifetime 0 + we should add a wait... + */ + + for(i=0; i< IRDP_LAST_ADVERT_MESSAGES; i++) + { + irdp->irdp_sent++; + irdp_advertisement(ifp, p); + } + } +} + + +void process_solicit (struct interface *ifp) +{ + struct zebra_if *zi=ifp->info; + struct irdp_interface *irdp=&zi->irdp; + u_int32_t timer; + + /* When SOLICIT is active we reject further incoming solicits + this keeps down the answering rate so we don't have think + about DoS attacks here. */ + + if( irdp->flags & IF_SOLICIT) return; + + irdp->flags |= IF_SOLICIT; + if(irdp->t_advertise) thread_cancel(irdp->t_advertise); + irdp->t_advertise = NULL; + + timer = (random () % MAX_RESPONSE_DELAY) + 1; + + irdp->t_advertise = thread_add_timer(zebrad.master, + irdp_send_thread, + ifp, + timer); +} + +void irdp_finish() +{ + + struct listnode *node, *nnode; + struct interface *ifp; + struct zebra_if *zi; + struct irdp_interface *irdp; + + zlog_info("IRDP: Received shutdown notification."); + + for (ALL_LIST_ELEMENTS (iflist, node, nnode, ifp)) + { + zi = ifp->info; + + if (!zi) + continue; + irdp = &zi->irdp; + if (!irdp) + continue; + + if (irdp->flags & IF_ACTIVE ) + { + irdp->flags |= IF_SHUTDOWN; + irdp_advert_off(ifp); + } + } +} + +#endif /* HAVE_IRDP */ diff --git a/zebra/irdp_packet.c b/zebra/irdp_packet.c new file mode 100644 index 0000000..0d31050 --- /dev/null +++ b/zebra/irdp_packet.c @@ -0,0 +1,368 @@ +/* + * + * Copyright (C) 2000 Robert Olsson. + * Swedish University of Agricultural Sciences + * + * 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. + */ + +/* + * This work includes work with the following copywrite: + * + * Copyright (C) 1997, 2000 Kunihiro Ishiguro + * + */ + +/* + * Thanks to Jens Låås at Swedish University of Agricultural Sciences + * for reviewing and tests. + */ + + +#include + + +#ifdef HAVE_IRDP + +#include "if.h" +#include "vty.h" +#include "sockunion.h" +#include "prefix.h" +#include "command.h" +#include "memory.h" +#include "stream.h" +#include "ioctl.h" +#include "connected.h" +#include "log.h" +#include "zclient.h" +#include "thread.h" +#include "zebra/interface.h" +#include "zebra/rtadv.h" +#include "zebra/rib.h" +#include "zebra/zserv.h" +#include "zebra/redistribute.h" +#include "zebra/irdp.h" +#include +#include "if.h" +#include "checksum.h" +#include "sockunion.h" +#include "log.h" +#include "sockopt.h" + + +/* GLOBAL VARS */ + +int irdp_sock = -1; + +extern struct zebra_t zebrad; +extern struct thread *t_irdp_raw; + +static void +parse_irdp_packet(char *p, + int len, + struct interface *ifp) +{ + struct ip *ip = (struct ip *)p ; + struct icmphdr *icmp; + struct in_addr src; + int ip_hlen, iplen, datalen; + struct zebra_if *zi; + struct irdp_interface *irdp; + + zi = ifp->info; + if (!zi) + return; + + irdp = &zi->irdp; + if (!irdp) + return; + + ip_hlen = ip->ip_hl << 2; + + sockopt_iphdrincl_swab_systoh (ip); + + iplen = ip->ip_len; + datalen = len - ip_hlen; + src = ip->ip_src; + + if (len != iplen) + { + zlog_err ("IRDP: RX length doesnt match IP length"); + return; + } + + if (iplen < ICMP_MINLEN) + { + zlog_err ("IRDP: RX ICMP packet too short from %s\n", + inet_ntoa (src)); + return; + } + + /* XXX: RAW doesnt receive link-layer, surely? ??? */ + /* Check so we don't checksum packets longer than oure RX_BUF - (ethlen + + len of IP-header) 14+20 */ + if (iplen > IRDP_RX_BUF-34) + { + zlog_err ("IRDP: RX ICMP packet too long from %s\n", + inet_ntoa (src)); + return; + } + + icmp = (struct icmphdr *) (p+ip_hlen); + + /* check icmp checksum */ + if (in_cksum (icmp, datalen) != icmp->checksum) + { + zlog_warn ("IRDP: RX ICMP packet from %s. Bad checksum, silently ignored", + inet_ntoa (src)); + return; + } + + /* Handle just only IRDP */ + if (!(icmp->type == ICMP_ROUTERADVERT + || icmp->type == ICMP_ROUTERSOLICIT)) + return; + + if (icmp->code != 0) + { + zlog_warn ("IRDP: RX packet type %d from %s. Bad ICMP type code," + " silently ignored", + icmp->type, inet_ntoa (src)); + return; + } + + if (! ((ntohl (ip->ip_dst.s_addr) == INADDR_BROADCAST) + && (irdp->flags & IF_BROADCAST)) + || + (ntohl (ip->ip_dst.s_addr) == INADDR_ALLRTRS_GROUP + && !(irdp->flags & IF_BROADCAST))) + { + zlog_warn ("IRDP: RX illegal from %s to %s while %s operates in %s\n", + inet_ntoa (src), + ntohl (ip->ip_dst.s_addr) == INADDR_ALLRTRS_GROUP ? + "multicast" : inet_ntoa (ip->ip_dst), + ifp->name, + irdp->flags & IF_BROADCAST ? "broadcast" : "multicast"); + + zlog_warn ("IRDP: Please correct settings\n"); + return; + } + + switch (icmp->type) + { + case ICMP_ROUTERADVERT: + break; + + case ICMP_ROUTERSOLICIT: + + if(irdp->flags & IF_DEBUG_MESSAGES) + zlog_debug ("IRDP: RX Solicit on %s from %s\n", + ifp->name, + inet_ntoa (src)); + + process_solicit(ifp); + break; + + default: + zlog_warn ("IRDP: RX type %d from %s. Bad ICMP type, silently ignored", + icmp->type, + inet_ntoa (src)); + } +} + +static int +irdp_recvmsg (int sock, u_char *buf, int size, int *ifindex) +{ + struct msghdr msg; + struct iovec iov; + char adata[CMSG_SPACE( SOPT_SIZE_CMSG_PKTINFO_IPV4() )]; + int ret; + + msg.msg_name = (void *)0; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = (void *) adata; + msg.msg_controllen = sizeof adata; + + iov.iov_base = buf; + iov.iov_len = size; + + ret = recvmsg (sock, &msg, 0); + if (ret < 0) { + zlog_warn("IRDP: recvmsg: read error %s", safe_strerror(errno)); + return ret; + } + + if (msg.msg_flags & MSG_TRUNC) { + zlog_warn("IRDP: recvmsg: truncated message"); + return ret; + } + if (msg.msg_flags & MSG_CTRUNC) { + zlog_warn("IRDP: recvmsg: truncated control message"); + return ret; + } + + *ifindex = getsockopt_ifindex (AF_INET, &msg); + + return ret; +} + +int irdp_read_raw(struct thread *r) +{ + struct interface *ifp; + struct zebra_if *zi; + struct irdp_interface *irdp; + char buf[IRDP_RX_BUF]; + int ret, ifindex = 0; + + int irdp_sock = THREAD_FD (r); + t_irdp_raw = thread_add_read (zebrad.master, irdp_read_raw, NULL, irdp_sock); + + ret = irdp_recvmsg (irdp_sock, (u_char *) buf, IRDP_RX_BUF, &ifindex); + + if (ret < 0) zlog_warn ("IRDP: RX Error length = %d", ret); + + ifp = if_lookup_by_index(ifindex); + if(! ifp ) return ret; + + zi= ifp->info; + if(! zi ) return ret; + + irdp = &zi->irdp; + if(! irdp ) return ret; + + if(! (irdp->flags & IF_ACTIVE)) { + + if(irdp->flags & IF_DEBUG_MISC) + zlog_debug("IRDP: RX ICMP for disabled interface %s\n", ifp->name); + return 0; + } + + if(irdp->flags & IF_DEBUG_PACKET) { + int i; + zlog_debug("IRDP: RX (idx %d) ", ifindex); + for(i=0; i < ret; i++) zlog_debug( "IRDP: RX %x ", buf[i]&0xFF); + } + + parse_irdp_packet(buf, ret, ifp); + + return ret; +} + +void +send_packet(struct interface *ifp, + struct stream *s, + u_int32_t dst, + struct prefix *p, + u_int32_t ttl) +{ + static struct sockaddr_in sockdst = {AF_INET}; + struct ip *ip; + struct icmphdr *icmp; + struct msghdr *msg; + struct cmsghdr *cmsg; + struct iovec iovector; + char msgbuf[256]; + char buf[256]; + struct in_pktinfo *pktinfo; + u_long src; + int on; + + if (!(ifp->flags & IFF_UP)) + return; + + if (p) + src = ntohl(p->u.prefix4.s_addr); + else + src = 0; /* Is filled in */ + + ip = (struct ip *) buf; + ip->ip_hl = sizeof(struct ip) >> 2; + ip->ip_v = IPVERSION; + ip->ip_tos = 0xC0; + ip->ip_off = 0L; + ip->ip_p = 1; /* IP_ICMP */ + ip->ip_ttl = ttl; + ip->ip_src.s_addr = src; + ip->ip_dst.s_addr = dst; + icmp = (struct icmphdr *) (buf + sizeof (struct ip)); + + /* Merge IP header with icmp packet */ + assert (stream_get_endp(s) < (sizeof (buf) - sizeof (struct ip))); + stream_get(icmp, s, stream_get_endp(s)); + + /* icmp->checksum is already calculated */ + ip->ip_len = sizeof(struct ip) + stream_get_endp(s); + + on = 1; + if (setsockopt(irdp_sock, IPPROTO_IP, IP_HDRINCL, + (char *) &on, sizeof(on)) < 0) + zlog_warn("sendto %s", safe_strerror (errno)); + + + if(dst == INADDR_BROADCAST ) { + on = 1; + if (setsockopt(irdp_sock, SOL_SOCKET, SO_BROADCAST, + (char *) &on, sizeof(on)) < 0) + zlog_warn("sendto %s", safe_strerror (errno)); + } + + if(dst != INADDR_BROADCAST) { + on = 0; + if( setsockopt(irdp_sock,IPPROTO_IP, IP_MULTICAST_LOOP, + (char *)&on,sizeof(on)) < 0) + zlog_warn("sendto %s", safe_strerror (errno)); + } + + memset(&sockdst,0,sizeof(sockdst)); + sockdst.sin_family=AF_INET; + sockdst.sin_addr.s_addr = dst; + + cmsg = (struct cmsghdr *) (msgbuf + sizeof(struct msghdr)); + cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(struct in_pktinfo); + cmsg->cmsg_level = SOL_IP; + cmsg->cmsg_type = IP_PKTINFO; + pktinfo = (struct in_pktinfo *) CMSG_DATA(cmsg); + pktinfo->ipi_ifindex = ifp->ifindex; + pktinfo->ipi_spec_dst.s_addr = src; + pktinfo->ipi_addr.s_addr = src; + + iovector.iov_base = (void *) buf; + iovector.iov_len = ip->ip_len; + msg = (struct msghdr *) msgbuf; + msg->msg_name = &sockdst; + msg->msg_namelen = sizeof(sockdst); + msg->msg_iov = &iovector; + msg->msg_iovlen = 1; + msg->msg_control = cmsg; + msg->msg_controllen = cmsg->cmsg_len; + + sockopt_iphdrincl_swab_htosys (ip); + + if (sendmsg(irdp_sock, msg, 0) < 0) { + zlog_warn("sendto %s", safe_strerror (errno)); + } + /* printf("TX on %s idx %d\n", ifp->name, ifp->ifindex); */ +} + + +#endif /* HAVE_IRDP */ + + + diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c new file mode 100644 index 0000000..23b2153 --- /dev/null +++ b/zebra/kernel_netlink.c @@ -0,0 +1,20 @@ +/* Kernel communication using netlink 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. + */ diff --git a/zebra/kernel_null.c b/zebra/kernel_null.c new file mode 100644 index 0000000..1a16a75 --- /dev/null +++ b/zebra/kernel_null.c @@ -0,0 +1,62 @@ +/* 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 + +#include "zebra/zserv.h" +#include "zebra/rt.h" +#include "zebra/redistribute.h" +#include "zebra/connected.h" +#include "zebra/rib.h" + +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; } + +int kernel_address_add_ipv4 (struct interface *a, struct connected *b) +{ + zlog_debug ("%s", __func__); + SET_FLAG (b->conf, ZEBRA_IFC_REAL); + connected_add_ipv4 (a, 0, &b->address->u.prefix4, b->address->prefixlen, + (b->destination ? &b->destination->u.prefix4 : NULL), + NULL); + return 0; +} + +int kernel_address_delete_ipv4 (struct interface *a, struct connected *b) +{ + zlog_debug ("%s", __func__); + connected_delete_ipv4 (a, 0, &b->address->u.prefix4, b->address->prefixlen, + (b->destination ? &b->destination->u.prefix4 : NULL)); + return 0; +} + +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 new file mode 100644 index 0000000..64c6cbb --- /dev/null +++ b/zebra/kernel_socket.c @@ -0,0 +1,1367 @@ +/* Kernel communication using routing socket. + * 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 "if.h" +#include "prefix.h" +#include "sockunion.h" +#include "connected.h" +#include "memory.h" +#include "ioctl.h" +#include "log.h" +#include "str.h" +#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; + +/* + * 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. + * + * 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(ROUNDUP_TYPE) - 1))) : sizeof(ROUNDUP_TYPE)) + +#endif /* defined(ROUNDUP) */ + +/* + * Given a pointer (sockaddr or void *), return the number of bytes + * taken up by the sockaddr and any padding needed for alignment. + */ +#if defined(HAVE_STRUCT_SOCKADDR_SA_LEN) +#define SAROUNDUP(X) ROUNDUP(((struct sockaddr *)(X))->sa_len) +#elif defined(HAVE_IPV6) +/* + * One would hope all fixed-size structure definitions are aligned, + * but round them up nonetheless. + */ +#define SAROUNDUP(X) \ + (((struct sockaddr *)(X))->sa_family == AF_INET ? \ + ROUNDUP(sizeof(struct sockaddr_in)):\ + (((struct sockaddr *)(X))->sa_family == AF_INET6 ? \ + ROUNDUP(sizeof(struct sockaddr_in6)) : \ + (((struct sockaddr *)(X))->sa_family == AF_LINK ? \ + ROUNDUP(sizeof(struct sockaddr_dl)) : sizeof(struct sockaddr)))) +#else /* HAVE_IPV6 */ +#define SAROUNDUP(X) \ + (((struct sockaddr *)(X))->sa_family == AF_INET ? \ + ROUNDUP(sizeof(struct sockaddr_in)):\ + (((struct sockaddr *)(X))->sa_family == AF_LINK ? \ + ROUNDUP(sizeof(struct sockaddr_dl)) : sizeof(struct sockaddr))) +#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */ + +#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)) \ + { \ + int len = SAROUNDUP ((PNT)); \ + 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)) \ + { \ + int len = SAROUNDUP ((PNT)); \ + rta_copy((DEST), (PNT)); \ + (PNT) += len; \ + } + +#define RTA_NAME_GET(DEST, RTA, RTMADDRS, PNT, LEN) \ + if ((RTMADDRS) & (RTA)) \ + { \ + u_char *pdest = (u_char *) (DEST); \ + int len = SAROUNDUP ((PNT)); \ + struct sockaddr_dl *sdl = (struct sockaddr_dl *)(PNT); \ + if (IS_ZEBRA_DEBUG_KERNEL) \ + zlog_debug ("%s: RTA_SDL_GET nlen %d, alen %d", \ + __func__, sdl->sdl_nlen, sdl->sdl_alen); \ + if ( ((DEST) != NULL) && (sdl->sdl_family == AF_LINK) \ + && (sdl->sdl_nlen < IFNAMSIZ) && (sdl->sdl_nlen <= len) ) \ + { \ + memcpy (pdest, sdl->sdl_data, sdl->sdl_nlen); \ + pdest[sdl->sdl_nlen] = '\0'; \ + (LEN) = sdl->sdl_nlen; \ + } \ + (PNT) += len; \ + } \ + else \ + { \ + (LEN) = 0; \ + } +/* Routing socket message types. */ +const struct message rtm_type_str[] = +{ + {RTM_ADD, "RTM_ADD"}, + {RTM_DELETE, "RTM_DELETE"}, + {RTM_CHANGE, "RTM_CHANGE"}, + {RTM_GET, "RTM_GET"}, + {RTM_LOSING, "RTM_LOSING"}, + {RTM_REDIRECT, "RTM_REDIRECT"}, + {RTM_MISS, "RTM_MISS"}, + {RTM_LOCK, "RTM_LOCK"}, +#ifdef OLDADD + {RTM_OLDADD, "RTM_OLDADD"}, +#endif /* RTM_OLDADD */ +#ifdef RTM_OLDDEL + {RTM_OLDDEL, "RTM_OLDDEL"}, +#endif /* RTM_OLDDEL */ + {RTM_RESOLVE, "RTM_RESOLVE"}, + {RTM_NEWADDR, "RTM_NEWADDR"}, + {RTM_DELADDR, "RTM_DELADDR"}, + {RTM_IFINFO, "RTM_IFINFO"}, +#ifdef RTM_OIFINFO + {RTM_OIFINFO, "RTM_OIFINFO"}, +#endif /* RTM_OIFINFO */ +#ifdef RTM_NEWMADDR + {RTM_NEWMADDR, "RTM_NEWMADDR"}, +#endif /* RTM_NEWMADDR */ +#ifdef RTM_DELMADDR + {RTM_DELMADDR, "RTM_DELMADDR"}, +#endif /* RTM_DELMADDR */ +#ifdef RTM_IFANNOUNCE + {RTM_IFANNOUNCE, "RTM_IFANNOUNCE"}, +#endif /* RTM_IFANNOUNCE */ + {0, NULL} +}; + +static const struct message rtm_flag_str[] = +{ + {RTF_UP, "UP"}, + {RTF_GATEWAY, "GATEWAY"}, + {RTF_HOST, "HOST"}, + {RTF_REJECT, "REJECT"}, + {RTF_DYNAMIC, "DYNAMIC"}, + {RTF_MODIFIED, "MODIFIED"}, + {RTF_DONE, "DONE"}, +#ifdef RTF_MASK + {RTF_MASK, "MASK"}, +#endif /* RTF_MASK */ +#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 + {RTF_PRIVATE, "PRIVATE"}, +#endif /* RTF_PRIVATE */ + {RTF_PROTO1, "PROTO1"}, + {RTF_PROTO2, "PROTO2"}, +#ifdef RTF_PRCLONING + {RTF_PRCLONING, "PRCLONING"}, +#endif /* RTF_PRCLONING */ +#ifdef RTF_WASCLONED + {RTF_WASCLONED, "WASCLONED"}, +#endif /* RTF_WASCLONED */ +#ifdef RTF_PROTO3 + {RTF_PROTO3, "PROTO3"}, +#endif /* RTF_PROTO3 */ +#ifdef RTF_PINNED + {RTF_PINNED, "PINNED"}, +#endif /* RTF_PINNED */ +#ifdef RTF_LOCAL + {RTF_LOCAL, "LOCAL"}, +#endif /* RTF_LOCAL */ +#ifdef RTF_BROADCAST + {RTF_BROADCAST, "BROADCAST"}, +#endif /* RTF_BROADCAST */ +#ifdef RTF_MULTICAST + {RTF_MULTICAST, "MULTICAST"}, +#endif /* RTF_MULTICAST */ +#ifdef RTF_MULTIRT + {RTF_MULTIRT, "MULTIRT"}, +#endif /* RTF_MULTIRT */ +#ifdef RTF_SETSRC + {RTF_SETSRC, "SETSRC"}, +#endif /* RTF_SETSRC */ + {0, NULL} +}; + +/* Kernel routing update socket. */ +int routing_sock = -1; + +/* Yes I'm checking ugly routing socket behavior. */ +/* #define DEBUG */ + +/* Supported address family check. */ +static inline int +af_check (int family) +{ + if (family == AF_INET) + return 1; +#ifdef HAVE_IPV6 + if (family == AF_INET6) + return 1; +#endif /* HAVE_IPV6 */ + return 0; +} + +/* Dump routing table flag for debug purpose. */ +static void +rtm_flag_dump (int flag) +{ + const struct message *mes; + static char buf[BUFSIZ]; + + buf[0] = '\0'; + for (mes = rtm_flag_str; mes->key != 0; mes++) + { + if (mes->key & flag) + { + strlcat (buf, mes->str, BUFSIZ); + strlcat (buf, " ", BUFSIZ); + } + } + zlog_debug ("Kernel: %s", buf); +} + +#ifdef RTM_IFANNOUNCE +/* Interface adding function */ +static int +ifan_read (struct if_announcemsghdr *ifan) +{ + struct interface *ifp; + + ifp = if_lookup_by_index (ifan->ifan_index); + + if (ifp) + assert ( (ifp->ifindex == ifan->ifan_index) + || (ifp->ifindex == IFINDEX_INTERNAL) ); + + if ( (ifp == NULL) + || ((ifp->ifindex == IFINDEX_INTERNAL) + && (ifan->ifan_what == IFAN_ARRIVAL)) ) + { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug ("%s: creating interface for ifindex %d, name %s", + __func__, ifan->ifan_index, ifan->ifan_name); + + /* Create Interface */ + ifp = if_get_by_name_len(ifan->ifan_name, + strnlen(ifan->ifan_name, + 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) + if_delete_update (ifp); + + if_get_flags (ifp); + if_get_mtu (ifp); + if_get_metric (ifp); + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug ("%s: interface %s index %d", + __func__, ifan->ifan_name, ifan->ifan_index); + + return 0; +} +#endif /* RTM_IFANNOUNCE */ + +#ifdef HAVE_BSD_IFI_LINK_STATE +/* BSD link detect translation */ +static void +bsd_linkdetect_translate (struct if_msghdr *ifm) +{ + if ((ifm->ifm_data.ifi_link_state >= LINK_STATE_UP) || + (ifm->ifm_data.ifi_link_state == LINK_STATE_UNKNOWN)) + SET_FLAG(ifm->ifm_flags, IFF_RUNNING); + else + UNSET_FLAG(ifm->ifm_flags, IFF_RUNNING); +} +#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 + * sysctl (from interface_list). There may or may not be sockaddrs + * present after the header. + */ +int +ifm_read (struct if_msghdr *ifm) +{ + struct interface *ifp = NULL; + struct sockaddr_dl *sdl; + char ifname[IFNAMSIZ]; + short ifnlen = 0; + caddr_t cp; + + /* terminate ifname at head (for strnlen) and tail (for safety) */ + ifname[IFNAMSIZ - 1] = '\0'; + + /* paranoia: sanity check structure */ + if (ifm->ifm_msglen < sizeof(struct if_msghdr)) + { + zlog_err ("ifm_read: ifm->ifm_msglen %d too short\n", + ifm->ifm_msglen); + return -1; + } + + /* + * Check for a sockaddr_dl following the message. First, point to + * where a socakddr might be if one follows the message. + */ + cp = (void *)(ifm + 1); + +#ifdef SUNOS_5 + /* + * XXX This behavior should be narrowed to only the kernel versions + * for which the structures returned do not match the headers. + * + * if_msghdr_t on 64 bit kernels in Solaris 9 and earlier versions + * is 12 bytes larger than the 32 bit version. + */ + if (((struct sockaddr *) cp)->sa_family == AF_UNSPEC) + cp = cp + 12; +#endif + + RTA_ADDR_GET (NULL, RTA_DST, ifm->ifm_addrs, cp); + RTA_ADDR_GET (NULL, RTA_GATEWAY, ifm->ifm_addrs, cp); + RTA_ATTR_GET (NULL, RTA_NETMASK, ifm->ifm_addrs, cp); + RTA_ADDR_GET (NULL, RTA_GENMASK, ifm->ifm_addrs, cp); + sdl = (struct sockaddr_dl *)cp; + RTA_NAME_GET (ifname, RTA_IFP, ifm->ifm_addrs, cp, ifnlen); + RTA_ADDR_GET (NULL, RTA_IFA, ifm->ifm_addrs, cp); + RTA_ADDR_GET (NULL, RTA_AUTHOR, ifm->ifm_addrs, cp); + RTA_ADDR_GET (NULL, RTA_BRD, ifm->ifm_addrs, cp); + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug ("%s: sdl ifname %s", __func__, (ifnlen ? ifname : "(nil)")); + + /* + * Look up on ifindex first, because ifindices are the primary handle for + * interfaces across the user/kernel boundary, for most systems. (Some + * messages, such as up/down status changes on NetBSD, do not include a + * sockaddr_dl). + */ + if ( (ifp = if_lookup_by_index (ifm->ifm_index)) != NULL ) + { + /* we have an ifp, verify that the name matches as some systems, + * eg Solaris, have a 1:many association of ifindex:ifname + * if they dont match, we dont have the correct ifp and should + * set it back to NULL to let next check do lookup by name + */ + if (ifnlen && (strncmp (ifp->name, ifname, IFNAMSIZ) != 0) ) + { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug ("%s: ifp name %s doesnt match sdl name %s", + __func__, ifp->name, ifname); + ifp = NULL; + } + } + + /* + * If we dont have an ifp, try looking up by name. Particularly as some + * systems (Solaris) have a 1:many mapping of ifindex:ifname - the ifname + * is therefore our unique handle to that interface. + * + * Interfaces specified in the configuration file for which the ifindex + * has not been determined will have ifindex == IFINDEX_INTERNAL, and such + * interfaces are found by this search, and then their ifindex values can + * be filled in. + */ + if ( (ifp == NULL) && ifnlen) + ifp = if_lookup_by_name (ifname); + + /* + * If ifp still does not exist or has an invalid index (IFINDEX_INTERNAL), + * create or fill in an interface. + */ + if ((ifp == NULL) || (ifp->ifindex == IFINDEX_INTERNAL)) + { + /* + * To create or fill in an interface, a sockaddr_dl (via + * RTA_IFP) is required. + */ + if (!ifnlen) + { + zlog_warn ("Interface index %d (new) missing ifname\n", + ifm->ifm_index); + return -1; + } + +#ifndef RTM_IFANNOUNCE + /* Down->Down interface should be ignored here. + * See further comment below. + */ + if (!CHECK_FLAG (ifm->ifm_flags, IFF_UP)) + return 0; +#endif /* !RTM_IFANNOUNCE */ + + if (ifp == NULL) + { + /* Interface that zebra was not previously aware of, so create. */ + ifp = if_create (ifname, ifnlen); + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug ("%s: creating ifp for ifindex %d", + __func__, ifm->ifm_index); + } + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug ("%s: updated/created ifp, ifname %s, ifindex %d", + __func__, ifp->name, ifp->ifindex); + /* + * Fill in newly created interface structure, or larval + * structure with ifindex IFINDEX_INTERNAL. + */ + ifp->ifindex = ifm->ifm_index; + +#ifdef HAVE_BSD_IFI_LINK_STATE /* translate BSD kernel msg for link-state */ + bsd_linkdetect_translate(ifm); +#endif /* HAVE_BSD_IFI_LINK_STATE */ + + if_flags_update (ifp, ifm->ifm_flags); +#if defined(__bsdi__) + if_kvm_get_mtu (ifp); +#else + if_get_mtu (ifp); +#endif /* __bsdi__ */ + if_get_metric (ifp); + + /* + * XXX sockaddr_dl contents can be larger than the structure + * 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) + { +#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); + } + else + /* + * Interface structure exists. Adjust stored flags from + * notification. If interface has up->down or down->up + * transition, call state change routines (to adjust routes, + * notify routing daemons, etc.). (Other flag changes are stored + * but apparently do not trigger action.) + */ + { + if (ifp->ifindex != ifm->ifm_index) + { + zlog_warn ("%s: index mismatch, ifname %s, ifp index %d, " + "ifm index %d", + __func__, ifp->name, ifp->ifindex, ifm->ifm_index); + return -1; + } + +#ifdef HAVE_BSD_IFI_LINK_STATE /* translate BSD kernel msg for link-state */ + bsd_linkdetect_translate(ifm); +#endif /* HAVE_BSD_IFI_LINK_STATE */ + + /* update flags and handle operative->inoperative transition, if any */ + if_flags_update (ifp, ifm->ifm_flags); + +#ifndef RTM_IFANNOUNCE + if (!if_is_up (ifp)) + { + /* No RTM_IFANNOUNCE on this platform, so we can never + * distinguish between ~IFF_UP and delete. We must presume + * it has been deleted. + * Eg, Solaris will not notify us of unplumb. + * + * XXX: Fixme - this should be runtime detected + * So that a binary compiled on a system with IFANNOUNCE + * will still behave correctly if run on a platform without + */ + if_delete_update (ifp); + } +#endif /* RTM_IFANNOUNCE */ + if (if_is_up (ifp)) + { +#if defined(__bsdi__) + if_kvm_get_mtu (ifp); +#else + if_get_mtu (ifp); +#endif /* __bsdi__ */ + if_get_metric (ifp); + } + } + +#ifdef HAVE_NET_RT_IFLIST + ifp->stats = ifm->ifm_data; +#endif /* HAVE_NET_RT_IFLIST */ + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug ("%s: interface %s index %d", + __func__, ifp->name, ifp->ifindex); + + return 0; +} + +/* Address read from struct ifa_msghdr. */ +static void +ifam_read_mesg (struct ifa_msghdr *ifm, + union sockunion *addr, + union sockunion *mask, + union sockunion *brd, + char *ifname, + short *ifnlen) +{ + caddr_t pnt, end; + union sockunion dst; + union sockunion gateway; + + pnt = (caddr_t)(ifm + 1); + end = ((caddr_t)ifm) + ifm->ifam_msglen; + + /* Be sure structure is cleared */ + memset (mask, 0, sizeof (union sockunion)); + memset (addr, 0, sizeof (union sockunion)); + memset (brd, 0, sizeof (union sockunion)); + memset (&dst, 0, sizeof (union sockunion)); + memset (&gateway, 0, sizeof (union sockunion)); + + /* We fetch each socket variable into sockunion. */ + RTA_ADDR_GET (&dst, RTA_DST, ifm->ifam_addrs, pnt); + RTA_ADDR_GET (&gateway, RTA_GATEWAY, ifm->ifam_addrs, pnt); + RTA_ATTR_GET (mask, RTA_NETMASK, ifm->ifam_addrs, pnt); + RTA_ADDR_GET (NULL, RTA_GENMASK, ifm->ifam_addrs, pnt); + RTA_NAME_GET (ifname, RTA_IFP, ifm->ifam_addrs, pnt, *ifnlen); + RTA_ADDR_GET (addr, RTA_IFA, ifm->ifam_addrs, pnt); + RTA_ADDR_GET (NULL, RTA_AUTHOR, ifm->ifam_addrs, pnt); + RTA_ADDR_GET (brd, RTA_BRD, ifm->ifam_addrs, pnt); + + if (IS_ZEBRA_DEBUG_KERNEL) + { + int family = sockunion_family(addr); + switch (family) + { + case AF_INET: +#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, + (ifnlen ? ifname : "(nil)"), ifm->ifam_addrs, + ifm->ifam_flags, + inet_ntop(family,&addr->sin.sin_addr, + buf[0],sizeof(buf[0])), + ip_masklen(mask->sin.sin_addr), + inet_ntop(family,&brd->sin.sin_addr, + buf[1],sizeof(buf[1])), + inet_ntop(family,&dst.sin.sin_addr, + buf[2],sizeof(buf[2])), + inet_ntop(family,&gateway.sin.sin_addr, + buf[3],sizeof(buf[3]))); + } + break; + default: + zlog_debug ("%s: ifindex %d, ifname %s, ifam_addrs 0x%x", + __func__, ifm->ifam_index, + (ifnlen ? ifname : "(nil)"), ifm->ifam_addrs); + break; + } + } + + /* Assert read up end point matches to end point */ + if (pnt != end) + zlog_warn ("ifam_read() doesn't read all socket data"); +} + +/* Interface's address information get. */ +int +ifam_read (struct ifa_msghdr *ifam) +{ + struct interface *ifp = NULL; + union sockunion addr, mask, brd; + char ifname[INTERFACE_NAMSIZ]; + short ifnlen = 0; + char isalias = 0; + int flags = 0; + + ifname[0] = ifname[INTERFACE_NAMSIZ - 1] = '\0'; + + /* Allocate and read address information. */ + ifam_read_mesg (ifam, &addr, &mask, &brd, ifname, &ifnlen); + + if ((ifp = if_lookup_by_index(ifam->ifam_index)) == NULL) + { + zlog_warn ("%s: no interface for ifname %s, index %d", + __func__, ifname, ifam->ifam_index); + return -1; + } + + if (ifnlen && strncmp (ifp->name, ifname, INTERFACE_NAMSIZ)) + isalias = 1; + + /* N.B. The info in ifa_msghdr does not tell us whether the RTA_BRD + field contains a broadcast address or a peer address, so we are forced to + rely upon the interface type. */ + if (if_is_pointopoint(ifp)) + SET_FLAG(flags, ZEBRA_IFA_PEER); + +#if 0 + /* it might seem cute to grab the interface metric here, however + * we're processing an address update message, and so some systems + * (e.g. FBSD) dont bother to fill in ifam_metric. Disabled, but left + * in deliberately, as comment. + */ + ifp->metric = ifam->ifam_metric; +#endif + + /* Add connected address. */ + switch (sockunion_family (&addr)) + { + case AF_INET: + if (ifam->ifam_type == RTM_NEWADDR) + connected_add_ipv4 (ifp, flags, &addr.sin.sin_addr, + ip_masklen (mask.sin.sin_addr), + &brd.sin.sin_addr, + (isalias ? ifname : NULL)); + else + connected_delete_ipv4 (ifp, flags, &addr.sin.sin_addr, + ip_masklen (mask.sin.sin_addr), + &brd.sin.sin_addr); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + /* 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); + } + + if (ifam->ifam_type == RTM_NEWADDR) + connected_add_ipv6 (ifp, flags, &addr.sin6.sin6_addr, + ip6_masklen (mask.sin6.sin6_addr), + &brd.sin6.sin6_addr, + (isalias ? ifname : NULL)); + else + connected_delete_ipv6 (ifp, + &addr.sin6.sin6_addr, + ip6_masklen (mask.sin6.sin6_addr), + &brd.sin6.sin6_addr); + break; +#endif /* HAVE_IPV6 */ + default: + /* Unsupported family silently ignore... */ + break; + } + + /* Check interface flag for implicit up of the interface. */ + if_refresh (ifp); + +#ifdef SUNOS_5 + /* In addition to lacking IFANNOUNCE, on SUNOS IFF_UP is strange. + * See comments for SUNOS_5 in interface.c::if_flags_mangle. + * + * Here we take care of case where the real IFF_UP was previously + * unset (as kept in struct zebra_if.primary_state) and the mangled + * IFF_UP (ie IFF_UP set || listcount(connected) has now transitioned + * to unset due to the lost non-primary address having DELADDR'd. + * + * we must delete the interface, because in between here and next + * event for this interface-name the administrator could unplumb + * and replumb the interface. + */ + if (!if_is_up (ifp)) + if_delete_update (ifp); +#endif /* SUNOS_5 */ + + return 0; +} + +/* Interface function for reading kernel routing table information. */ +static int +rtm_read_mesg (struct rt_msghdr *rtm, + union sockunion *dest, + union sockunion *mask, + union sockunion *gate, + char *ifname, + short *ifnlen) +{ + caddr_t pnt, end; + + /* Pnt points out socket data start point. */ + pnt = (caddr_t)(rtm + 1); + end = ((caddr_t)rtm) + rtm->rtm_msglen; + + /* rt_msghdr version check. */ + if (rtm->rtm_version != RTM_VERSION) + zlog (NULL, LOG_WARNING, + "Routing message version different %d should be %d." + "This may cause problem\n", rtm->rtm_version, RTM_VERSION); + + /* Be sure structure is cleared */ + memset (dest, 0, sizeof (union sockunion)); + memset (gate, 0, sizeof (union sockunion)); + memset (mask, 0, sizeof (union sockunion)); + + /* We fetch each socket variable into sockunion. */ + RTA_ADDR_GET (dest, RTA_DST, rtm->rtm_addrs, pnt); + RTA_ADDR_GET (gate, RTA_GATEWAY, rtm->rtm_addrs, pnt); + RTA_ATTR_GET (mask, RTA_NETMASK, rtm->rtm_addrs, pnt); + RTA_ADDR_GET (NULL, RTA_GENMASK, rtm->rtm_addrs, pnt); + RTA_NAME_GET (ifname, RTA_IFP, rtm->rtm_addrs, pnt, *ifnlen); + RTA_ADDR_GET (NULL, RTA_IFA, rtm->rtm_addrs, pnt); + RTA_ADDR_GET (NULL, RTA_AUTHOR, rtm->rtm_addrs, pnt); + RTA_ADDR_GET (NULL, RTA_BRD, rtm->rtm_addrs, pnt); + + /* If there is netmask information set it's family same as + destination family*/ + if (rtm->rtm_addrs & RTA_NETMASK) + mask->sa.sa_family = dest->sa.sa_family; + + /* Assert read up to the end of pointer. */ + if (pnt != end) + zlog (NULL, LOG_WARNING, "rtm_read() doesn't read all socket data."); + + return rtm->rtm_flags; +} + +void +rtm_read (struct rt_msghdr *rtm) +{ + int flags; + u_char zebra_flags; + union sockunion dest, mask, gate; + char ifname[INTERFACE_NAMSIZ + 1]; + short ifnlen = 0; + + zebra_flags = 0; + + /* Read destination and netmask and gateway from rtm message + structure. */ + flags = rtm_read_mesg (rtm, &dest, &mask, &gate, ifname, &ifnlen); + if (!(flags & RTF_DONE)) + return; + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug ("%s: got rtm of type %d (%s)", __func__, rtm->rtm_type, + lookup (rtm_type_str, rtm->rtm_type)); + +#ifdef RTF_CLONED /*bsdi, netbsd 1.6*/ + if (flags & RTF_CLONED) + return; +#endif +#ifdef RTF_WASCLONED /*freebsd*/ + if (flags & RTF_WASCLONED) + return; +#endif + + if ((rtm->rtm_type == RTM_ADD || rtm->rtm_type == RTM_CHANGE) && ! (flags & RTF_UP)) + return; + + /* This is connected route. */ + if (! (flags & RTF_GATEWAY)) + return; + + if (flags & RTF_PROTO1) + SET_FLAG (zebra_flags, ZEBRA_FLAG_SELFROUTE); + + /* This is persistent route. */ + if (flags & RTF_STATIC) + SET_FLAG (zebra_flags, ZEBRA_FLAG_STATIC); + + /* This is a reject or blackhole route */ + if (flags & RTF_REJECT) + SET_FLAG (zebra_flags, ZEBRA_FLAG_REJECT); + if (flags & RTF_BLACKHOLE) + SET_FLAG (zebra_flags, ZEBRA_FLAG_BLACKHOLE); + + if (dest.sa.sa_family == AF_INET) + { + struct prefix_ipv4 p; + + p.family = AF_INET; + p.prefix = dest.sin.sin_addr; + if (flags & RTF_HOST) + p.prefixlen = IPV4_MAX_PREFIXLEN; + else + p.prefixlen = ip_masklen (mask.sin.sin_addr); + + /* Catch self originated messages and match them against our current RIB. + * At the same time, ignore unconfirmed messages, they should be tracked + * by rtm_write() and kernel_rtm_ipv4(). + */ + if (rtm->rtm_type != RTM_GET && rtm->rtm_pid == pid) + { + char buf[PREFIX_STRLEN], gate_buf[INET_ADDRSTRLEN]; + int ret; + if (! IS_ZEBRA_DEBUG_RIB) + return; + ret = rib_lookup_ipv4_route (&p, &gate, VRF_DEFAULT); + prefix2str (&p, buf, sizeof(buf)); + switch (rtm->rtm_type) + { + case RTM_ADD: + case RTM_GET: + case RTM_CHANGE: + /* The kernel notifies us about a new route in FIB created by us. + Do we have a correspondent entry in our RIB? */ + switch (ret) + { + case ZEBRA_RIB_NOTFOUND: + 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: 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: done Ok", + __func__, lookup (rtm_type_str, rtm->rtm_type), buf); + rib_lookup_and_dump (&p); + return; + break; + } + break; + case RTM_DELETE: + /* The kernel notifies us about a route deleted by us. Do we still + have it in the RIB? Do we have anything instead? */ + switch (ret) + { + case ZEBRA_RIB_FOUND_EXACT: + 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: 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: done Ok", + __func__, lookup (rtm_type_str, rtm->rtm_type), buf); + rib_lookup_and_dump (&p); + return; + break; + } + break; + default: + zlog_debug ("%s: %s: warning: loopback RTM of type %s received", + __func__, buf, lookup (rtm_type_str, rtm->rtm_type)); + } + return; + } + + /* Change, delete the old prefix, we have no further information + * to specify the route really + */ + if (rtm->rtm_type == RTM_CHANGE) + rib_delete_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, + 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, VRF_DEFAULT, 0, 0, 0, 0, SAFI_UNICAST); + else + 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) + { + /* One day we might have a debug section here like one in the + * IPv4 case above. Just ignore own messages at the moment. + */ + if (rtm->rtm_type != RTM_GET && rtm->rtm_pid == pid) + return; + struct prefix_ipv6 p; + ifindex_t ifindex = 0; + + p.family = AF_INET6; + p.prefix = dest.sin6.sin6_addr; + if (flags & RTF_HOST) + p.prefixlen = IPV6_MAX_PREFIXLEN; + else + p.prefixlen = ip6_masklen (mask.sin6.sin6_addr); + +#ifdef KAME + if (IN6_IS_ADDR_LINKLOCAL (&gate.sin6.sin6_addr)) + { + ifindex = IN6_LINKLOCAL_IFINDEX (gate.sin6.sin6_addr); + SET_IN6_LINKLOCAL_IFINDEX (gate.sin6.sin6_addr, 0); + } +#endif /* KAME */ + + /* CHANGE: delete the old prefix, we have no further information + * to specify the route really + */ + if (rtm->rtm_type == RTM_CHANGE) + rib_delete_ipv6 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, + 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, 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, + VRF_DEFAULT, SAFI_UNICAST); + } +#endif /* HAVE_IPV6 */ +} + +/* Interface function for the kernel routing table updates. Support + * for RTM_CHANGE will be needed. + * Exported only for rt_socket.c + */ +int +rtm_write (int message, + union sockunion *dest, + union sockunion *mask, + union sockunion *gate, + unsigned int index, + int zebra_flags, + int metric) +{ + int ret; + caddr_t pnt; + struct interface *ifp; + + /* Sequencial number of routing message. */ + static int msg_seq = 0; + + /* Struct of rt_msghdr and buffer for storing socket's data. */ + struct + { + struct rt_msghdr rtm; + char buf[512]; + } msg; + + if (routing_sock < 0) + return ZEBRA_ERR_EPERM; + + /* Clear and set rt_msghdr values */ + memset (&msg, 0, sizeof (struct rt_msghdr)); + msg.rtm.rtm_version = RTM_VERSION; + msg.rtm.rtm_type = message; + msg.rtm.rtm_seq = msg_seq++; + msg.rtm.rtm_addrs = RTA_DST; + msg.rtm.rtm_addrs |= RTA_GATEWAY; + msg.rtm.rtm_flags = RTF_UP; + msg.rtm.rtm_index = index; + + if (metric != 0) + { + msg.rtm.rtm_rmx.rmx_hopcount = metric; + msg.rtm.rtm_inits |= RTV_HOPCOUNT; + } + + ifp = if_lookup_by_index (index); + + 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 || message == RTM_CHANGE) && ifp && + (ifp->flags & IFF_POINTOPOINT) == 0) + msg.rtm.rtm_flags |= RTF_CLONING; +#endif /* RTF_CLONING */ + + /* If no protocol specific gateway is specified, use link + address for gateway. */ + if (! gate) + { + if (!ifp) + { + char dest_buf[INET_ADDRSTRLEN] = "NULL", mask_buf[INET_ADDRSTRLEN] = "255.255.255.255"; + if (dest) + inet_ntop (AF_INET, &dest->sin.sin_addr, dest_buf, INET_ADDRSTRLEN); + if (mask) + inet_ntop (AF_INET, &mask->sin.sin_addr, mask_buf, INET_ADDRSTRLEN); + zlog_warn ("%s: %s/%s: gate == NULL and no gateway found for ifindex %d", + __func__, dest_buf, mask_buf, index); + return -1; + } + gate = (union sockunion *) &((struct zebra_if *)ifp->info)->sdl; + } + + if (mask) + msg.rtm.rtm_addrs |= RTA_NETMASK; + else if (message == RTM_ADD || message == RTM_CHANGE) + msg.rtm.rtm_flags |= RTF_HOST; + + /* Tagging route with flags */ + msg.rtm.rtm_flags |= (RTF_PROTO1); + + /* Additional flags. */ + if (zebra_flags & ZEBRA_FLAG_BLACKHOLE) + msg.rtm.rtm_flags |= RTF_BLACKHOLE; + if (zebra_flags & ZEBRA_FLAG_REJECT) + msg.rtm.rtm_flags |= RTF_REJECT; + + +#define SOCKADDRSET(X,R) \ + if (msg.rtm.rtm_addrs & (R)) \ + { \ + int len = SAROUNDUP (X); \ + memcpy (pnt, (caddr_t)(X), len); \ + pnt += len; \ + } + + pnt = (caddr_t) msg.buf; + + /* Write each socket data into rtm message buffer */ + SOCKADDRSET (dest, RTA_DST); + SOCKADDRSET (gate, RTA_GATEWAY); + SOCKADDRSET (mask, RTA_NETMASK); + + msg.rtm.rtm_msglen = pnt - (caddr_t) &msg; + + ret = write (routing_sock, &msg, msg.rtm.rtm_msglen); + + if (ret != msg.rtm.rtm_msglen) + { + if (errno == EEXIST) + return ZEBRA_ERR_RTEXIST; + if (errno == ENETUNREACH) + return ZEBRA_ERR_RTUNREACH; + if (errno == ESRCH) + return ZEBRA_ERR_RTNOEXIST; + + zlog_warn ("%s: write : %s (%d)", __func__, safe_strerror (errno), errno); + return ZEBRA_ERR_KERNEL; + } + return ZEBRA_ERR_NOERROR; +} + + +#include "thread.h" +#include "zebra/zserv.h" + +/* For debug purpose. */ +static void +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 %lld, rtm_addrs 0x%x", + (long long)rtm->rtm_pid, rtm->rtm_addrs); +} + +/* This is pretty gross, better suggestions welcome -- mhandler */ +#ifndef RTAX_MAX +#ifdef RTA_NUMBITS +#define RTAX_MAX RTA_NUMBITS +#else +#define RTAX_MAX 8 +#endif /* RTA_NUMBITS */ +#endif /* RTAX_MAX */ + +/* Kernel routing table and interface updates via routing socket. */ +static int +kernel_read (struct thread *thread) +{ + int sock; + int nbytes; + struct rt_msghdr *rtm; + + /* + * This must be big enough for any message the kernel might send. + * Rather than determining how many sockaddrs of what size might be + * in each particular message, just use RTAX_MAX of sockaddr_storage + * for each. Note that the sockaddrs must be after each message + * definition, or rather after whichever happens to be the largest, + * since the buffer needs to be big enough for a message and the + * sockaddrs together. + */ + union + { + /* Routing information. */ + struct + { + struct rt_msghdr rtm; + struct sockaddr_storage addr[RTAX_MAX]; + } r; + + /* Interface information. */ + struct + { + struct if_msghdr ifm; + struct sockaddr_storage addr[RTAX_MAX]; + } im; + + /* Interface address information. */ + struct + { + struct ifa_msghdr ifa; + struct sockaddr_storage addr[RTAX_MAX]; + } ia; + +#ifdef RTM_IFANNOUNCE + /* Interface arrival/departure */ + struct + { + struct if_announcemsghdr ifan; + struct sockaddr_storage addr[RTAX_MAX]; + } ian; +#endif /* RTM_IFANNOUNCE */ + + } buf; + + /* Fetch routing socket. */ + sock = THREAD_FD (thread); + + nbytes= read (sock, &buf, sizeof buf); + + if (nbytes <= 0) + { + if (nbytes < 0 && errno != EWOULDBLOCK && errno != EAGAIN) + zlog_warn ("routing socket error: %s", safe_strerror (errno)); + return 0; + } + + thread_add_read (zebrad.master, kernel_read, NULL, sock); + + if (IS_ZEBRA_DEBUG_KERNEL) + rtmsg_debug (&buf.r.rtm); + + rtm = &buf.r.rtm; + + /* + * Ensure that we didn't drop any data, so that processing routines + * can assume they have the whole message. + */ + if (rtm->rtm_msglen != nbytes) + { + zlog_warn ("kernel_read: rtm->rtm_msglen %d, nbytes %d, type %d\n", + rtm->rtm_msglen, nbytes, rtm->rtm_type); + return -1; + } + + switch (rtm->rtm_type) + { + case RTM_ADD: + case RTM_DELETE: + case RTM_CHANGE: + rtm_read (rtm); + break; + case RTM_IFINFO: + ifm_read (&buf.im.ifm); + break; + case RTM_NEWADDR: + case RTM_DELADDR: + ifam_read (&buf.ia.ifa); + break; +#ifdef RTM_IFANNOUNCE + case RTM_IFANNOUNCE: + ifan_read (&buf.ian.ifan); + break; +#endif /* RTM_IFANNOUNCE */ + default: + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("Unprocessed RTM_type: %d", rtm->rtm_type); + break; + } + return 0; +} + +/* Make routing socket. */ +static 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"); + + routing_sock = socket (AF_ROUTE, SOCK_RAW, 0); + + if (routing_sock < 0) + { + if ( zserv_privs.change (ZPRIVS_LOWER) ) + zlog_err ("routing_socket: Can't lower privileges"); + zlog_warn ("Can't init kernel routing socket"); + return; + } + + /* XXX: Socket should be NONBLOCK, however as we currently + * discard failed writes, this will lead to inconsistencies. + * For now, socket must be blocking. + */ + /*if (fcntl (routing_sock, F_SETFL, O_NONBLOCK) < 0) + zlog_warn ("Can't set O_NONBLOCK to routing socket");*/ + + if ( zserv_privs.change (ZPRIVS_LOWER) ) + zlog_err ("routing_socket: Can't lower privileges"); + + /* kernel_read needs rewrite. */ + thread_add_read (zebrad.master, kernel_read, NULL, routing_sock); +} + +/* Exported interface function. This function simply calls + routing_socket (). */ +void +kernel_init (struct zebra_vrf *zvrf) +{ + routing_socket (zvrf); +} + +void +kernel_terminate (struct zebra_vrf *zvrf) +{ + return; +} diff --git a/zebra/kernel_socket.h b/zebra/kernel_socket.h new file mode 100644 index 0000000..e9558ad --- /dev/null +++ b/zebra/kernel_socket.h @@ -0,0 +1,33 @@ +/* + * Exported kernel_socket functions, exported only for convenience of + * sysctl methods. + * + * 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_KERNEL_SOCKET_H +#define __ZEBRA_KERNEL_SOCKET_H + +extern void rtm_read (struct rt_msghdr *); +extern int ifam_read (struct ifa_msghdr *); +extern int ifm_read (struct if_msghdr *); +extern int rtm_write (int, union sockunion *, union sockunion *, + union sockunion *, unsigned int, int, int); +extern const struct message rtm_type_str[]; + +#endif /* __ZEBRA_KERNEL_SOCKET_H */ diff --git a/zebra/main.c b/zebra/main.c new file mode 100644 index 0000000..35cb159 --- /dev/null +++ b/zebra/main.c @@ -0,0 +1,498 @@ +/* zebra daemon main routine. + * 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 +#include "getopt.h" +#include "command.h" +#include "thread.h" +#include "filter.h" +#include "memory.h" +#include "prefix.h" +#include "log.h" +#include "plist.h" +#include "privs.h" +#include "sigevent.h" +#include "vrf.h" + +#include "zebra/rib.h" +#include "zebra/zserv.h" +#include "zebra/debug.h" +#include "zebra/router-id.h" +#include "zebra/irdp.h" +#include "zebra/rtadv.h" +#include "zebra/zebra_fpm.h" + +/* Zebra instance */ +struct zebra_t zebrad = +{ + .rtm_table_default = 0, +}; + +/* process id. */ +pid_t pid; + +/* Pacify zclient.o in libzebra, which expects this variable. */ +struct thread_master *master; + +/* Route retain mode flag. */ +int retain_mode = 0; + +/* Don't delete kernel route. */ +int keep_kernel_mode = 0; + +#ifdef HAVE_NETLINK +/* Receive buffer size for netlink socket */ +u_int32_t nl_rcvbufsize = 0; +#endif /* HAVE_NETLINK */ + +/* Command line options. */ +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'}, + { "help", no_argument, NULL, 'h'}, + { "vty_addr", required_argument, NULL, 'A'}, + { "vty_port", required_argument, NULL, 'P'}, + { "retain", no_argument, NULL, 'r'}, + { "dryrun", no_argument, NULL, 'C'}, +#ifdef HAVE_NETLINK + { "nl-bufsize", required_argument, NULL, 's'}, +#endif /* HAVE_NETLINK */ + { "user", required_argument, NULL, 'u'}, + { "group", required_argument, NULL, 'g'}, + { "version", no_argument, NULL, 'v'}, + { 0 } +}; + +zebra_capabilities_t _caps_p [] = +{ + ZCAP_NET_ADMIN, + ZCAP_SYS_ADMIN, + ZCAP_NET_RAW, +}; + +/* zebra privileges to run with */ +struct zebra_privs_t zserv_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 +}; + +/* Default configuration file path. */ +char config_default[] = SYSCONFDIR DEFAULT_CONFIG_FILE; + +/* Process ID saved for use by init system */ +const char *pid_file = PATH_ZEBRA_PID; + +/* Help information display. */ +static void +usage (char *progname, int status) +{ + if (status != 0) + fprintf (stderr, "Try `%s --help' for more information.\n", progname); + else + { + printf ("Usage : %s [OPTION...]\n\n"\ + "Daemon which manages kernel routing table management and "\ + "redistribution between different routing protocols.\n\n"\ + "-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 "\ + "zebra.\n"\ + "-C, --dryrun Check configuration for validity and exit\n"\ + "-A, --vty_addr Set vty's bind address\n"\ + "-P, --vty_port Set vty's port number\n"\ + "-r, --retain When program terminates, retain added route "\ + "by zebra.\n"\ + "-u, --user User to run as\n"\ + "-g, --group Group to run as\n", progname); +#ifdef HAVE_NETLINK + printf ("-s, --nl-bufsize Set netlink receive buffer size\n"); +#endif /* HAVE_NETLINK */ + printf ("-v, --version Print program version\n"\ + "-h, --help Display this help and exit\n"\ + "\n"\ + "Report bugs to %s\n", ZEBRA_BUG_ADDRESS); + } + + exit (status); +} + +/* SIGHUP handler. */ +static void +sighup (void) +{ + zlog_info ("SIGHUP received"); + + /* Reload of config file. */ + ; +} + +/* SIGINT handler. */ +static void +sigint (void) +{ + zlog_notice ("Terminating on signal"); + + if (!retain_mode) + rib_close (); +#ifdef HAVE_IRDP + irdp_finish(); +#endif + + exit (0); +} + +/* SIGUSR1 handler. */ +static void +sigusr1 (void) +{ + zlog_rotate (NULL); +} + +struct quagga_signal_t zebra_signals[] = +{ + { + .signal = SIGHUP, + .handler = &sighup, + }, + { + .signal = SIGUSR1, + .handler = &sigusr1, + }, + { + .signal = SIGINT, + .handler = &sigint, + }, + { + .signal = SIGTERM, + .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) +{ + char *p; + char *vty_addr = NULL; + int vty_port = ZEBRA_VTY_PORT; + int dryrun = 0; + int batch_mode = 0; + int daemon_mode = 0; + char *config_file = NULL; + char *progname; + struct thread thread; + char *zserv_path = NULL; + char *fpm_format = NULL; + + /* Set umask before anything for security */ + umask (0027); + + /* preserve my name */ + progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]); + + zlog_default = openzlog (progname, ZLOG_ZEBRA, + LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON); + + while (1) + { + int opt; + +#ifdef HAVE_NETLINK + opt = getopt_long (argc, argv, "bdkf:F:i:z:hA:P:ru:g:vs:C", longopts, 0); +#else + opt = getopt_long (argc, argv, "bdkf:F:i:z:hA:P:ru:g:vC", longopts, 0); +#endif /* HAVE_NETLINK */ + + if (opt == EOF) + break; + + switch (opt) + { + case 0: + break; + case 'b': + batch_mode = 1; + case 'd': + daemon_mode = 1; + break; + case 'k': + keep_kernel_mode = 1; + break; + case 'C': + dryrun = 1; + break; + case 'f': + config_file = optarg; + break; + case 'F': + fpm_format = optarg; + break; + case 'A': + vty_addr = optarg; + break; + case 'i': + pid_file = optarg; + break; + case 'z': + zserv_path = optarg; + break; + case 'P': + /* Deal with atoi() returning 0 on failure, and zebra not + listening on zebra port... */ + if (strcmp(optarg, "0") == 0) + { + vty_port = 0; + break; + } + vty_port = atoi (optarg); + if (vty_port <= 0 || vty_port > 0xffff) + vty_port = ZEBRA_VTY_PORT; + break; + case 'r': + retain_mode = 1; + break; +#ifdef HAVE_NETLINK + case 's': + nl_rcvbufsize = atoi (optarg); + break; +#endif /* HAVE_NETLINK */ + case 'u': + zserv_privs.user = optarg; + break; + case 'g': + zserv_privs.group = optarg; + break; + case 'v': + print_version (progname); + exit (0); + break; + case 'h': + usage (progname, 0); + break; + default: + usage (progname, 1); + break; + } + } + + /* Make master thread emulator. */ + zebrad.master = thread_master_create (); + + /* privs initialise */ + zprivs_init (&zserv_privs); + + /* Vty related initialize. */ + signal_init (zebrad.master, array_size(zebra_signals), zebra_signals); + cmd_init (1); + vty_init (zebrad.master); + memory_init (); + + /* Zebra related initialize. */ + zebra_init (); + rib_init (); + zebra_if_init (); + zebra_debug_init (); + router_id_cmd_init (); + zebra_vty_init (); + access_list_init (); + prefix_list_init (); +#if defined (HAVE_RTADV) + rtadv_cmd_init (); +#endif +#ifdef HAVE_IRDP + irdp_init(); +#endif + + /* For debug purpose. */ + /* SET_FLAG (zebra_debug_event, ZEBRA_DEBUG_EVENT); */ + + /* 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 + * zebra->ribq structure until we enter the main execution loop. + * The notifications from kernel will show originating PID equal + * to that after daemon() completes (if ever called). + */ + vty_read_config (config_file, config_default); + + /* 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 (); + + /* Exit when zebra is working in batch mode. */ + if (batch_mode) + exit (0); + + /* Daemonize. */ + if (daemon_mode && daemon (0, 0) < 0) + { + zlog_err("Zebra daemon failed: %s", strerror(errno)); + exit (1); + } + + /* Output pid of zebra. */ + pid_output (pid_file); + + /* After we have successfully acquired the pidfile, we can be sure + * about being the only copy of zebra process, which is submitting + * changes to the FIB. + * Clean up zebra-originated routes. The requests will be sent to OS + * immediately, so originating PID in notifications from kernel + * will be equal to the current getpid(). To know about such routes, + * we have to have route_read() called before. + */ + if (! keep_kernel_mode) + rib_sweep_route (); + + /* Needed for BSD routing socket. */ + pid = getpid (); + + /* This must be done only after locking pidfile (bug #403). */ + zebra_zserv_socket_init (zserv_path); + + /* Make vty server socket. */ + vty_serv_sock (vty_addr, vty_port, ZEBRA_VTYSH_PATH); + + /* Print banner. */ + zlog_notice ("Zebra %s starting: vty@%d", QUAGGA_VERSION, vty_port); + + while (thread_fetch (zebrad.master, &thread)) + thread_call (&thread); + + /* Not reached... */ + return 0; +} diff --git a/zebra/misc_null.c b/zebra/misc_null.c new file mode 100644 index 0000000..18977d2 --- /dev/null +++ b/zebra/misc_null.c @@ -0,0 +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; } +#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/redistribute.c b/zebra/redistribute.c new file mode 100644 index 0000000..a7a6b25 --- /dev/null +++ b/zebra/redistribute.c @@ -0,0 +1,427 @@ +/* Redistribution Handler + * Copyright (C) 1998 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 "vector.h" +#include "vty.h" +#include "command.h" +#include "prefix.h" +#include "table.h" +#include "stream.h" +#include "zclient.h" +#include "linklist.h" +#include "log.h" +#include "vrf.h" + +#include "zebra/rib.h" +#include "zebra/zserv.h" +#include "zebra/redistribute.h" +#include "zebra/debug.h" +#include "zebra/router-id.h" + +/* master zebra server structure */ +extern struct zebra_t zebrad; + +int +zebra_check_addr (struct prefix *p) +{ + if (p->family == AF_INET) + { + u_int32_t addr; + + addr = p->u.prefix4.s_addr; + addr = ntohl (addr); + + if (IPV4_NET127 (addr) + || IN_CLASSD (addr) + || IPV4_LINKLOCAL(addr)) + return 0; + } +#ifdef HAVE_IPV6 + if (p->family == AF_INET6) + { + if (IN6_IS_ADDR_LOOPBACK (&p->u.prefix6)) + return 0; + if (IN6_IS_ADDR_LINKLOCAL(&p->u.prefix6)) + return 0; + } +#endif /* HAVE_IPV6 */ + return 1; +} + +int +is_default (struct prefix *p) +{ + if (p->family == AF_INET) + if (p->u.prefix4.s_addr == 0 && p->prefixlen == 0) + return 1; +#ifdef HAVE_IPV6 +#if 0 /* IPv6 default separation is now pending until protocol daemon + can handle that. */ + if (p->family == AF_INET6) + if (IN6_IS_ADDR_UNSPECIFIED (&p->u.prefix6) && p->prefixlen == 0) + return 1; +#endif /* 0 */ +#endif /* HAVE_IPV6 */ + return 0; +} + +static void +zebra_redistribute_default (struct zserv *client, vrf_id_t vrf_id) +{ + struct prefix_ipv4 p; + struct route_table *table; + struct route_node *rn; + struct rib *newrib; +#ifdef HAVE_IPV6 + struct prefix_ipv6 p6; +#endif /* HAVE_IPV6 */ + + + /* Lookup default route. */ + memset (&p, 0, sizeof (struct prefix_ipv4)); + p.family = AF_INET; + + /* Lookup table. */ + table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, vrf_id); + if (table) + { + rn = route_node_lookup (table, (struct prefix *)&p); + if (rn) + { + 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); + route_unlock_node (rn); + } + } + +#ifdef HAVE_IPV6 + /* Lookup default route. */ + memset (&p6, 0, sizeof (struct prefix_ipv6)); + p6.family = AF_INET6; + + /* Lookup table. */ + table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, vrf_id); + if (table) + { + rn = route_node_lookup (table, (struct prefix *)&p6); + if (rn) + { + 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); + route_unlock_node (rn); + } + } +#endif /* HAVE_IPV6 */ +} + +/* Redistribute routes. */ +static void +zebra_redistribute (struct zserv *client, int type, vrf_id_t vrf_id) +{ + struct rib *newrib; + struct route_table *table; + struct route_node *rn; + + table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, vrf_id); + if (table) + for (rn = route_top (table); rn; rn = route_next (rn)) + 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 = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, vrf_id); + if (table) + for (rn = route_top (table); rn; rn = route_next (rn)) + 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)) + { + client->redist_v6_add_cnt++; + zsend_route_multipath (ZEBRA_IPV6_ROUTE_ADD, client, &rn->p, newrib); + } +#endif /* HAVE_IPV6 */ +} + +void +redistribute_add (struct prefix *p, struct rib *rib) +{ + struct listnode *node, *nnode; + struct zserv *client; + + for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client)) + { + 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) + { + client->redist_v4_add_cnt++; + zsend_route_multipath (ZEBRA_IPV4_ROUTE_ADD, client, p, rib); + } + if (p->family == AF_INET6) + { + client->redist_v6_add_cnt++; + zsend_route_multipath (ZEBRA_IPV6_ROUTE_ADD, client, p, rib); + } + } + } +} + +void +redistribute_delete (struct prefix *p, struct rib *rib) +{ + struct listnode *node, *nnode; + struct zserv *client; + + /* Add DISTANCE_INFINITY check. */ + if (rib->distance == DISTANCE_INFINITY) + return; + + for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client)) + { + 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); +#ifdef HAVE_IPV6 + if (p->family == AF_INET6) + zsend_route_multipath (ZEBRA_IPV6_ROUTE_DELETE, client, p, rib); +#endif /* HAVE_IPV6 */ + } + } +} + +void +zebra_redistribute_add (int command, struct zserv *client, int length, + vrf_id_t vrf_id) +{ + int type; + + type = stream_getc (client->ibuf); + + if (type == 0 || type >= ZEBRA_ROUTE_MAX) + return; + + if (! vrf_bitmap_check (client->redist[type], vrf_id)) + { + 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, + vrf_id_t vrf_id) +{ + int type; + + type = stream_getc (client->ibuf); + + if (type == 0 || type >= ZEBRA_ROUTE_MAX) + return; + + vrf_bitmap_unset (client->redist[type], vrf_id); +} + +void +zebra_redistribute_default_add (int command, struct zserv *client, int length, + vrf_id_t vrf_id) +{ + 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, vrf_id_t vrf_id) +{ + vrf_bitmap_unset (client->redist_default, vrf_id); +} + +/* Interface up information. */ +void +zebra_interface_up_update (struct interface *ifp) +{ + struct listnode *node, *nnode; + struct zserv *client; + + if (IS_ZEBRA_DEBUG_EVENT) + zlog_debug ("MESSAGE: ZEBRA_INTERFACE_UP %s", ifp->name); + + for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client)) + if (client->ifinfo) + { + zsend_interface_update (ZEBRA_INTERFACE_UP, client, ifp); + zsend_interface_link_params (client, ifp); + } +} + +/* Interface down information. */ +void +zebra_interface_down_update (struct interface *ifp) +{ + struct listnode *node, *nnode; + struct zserv *client; + + if (IS_ZEBRA_DEBUG_EVENT) + 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); + } +} + +/* Interface information update. */ +void +zebra_interface_add_update (struct interface *ifp) +{ + struct listnode *node, *nnode; + struct zserv *client; + + if (IS_ZEBRA_DEBUG_EVENT) + zlog_debug ("MESSAGE: ZEBRA_INTERFACE_ADD %s", ifp->name); + + for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client)) + if (client->ifinfo) + { + client->ifadd_cnt++; + zsend_interface_add (client, ifp); + zsend_interface_link_params (client, ifp); + } +} + +void +zebra_interface_delete_update (struct interface *ifp) +{ + struct listnode *node, *nnode; + struct zserv *client; + + if (IS_ZEBRA_DEBUG_EVENT) + zlog_debug ("MESSAGE: ZEBRA_INTERFACE_DELETE %s", ifp->name); + + for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client)) + if (client->ifinfo) + { + client->ifdel_cnt++; + zsend_interface_delete (client, ifp); + } +} + +/* Interface address addition. */ +void +zebra_interface_address_add_update (struct interface *ifp, + struct connected *ifc) +{ + struct listnode *node, *nnode; + struct zserv *client; + struct prefix *p; + + if (IS_ZEBRA_DEBUG_EVENT) + { + char buf[PREFIX_STRLEN]; + + p = ifc->address; + 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)) + { + client->connected_rt_add_cnt++; + zsend_interface_address (ZEBRA_INTERFACE_ADDRESS_ADD, client, ifp, ifc); + } +} + +/* Interface address deletion. */ +void +zebra_interface_address_delete_update (struct interface *ifp, + struct connected *ifc) +{ + struct listnode *node, *nnode; + struct zserv *client; + struct prefix *p; + + if (IS_ZEBRA_DEBUG_EVENT) + { + char buf[PREFIX_STRLEN]; + + p = ifc->address; + 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)) + { + 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 new file mode 100644 index 0000000..ce84009 --- /dev/null +++ b/zebra/redistribute.h @@ -0,0 +1,58 @@ +/* + * Redistribution Handler + * 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 _ZEBRA_REDISTRIBUTE_H +#define _ZEBRA_REDISTRIBUTE_H + +#include "table.h" +#include "zserv.h" + +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, + 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 *); + +extern void zebra_interface_up_update (struct interface *); +extern void zebra_interface_down_update (struct interface *); + +extern void zebra_interface_add_update (struct interface *); +extern void zebra_interface_delete_update (struct interface *); + +extern void zebra_interface_address_add_update (struct interface *, + struct connected *); +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 new file mode 100644 index 0000000..5584d12 --- /dev/null +++ b/zebra/redistribute_null.c @@ -0,0 +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, + 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 new file mode 100644 index 0000000..0191f57 --- /dev/null +++ b/zebra/rib.h @@ -0,0 +1,603 @@ +/* + * Routing Information Base header + * 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. + */ + +#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 + +struct rib +{ + /* Link list. */ + struct rib *next; + struct rib *prev; + + /* Nexthop structure */ + struct nexthop *nexthop; + + /* 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; + + /* Flags of this route. + * This flag's definition is in lib/zebra.h ZEBRA_FLAG_* and is exposed + * to clients via Zserv + */ + u_char flags; + + /* 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; + u_char nexthop_active_num; + u_char nexthop_fib_num; +}; + +/* meta-queue structure: + * sub-queue 0: connected, kernel + * sub-queue 1: static + * sub-queue 2: RIP, RIPng, OSPF, OSPF6, IS-IS + * sub-queue 3: iBGP, eBGP + * sub-queue 4: any other origin (if any) + */ +#define MQ_SIZE 5 +struct meta_queue +{ + struct list *subq[MQ_SIZE]; + u_int32_t size; /* sum of lengths of all subqueues */ +}; + +/* + * Structure that represents a single destination (prefix). + */ +typedef struct rib_dest_t_ +{ + + /* + * Back pointer to the route node for this destination. This helps + * us get to the prefix that this structure is for. + */ + struct route_node *rnode; + + /* + * Doubly-linked list of routes for this prefix. + */ + struct rib *routes; + + /* + * Flags, see below. + */ + u_int32_t flags; + + /* + * Linkage to put dest on the FPM processing queue. + */ + TAILQ_ENTRY(rib_dest_t_) fpm_q_entries; + +} rib_dest_t; + +#define RIB_ROUTE_QUEUED(x) (1 << (x)) + +/* + * The maximum qindex that can be used. + */ +#define ZEBRA_MAX_QINDEX (MQ_SIZE - 1) + +/* + * This flag indicates that a given prefix has been 'advertised' to + * the FPM to be installed in the forwarding plane. + */ +#define RIB_DEST_SENT_TO_FPM (1 << (ZEBRA_MAX_QINDEX + 1)) + +/* + * This flag is set when we need to send an update to the FPM about a + * dest. + */ +#define RIB_DEST_UPDATE_FPM (1 << (ZEBRA_MAX_QINDEX + 2)) + +/* + * Macro to iterate over each route for a destination (prefix). + */ +#define RIB_DEST_FOREACH_ROUTE(dest, rib) \ + for ((rib) = (dest) ? (dest)->routes : NULL; (rib); (rib) = (rib)->next) + +/* + * Same as above, but allows the current node to be unlinked. + */ +#define RIB_DEST_FOREACH_ROUTE_SAFE(dest, rib, next) \ + for ((rib) = (dest) ? (dest)->routes : NULL; \ + (rib) && ((next) = (rib)->next, 1); \ + (rib) = (next)) + +#define RNODE_FOREACH_RIB(rn, rib) \ + RIB_DEST_FOREACH_ROUTE (rib_dest_from_rnode (rn), rib) + +#define RNODE_FOREACH_RIB_SAFE(rn, rib, next) \ + RIB_DEST_FOREACH_ROUTE_SAFE (rib_dest_from_rnode (rn), rib, next) + +/* Static route information. */ +struct static_route +{ + /* For linked list. */ + 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_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. */ + union g_addr addr; + char *ifname; + + /* bit flags */ + u_char flags; +/* + see ZEBRA_FLAG_REJECT + ZEBRA_FLAG_BLACKHOLE + */ +}; + +/* 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 +{ + struct nexthop *nexthop; + vrf_id_t vrf_id; +}; + + +#if defined (HAVE_RTADV) +/* 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; +}; +#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 zebra_vrf +{ + /* Identifier. */ + vrf_id_t vrf_id; + + /* Routing table name. */ + char *name; + + /* Description. */ + char *desc; + + /* FIB identifier. */ + u_char fib_id; + + /* Routing table. */ + struct route_table *table[AFI_MAX][SAFI_MAX]; + + /* 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]; +}; + +/* + * 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 *); +#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 + +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 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, + 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, ifindex_t ifindex, + vrf_id_t, safi_t safi); + +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 *, vrf_id_t); + +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_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_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); + +extern int +rib_add_ipv6 (int type, int flags, struct prefix_ipv6 *p, + 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, ifindex_t ifindex, vrf_id_t vrf_id, safi_t safi); + +extern struct rib *rib_lookup_ipv6 (struct in6_addr *, vrf_id_t); + +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, 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, 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; +} + +/* + * 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 new file mode 100644 index 0000000..a4eb73a --- /dev/null +++ b/zebra/router-id.c @@ -0,0 +1,307 @@ +/* + * Router ID for zebra daemon. + * + * Copyright (C) 2004 James R. Leu + * + * This file is part of Quagga routing suite. + * + * 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. + */ + +#include + +#include "if.h" +#include "vty.h" +#include "sockunion.h" +#include "prefix.h" +#include "stream.h" +#include "command.h" +#include "memory.h" +#include "ioctl.h" +#include "connected.h" +#include "network.h" +#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" + +/* master zebra server structure */ +extern struct zebra_t zebrad; + +static struct connected * +router_id_find_node (struct list *l, struct connected *ifc) +{ + struct listnode *node; + struct connected *c; + + for (ALL_LIST_ELEMENTS_RO (l, node, c)) + if (prefix_same (ifc->address, c->address)) + return c; + + return NULL; +} + +static int +router_id_bad_address (struct connected *ifc) +{ + if (ifc->address->family != AF_INET) + return 1; + + /* non-redistributable addresses shouldn't be used for RIDs either */ + if (!zebra_check_addr (ifc->address)) + return 1; + + return 0; +} + +void +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 (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 (zvrf->rid_lo_sorted_list); + c = listgetdata (node); + p->u.prefix4.s_addr = c->address->u.prefix4.s_addr; + } + else if (!list_isempty (zvrf->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, 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); + + zvrf->rid_user_assigned.u.prefix4.s_addr = p->u.prefix4.s_addr; + + router_id_get (&p2, vrf_id); + + for (ALL_LIST_ELEMENTS_RO (zebrad.client_list, node, client)) + zsend_router_id_update (client, &p2, vrf_id); +} + +void +router_id_add_address (struct connected *ifc) +{ + struct list *l = NULL; + struct listnode *node; + 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, zvrf->vrf_id); + + if (!strncmp (ifc->ifp->name, "lo", 2) + || !strncmp (ifc->ifp->name, "dummy", 5)) + l = zvrf->rid_lo_sorted_list; + else + l = zvrf->rid_all_sorted_list; + + if (!router_id_find_node (l, ifc)) + listnode_add_sort (l, ifc); + + 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, zvrf->vrf_id); +} + +void +router_id_del_address (struct connected *ifc) +{ + struct connected *c; + struct list *l; + struct prefix after; + 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, zvrf->vrf_id); + + if (!strncmp (ifc->ifp->name, "lo", 2) + || !strncmp (ifc->ifp->name, "dummy", 5)) + l = zvrf->rid_lo_sorted_list; + else + l = zvrf->rid_all_sorted_list; + + if ((c = router_id_find_node (l, ifc))) + listnode_delete (l, c); + + 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, zvrf->vrf_id); +} + +void +router_id_write (struct vty *vty) +{ + 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, + router_id_cmd, + "router-id A.B.C.D", + "Manually set the router-id\n" + "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) + return CMD_WARNING; + + rid.prefixlen = 32; + rid.family = AF_INET; + + 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", + NO_STR + "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; + + 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; + + return IPV4_ADDR_CMP(&ifa->address->u.prefix4.s_addr,&ifb->address->u.prefix4.s_addr); +} + +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 (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)); + + zvrf->rid_all_sorted_list->cmp = router_id_cmp; + zvrf->rid_lo_sorted_list->cmp = router_id_cmp; + + 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 new file mode 100644 index 0000000..46d300e --- /dev/null +++ b/zebra/router-id.h @@ -0,0 +1,41 @@ +/* + * Router ID for zebra daemon. + * + * Copyright (C) 2004 James R. Leu + * + * This file is part of Quagga routing suite. + * + * 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 _ROUTER_ID_H_ +#define _ROUTER_ID_H_ + +#include + +#include "memory.h" +#include "prefix.h" +#include "zclient.h" +#include "if.h" + +extern void router_id_add_address(struct connected *); +extern void router_id_del_address(struct connected *); +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 *, vrf_id_t); + +#endif diff --git a/zebra/rt.h b/zebra/rt.h new file mode 100644 index 0000000..8c1c476 --- /dev/null +++ b/zebra/rt.h @@ -0,0 +1,35 @@ +/* + * kernel routing table update prototype. + * Copyright (C) 1998 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_H +#define _ZEBRA_RT_H + +#include "prefix.h" +#include "if.h" +#include "zebra/rib.h" + +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 *); + +#endif /* _ZEBRA_RT_H */ diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c new file mode 100644 index 0000000..fc6e373 --- /dev/null +++ b/zebra/rt_netlink.c @@ -0,0 +1,2069 @@ +/* Kernel routing table updates using netlink over GNU/Linux system. + * 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. + */ + +#include +#include + +/* Hack for GNU libc version 2. */ +#ifndef MSG_TRUNC +#define MSG_TRUNC 0x20 +#endif /* MSG_TRUNC */ + +#include "linklist.h" +#include "if.h" +#include "log.h" +#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" +#include "zebra/redistribute.h" +#include "zebra/interface.h" +#include "zebra/debug.h" + +#include "rt_netlink.h" + +static const struct message nlmsg_str[] = { + {RTM_NEWROUTE, "RTM_NEWROUTE"}, + {RTM_DELROUTE, "RTM_DELROUTE"}, + {RTM_GETROUTE, "RTM_GETROUTE"}, + {RTM_NEWLINK, "RTM_NEWLINK"}, + {RTM_DELLINK, "RTM_DELLINK"}, + {RTM_GETLINK, "RTM_GETLINK"}, + {RTM_NEWADDR, "RTM_NEWADDR"}, + {RTM_DELADDR, "RTM_DELADDR"}, + {RTM_GETADDR, "RTM_GETADDR"}, + {0, NULL} +}; + +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, ifindex_t ifi_index) +{ + struct interface *oifp; + + if (((oifp = if_lookup_by_index(ifi_index)) != NULL) && (oifp != ifp)) + { + if (ifi_index == IFINDEX_INTERNAL) + zlog_err("Netlink is setting interface %s ifindex to reserved " + "internal value %u", ifp->name, ifi_index); + else + { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("interface index %d was renamed from %s to %s", + ifi_index, oifp->name, ifp->name); + if (if_is_up(oifp)) + zlog_err("interface rename detected on up interface: index %d " + "was renamed from %s to %s, results are uncertain!", + ifi_index, oifp->name, ifp->name); + if_delete_update(oifp); + } + } + ifp->ifindex = ifi_index; +} + +#ifndef SO_RCVBUFFORCE +#define SO_RCVBUFFORCE (33) +#endif + +static int +netlink_recvbuf (struct nlsock *nl, uint32_t newsize) +{ + u_int32_t oldsize; + socklen_t newlen = sizeof(newsize); + socklen_t oldlen = sizeof(oldsize); + int ret; + + ret = getsockopt(nl->sock, SOL_SOCKET, SO_RCVBUF, &oldsize, &oldlen); + if (ret < 0) + { + zlog (NULL, LOG_ERR, "Can't get %s receive buffer size: %s", nl->name, + safe_strerror (errno)); + return -1; + } + + /* 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, + safe_strerror (errno)); + return -1; + } + + ret = getsockopt(nl->sock, SOL_SOCKET, SO_RCVBUF, &newsize, &newlen); + if (ret < 0) + { + zlog (NULL, LOG_ERR, "Can't get %s receive buffer size: %s", nl->name, + safe_strerror (errno)); + return -1; + } + + zlog (NULL, LOG_INFO, + "Setting netlink socket receive buffer size: %u -> %u", + oldsize, newsize); + return 0; +} + +/* Make socket for Linux netlink interface. */ +static int +netlink_socket (struct nlsock *nl, unsigned long groups, vrf_id_t vrf_id) +{ + int ret; + struct sockaddr_nl snl; + int sock; + int namelen; + int save_errno; + + 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, + safe_strerror (errno)); + return -1; + } + + memset (&snl, 0, sizeof snl); + snl.nl_family = AF_NETLINK; + snl.nl_groups = groups; + + /* Bind the socket to the netlink structure for anything. */ + ret = bind (sock, (struct sockaddr *) &snl, sizeof snl); + save_errno = errno; + if (zserv_privs.change (ZPRIVS_LOWER)) + zlog (NULL, LOG_ERR, "Can't lower privileges"); + + if (ret < 0) + { + zlog (NULL, LOG_ERR, "Can't bind %s socket to group 0x%x: %s", + nl->name, snl.nl_groups, safe_strerror (save_errno)); + close (sock); + return -1; + } + + /* multiple netlink sockets will have different nl_pid */ + namelen = sizeof snl; + ret = getsockname (sock, (struct sockaddr *) &snl, (socklen_t *) &namelen); + if (ret < 0 || namelen != sizeof snl) + { + zlog (NULL, LOG_ERR, "Can't get %s socket name: %s", nl->name, + safe_strerror (errno)); + close (sock); + return -1; + } + + nl->snl = snl; + nl->sock = sock; + return ret; +} + +/* Get type specified information from netlink. */ +static int +netlink_request (int family, int type, struct nlsock *nl) +{ + int ret; + struct sockaddr_nl snl; + int save_errno; + + struct + { + struct nlmsghdr nlh; + struct rtgenmsg g; + } req; + + + /* Check netlink socket. */ + if (nl->sock < 0) + { + zlog (NULL, LOG_ERR, "%s socket isn't active.", nl->name); + return -1; + } + + memset (&snl, 0, sizeof snl); + snl.nl_family = AF_NETLINK; + + memset (&req, 0, sizeof req); + req.nlh.nlmsg_len = sizeof req; + req.nlh.nlmsg_type = type; + req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; + req.nlh.nlmsg_pid = nl->snl.nl_pid; + req.nlh.nlmsg_seq = ++nl->seq; + req.g.rtgen_family = family; + + /* linux appears to check capabilities on every message + * have to raise caps for every message sent + */ + if (zserv_privs.change (ZPRIVS_RAISE)) + { + zlog (NULL, LOG_ERR, "Can't raise privileges"); + return -1; + } + + ret = sendto (nl->sock, (void *) &req, sizeof req, 0, + (struct sockaddr *) &snl, sizeof snl); + save_errno = errno; + + if (zserv_privs.change (ZPRIVS_LOWER)) + zlog (NULL, LOG_ERR, "Can't lower privileges"); + + if (ret < 0) + { + zlog (NULL, LOG_ERR, "%s sendto failed: %s", nl->name, + safe_strerror (save_errno)); + return -1; + } + + return 0; +} + +/* 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 *, + vrf_id_t), + struct nlsock *nl, struct zebra_vrf *zvrf) +{ + int status; + int ret = 0; + int error; + + while (1) + { + struct iovec iov = { + .iov_base = nl_rcvbuf.p, + .iov_len = nl_rcvbuf.size, + }; + struct sockaddr_nl snl; + 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); + if (status < 0) + { + if (errno == EINTR) + continue; + if (errno == EWOULDBLOCK || errno == EAGAIN) + break; + zlog (NULL, LOG_ERR, "%s recvmsg overrun: %s", + nl->name, safe_strerror(errno)); + continue; + } + + if (status == 0) + { + zlog (NULL, LOG_ERR, "%s EOF", nl->name); + return -1; + } + + if (msg.msg_namelen != sizeof snl) + { + zlog (NULL, LOG_ERR, "%s sender address length error: length %d", + nl->name, msg.msg_namelen); + return -1; + } + + for (h = (struct nlmsghdr *) nl_rcvbuf.p; + NLMSG_OK (h, (unsigned int) status); + h = NLMSG_NEXT (h, status)) + { + /* Finish of reading. */ + if (h->nlmsg_type == NLMSG_DONE) + return ret; + + /* Error handling. */ + if (h->nlmsg_type == NLMSG_ERROR) + { + struct nlmsgerr *err = (struct nlmsgerr *) NLMSG_DATA (h); + int errnum = err->error; + int msg_type = err->msg.nlmsg_type; + + /* If the error field is zero, then this is an ACK */ + if (err->error == 0) + { + if (IS_ZEBRA_DEBUG_KERNEL) + { + zlog_debug ("%s: %s ACK: type=%s(%u), seq=%u, pid=%u", + __FUNCTION__, nl->name, + lookup (nlmsg_str, err->msg.nlmsg_type), + err->msg.nlmsg_type, err->msg.nlmsg_seq, + err->msg.nlmsg_pid); + } + + /* return if not a multipart message, otherwise continue */ + if (!(h->nlmsg_flags & NLM_F_MULTI)) + { + return 0; + } + continue; + } + + if (h->nlmsg_len < NLMSG_LENGTH (sizeof (struct nlmsgerr))) + { + zlog (NULL, LOG_ERR, "%s error: message truncated", + nl->name); + return -1; + } + + /* Deal with errors that occur because of races in link handling */ + if (nl == &zvrf->netlink_cmd + && ((msg_type == RTM_DELROUTE && + (-errnum == ENODEV || -errnum == ESRCH)) + || (msg_type == RTM_NEWROUTE && -errnum == EEXIST))) + { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug ("%s: error: %s type=%s(%u), seq=%u, pid=%u", + nl->name, safe_strerror (-errnum), + lookup (nlmsg_str, msg_type), + msg_type, err->msg.nlmsg_seq, err->msg.nlmsg_pid); + return 0; + } + + zlog_err ("%s error: %s, type=%s(%u), seq=%u, pid=%u", + nl->name, safe_strerror (-errnum), + lookup (nlmsg_str, msg_type), + msg_type, err->msg.nlmsg_seq, err->msg.nlmsg_pid); + return -1; + } + + /* OK we got netlink message. */ + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug ("netlink_parse_info: %s type %s(%u), seq=%u, pid=%u", + nl->name, + lookup (nlmsg_str, h->nlmsg_type), h->nlmsg_type, + h->nlmsg_seq, h->nlmsg_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", + zvrf->netlink_cmd.name, nl->name); + continue; + } + + error = (*filter) (&snl, h, zvrf->vrf_id); + if (error < 0) + { + zlog (NULL, LOG_ERR, "%s filter function error", nl->name); + ret = error; + } + } + + /* After error care. */ + if (msg.msg_flags & MSG_TRUNC) + { + zlog (NULL, LOG_ERR, "%s error: message truncated!", nl->name); + zlog (NULL, LOG_ERR, + "Must restart with larger --nl-bufsize value!"); + continue; + } + if (status) + { + zlog (NULL, LOG_ERR, "%s error: data remnant size %d", nl->name, + status); + return -1; + } + } + return ret; +} + +/* Utility function for parse rtattr. */ +static void +netlink_parse_rtattr (struct rtattr **tb, int max, struct rtattr *rta, + int len) +{ + while (RTA_OK (rta, len)) + { + if (rta->rta_type <= max) + tb[rta->rta_type] = rta; + rta = RTA_NEXT (rta, len); + } +} + +/* 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, + vrf_id_t vrf_id) +{ + int len; + struct ifinfomsg *ifi; + struct rtattr *tb[IFLA_MAX + 1]; + struct interface *ifp; + char *name; + + ifi = NLMSG_DATA (h); + + if (h->nlmsg_type != RTM_NEWLINK) + return 0; + + len = h->nlmsg_len - NLMSG_LENGTH (sizeof (struct ifinfomsg)); + if (len < 0) + return -1; + + /* Looking up interface name. */ + memset (tb, 0, sizeof tb); + netlink_parse_rtattr (tb, IFLA_MAX, IFLA_RTA (ifi), len); + +#ifdef IFLA_WIRELESS + /* check for wireless messages to ignore */ + if ((tb[IFLA_WIRELESS] != NULL) && (ifi->ifi_change == 0)) + { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug ("%s: ignoring IFLA_WIRELESS message", __func__); + return 0; + } +#endif /* IFLA_WIRELESS */ + + if (tb[IFLA_IFNAME] == NULL) + return -1; + name = (char *) RTA_DATA (tb[IFLA_IFNAME]); + + /* Add interface. */ + 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 = 0; + + /* Hardware type and address. */ + ifp->ll_type = netlink_to_zebra_link_type (ifi->ifi_type); + netlink_interface_update_hw_addr (tb, ifp); + + if_add_update (ifp); + + return 0; +} + +/* Lookup interface IPv4/IPv6 address. */ +static int +netlink_interface_addr (struct sockaddr_nl *snl, struct nlmsghdr *h, + vrf_id_t vrf_id) +{ + int len; + struct ifaddrmsg *ifa; + struct rtattr *tb[IFA_MAX + 1]; + struct interface *ifp; + void *addr; + void *broad; + u_char flags = 0; + char *label = NULL; + + ifa = NLMSG_DATA (h); + + if (ifa->ifa_family != AF_INET +#ifdef HAVE_IPV6 + && ifa->ifa_family != AF_INET6 +#endif /* HAVE_IPV6 */ + ) + return 0; + + if (h->nlmsg_type != RTM_NEWADDR && h->nlmsg_type != RTM_DELADDR) + return 0; + + len = h->nlmsg_len - NLMSG_LENGTH (sizeof (struct ifaddrmsg)); + if (len < 0) + return -1; + + memset (tb, 0, sizeof tb); + netlink_parse_rtattr (tb, IFA_MAX, IFA_RTA (ifa), len); + + 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 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 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]), + buf, BUFSIZ), ifa->ifa_prefixlen); + if (tb[IFA_ADDRESS]) + zlog_debug (" IFA_ADDRESS %s/%d", + inet_ntop (ifa->ifa_family, RTA_DATA (tb[IFA_ADDRESS]), + buf, BUFSIZ), ifa->ifa_prefixlen); + if (tb[IFA_BROADCAST]) + zlog_debug (" IFA_BROADCAST %s/%d", + inet_ntop (ifa->ifa_family, RTA_DATA (tb[IFA_BROADCAST]), + buf, BUFSIZ), ifa->ifa_prefixlen); + if (tb[IFA_LABEL] && strcmp (ifp->name, RTA_DATA (tb[IFA_LABEL]))) + zlog_debug (" IFA_LABEL %s", (char *)RTA_DATA (tb[IFA_LABEL])); + + if (tb[IFA_CACHEINFO]) + { + struct ifa_cacheinfo *ci = RTA_DATA (tb[IFA_CACHEINFO]); + zlog_debug (" IFA_CACHEINFO pref %d, valid %d", + ci->ifa_prefered, ci->ifa_valid); + } + } + + /* logic copied from iproute2/ip/ipaddress.c:print_addrinfo() */ + if (tb[IFA_LOCAL] == NULL) + tb[IFA_LOCAL] = tb[IFA_ADDRESS]; + if (tb[IFA_ADDRESS] == NULL) + tb[IFA_ADDRESS] = tb[IFA_LOCAL]; + + /* local interface address */ + addr = (tb[IFA_LOCAL] ? RTA_DATA(tb[IFA_LOCAL]) : NULL); + + /* is there a peer address? */ + if (tb[IFA_ADDRESS] && + memcmp(RTA_DATA(tb[IFA_ADDRESS]), RTA_DATA(tb[IFA_LOCAL]), RTA_PAYLOAD(tb[IFA_ADDRESS]))) + { + broad = RTA_DATA(tb[IFA_ADDRESS]); + SET_FLAG (flags, ZEBRA_IFA_PEER); + } + else + /* seeking a broadcast address */ + broad = (tb[IFA_BROADCAST] ? RTA_DATA(tb[IFA_BROADCAST]) : NULL); + + /* addr is primary key, SOL if we don't have one */ + if (addr == NULL) + { + zlog_debug ("%s: NULL address", __func__); + return -1; + } + + /* Flags. */ + if (ifa->ifa_flags & IFA_F_SECONDARY) + SET_FLAG (flags, ZEBRA_IFA_SECONDARY); + + /* Label */ + if (tb[IFA_LABEL]) + label = (char *) RTA_DATA (tb[IFA_LABEL]); + + if (ifp && label && strcmp (ifp->name, label) == 0) + label = NULL; + + /* Register interface address to the interface. */ + if (ifa->ifa_family == AF_INET) + { + if (h->nlmsg_type == RTM_NEWADDR) + connected_add_ipv4 (ifp, flags, + (struct in_addr *) addr, ifa->ifa_prefixlen, + (struct in_addr *) broad, label); + else + connected_delete_ipv4 (ifp, flags, + (struct in_addr *) addr, ifa->ifa_prefixlen, + (struct in_addr *) broad); + } +#ifdef HAVE_IPV6 + if (ifa->ifa_family == AF_INET6) + { + if (h->nlmsg_type == RTM_NEWADDR) + connected_add_ipv6 (ifp, flags, + (struct in6_addr *) addr, ifa->ifa_prefixlen, + (struct in6_addr *) broad, label); + else + connected_delete_ipv6 (ifp, + (struct in6_addr *) addr, ifa->ifa_prefixlen, + (struct in6_addr *) broad); + } +#endif /* HAVE_IPV6 */ + + return 0; +} + +/* Looking up routing table by netlink interface. */ +static int +netlink_routing_table (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 flags = 0; + + char anyaddr[16] = { 0 }; + + int index; + int table; + u_int32_t mtu = 0; + + void *dest; + void *gate; + void *src; + + rtm = NLMSG_DATA (h); + + if (h->nlmsg_type != RTM_NEWROUTE) + return 0; + if (rtm->rtm_type != RTN_UNICAST) + return 0; + + table = rtm->rtm_table; +#if 0 /* we weed them out later in rib_weed_tables () */ + if (table != RT_TABLE_MAIN && table != zebrad.rtm_table_default) + return 0; +#endif + + len = h->nlmsg_len - NLMSG_LENGTH (sizeof (struct rtmsg)); + if (len < 0) + return -1; + + memset (tb, 0, sizeof tb); + netlink_parse_rtattr (tb, RTA_MAX, RTM_RTA (rtm), len); + + if (rtm->rtm_flags & RTM_F_CLONED) + return 0; + if (rtm->rtm_protocol == RTPROT_REDIRECT) + return 0; + if (rtm->rtm_protocol == RTPROT_KERNEL) + return 0; + + if (rtm->rtm_src_len != 0) + return 0; + + /* Route which inserted by Zebra. */ + if (rtm->rtm_protocol == RTPROT_ZEBRA) + flags |= ZEBRA_FLAG_SELFROUTE; + + index = 0; + dest = NULL; + gate = NULL; + src = NULL; + + if (tb[RTA_OIF]) + index = *(int *) RTA_DATA (tb[RTA_OIF]); + + if (tb[RTA_DST]) + dest = RTA_DATA (tb[RTA_DST]); + else + dest = anyaddr; + + if (tb[RTA_PREFSRC]) + src = RTA_DATA (tb[RTA_PREFSRC]); + + if (tb[RTA_GATEWAY]) + gate = RTA_DATA (tb[RTA_GATEWAY]); + + 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) + { + struct prefix_ipv4 p; + p.family = AF_INET; + memcpy (&p.prefix, dest, 4); + p.prefixlen = rtm->rtm_dst_len; + + 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) + { + struct prefix_ipv6 p; + p.family = AF_INET6; + memcpy (&p.prefix, dest, 16); + p.prefixlen = rtm->rtm_dst_len; + + rib_add_ipv6 (ZEBRA_ROUTE_KERNEL, flags, &p, gate, index, vrf_id, + table, 0, mtu, 0, SAFI_UNICAST); + } +#endif /* HAVE_IPV6 */ + + return 0; +} + +static const struct message rtproto_str[] = { + {RTPROT_REDIRECT, "redirect"}, + {RTPROT_KERNEL, "kernel"}, + {RTPROT_BOOT, "boot"}, + {RTPROT_STATIC, "static"}, + {RTPROT_GATED, "GateD"}, + {RTPROT_RA, "router advertisement"}, + {RTPROT_MRT, "MRT"}, + {RTPROT_ZEBRA, "Zebra"}, +#ifdef RTPROT_BIRD + {RTPROT_BIRD, "BIRD"}, +#endif /* RTPROT_BIRD */ + {0, NULL} +}; + +/* Routing information change from the kernel. */ +static int +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; + u_int32_t mtu = 0; + + void *dest; + void *gate; + void *src; + + rtm = NLMSG_DATA (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 vrf %u\n", h->nlmsg_type, vrf_id); + return 0; + } + + /* Connected route. */ + if (IS_ZEBRA_DEBUG_KERNEL) + 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), + vrf_id); + + if (rtm->rtm_type != RTN_UNICAST) + { + return 0; + } + + table = rtm->rtm_table; + if (table != RT_TABLE_MAIN && table != zebrad.rtm_table_default) + { + return 0; + } + + len = h->nlmsg_len - NLMSG_LENGTH (sizeof (struct rtmsg)); + if (len < 0) + return -1; + + memset (tb, 0, sizeof tb); + netlink_parse_rtattr (tb, RTA_MAX, RTM_RTA (rtm), len); + + if (rtm->rtm_flags & RTM_F_CLONED) + return 0; + if (rtm->rtm_protocol == RTPROT_REDIRECT) + return 0; + if (rtm->rtm_protocol == RTPROT_KERNEL) + return 0; + + 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, vrf %u", vrf_id); + return 0; + } + + index = 0; + dest = NULL; + gate = NULL; + src = NULL; + + if (tb[RTA_OIF]) + index = *(int *) RTA_DATA (tb[RTA_OIF]); + + if (tb[RTA_DST]) + dest = RTA_DATA (tb[RTA_DST]); + else + dest = anyaddr; + + if (tb[RTA_GATEWAY]) + gate = RTA_DATA (tb[RTA_GATEWAY]); + + if (tb[RTA_PREFSRC]) + src = RTA_DATA (tb[RTA_PREFSRC]); + + 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) + { + struct prefix_ipv4 p; + p.family = AF_INET; + memcpy (&p.prefix, dest, 4); + p.prefixlen = rtm->rtm_dst_len; + + if (IS_ZEBRA_DEBUG_KERNEL) + { + 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) + { + 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, zebra_flags, &p, gate, + index, vrf_id, SAFI_UNICAST); + } + +#ifdef HAVE_IPV6 + if (rtm->rtm_family == AF_INET6) + { + struct prefix_ipv6 p; + + p.family = AF_INET6; + memcpy (&p.prefix, dest, 16); + p.prefixlen = rtm->rtm_dst_len; + + if (IS_ZEBRA_DEBUG_KERNEL) + { + 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, vrf_id, table, + 0, mtu, 0, SAFI_UNICAST); + else + rib_delete_ipv6 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, gate, index, vrf_id, + SAFI_UNICAST); + } +#endif /* HAVE_IPV6 */ + + return 0; +} + +static int +netlink_link_change (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; + + ifi = NLMSG_DATA (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 vrf %u\n", + h->nlmsg_type, vrf_id); + return 0; + } + + len = h->nlmsg_len - NLMSG_LENGTH (sizeof (struct ifinfomsg)); + if (len < 0) + return -1; + + /* Looking up interface name. */ + memset (tb, 0, sizeof tb); + netlink_parse_rtattr (tb, IFLA_MAX, IFLA_RTA (ifi), len); + +#ifdef IFLA_WIRELESS + /* check for wireless messages to ignore */ + if ((tb[IFLA_WIRELESS] != NULL) && (ifi->ifi_change == 0)) + { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug ("%s: ignoring IFLA_WIRELESS message, vrf %u", __func__, + vrf_id); + return 0; + } +#endif /* IFLA_WIRELESS */ + + if (tb[IFLA_IFNAME] == NULL) + return -1; + name = (char *) RTA_DATA (tb[IFLA_IFNAME]); + + /* Add interface. */ + if (h->nlmsg_type == RTM_NEWLINK) + { + 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_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 = 0; + + netlink_interface_update_hw_addr (tb, ifp); + + /* If new link is added. */ + if_add_update (ifp); + } + else + { + /* Interface status change. */ + set_ifindex(ifp, ifi->ifi_index); + ifp->mtu6 = ifp->mtu = *(int *) RTA_DATA (tb[IFLA_MTU]); + ifp->metric = 0; + + netlink_interface_update_hw_addr (tb, ifp); + + if (if_is_operative (ifp)) + { + ifp->flags = ifi->ifi_flags & 0x0000fffff; + if (!if_is_operative (ifp)) + if_down (ifp); + else + /* Must notify client daemons of new interface status. */ + zebra_interface_up_update (ifp); + } + else + { + ifp->flags = ifi->ifi_flags & 0x0000fffff; + if (if_is_operative (ifp)) + if_up (ifp); + } + } + } + else + { + /* RTM_DELLINK. */ + ifp = if_lookup_by_name_vrf (name, vrf_id); + + if (ifp == NULL) + { + zlog_warn ("interface %s vrf %u is deleted but can't find", + name, vrf_id); + return 0; + } + + if_delete_update (ifp); + } + + return 0; +} + +static int +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 ) + { + zlog ( NULL, LOG_ERR, "Ignoring message from pid %u", snl->nl_pid ); + return 0; + } + + switch (h->nlmsg_type) + { + case RTM_NEWROUTE: + return netlink_route_change (snl, h, vrf_id); + break; + case RTM_DELROUTE: + return netlink_route_change (snl, h, vrf_id); + break; + case RTM_NEWLINK: + return netlink_link_change (snl, h, vrf_id); + break; + case RTM_DELLINK: + return netlink_link_change (snl, h, vrf_id); + break; + case RTM_NEWADDR: + return netlink_interface_addr (snl, h, vrf_id); + break; + case RTM_DELADDR: + return netlink_interface_addr (snl, h, vrf_id); + break; + default: + zlog_warn ("Unknown netlink nlmsg_type %d vrf %u\n", h->nlmsg_type, + vrf_id); + break; + } + return 0; +} + +/* Interface lookup by netlink socket. */ +int +interface_lookup_netlink (struct zebra_vrf *zvrf) +{ + int ret; + + /* Get interface information. */ + ret = netlink_request (AF_PACKET, RTM_GETLINK, &zvrf->netlink_cmd); + if (ret < 0) + return ret; + 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, &zvrf->netlink_cmd); + if (ret < 0) + return ret; + 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, &zvrf->netlink_cmd); + if (ret < 0) + return ret; + ret = netlink_parse_info (netlink_interface_addr, &zvrf->netlink_cmd, zvrf); + if (ret < 0) + return ret; +#endif /* HAVE_IPV6 */ + + return 0; +} + +/* Routing table read function using netlink interface. Only called + bootstrap time. */ +int +netlink_route_read (struct zebra_vrf *zvrf) +{ + int ret; + + /* Get IPv4 routing table. */ + ret = netlink_request (AF_INET, RTM_GETROUTE, &zvrf->netlink_cmd); + if (ret < 0) + return ret; + 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, &zvrf->netlink_cmd); + if (ret < 0) + return ret; + ret = netlink_parse_info (netlink_routing_table, &zvrf->netlink_cmd, zvrf); + if (ret < 0) + return ret; +#endif /* HAVE_IPV6 */ + + return 0; +} + +/* Utility function comes from iproute2. + Authors: Alexey Kuznetsov, */ +int +addattr_l (struct nlmsghdr *n, size_t maxlen, int type, void *data, size_t alen) +{ + size_t len; + struct rtattr *rta; + + len = RTA_LENGTH (alen); + + if (NLMSG_ALIGN (n->nlmsg_len) + len > maxlen) + return -1; + + rta = (struct rtattr *) (((char *) n) + NLMSG_ALIGN (n->nlmsg_len)); + rta->rta_type = type; + rta->rta_len = len; + memcpy (RTA_DATA (rta), data, alen); + n->nlmsg_len = NLMSG_ALIGN (n->nlmsg_len) + len; + + return 0; +} + +int +rta_addattr_l (struct rtattr *rta, size_t maxlen, int type, void *data, + size_t alen) +{ + size_t len; + struct rtattr *subrta; + + len = RTA_LENGTH (alen); + + if (RTA_ALIGN (rta->rta_len) + len > maxlen) + return -1; + + subrta = (struct rtattr *) (((char *) rta) + RTA_ALIGN (rta->rta_len)); + subrta->rta_type = type; + subrta->rta_len = len; + memcpy (RTA_DATA (subrta), data, alen); + rta->rta_len = NLMSG_ALIGN (rta->rta_len) + len; + + return 0; +} + +/* Utility function comes from iproute2. + Authors: Alexey Kuznetsov, */ +int +addattr32 (struct nlmsghdr *n, size_t maxlen, int type, int data) +{ + size_t len; + struct rtattr *rta; + + len = RTA_LENGTH (4); + + if (NLMSG_ALIGN (n->nlmsg_len) + len > maxlen) + return -1; + + rta = (struct rtattr *) (((char *) n) + NLMSG_ALIGN (n->nlmsg_len)); + rta->rta_type = type; + rta->rta_len = len; + memcpy (RTA_DATA (rta), &data, 4); + n->nlmsg_len = NLMSG_ALIGN (n->nlmsg_len) + len; + + return 0; +} + +static int +netlink_talk_filter (struct sockaddr_nl *snl, struct nlmsghdr *h, + vrf_id_t vrf_id) +{ + 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, struct zebra_vrf *zvrf) +{ + int status; + struct sockaddr_nl snl; + 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); + snl.nl_family = AF_NETLINK; + + n->nlmsg_seq = ++nl->seq; + + /* Request an acknowledgement by setting NLM_F_ACK */ + n->nlmsg_flags |= NLM_F_ACK; + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug ("netlink_talk: %s type %s(%u), seq=%u", nl->name, + lookup (nlmsg_str, n->nlmsg_type), n->nlmsg_type, + n->nlmsg_seq); + + /* Send message to netlink interface. */ + if (zserv_privs.change (ZPRIVS_RAISE)) + zlog (NULL, LOG_ERR, "Can't raise privileges"); + status = sendmsg (nl->sock, &msg, 0); + save_errno = errno; + if (zserv_privs.change (ZPRIVS_LOWER)) + zlog (NULL, LOG_ERR, "Can't lower privileges"); + + if (status < 0) + { + zlog (NULL, LOG_ERR, "netlink_talk sendmsg() error: %s", + safe_strerror (save_errno)); + return -1; + } + + + /* + * Get reply from netlink socket. + * The reply should either be an acknowlegement or an error. + */ + return netlink_parse_info (netlink_talk_filter, nl, zvrf); +} + +/* 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) +{ + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ONLINK)) + rtmsg->rtm_flags |= RTNH_F_ONLINK; + if (nexthop->type == NEXTHOP_TYPE_IPV4 + || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) + { + addattr_l (nlmsg, req_size, RTA_GATEWAY, + &nexthop->gate.ipv4, bytelen); + if (nexthop->src.ipv4.s_addr) + addattr_l (nlmsg, req_size, RTA_PREFSRC, + &nexthop->src.ipv4, bytelen); + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("netlink_route_multipath() (%s): " + "nexthop via %s if %u", + routedesc, + inet_ntoa (nexthop->gate.ipv4), + nexthop->ifindex); + } +#ifdef HAVE_IPV6 + if (nexthop->type == NEXTHOP_TYPE_IPV6 + || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME + || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) + { + addattr_l (nlmsg, req_size, RTA_GATEWAY, + &nexthop->gate.ipv6, bytelen); + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("netlink_route_multipath() (%s): " + "nexthop via %s if %u", + routedesc, + inet6_ntoa (nexthop->gate.ipv6), + nexthop->ifindex); + } +#endif /* HAVE_IPV6 */ + if (nexthop->type == NEXTHOP_TYPE_IFINDEX + || nexthop->type == NEXTHOP_TYPE_IFNAME + || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) + { + addattr32 (nlmsg, req_size, RTA_OIF, nexthop->ifindex); + + if (nexthop->src.ipv4.s_addr) + addattr_l (nlmsg, req_size, RTA_PREFSRC, + &nexthop->src.ipv4, bytelen); + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("netlink_route_multipath() (%s): " + "nexthop via if %u", routedesc, nexthop->ifindex); + } + + if (nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX + || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME) + { + addattr32 (nlmsg, req_size, RTA_OIF, nexthop->ifindex); + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("netlink_route_multipath() (%s): " + "nexthop via if %u", routedesc, nexthop->ifindex); + } +} + +/* This function takes a nexthop as argument and + * appends to the given rtattr/rtnexthop pair the + * representation of the nexthop. If the nexthop + * defines a preferred source, the src parameter + * will be modified to point to that src, otherwise + * it will be kept unmodified. + * + * @param routedesc: Human readable description of route type + * (direct/recursive, single-/multipath) + * @param bytelen: Length of addresses in bytes. + * @param nexthop: Nexthop information + * @param rta: rtnetlink attribute structure + * @param rtnh: pointer to an rtnetlink nexthop structure + * @param src: pointer pointing to a location where + * the prefsrc should be stored. + */ +static void +_netlink_route_build_multipath( + const char *routedesc, + int bytelen, + struct nexthop *nexthop, + struct rtattr *rta, + struct rtnexthop *rtnh, + union g_addr **src + ) +{ + rtnh->rtnh_len = sizeof (*rtnh); + rtnh->rtnh_flags = 0; + rtnh->rtnh_hops = 0; + rta->rta_len += rtnh->rtnh_len; + + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ONLINK)) + rtnh->rtnh_flags |= RTNH_F_ONLINK; + + if (nexthop->type == NEXTHOP_TYPE_IPV4 + || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) + { + rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY, + &nexthop->gate.ipv4, bytelen); + rtnh->rtnh_len += sizeof (struct rtattr) + bytelen; + + if (nexthop->src.ipv4.s_addr) + *src = &nexthop->src; + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("netlink_route_multipath() (%s): " + "nexthop via %s if %u", + routedesc, + inet_ntoa (nexthop->gate.ipv4), + nexthop->ifindex); + } +#ifdef HAVE_IPV6 + if (nexthop->type == NEXTHOP_TYPE_IPV6 + || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME + || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) + { + rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY, + &nexthop->gate.ipv6, bytelen); + rtnh->rtnh_len += sizeof (struct rtattr) + bytelen; + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("netlink_route_multipath() (%s): " + "nexthop via %s if %u", + routedesc, + inet6_ntoa (nexthop->gate.ipv6), + nexthop->ifindex); + } +#endif /* HAVE_IPV6 */ + /* ifindex */ + if (nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX + || nexthop->type == NEXTHOP_TYPE_IFINDEX + || nexthop->type == NEXTHOP_TYPE_IFNAME) + { + rtnh->rtnh_ifindex = nexthop->ifindex; + if (nexthop->src.ipv4.s_addr) + *src = &nexthop->src; + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("netlink_route_multipath() (%s): " + "nexthop via if %u", routedesc, nexthop->ifindex); + } + else if (nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME + || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) + { + rtnh->rtnh_ifindex = nexthop->ifindex; + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("netlink_route_multipath() (%s): " + "nexthop via if %u", routedesc, nexthop->ifindex); + } + else + { + rtnh->rtnh_ifindex = 0; + } +} + +/* Log debug information for netlink_route_multipath + * if debug logging is enabled. + * + * @param cmd: Netlink command which is to be processed + * @param p: Prefix for which the change is due + * @param nexthop: Nexthop which is currently processed + * @param routedesc: Semantic annotation for nexthop + * (recursive, multipath, etc.) + * @param family: Address family which the change concerns + */ +static void +_netlink_route_debug( + int cmd, + struct prefix *p, + struct nexthop *nexthop, + const char *routedesc, + int family, + 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 bytelen; + struct sockaddr_nl snl; + 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[NL_PKT_BUF_SIZE]; + } 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_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_LINK; + + 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) + req.r.rtm_type = RTN_BLACKHOLE; + else if (rib->flags & ZEBRA_FLAG_REJECT) + req.r.rtm_type = RTN_UNREACHABLE; + else + assert (RTN_BLACKHOLE != RTN_UNREACHABLE); /* false */ + } + else + req.r.rtm_type = RTN_UNICAST; + } + + addattr_l (&req.n, sizeof req, RTA_DST, &p->u.prefix, bytelen); + + /* 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 (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; + } + + /* 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) + { + 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"; + + _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); + + nexthop_num++; + break; + } + } + } + else + { + char buf[NL_PKT_BUF_SIZE]; + struct rtattr *rta = (void *) buf; + struct rtnexthop *rtnh; + union g_addr *src = NULL; + + rta->rta_type = RTA_MULTIPATH; + rta->rta_len = RTA_LENGTH (0); + rtnh = RTA_DATA (rta); + + nexthop_num = 0; + 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++; + + _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) + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + } + } + if (src) + addattr_l (&req.n, sizeof req, RTA_PREFSRC, &src->ipv4, bytelen); + + if (rta->rta_len > RTA_LENGTH (0)) + addattr_l (&req.n, NL_PKT_BUF_SIZE, RTA_MULTIPATH, RTA_DATA (rta), + RTA_PAYLOAD (rta)); + } + + /* 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: + + /* Destination netlink address. */ + memset (&snl, 0, sizeof snl); + snl.nl_family = AF_NETLINK; + + /* Talk to netlink socket. */ + return netlink_talk (&req.n, &zvrf->netlink_cmd, zvrf); +} + +int +kernel_route_rib (struct prefix *p, struct rib *old, struct rib *new) +{ + 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); +} + +/* Interface address modification. */ +static int +netlink_address (int cmd, int family, struct interface *ifp, + struct connected *ifc) +{ + int bytelen; + struct prefix *p; + + struct + { + struct nlmsghdr n; + struct ifaddrmsg ifa; + 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 - NL_PKT_BUF_SIZE); + + bytelen = (family == AF_INET ? 4 : 16); + + req.n.nlmsg_len = NLMSG_LENGTH (sizeof (struct ifaddrmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = cmd; + req.ifa.ifa_family = family; + + req.ifa.ifa_index = ifp->ifindex; + req.ifa.ifa_prefixlen = p->prefixlen; + + addattr_l (&req.n, sizeof req, IFA_LOCAL, &p->u.prefix, bytelen); + + if (family == AF_INET && cmd == RTM_NEWADDR) + { + if (!CONNECTED_PEER(ifc) && ifc->destination) + { + p = ifc->destination; + addattr_l (&req.n, sizeof req, IFA_BROADCAST, &p->u.prefix, + bytelen); + } + } + + if (CHECK_FLAG (ifc->flags, ZEBRA_IFA_SECONDARY)) + SET_FLAG (req.ifa.ifa_flags, IFA_F_SECONDARY); + + if (ifc->label) + addattr_l (&req.n, sizeof req, IFA_LABEL, ifc->label, + strlen (ifc->label) + 1); + + return netlink_talk (&req.n, &zvrf->netlink_cmd, zvrf); +} + +int +kernel_address_add_ipv4 (struct interface *ifp, struct connected *ifc) +{ + return netlink_address (RTM_NEWADDR, AF_INET, ifp, ifc); +} + +int +kernel_address_delete_ipv4 (struct interface *ifp, struct connected *ifc) +{ + return netlink_address (RTM_DELADDR, AF_INET, ifp, ifc); +} + + +extern struct thread_master *master; + +/* Kernel route reflection. */ +static int +kernel_read (struct thread *thread) +{ + 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; +} + +/* Filter out messages from self that occur on listener socket, + caused by our actions on the command socket + */ +static void netlink_install_filter (int sock, __u32 pid) +{ + struct sock_filter filter[] = { + /* 0: ldh [4] */ + BPF_STMT(BPF_LD|BPF_ABS|BPF_H, offsetof(struct nlmsghdr, nlmsg_type)), + /* 1: jeq 0x18 jt 3 jf 6 */ + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_NEWROUTE), 1, 0), + /* 2: jeq 0x19 jt 3 jf 6 */ + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_DELROUTE), 0, 3), + /* 3: ldw [12] */ + BPF_STMT(BPF_LD|BPF_ABS|BPF_W, offsetof(struct nlmsghdr, nlmsg_pid)), + /* 4: jeq XX jt 5 jf 6 */ + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htonl(pid), 0, 1), + /* 5: ret 0 (skip) */ + BPF_STMT(BPF_RET|BPF_K, 0), + /* 6: ret 0xffff (keep) */ + BPF_STMT(BPF_RET|BPF_K, 0xffff), + }; + + struct sock_fprog prog = { + .len = array_size(filter), + .filter = filter, + }; + + if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &prog, sizeof(prog)) < 0) + zlog_warn ("Can't install socket filter: %s\n", safe_strerror(errno)); +} + +/* Exported interface function. This function simply calls + netlink_socket (). */ +void +kernel_init (struct zebra_vrf *zvrf) +{ + unsigned long groups; + + groups = RTMGRP_LINK | RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_IFADDR; +#ifdef HAVE_IPV6 + groups |= RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_IFADDR; +#endif /* HAVE_IPV6 */ + netlink_socket (&zvrf->netlink, groups, zvrf->vrf_id); + netlink_socket (&zvrf->netlink_cmd, 0, zvrf->vrf_id); + + /* Register kernel socket. */ + 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 (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 (&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; + } + + 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 0000000..9fc7001 --- /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 new file mode 100644 index 0000000..9dd4582 --- /dev/null +++ b/zebra/rt_socket.c @@ -0,0 +1,397 @@ +/* + * Kernel routing table updates by routing socket. + * 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 "if.h" +#include "prefix.h" +#include "sockunion.h" +#include "log.h" +#include "str.h" +#include "privs.h" + +#include "zebra/debug.h" +#include "zebra/rib.h" +#include "zebra/rt.h" +#include "zebra/kernel_socket.h" + +extern struct zebra_privs_t zserv_privs; + +/* kernel socket export */ +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 +sin_masklen (struct in_addr mask) +{ + char *p, *lim; + int len; + struct sockaddr_in sin; + + if (mask.s_addr == 0) + return sizeof (long); + + sin.sin_addr = mask; + len = sizeof (struct sockaddr_in); + + lim = (char *) &sin.sin_addr; + p = lim + sizeof (sin.sin_addr); + + while (*--p == 0 && p >= lim) + 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) + +{ + struct sockaddr_in *mask = NULL; + struct sockaddr_in sin_dest, sin_mask, sin_gate; + struct nexthop *nexthop, *tnexthop; + int recursing; + int nexthop_num = 0; + ifindex_t ifindex = 0; + int gate = 0; + int error; + char prefix_buf[PREFIX_STRLEN]; + + if (IS_ZEBRA_DEBUG_RIB) + 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 + sin_dest.sin_len = sizeof (struct sockaddr_in); +#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ + sin_dest.sin_addr = p->u.prefix4; + + memset (&sin_mask, 0, sizeof (struct sockaddr_in)); + + 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 */ + + /* Make gateway. */ + 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"; + + /* + * XXX We need to refrain from kernel operations in some cases, + * but this if statement seems overly cautious - what about + * other than ADD and DELETE? + */ + if ((cmd == RTM_ADD + && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + || (cmd == RTM_DELETE + && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) + )) + { + 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; + } + + if (gate && p->prefixlen == 32) + mask = NULL; + else + { + masklen2ip (p->prefixlen, &sin_mask.sin_addr); + sin_mask.sin_family = AF_INET; +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + sin_mask.sin_len = sin_masklen (sin_mask.sin_addr); +#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ + mask = &sin_mask; + } + + error = rtm_write (cmd, + (union sockunion *)&sin_dest, + (union sockunion *)mask, + gate ? (union sockunion *)&sin_gate : NULL, + ifindex, + rib->flags, + rib->metric); + + if (IS_ZEBRA_DEBUG_RIB) + { + if (!gate) + { + 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); + } + + switch (error) + { + /* We only flag nexthops as being in FIB if rtm_write() did its work. */ + case ZEBRA_ERR_NOERROR: + nexthop_num++; + if (IS_ZEBRA_DEBUG_RIB) + 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; + + /* The only valid case for this error is kernel's failure to install + * a multipath route, which is common for FreeBSD. This should be + * ignored silently, but logged as an error otherwise. + */ + case ZEBRA_ERR_RTEXIST: + if (cmd != RTM_ADD) + zlog_err ("%s: rtm_write() returned %d for command %d", + __func__, error, cmd); + continue; + break; + + /* Given that our NEXTHOP_FLAG_FIB matches real kernel FIB, it isn't + * normal to get any other messages in ANY case. + */ + case ZEBRA_ERR_RTNOEXIST: + case ZEBRA_ERR_RTUNREACH: + default: + 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) */ + else + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug ("%s: odd command %s for flags %d", + __func__, lookup (rtm_type_str, cmd), nexthop->flags); + } /* for (ALL_NEXTHOPS_RO(...))*/ + + /* If there was no useful nexthop, then complain. */ + if (nexthop_num == 0 && IS_ZEBRA_DEBUG_KERNEL) + zlog_debug ("%s: No useful nexthops were found in RIB entry %p", __func__, rib); + + return 0; /*XXX*/ +} + +#ifdef HAVE_IPV6 + +#ifdef SIN6_LEN +/* Calculate sin6_len value for netmask socket value. */ +static int +sin6_masklen (struct in6_addr mask) +{ + struct sockaddr_in6 sin6; + char *p, *lim; + int len; + + if (IN6_IS_ADDR_UNSPECIFIED (&mask)) + return sizeof (long); + + sin6.sin6_addr = mask; + len = sizeof (struct sockaddr_in6); + + lim = (char *) & sin6.sin6_addr; + p = lim + sizeof (sin6.sin6_addr); + + while (*--p == 0 && p >= lim) + len--; + + return len; +} +#endif /* SIN6_LEN */ + +/* Interface between zebra message and rtm message. */ +static int +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, *tnexthop; + int recursing; + int nexthop_num = 0; + ifindex_t ifindex = 0; + int gate = 0; + int error; + + 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 */ + sin_dest.sin6_addr = p->u.prefix6; + + memset (&sin_mask, 0, sizeof (struct sockaddr_in6)); + + memset (&sin_gate, 0, sizeof (struct sockaddr_in6)); + sin_gate.sin6_family = AF_INET6; +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + sin_gate.sin6_len = sizeof (struct sockaddr_in6); +#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ + + /* Make gateway. */ + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) + { + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + + gate = 0; + + if ((cmd == RTM_ADD + && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + || (cmd == RTM_DELETE +#if 0 + && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) +#endif + )) + { + 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; + + if (cmd == RTM_ADD) + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + } + + /* 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(&sin_gate.sin6_addr)) + SET_IN6_LINKLOCAL_IFINDEX (sin_gate.sin6_addr, ifindex); +#endif /* KAME */ + + if (gate && p->prefixlen == 128) + mask = NULL; + else + { + masklen2ip6 (p->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; + } + + error = rtm_write (cmd, + (union sockunion *) &sin_dest, + (union sockunion *) mask, + gate ? (union sockunion *)&sin_gate : NULL, + ifindex, + rib->flags, + rib->metric); + +#if 0 + if (error) + { + zlog_info ("kernel_rtm_ipv6(): nexthop %d add error=%d.", + nexthop_num, error); + } +#else + (void)error; +#endif + + nexthop_num++; + } + + /* If there is no useful nexthop then return. */ + if (nexthop_num == 0) + { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug ("kernel_rtm_ipv6(): No useful nexthop."); + return 0; + } + + return 0; /*XXX*/ +} + +#endif + +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_route_rib (struct prefix *p, struct rib *old, struct rib *new) +{ + int route = 0; + + if (zserv_privs.change(ZPRIVS_RAISE)) + zlog (NULL, LOG_ERR, "Can't raise privileges"); + + if (old) + route |= kernel_rtm (RTM_DELETE, p, old); + + if (new) + route |= kernel_rtm (RTM_ADD, p, new); + + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog (NULL, LOG_ERR, "Can't lower privileges"); + + return route; +} diff --git a/zebra/rtadv.c b/zebra/rtadv.c new file mode 100644 index 0000000..2f62714 --- /dev/null +++ b/zebra/rtadv.c @@ -0,0 +1,1795 @@ +/* Router advertisement + * Copyright (C) 2005 6WIND + * 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 "memory.h" +#include "sockopt.h" +#include "thread.h" +#include "if.h" +#include "log.h" +#include "prefix.h" +#include "linklist.h" +#include "command.h" +#include "privs.h" +#include "vrf.h" + +#include "zebra/interface.h" +#include "zebra/rtadv.h" +#include "zebra/debug.h" +#include "zebra/rib.h" +#include "zebra/zserv.h" + +extern struct zebra_privs_t zserv_privs; + +#if defined (HAVE_IPV6) && defined (HAVE_RTADV) + +#ifdef OPEN_BSD +#include +#endif + +/* If RFC2133 definition is used. */ +#ifndef IPV6_JOIN_GROUP +#define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP +#endif +#ifndef IPV6_LEAVE_GROUP +#define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP +#endif + +#define ALLNODE "ff02::1" +#define ALLROUTER "ff02::2" + +extern struct zebra_t zebrad; + +enum rtadv_event {RTADV_START, RTADV_STOP, RTADV_TIMER, + RTADV_TIMER_MSEC, RTADV_READ}; + +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 *); + +static int +rtadv_recv_packet (int sock, u_char *buf, int buflen, + struct sockaddr_in6 *from, ifindex_t *ifindex, + int *hoplimit) +{ + int ret; + struct msghdr msg; + struct iovec iov; + struct cmsghdr *cmsgptr; + struct in6_addr dst; + + char adata[1024]; + + /* Fill in message and iovec. */ + msg.msg_name = (void *) from; + msg.msg_namelen = sizeof (struct sockaddr_in6); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = (void *) adata; + msg.msg_controllen = sizeof adata; + iov.iov_base = buf; + iov.iov_len = buflen; + + /* If recvmsg fail return minus value. */ + ret = recvmsg (sock, &msg, 0); + if (ret < 0) + return ret; + + for (cmsgptr = ZCMSG_FIRSTHDR(&msg); cmsgptr != NULL; + cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) + { + /* I want interface index which this packet comes from. */ + if (cmsgptr->cmsg_level == IPPROTO_IPV6 && + cmsgptr->cmsg_type == IPV6_PKTINFO) + { + struct in6_pktinfo *ptr; + + ptr = (struct in6_pktinfo *) CMSG_DATA (cmsgptr); + *ifindex = ptr->ipi6_ifindex; + memcpy(&dst, &ptr->ipi6_addr, sizeof(ptr->ipi6_addr)); + } + + /* Incoming packet's hop limit. */ + if (cmsgptr->cmsg_level == IPPROTO_IPV6 && + cmsgptr->cmsg_type == IPV6_HOPLIMIT) + { + int *hoptr = (int *) CMSG_DATA (cmsgptr); + *hoplimit = *hoptr; + } + } + return ret; +} + +#define RTADV_MSG_SIZE 4096 + +/* Send router advertisement packet. */ +static void +rtadv_send_packet (int sock, struct interface *ifp) +{ + struct msghdr msg; + struct iovec iov; + struct cmsghdr *cmsgptr; + struct in6_pktinfo *pkt; + struct sockaddr_in6 addr; +#ifdef HAVE_STRUCT_SOCKADDR_DL + struct sockaddr_dl *sdl; +#endif /* HAVE_STRUCT_SOCKADDR_DL */ + static void *adata = NULL; + unsigned char buf[RTADV_MSG_SIZE]; + struct nd_router_advert *rtadv; + int ret; + int len = 0; + struct zebra_if *zif; + struct rtadv_prefix *rprefix; + u_char all_nodes_addr[] = {0xff,0x02,0,0,0,0,0,0,0,0,0,0,0,0,0,1}; + struct listnode *node; + u_int16_t pkt_RouterLifetime; + + /* + * Allocate control message bufffer. This is dynamic because + * CMSG_SPACE is not guaranteed not to call a function. Note that + * the size will be different on different architectures due to + * differing alignment rules. + */ + if (adata == NULL) + { + /* XXX Free on shutdown. */ + adata = malloc(CMSG_SPACE(sizeof(struct in6_pktinfo))); + + if (adata == NULL) + zlog_err("rtadv_send_packet: can't malloc control data\n"); + } + + /* Logging of packet. */ + if (IS_ZEBRA_DEBUG_PACKET) + zlog_debug ("Router advertisement send to %s", ifp->name); + + /* Fill in sockaddr_in6. */ + memset (&addr, 0, sizeof (struct sockaddr_in6)); + addr.sin6_family = AF_INET6; +#ifdef SIN6_LEN + addr.sin6_len = sizeof (struct sockaddr_in6); +#endif /* SIN6_LEN */ + addr.sin6_port = htons (IPPROTO_ICMPV6); + IPV6_ADDR_COPY (&addr.sin6_addr, all_nodes_addr); + + /* Fetch interface information. */ + zif = ifp->info; + + /* Make router advertisement message. */ + rtadv = (struct nd_router_advert *) buf; + + rtadv->nd_ra_type = ND_ROUTER_ADVERT; + rtadv->nd_ra_code = 0; + rtadv->nd_ra_cksum = 0; + + rtadv->nd_ra_curhoplimit = 64; + + /* RFC4191: Default Router Preference is 0 if Router Lifetime is 0. */ + rtadv->nd_ra_flags_reserved = + zif->rtadv.AdvDefaultLifetime == 0 ? 0 : zif->rtadv.DefaultPreference; + rtadv->nd_ra_flags_reserved <<= 3; + + if (zif->rtadv.AdvManagedFlag) + rtadv->nd_ra_flags_reserved |= ND_RA_FLAG_MANAGED; + if (zif->rtadv.AdvOtherConfigFlag) + rtadv->nd_ra_flags_reserved |= ND_RA_FLAG_OTHER; + if (zif->rtadv.AdvHomeAgentFlag) + rtadv->nd_ra_flags_reserved |= ND_RA_FLAG_HOME_AGENT; + /* Note that according to Neighbor Discovery (RFC 4861 [18]), + * AdvDefaultLifetime is by default based on the value of + * MaxRtrAdvInterval. AdvDefaultLifetime is used in the Router Lifetime + * field of Router Advertisements. Given that this field is expressed + * in seconds, a small MaxRtrAdvInterval value can result in a zero + * value for this field. To prevent this, routers SHOULD keep + * AdvDefaultLifetime in at least one second, even if the use of + * MaxRtrAdvInterval would result in a smaller value. -- RFC6275, 7.5 */ + pkt_RouterLifetime = zif->rtadv.AdvDefaultLifetime != -1 ? + zif->rtadv.AdvDefaultLifetime : + MAX (1, 0.003 * zif->rtadv.MaxRtrAdvInterval); + rtadv->nd_ra_router_lifetime = htons (pkt_RouterLifetime); + rtadv->nd_ra_reachable = htonl (zif->rtadv.AdvReachableTime); + rtadv->nd_ra_retransmit = htonl (0); + + len = sizeof (struct nd_router_advert); + + /* If both the Home Agent Preference and Home Agent Lifetime are set to + * their default values specified above, this option SHOULD NOT be + * included in the Router Advertisement messages sent by this home + * agent. -- RFC6275, 7.4 */ + if + ( + zif->rtadv.AdvHomeAgentFlag && + (zif->rtadv.HomeAgentPreference || zif->rtadv.HomeAgentLifetime != -1) + ) + { + struct nd_opt_homeagent_info *ndopt_hai = + (struct nd_opt_homeagent_info *)(buf + len); + ndopt_hai->nd_opt_hai_type = ND_OPT_HA_INFORMATION; + ndopt_hai->nd_opt_hai_len = 1; + ndopt_hai->nd_opt_hai_reserved = 0; + ndopt_hai->nd_opt_hai_preference = htons(zif->rtadv.HomeAgentPreference); + /* 16-bit unsigned integer. The lifetime associated with the home + * agent in units of seconds. The default value is the same as the + * Router Lifetime, as specified in the main body of the Router + * Advertisement. The maximum value corresponds to 18.2 hours. A + * value of 0 MUST NOT be used. -- RFC6275, 7.5 */ + ndopt_hai->nd_opt_hai_lifetime = htons + ( + zif->rtadv.HomeAgentLifetime != -1 ? + zif->rtadv.HomeAgentLifetime : + MAX (1, pkt_RouterLifetime) /* 0 is OK for RL, but not for HAL*/ + ); + len += sizeof(struct nd_opt_homeagent_info); + } + + if (zif->rtadv.AdvIntervalOption) + { + struct nd_opt_adv_interval *ndopt_adv = + (struct nd_opt_adv_interval *)(buf + len); + ndopt_adv->nd_opt_ai_type = ND_OPT_ADV_INTERVAL; + ndopt_adv->nd_opt_ai_len = 1; + ndopt_adv->nd_opt_ai_reserved = 0; + ndopt_adv->nd_opt_ai_interval = htonl(zif->rtadv.MaxRtrAdvInterval); + len += sizeof(struct nd_opt_adv_interval); + } + + /* Fill in prefix. */ + for (ALL_LIST_ELEMENTS_RO (zif->rtadv.AdvPrefixList, node, rprefix)) + { + struct nd_opt_prefix_info *pinfo; + + pinfo = (struct nd_opt_prefix_info *) (buf + len); + + pinfo->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION; + pinfo->nd_opt_pi_len = 4; + pinfo->nd_opt_pi_prefix_len = rprefix->prefix.prefixlen; + + pinfo->nd_opt_pi_flags_reserved = 0; + if (rprefix->AdvOnLinkFlag) + pinfo->nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_ONLINK; + if (rprefix->AdvAutonomousFlag) + pinfo->nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_AUTO; + if (rprefix->AdvRouterAddressFlag) + pinfo->nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_RADDR; + + pinfo->nd_opt_pi_valid_time = htonl (rprefix->AdvValidLifetime); + pinfo->nd_opt_pi_preferred_time = htonl (rprefix->AdvPreferredLifetime); + pinfo->nd_opt_pi_reserved2 = 0; + + IPV6_ADDR_COPY (&pinfo->nd_opt_pi_prefix, &rprefix->prefix.prefix); + +#ifdef DEBUG + { + u_char buf[INET6_ADDRSTRLEN]; + + zlog_debug ("DEBUG %s", inet_ntop (AF_INET6, &pinfo->nd_opt_pi_prefix, + buf, INET6_ADDRSTRLEN)); + + } +#endif /* DEBUG */ + + len += sizeof (struct nd_opt_prefix_info); + } + + /* Hardware address. */ + if (ifp->hw_addr_len != 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++] = (ifp->hw_addr_len + 9) >> 3; + + memcpy (buf + len, ifp->hw_addr, ifp->hw_addr_len); + len += ifp->hw_addr_len; + + /* Pad option to end on an octet boundary. */ + memset (buf + len, 0, -(ifp->hw_addr_len + 2) & 0x7); + len += -(ifp->hw_addr_len + 2) & 0x7; + } + + /* MTU */ + if (zif->rtadv.AdvLinkMTU) + { + struct nd_opt_mtu * opt = (struct nd_opt_mtu *) (buf + len); + opt->nd_opt_mtu_type = ND_OPT_MTU; + opt->nd_opt_mtu_len = 1; + opt->nd_opt_mtu_reserved = 0; + opt->nd_opt_mtu_mtu = htonl (zif->rtadv.AdvLinkMTU); + len += sizeof (struct nd_opt_mtu); + } + + msg.msg_name = (void *) &addr; + msg.msg_namelen = sizeof (struct sockaddr_in6); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = (void *) adata; + msg.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); + msg.msg_flags = 0; + iov.iov_base = buf; + iov.iov_len = len; + + cmsgptr = ZCMSG_FIRSTHDR(&msg); + cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + cmsgptr->cmsg_level = IPPROTO_IPV6; + cmsgptr->cmsg_type = IPV6_PKTINFO; + + pkt = (struct in6_pktinfo *) CMSG_DATA (cmsgptr); + memset (&pkt->ipi6_addr, 0, sizeof (struct in6_addr)); + pkt->ipi6_ifindex = ifp->ifindex; + + ret = sendmsg (sock, &msg, 0); + if (ret < 0) + { + zlog_err ("rtadv_send_packet: sendmsg %d (%s)\n", + errno, safe_strerror(errno)); + } +} + +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; + + zvrf->rtadv.ra_timer = NULL; + if (zvrf->rtadv.adv_msec_if_count == 0) + { + period = 1000; /* 1 s */ + rtadv_event (zvrf, RTADV_TIMER, 1 /* 1 s */); + } + else + { + period = 10; /* 10 ms */ + rtadv_event (zvrf, RTADV_TIMER_MSEC, 10 /* 10 ms */); + } + + for (ALL_LIST_ELEMENTS (vrf_iflist (zvrf->vrf_id), node, nnode, ifp)) + { + if (if_is_loopback (ifp) || ! if_is_operative (ifp)) + continue; + + zif = ifp->info; + + if (zif->rtadv.AdvSendAdvertisements) + { + zif->rtadv.AdvIntervalTimer -= period; + if (zif->rtadv.AdvIntervalTimer <= 0) + { + /* 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 (zvrf->rtadv.sock, ifp); + } + } + } + return 0; +} + +static void +rtadv_process_solicit (struct interface *ifp) +{ + struct zebra_vrf *zvrf = vrf_info_lookup (ifp->vrf_id); + + zlog_info ("Router solicitation received on %s vrf %u", ifp->name, zvrf->vrf_id); + + rtadv_send_packet (zvrf->rtadv.sock, ifp); +} + +static void +rtadv_process_advert (void) +{ + zlog_info ("Router advertisement received"); +} + +static void +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_vrf (ifindex, vrf_id); + if (ifp == NULL) + { + zlog_warn ("Unknown interface index: %d, vrf %u", ifindex, vrf_id); + return; + } + + if (if_is_loopback (ifp)) + return; + + /* Check interface configuration. */ + zif = ifp->info; + if (! zif->rtadv.AdvSendAdvertisements) + return; + + /* ICMP message length check. */ + if (len < sizeof (struct icmp6_hdr)) + { + zlog_warn ("Invalid ICMPV6 packet length: %d", len); + return; + } + + icmph = (struct icmp6_hdr *) buf; + + /* ICMP message type check. */ + if (icmph->icmp6_type != ND_ROUTER_SOLICIT && + icmph->icmp6_type != ND_ROUTER_ADVERT) + { + zlog_warn ("Unwanted ICMPV6 message type: %d", icmph->icmp6_type); + return; + } + + /* Hoplimit check. */ + if (hoplimit >= 0 && hoplimit != 255) + { + zlog_warn ("Invalid hoplimit %d for router advertisement ICMP packet", + hoplimit); + return; + } + + /* Check ICMP message type. */ + if (icmph->icmp6_type == ND_ROUTER_SOLICIT) + rtadv_process_solicit (ifp); + else if (icmph->icmp6_type == ND_ROUTER_ADVERT) + rtadv_process_advert (); + + return; +} + +static int +rtadv_read (struct thread *thread) +{ + int sock; + int len; + u_char buf[RTADV_MSG_SIZE]; + struct sockaddr_in6 from; + ifindex_t ifindex = 0; + int hoplimit = -1; + struct zebra_vrf *zvrf = THREAD_ARG (thread); + + sock = THREAD_FD (thread); + zvrf->rtadv.ra_read = NULL; + + /* Register myself. */ + rtadv_event (zvrf, RTADV_READ, sock); + + len = rtadv_recv_packet (sock, buf, sizeof (buf), &from, &ifindex, &hoplimit); + + if (len < 0) + { + zlog_warn ("router solicitation recv failed: %s.", safe_strerror (errno)); + return len; + } + + rtadv_process_packet (buf, (unsigned)len, ifindex, hoplimit, zvrf->vrf_id); + + return 0; +} + +static int +rtadv_make_socket (vrf_id_t vrf_id) +{ + int sock; + int ret; + struct icmp6_filter filter; + + if ( zserv_privs.change (ZPRIVS_RAISE) ) + zlog_err ("rtadv_make_socket: could not raise privs, %s", + safe_strerror (errno) ); + + 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", + safe_strerror (errno) ); + + /* When we can't make ICMPV6 socket simply back. Router + advertisement feature will not be supported. */ + if (sock < 0) + { + close (sock); + return -1; + } + + ret = setsockopt_ipv6_pktinfo (sock, 1); + if (ret < 0) + { + close (sock); + return ret; + } + ret = setsockopt_ipv6_multicast_loop (sock, 0); + if (ret < 0) + { + close (sock); + return ret; + } + ret = setsockopt_ipv6_unicast_hops (sock, 255); + if (ret < 0) + { + close (sock); + return ret; + } + ret = setsockopt_ipv6_multicast_hops (sock, 255); + if (ret < 0) + { + close (sock); + return ret; + } + ret = setsockopt_ipv6_hoplimit (sock, 1); + if (ret < 0) + { + close (sock); + return ret; + } + + ICMP6_FILTER_SETBLOCKALL(&filter); + ICMP6_FILTER_SETPASS (ND_ROUTER_SOLICIT, &filter); + ICMP6_FILTER_SETPASS (ND_ROUTER_ADVERT, &filter); + + ret = setsockopt (sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, + sizeof (struct icmp6_filter)); + if (ret < 0) + { + zlog_info ("ICMP6_FILTER set fail: %s", safe_strerror (errno)); + return ret; + } + + return sock; +} + +static struct rtadv_prefix * +rtadv_prefix_new (void) +{ + return XCALLOC (MTYPE_RTADV_PREFIX, sizeof (struct rtadv_prefix)); +} + +static void +rtadv_prefix_free (struct rtadv_prefix *rtadv_prefix) +{ + XFREE (MTYPE_RTADV_PREFIX, rtadv_prefix); +} + +static struct rtadv_prefix * +rtadv_prefix_lookup (struct list *rplist, struct prefix_ipv6 *p) +{ + struct listnode *node; + struct rtadv_prefix *rprefix; + + for (ALL_LIST_ELEMENTS_RO (rplist, node, rprefix)) + if (prefix_same ((struct prefix *) &rprefix->prefix, (struct prefix *) p)) + return rprefix; + return NULL; +} + +static struct rtadv_prefix * +rtadv_prefix_get (struct list *rplist, struct prefix_ipv6 *p) +{ + struct rtadv_prefix *rprefix; + + rprefix = rtadv_prefix_lookup (rplist, p); + if (rprefix) + return rprefix; + + rprefix = rtadv_prefix_new (); + memcpy (&rprefix->prefix, p, sizeof (struct prefix_ipv6)); + listnode_add (rplist, rprefix); + + return rprefix; +} + +static void +rtadv_prefix_set (struct zebra_if *zif, struct rtadv_prefix *rp) +{ + struct rtadv_prefix *rprefix; + + rprefix = rtadv_prefix_get (zif->rtadv.AdvPrefixList, &rp->prefix); + + /* Set parameters. */ + rprefix->AdvValidLifetime = rp->AdvValidLifetime; + rprefix->AdvPreferredLifetime = rp->AdvPreferredLifetime; + rprefix->AdvOnLinkFlag = rp->AdvOnLinkFlag; + rprefix->AdvAutonomousFlag = rp->AdvAutonomousFlag; + rprefix->AdvRouterAddressFlag = rp->AdvRouterAddressFlag; +} + +static int +rtadv_prefix_reset (struct zebra_if *zif, struct rtadv_prefix *rp) +{ + struct rtadv_prefix *rprefix; + + rprefix = rtadv_prefix_lookup (zif->rtadv.AdvPrefixList, &rp->prefix); + if (rprefix != NULL) + { + listnode_delete (zif->rtadv.AdvPrefixList, (void *) rprefix); + rtadv_prefix_free (rprefix); + return 1; + } + else + return 0; +} + +DEFUN (ipv6_nd_suppress_ra, + ipv6_nd_suppress_ra_cmd, + "ipv6 nd suppress-ra", + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Suppress Router Advertisement\n") +{ + 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)) + { + vty_out (vty, "Invalid interface%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (zif->rtadv.AdvSendAdvertisements) + { + zif->rtadv.AdvSendAdvertisements = 0; + zif->rtadv.AdvIntervalTimer = 0; + zvrf->rtadv.adv_if_count--; + + if_leave_all_router (zvrf->rtadv.sock, ifp); + + if (zvrf->rtadv.adv_if_count == 0) + rtadv_event (zvrf, RTADV_STOP, 0); + } + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_nd_suppress_ra, + no_ipv6_nd_suppress_ra_cmd, + "no ipv6 nd suppress-ra", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Suppress Router Advertisement\n") +{ + 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)) + { + vty_out (vty, "Invalid interface%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (! zif->rtadv.AdvSendAdvertisements) + { + zif->rtadv.AdvSendAdvertisements = 1; + zif->rtadv.AdvIntervalTimer = 0; + zvrf->rtadv.adv_if_count++; + + if_join_all_router (zvrf->rtadv.sock, ifp); + + if (zvrf->rtadv.adv_if_count == 1) + rtadv_event (zvrf, RTADV_START, zvrf->rtadv.sock); + } + + return CMD_SUCCESS; +} + +DEFUN (ipv6_nd_ra_interval_msec, + ipv6_nd_ra_interval_msec_cmd, + "ipv6 nd ra-interval msec <70-1800000>", + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Router Advertisement interval\n" + "Router Advertisement interval in milliseconds\n") +{ + 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)) + { + vty_out (vty, "This ra-interval would conflict with configured ra-lifetime!%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (zif->rtadv.MaxRtrAdvInterval % 1000) + zvrf->rtadv.adv_msec_if_count--; + + if (interval % 1000) + zvrf->rtadv.adv_msec_if_count++; + + zif->rtadv.MaxRtrAdvInterval = interval; + zif->rtadv.MinRtrAdvInterval = 0.33 * interval; + zif->rtadv.AdvIntervalTimer = 0; + + return CMD_SUCCESS; +} + +DEFUN (ipv6_nd_ra_interval, + ipv6_nd_ra_interval_cmd, + "ipv6 nd ra-interval <1-1800>", + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Router Advertisement interval\n" + "Router Advertisement interval in seconds\n") +{ + 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)) + { + vty_out (vty, "This ra-interval would conflict with configured ra-lifetime!%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (zif->rtadv.MaxRtrAdvInterval % 1000) + zvrf->rtadv.adv_msec_if_count--; + + /* convert to milliseconds */ + interval = interval * 1000; + + zif->rtadv.MaxRtrAdvInterval = interval; + zif->rtadv.MinRtrAdvInterval = 0.33 * interval; + zif->rtadv.AdvIntervalTimer = 0; + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_nd_ra_interval, + no_ipv6_nd_ra_interval_cmd, + "no ipv6 nd ra-interval", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Router Advertisement interval\n") +{ + 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) + zvrf->rtadv.adv_msec_if_count--; + + zif->rtadv.MaxRtrAdvInterval = RTADV_MAX_RTR_ADV_INTERVAL; + zif->rtadv.MinRtrAdvInterval = RTADV_MIN_RTR_ADV_INTERVAL; + zif->rtadv.AdvIntervalTimer = zif->rtadv.MaxRtrAdvInterval; + + return CMD_SUCCESS; +} + +ALIAS (no_ipv6_nd_ra_interval, + no_ipv6_nd_ra_interval_val_cmd, + "no ipv6 nd ra-interval <1-1800>", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Router Advertisement interval\n") + +ALIAS (no_ipv6_nd_ra_interval, + no_ipv6_nd_ra_interval_msec_val_cmd, + "no ipv6 nd ra-interval msec <1-1800000>", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Router Advertisement interval\n" + "Router Advertisement interval in milliseconds\n") + +DEFUN (ipv6_nd_ra_lifetime, + ipv6_nd_ra_lifetime_cmd, + "ipv6 nd ra-lifetime <0-9000>", + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Router lifetime\n" + "Router lifetime in seconds (0 stands for a non-default gw)\n") +{ + int lifetime; + struct interface *ifp; + struct zebra_if *zif; + + ifp = (struct interface *) vty->index; + zif = ifp->info; + + VTY_GET_INTEGER_RANGE ("router lifetime", lifetime, argv[0], 0, 9000); + + /* The value to be placed in the Router Lifetime field + * of Router Advertisements sent from the interface, + * in seconds. MUST be either zero or between + * MaxRtrAdvInterval and 9000 seconds. -- RFC4861, 6.2.1 */ + if ((lifetime != 0 && lifetime * 1000 < zif->rtadv.MaxRtrAdvInterval)) + { + vty_out (vty, "This ra-lifetime would conflict with configured ra-interval%s", VTY_NEWLINE); + return CMD_WARNING; + } + + zif->rtadv.AdvDefaultLifetime = lifetime; + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_nd_ra_lifetime, + no_ipv6_nd_ra_lifetime_cmd, + "no ipv6 nd ra-lifetime", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Router lifetime\n") +{ + struct interface *ifp; + struct zebra_if *zif; + + ifp = (struct interface *) vty->index; + zif = ifp->info; + + zif->rtadv.AdvDefaultLifetime = -1; + + return CMD_SUCCESS; +} + +ALIAS (no_ipv6_nd_ra_lifetime, + no_ipv6_nd_ra_lifetime_val_cmd, + "no ipv6 nd ra-lifetime <0-9000>", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Router lifetime\n" + "Router lifetime in seconds (0 stands for a non-default gw)\n") + +DEFUN (ipv6_nd_reachable_time, + ipv6_nd_reachable_time_cmd, + "ipv6 nd reachable-time <1-3600000>", + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Reachable time\n" + "Reachable time in milliseconds\n") +{ + struct interface *ifp = (struct interface *) vty->index; + struct zebra_if *zif = ifp->info; + VTY_GET_INTEGER_RANGE ("reachable time", zif->rtadv.AdvReachableTime, argv[0], 1, RTADV_MAX_REACHABLE_TIME); + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_nd_reachable_time, + no_ipv6_nd_reachable_time_cmd, + "no ipv6 nd reachable-time", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Reachable time\n") +{ + struct interface *ifp; + struct zebra_if *zif; + + ifp = (struct interface *) vty->index; + zif = ifp->info; + + zif->rtadv.AdvReachableTime = 0; + + return CMD_SUCCESS; +} + +ALIAS (no_ipv6_nd_reachable_time, + no_ipv6_nd_reachable_time_val_cmd, + "no ipv6 nd reachable-time <1-3600000>", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Reachable time\n" + "Reachable time in milliseconds\n") + +DEFUN (ipv6_nd_homeagent_preference, + ipv6_nd_homeagent_preference_cmd, + "ipv6 nd home-agent-preference <0-65535>", + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Home Agent preference\n" + "preference value (default is 0, least preferred)\n") +{ + struct interface *ifp = (struct interface *) vty->index; + struct zebra_if *zif = ifp->info; + VTY_GET_INTEGER_RANGE ("home agent preference", zif->rtadv.HomeAgentPreference, argv[0], 0, 65535); + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_nd_homeagent_preference, + no_ipv6_nd_homeagent_preference_cmd, + "no ipv6 nd home-agent-preference", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Home Agent preference\n") +{ + struct interface *ifp; + struct zebra_if *zif; + + ifp = (struct interface *) vty->index; + zif = ifp->info; + + zif->rtadv.HomeAgentPreference = 0; + + return CMD_SUCCESS; +} + +ALIAS (no_ipv6_nd_homeagent_preference, + no_ipv6_nd_homeagent_preference_val_cmd, + "no ipv6 nd home-agent-preference <0-65535>", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Home Agent preference\n" + "preference value (default is 0, least preferred)\n") + +DEFUN (ipv6_nd_homeagent_lifetime, + ipv6_nd_homeagent_lifetime_cmd, + "ipv6 nd home-agent-lifetime <0-65520>", + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Home Agent lifetime\n" + "Home Agent lifetime in seconds (0 to track ra-lifetime)\n") +{ + struct interface *ifp = (struct interface *) vty->index; + struct zebra_if *zif = ifp->info; + VTY_GET_INTEGER_RANGE ("home agent lifetime", zif->rtadv.HomeAgentLifetime, argv[0], 0, RTADV_MAX_HALIFETIME); + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_nd_homeagent_lifetime, + no_ipv6_nd_homeagent_lifetime_cmd, + "no ipv6 nd home-agent-lifetime", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Home Agent lifetime\n") +{ + struct interface *ifp; + struct zebra_if *zif; + + ifp = (struct interface *) vty->index; + zif = ifp->info; + + zif->rtadv.HomeAgentLifetime = -1; + + return CMD_SUCCESS; +} + +ALIAS (no_ipv6_nd_homeagent_lifetime, + no_ipv6_nd_homeagent_lifetime_val_cmd, + "no ipv6 nd home-agent-lifetime <0-65520>", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Home Agent lifetime\n" + "Home Agent lifetime in seconds (0 to track ra-lifetime)\n") + +DEFUN (ipv6_nd_managed_config_flag, + ipv6_nd_managed_config_flag_cmd, + "ipv6 nd managed-config-flag", + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Managed address configuration flag\n") +{ + struct interface *ifp; + struct zebra_if *zif; + + ifp = (struct interface *) vty->index; + zif = ifp->info; + + zif->rtadv.AdvManagedFlag = 1; + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_nd_managed_config_flag, + no_ipv6_nd_managed_config_flag_cmd, + "no ipv6 nd managed-config-flag", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Managed address configuration flag\n") +{ + struct interface *ifp; + struct zebra_if *zif; + + ifp = (struct interface *) vty->index; + zif = ifp->info; + + zif->rtadv.AdvManagedFlag = 0; + + return CMD_SUCCESS; +} + +DEFUN (ipv6_nd_homeagent_config_flag, + ipv6_nd_homeagent_config_flag_cmd, + "ipv6 nd home-agent-config-flag", + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Home Agent configuration flag\n") +{ + struct interface *ifp; + struct zebra_if *zif; + + ifp = (struct interface *) vty->index; + zif = ifp->info; + + zif->rtadv.AdvHomeAgentFlag = 1; + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_nd_homeagent_config_flag, + no_ipv6_nd_homeagent_config_flag_cmd, + "no ipv6 nd home-agent-config-flag", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Home Agent configuration flag\n") +{ + struct interface *ifp; + struct zebra_if *zif; + + ifp = (struct interface *) vty->index; + zif = ifp->info; + + zif->rtadv.AdvHomeAgentFlag = 0; + + return CMD_SUCCESS; +} + +DEFUN (ipv6_nd_adv_interval_config_option, + ipv6_nd_adv_interval_config_option_cmd, + "ipv6 nd adv-interval-option", + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Advertisement Interval Option\n") +{ + struct interface *ifp; + struct zebra_if *zif; + + ifp = (struct interface *) vty->index; + zif = ifp->info; + + zif->rtadv.AdvIntervalOption = 1; + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_nd_adv_interval_config_option, + no_ipv6_nd_adv_interval_config_option_cmd, + "no ipv6 nd adv-interval-option", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Advertisement Interval Option\n") +{ + struct interface *ifp; + struct zebra_if *zif; + + ifp = (struct interface *) vty->index; + zif = ifp->info; + + zif->rtadv.AdvIntervalOption = 0; + + return CMD_SUCCESS; +} + +DEFUN (ipv6_nd_other_config_flag, + ipv6_nd_other_config_flag_cmd, + "ipv6 nd other-config-flag", + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Other statefull configuration flag\n") +{ + struct interface *ifp; + struct zebra_if *zif; + + ifp = (struct interface *) vty->index; + zif = ifp->info; + + zif->rtadv.AdvOtherConfigFlag = 1; + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_nd_other_config_flag, + no_ipv6_nd_other_config_flag_cmd, + "no ipv6 nd other-config-flag", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Other statefull configuration flag\n") +{ + struct interface *ifp; + struct zebra_if *zif; + + ifp = (struct interface *) vty->index; + zif = ifp->info; + + zif->rtadv.AdvOtherConfigFlag = 0; + + return CMD_SUCCESS; +} + +DEFUN (ipv6_nd_prefix, + ipv6_nd_prefix_cmd, + "ipv6 nd prefix X:X::X:X/M (<0-4294967295>|infinite) " + "(<0-4294967295>|infinite) (off-link|) (no-autoconfig|) (router-address|)", + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Prefix information\n" + "IPv6 prefix\n" + "Valid lifetime in seconds\n" + "Infinite valid lifetime\n" + "Preferred lifetime in seconds\n" + "Infinite preferred lifetime\n" + "Do not use prefix for onlink determination\n" + "Do not use prefix for autoconfiguration\n" + "Set Router Address flag\n") +{ + int i; + int ret; + int cursor = 1; + struct interface *ifp; + struct zebra_if *zebra_if; + struct rtadv_prefix rp; + + ifp = (struct interface *) vty->index; + zebra_if = ifp->info; + + ret = str2prefix_ipv6 (argv[0], &rp.prefix); + if (!ret) + { + vty_out (vty, "Malformed IPv6 prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + apply_mask_ipv6 (&rp.prefix); /* RFC4861 4.6.2 */ + rp.AdvOnLinkFlag = 1; + rp.AdvAutonomousFlag = 1; + rp.AdvRouterAddressFlag = 0; + rp.AdvValidLifetime = RTADV_VALID_LIFETIME; + rp.AdvPreferredLifetime = RTADV_PREFERRED_LIFETIME; + + if (argc > 1) + { + if ((isdigit((unsigned char)argv[1][0])) + || strncmp (argv[1], "i", 1) == 0) + { + if ( strncmp (argv[1], "i", 1) == 0) + rp.AdvValidLifetime = UINT32_MAX; + else + rp.AdvValidLifetime = (u_int32_t) strtoll (argv[1], + (char **)NULL, 10); + + if ( strncmp (argv[2], "i", 1) == 0) + rp.AdvPreferredLifetime = UINT32_MAX; + else + rp.AdvPreferredLifetime = (u_int32_t) strtoll (argv[2], + (char **)NULL, 10); + + if (rp.AdvPreferredLifetime > rp.AdvValidLifetime) + { + vty_out (vty, "Invalid preferred lifetime%s", VTY_NEWLINE); + return CMD_WARNING; + } + cursor = cursor + 2; + } + if (argc > cursor) + { + for (i = cursor; i < argc; i++) + { + if (strncmp (argv[i], "of", 2) == 0) + rp.AdvOnLinkFlag = 0; + if (strncmp (argv[i], "no", 2) == 0) + rp.AdvAutonomousFlag = 0; + if (strncmp (argv[i], "ro", 2) == 0) + rp.AdvRouterAddressFlag = 1; + } + } + } + + rtadv_prefix_set (zebra_if, &rp); + + return CMD_SUCCESS; +} + +ALIAS (ipv6_nd_prefix, + ipv6_nd_prefix_val_nortaddr_cmd, + "ipv6 nd prefix X:X::X:X/M (<0-4294967295>|infinite) " + "(<0-4294967295>|infinite) (off-link|) (no-autoconfig|)", + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Prefix information\n" + "IPv6 prefix\n" + "Valid lifetime in seconds\n" + "Infinite valid lifetime\n" + "Preferred lifetime in seconds\n" + "Infinite preferred lifetime\n" + "Do not use prefix for onlink determination\n" + "Do not use prefix for autoconfiguration\n") + +ALIAS (ipv6_nd_prefix, + ipv6_nd_prefix_val_rev_cmd, + "ipv6 nd prefix X:X::X:X/M (<0-4294967295>|infinite) " + "(<0-4294967295>|infinite) (no-autoconfig|) (off-link|)", + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Prefix information\n" + "IPv6 prefix\n" + "Valid lifetime in seconds\n" + "Infinite valid lifetime\n" + "Preferred lifetime in seconds\n" + "Infinite preferred lifetime\n" + "Do not use prefix for autoconfiguration\n" + "Do not use prefix for onlink determination\n") + +ALIAS (ipv6_nd_prefix, + ipv6_nd_prefix_val_rev_rtaddr_cmd, + "ipv6 nd prefix X:X::X:X/M (<0-4294967295>|infinite) " + "(<0-4294967295>|infinite) (no-autoconfig|) (off-link|) (router-address|)", + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Prefix information\n" + "IPv6 prefix\n" + "Valid lifetime in seconds\n" + "Infinite valid lifetime\n" + "Preferred lifetime in seconds\n" + "Infinite preferred lifetime\n" + "Do not use prefix for autoconfiguration\n" + "Do not use prefix for onlink determination\n" + "Set Router Address flag\n") + +ALIAS (ipv6_nd_prefix, + ipv6_nd_prefix_val_noauto_cmd, + "ipv6 nd prefix X:X::X:X/M (<0-4294967295>|infinite) " + "(<0-4294967295>|infinite) (no-autoconfig|)", + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Prefix information\n" + "IPv6 prefix\n" + "Valid lifetime in seconds\n" + "Infinite valid lifetime\n" + "Preferred lifetime in seconds\n" + "Infinite preferred lifetime\n" + "Do not use prefix for autoconfiguration") + +ALIAS (ipv6_nd_prefix, + ipv6_nd_prefix_val_offlink_cmd, + "ipv6 nd prefix X:X::X:X/M (<0-4294967295>|infinite) " + "(<0-4294967295>|infinite) (off-link|)", + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Prefix information\n" + "IPv6 prefix\n" + "Valid lifetime in seconds\n" + "Infinite valid lifetime\n" + "Preferred lifetime in seconds\n" + "Infinite preferred lifetime\n" + "Do not use prefix for onlink determination\n") + +ALIAS (ipv6_nd_prefix, + ipv6_nd_prefix_val_rtaddr_cmd, + "ipv6 nd prefix X:X::X:X/M (<0-4294967295>|infinite) " + "(<0-4294967295>|infinite) (router-address|)", + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Prefix information\n" + "IPv6 prefix\n" + "Valid lifetime in seconds\n" + "Infinite valid lifetime\n" + "Preferred lifetime in seconds\n" + "Infinite preferred lifetime\n" + "Set Router Address flag\n") + +ALIAS (ipv6_nd_prefix, + ipv6_nd_prefix_val_cmd, + "ipv6 nd prefix X:X::X:X/M (<0-4294967295>|infinite) " + "(<0-4294967295>|infinite)", + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Prefix information\n" + "IPv6 prefix\n" + "Valid lifetime in seconds\n" + "Infinite valid lifetime\n" + "Preferred lifetime in seconds\n" + "Infinite preferred lifetime\n") + +ALIAS (ipv6_nd_prefix, + ipv6_nd_prefix_noval_cmd, + "ipv6 nd prefix X:X::X:X/M (no-autoconfig|) (off-link|)", + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Prefix information\n" + "IPv6 prefix\n" + "Do not use prefix for autoconfiguration\n" + "Do not use prefix for onlink determination\n") + +ALIAS (ipv6_nd_prefix, + ipv6_nd_prefix_noval_rev_cmd, + "ipv6 nd prefix X:X::X:X/M (off-link|) (no-autoconfig|)", + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Prefix information\n" + "IPv6 prefix\n" + "Do not use prefix for onlink determination\n" + "Do not use prefix for autoconfiguration\n") + +ALIAS (ipv6_nd_prefix, + ipv6_nd_prefix_noval_noauto_cmd, + "ipv6 nd prefix X:X::X:X/M (no-autoconfig|)", + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Prefix information\n" + "IPv6 prefix\n" + "Do not use prefix for autoconfiguration\n") + +ALIAS (ipv6_nd_prefix, + ipv6_nd_prefix_noval_offlink_cmd, + "ipv6 nd prefix X:X::X:X/M (off-link|)", + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Prefix information\n" + "IPv6 prefix\n" + "Do not use prefix for onlink determination\n") + +ALIAS (ipv6_nd_prefix, + ipv6_nd_prefix_noval_rtaddr_cmd, + "ipv6 nd prefix X:X::X:X/M (router-address|)", + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Prefix information\n" + "IPv6 prefix\n" + "Set Router Address flag\n") + +ALIAS (ipv6_nd_prefix, + ipv6_nd_prefix_prefix_cmd, + "ipv6 nd prefix X:X::X:X/M", + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Prefix information\n" + "IPv6 prefix\n") + +DEFUN (no_ipv6_nd_prefix, + no_ipv6_nd_prefix_cmd, + "no ipv6 nd prefix IPV6PREFIX", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Prefix information\n" + "IPv6 prefix\n") +{ + int ret; + struct interface *ifp; + struct zebra_if *zebra_if; + struct rtadv_prefix rp; + + ifp = (struct interface *) vty->index; + zebra_if = ifp->info; + + ret = str2prefix_ipv6 (argv[0], &rp.prefix); + if (!ret) + { + vty_out (vty, "Malformed IPv6 prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + apply_mask_ipv6 (&rp.prefix); /* RFC4861 4.6.2 */ + + ret = rtadv_prefix_reset (zebra_if, &rp); + if (!ret) + { + vty_out (vty, "Non-exist IPv6 prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN (ipv6_nd_router_preference, + ipv6_nd_router_preference_cmd, + "ipv6 nd router-preference (high|medium|low)", + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Default router preference\n" + "High default router preference\n" + "Low default router preference\n" + "Medium default router preference (default)\n") +{ + struct interface *ifp; + struct zebra_if *zif; + int i = 0; + + ifp = (struct interface *) vty->index; + zif = ifp->info; + + while (0 != rtadv_pref_strs[i]) + { + if (strncmp (argv[0], rtadv_pref_strs[i], 1) == 0) + { + zif->rtadv.DefaultPreference = i; + return CMD_SUCCESS; + } + i++; + } + + return CMD_ERR_NO_MATCH; +} + +DEFUN (no_ipv6_nd_router_preference, + no_ipv6_nd_router_preference_cmd, + "no ipv6 nd router-preference", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Default router preference\n") +{ + struct interface *ifp; + struct zebra_if *zif; + + ifp = (struct interface *) vty->index; + zif = ifp->info; + + zif->rtadv.DefaultPreference = RTADV_PREF_MEDIUM; /* Default per RFC4191. */ + + return CMD_SUCCESS; +} + +ALIAS (no_ipv6_nd_router_preference, + no_ipv6_nd_router_preference_val_cmd, + "no ipv6 nd router-preference (high|medium|low)", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Default router preference\n" + "High default router preference\n" + "Low default router preference\n" + "Medium default router preference (default)\n") + +DEFUN (ipv6_nd_mtu, + ipv6_nd_mtu_cmd, + "ipv6 nd mtu <1-65535>", + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Advertised MTU\n" + "MTU in bytes\n") +{ + struct interface *ifp = (struct interface *) vty->index; + struct zebra_if *zif = ifp->info; + VTY_GET_INTEGER_RANGE ("MTU", zif->rtadv.AdvLinkMTU, argv[0], 1, 65535); + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_nd_mtu, + no_ipv6_nd_mtu_cmd, + "no ipv6 nd mtu", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Advertised MTU\n") +{ + struct interface *ifp = (struct interface *) vty->index; + struct zebra_if *zif = ifp->info; + zif->rtadv.AdvLinkMTU = 0; + return CMD_SUCCESS; +} + +ALIAS (no_ipv6_nd_mtu, + no_ipv6_nd_mtu_val_cmd, + "no ipv6 nd mtu <1-65535>", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Advertised MTU\n" + "MTU in bytes\n") + +/* Write configuration about router advertisement. */ +void +rtadv_config_write (struct vty *vty, struct interface *ifp) +{ + struct zebra_if *zif; + struct listnode *node; + struct rtadv_prefix *rprefix; + char buf[PREFIX_STRLEN]; + int interval; + + zif = ifp->info; + + if (! if_is_loopback (ifp)) + { + if (zif->rtadv.AdvSendAdvertisements) + vty_out (vty, " no ipv6 nd suppress-ra%s", VTY_NEWLINE); + } + + + interval = zif->rtadv.MaxRtrAdvInterval; + if (interval % 1000) + vty_out (vty, " ipv6 nd ra-interval msec %d%s", interval, + VTY_NEWLINE); + else + if (interval != RTADV_MAX_RTR_ADV_INTERVAL) + vty_out (vty, " ipv6 nd ra-interval %d%s", interval / 1000, + VTY_NEWLINE); + + if (zif->rtadv.AdvIntervalOption) + vty_out (vty, " ipv6 nd adv-interval-option%s", VTY_NEWLINE); + + if (zif->rtadv.AdvDefaultLifetime != -1) + vty_out (vty, " ipv6 nd ra-lifetime %d%s", zif->rtadv.AdvDefaultLifetime, + VTY_NEWLINE); + + if (zif->rtadv.HomeAgentPreference) + vty_out (vty, " ipv6 nd home-agent-preference %u%s", + zif->rtadv.HomeAgentPreference, VTY_NEWLINE); + + if (zif->rtadv.HomeAgentLifetime != -1) + vty_out (vty, " ipv6 nd home-agent-lifetime %u%s", + zif->rtadv.HomeAgentLifetime, VTY_NEWLINE); + + if (zif->rtadv.AdvHomeAgentFlag) + vty_out (vty, " ipv6 nd home-agent-config-flag%s", VTY_NEWLINE); + + if (zif->rtadv.AdvReachableTime) + vty_out (vty, " ipv6 nd reachable-time %d%s", zif->rtadv.AdvReachableTime, + VTY_NEWLINE); + + if (zif->rtadv.AdvManagedFlag) + vty_out (vty, " ipv6 nd managed-config-flag%s", VTY_NEWLINE); + + if (zif->rtadv.AdvOtherConfigFlag) + vty_out (vty, " ipv6 nd other-config-flag%s", VTY_NEWLINE); + + if (zif->rtadv.DefaultPreference != RTADV_PREF_MEDIUM) + vty_out (vty, " ipv6 nd router-preference %s%s", + rtadv_pref_strs[zif->rtadv.DefaultPreference], + VTY_NEWLINE); + + if (zif->rtadv.AdvLinkMTU) + vty_out (vty, " ipv6 nd mtu %d%s", zif->rtadv.AdvLinkMTU, VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO (zif->rtadv.AdvPrefixList, node, rprefix)) + { + vty_out (vty, " ipv6 nd prefix %s", + prefix2str (&rprefix->prefix, buf, sizeof(buf))); + if ((rprefix->AdvValidLifetime != RTADV_VALID_LIFETIME) || + (rprefix->AdvPreferredLifetime != RTADV_PREFERRED_LIFETIME)) + { + if (rprefix->AdvValidLifetime == UINT32_MAX) + vty_out (vty, " infinite"); + else + vty_out (vty, " %u", rprefix->AdvValidLifetime); + if (rprefix->AdvPreferredLifetime == UINT32_MAX) + vty_out (vty, " infinite"); + else + vty_out (vty, " %u", rprefix->AdvPreferredLifetime); + } + if (!rprefix->AdvOnLinkFlag) + vty_out (vty, " off-link"); + if (!rprefix->AdvAutonomousFlag) + vty_out (vty, " no-autoconfig"); + if (rprefix->AdvRouterAddressFlag) + vty_out (vty, " router-address"); + vty_out (vty, "%s", VTY_NEWLINE); + } +} + + +static void +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, zvrf, val); + if (! rtadv->ra_timer) + rtadv->ra_timer = thread_add_event (zebrad.master, rtadv_timer, + zvrf, 0); + break; + case RTADV_STOP: + if (rtadv->ra_timer) + { + thread_cancel (rtadv->ra_timer); + rtadv->ra_timer = NULL; + } + if (rtadv->ra_read) + { + thread_cancel (rtadv->ra_read); + rtadv->ra_read = NULL; + } + break; + case RTADV_TIMER: + if (! rtadv->ra_timer) + 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, + zvrf, val); + break; + case RTADV_READ: + if (! rtadv->ra_read) + rtadv->ra_read = thread_add_read (zebrad.master, rtadv_read, zvrf, val); + break; + default: + break; + } + return; +} + +void +rtadv_init (struct zebra_vrf *zvrf) +{ + zvrf->rtadv.sock = rtadv_make_socket (zvrf->vrf_id); +} + +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; + } + + 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); + install_element (INTERFACE_NODE, &ipv6_nd_ra_interval_msec_cmd); + install_element (INTERFACE_NODE, &no_ipv6_nd_ra_interval_cmd); + install_element (INTERFACE_NODE, &no_ipv6_nd_ra_interval_val_cmd); + install_element (INTERFACE_NODE, &no_ipv6_nd_ra_interval_msec_val_cmd); + install_element (INTERFACE_NODE, &ipv6_nd_ra_lifetime_cmd); + install_element (INTERFACE_NODE, &no_ipv6_nd_ra_lifetime_cmd); + install_element (INTERFACE_NODE, &no_ipv6_nd_ra_lifetime_val_cmd); + install_element (INTERFACE_NODE, &ipv6_nd_reachable_time_cmd); + install_element (INTERFACE_NODE, &no_ipv6_nd_reachable_time_cmd); + install_element (INTERFACE_NODE, &no_ipv6_nd_reachable_time_val_cmd); + install_element (INTERFACE_NODE, &ipv6_nd_managed_config_flag_cmd); + install_element (INTERFACE_NODE, &no_ipv6_nd_managed_config_flag_cmd); + install_element (INTERFACE_NODE, &ipv6_nd_other_config_flag_cmd); + install_element (INTERFACE_NODE, &no_ipv6_nd_other_config_flag_cmd); + install_element (INTERFACE_NODE, &ipv6_nd_homeagent_config_flag_cmd); + install_element (INTERFACE_NODE, &no_ipv6_nd_homeagent_config_flag_cmd); + install_element (INTERFACE_NODE, &ipv6_nd_homeagent_preference_cmd); + install_element (INTERFACE_NODE, &no_ipv6_nd_homeagent_preference_cmd); + install_element (INTERFACE_NODE, &no_ipv6_nd_homeagent_preference_val_cmd); + install_element (INTERFACE_NODE, &ipv6_nd_homeagent_lifetime_cmd); + install_element (INTERFACE_NODE, &no_ipv6_nd_homeagent_lifetime_cmd); + install_element (INTERFACE_NODE, &no_ipv6_nd_homeagent_lifetime_val_cmd); + install_element (INTERFACE_NODE, &ipv6_nd_adv_interval_config_option_cmd); + install_element (INTERFACE_NODE, &no_ipv6_nd_adv_interval_config_option_cmd); + install_element (INTERFACE_NODE, &ipv6_nd_prefix_cmd); + install_element (INTERFACE_NODE, &ipv6_nd_prefix_val_rev_rtaddr_cmd); + install_element (INTERFACE_NODE, &ipv6_nd_prefix_val_nortaddr_cmd); + install_element (INTERFACE_NODE, &ipv6_nd_prefix_val_rev_cmd); + install_element (INTERFACE_NODE, &ipv6_nd_prefix_val_noauto_cmd); + install_element (INTERFACE_NODE, &ipv6_nd_prefix_val_offlink_cmd); + install_element (INTERFACE_NODE, &ipv6_nd_prefix_val_rtaddr_cmd); + install_element (INTERFACE_NODE, &ipv6_nd_prefix_val_cmd); + install_element (INTERFACE_NODE, &ipv6_nd_prefix_noval_cmd); + install_element (INTERFACE_NODE, &ipv6_nd_prefix_noval_rev_cmd); + install_element (INTERFACE_NODE, &ipv6_nd_prefix_noval_noauto_cmd); + install_element (INTERFACE_NODE, &ipv6_nd_prefix_noval_offlink_cmd); + install_element (INTERFACE_NODE, &ipv6_nd_prefix_noval_rtaddr_cmd); + install_element (INTERFACE_NODE, &ipv6_nd_prefix_prefix_cmd); + install_element (INTERFACE_NODE, &no_ipv6_nd_prefix_cmd); + install_element (INTERFACE_NODE, &ipv6_nd_router_preference_cmd); + install_element (INTERFACE_NODE, &no_ipv6_nd_router_preference_cmd); + install_element (INTERFACE_NODE, &no_ipv6_nd_router_preference_val_cmd); + install_element (INTERFACE_NODE, &ipv6_nd_mtu_cmd); + install_element (INTERFACE_NODE, &no_ipv6_nd_mtu_cmd); + install_element (INTERFACE_NODE, &no_ipv6_nd_mtu_val_cmd); +} + +static int +if_join_all_router (int sock, struct interface *ifp) +{ + int ret; + + struct ipv6_mreq mreq; + + memset (&mreq, 0, sizeof (struct ipv6_mreq)); + inet_pton (AF_INET6, ALLROUTER, &mreq.ipv6mr_multiaddr); + mreq.ipv6mr_interface = ifp->ifindex; + + ret = setsockopt (sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, + (char *) &mreq, sizeof mreq); + if (ret < 0) + zlog_warn ("can't setsockopt IPV6_JOIN_GROUP: %s", safe_strerror (errno)); + + zlog_info ("rtadv: %s join to all-routers multicast group", ifp->name); + + return 0; +} + +static int +if_leave_all_router (int sock, struct interface *ifp) +{ + int ret; + + struct ipv6_mreq mreq; + + memset (&mreq, 0, sizeof (struct ipv6_mreq)); + inet_pton (AF_INET6, ALLROUTER, &mreq.ipv6mr_multiaddr); + mreq.ipv6mr_interface = ifp->ifindex; + + ret = setsockopt (sock, IPPROTO_IPV6, IPV6_LEAVE_GROUP, + (char *) &mreq, sizeof mreq); + if (ret < 0) + zlog_warn ("can't setsockopt IPV6_LEAVE_GROUP: %s", safe_strerror (errno)); + + zlog_info ("rtadv: %s leave from all-routers multicast group", ifp->name); + + return 0; +} + +#else +void +rtadv_init (struct zebra_vrf *zvrf) +{ + /* Empty.*/; +} +void +rtadv_terminate (struct zebra_vrf *zvrf) +{ + /* Empty.*/; +} +void +rtadv_cmd_init (void) +{ + /* Empty.*/; +} +#endif /* HAVE_RTADV && HAVE_IPV6 */ diff --git a/zebra/rtadv.h b/zebra/rtadv.h new file mode 100644 index 0000000..160814b --- /dev/null +++ b/zebra/rtadv.h @@ -0,0 +1,107 @@ +/* Router advertisement + * Copyright (C) 2005 6WIND + * 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 _ZEBRA_RTADV_H +#define _ZEBRA_RTADV_H + +#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 +{ + /* Prefix to be advertised. */ + struct prefix_ipv6 prefix; + + /* The value to be placed in the Valid Lifetime in the Prefix */ + u_int32_t AdvValidLifetime; +#define RTADV_VALID_LIFETIME 2592000 + + /* The value to be placed in the on-link flag */ + int AdvOnLinkFlag; + + /* The value to be placed in the Preferred Lifetime in the Prefix + Information option, in seconds.*/ + u_int32_t AdvPreferredLifetime; +#define RTADV_PREFERRED_LIFETIME 604800 + + /* The value to be placed in the Autonomous Flag. */ + int AdvAutonomousFlag; + + /* The value to be placed in the Router Address Flag [RFC6275 7.2]. */ + int AdvRouterAddressFlag; +#ifndef ND_OPT_PI_FLAG_RADDR +#define ND_OPT_PI_FLAG_RADDR 0x20 +#endif + +}; + +extern void rtadv_config_write (struct vty *, struct interface *); + +/* RFC4584 Extension to Sockets API for Mobile IPv6 */ + +#ifndef ND_OPT_ADV_INTERVAL +#define ND_OPT_ADV_INTERVAL 7 /* Adv Interval Option */ +#endif +#ifndef ND_OPT_HA_INFORMATION +#define ND_OPT_HA_INFORMATION 8 /* HA Information Option */ +#endif + +#ifndef HAVE_STRUCT_ND_OPT_ADV_INTERVAL +struct nd_opt_adv_interval { /* Advertisement interval option */ + uint8_t nd_opt_ai_type; + uint8_t nd_opt_ai_len; + uint16_t nd_opt_ai_reserved; + uint32_t nd_opt_ai_interval; +} __attribute__((__packed__)); +#else +#ifndef HAVE_STRUCT_ND_OPT_ADV_INTERVAL_ND_OPT_AI_TYPE +/* fields may have to be renamed */ +#define nd_opt_ai_type nd_opt_adv_interval_type +#define nd_opt_ai_len nd_opt_adv_interval_len +#define nd_opt_ai_reserved nd_opt_adv_interval_reserved +#define nd_opt_ai_interval nd_opt_adv_interval_ival +#endif +#endif + +#ifndef HAVE_STRUCT_ND_OPT_HOMEAGENT_INFO +struct nd_opt_homeagent_info { /* Home Agent info */ + u_int8_t nd_opt_hai_type; + u_int8_t nd_opt_hai_len; + u_int16_t nd_opt_hai_reserved; + u_int16_t nd_opt_hai_preference; + u_int16_t nd_opt_hai_lifetime; +} __attribute__((__packed__)); +#endif + +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 new file mode 100644 index 0000000..e1ec670 --- /dev/null +++ b/zebra/rtread_getmsg.c @@ -0,0 +1,239 @@ +/* + * Kernel routing table readup by getmsg(2) + * Copyright (C) 1999 Michael Handler + * + * 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 "vrf.h" +#include "vty.h" + +#include "zebra/rib.h" +#include "zebra/zserv.h" + +#include +#include + +/* Solaris defines these in both and , sigh */ +#ifdef SUNOS_5 +#include +#ifndef T_CURRENT +#define T_CURRENT MI_T_CURRENT +#endif /* T_CURRENT */ +#ifndef IRE_CACHE +#define IRE_CACHE 0x0020 /* Cached Route entry */ +#endif /* IRE_CACHE */ +#ifndef IRE_HOST_REDIRECT +#define IRE_HOST_REDIRECT 0x0200 /* Host route entry from redirects */ +#endif /* IRE_HOST_REDIRECT */ +#ifndef IRE_CACHETABLE +#define IRE_CACHETABLE (IRE_CACHE | IRE_BROADCAST | IRE_LOCAL | \ + IRE_LOOPBACK) +#endif /* IRE_CACHETABLE */ +#undef IPOPT_EOL +#undef IPOPT_NOP +#undef IPOPT_LSRR +#undef IPOPT_RR +#undef IPOPT_SSRR +#endif /* SUNOS_5 */ + +#include +#include +#include + +/* device to read IP routing table from */ +#ifndef _PATH_GETMSG_ROUTE +#define _PATH_GETMSG_ROUTE "/dev/ip" +#endif /* _PATH_GETMSG_ROUTE */ + +#define RT_BUFSIZ 8192 + +static void +handle_route_entry (mib2_ipRouteEntry_t *routeEntry) +{ + struct prefix_ipv4 prefix; + struct in_addr tmpaddr, gateway; + u_char zebra_flags = 0; + + if (routeEntry->ipRouteInfo.re_ire_type & IRE_CACHETABLE) + return; + + if (routeEntry->ipRouteInfo.re_ire_type & IRE_HOST_REDIRECT) + zebra_flags |= ZEBRA_FLAG_SELFROUTE; + + prefix.family = AF_INET; + + tmpaddr.s_addr = routeEntry->ipRouteDest; + prefix.prefix = tmpaddr; + + tmpaddr.s_addr = routeEntry->ipRouteMask; + prefix.prefixlen = ip_masklen (tmpaddr); + + gateway.s_addr = routeEntry->ipRouteNextHop; + + rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags, &prefix, + &gateway, NULL, 0, VRF_DEFAULT, RT_TABLE_MAIN, + 0, 0, 0, SAFI_UNICAST); +} + +void +route_read (struct zebra_vrf *zvrf) +{ + char storage[RT_BUFSIZ]; + + struct T_optmgmt_req *TLIreq = (struct T_optmgmt_req *) storage; + struct T_optmgmt_ack *TLIack = (struct T_optmgmt_ack *) storage; + struct T_error_ack *TLIerr = (struct T_error_ack *) storage; + + struct opthdr *MIB2hdr; + + mib2_ipRouteEntry_t *routeEntry, *lastRouteEntry; + + 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)); + return; + } + + TLIreq->PRIM_type = T_OPTMGMT_REQ; + TLIreq->OPT_offset = sizeof (struct T_optmgmt_req); + TLIreq->OPT_length = sizeof (struct opthdr); + TLIreq->MGMT_flags = T_CURRENT; + + MIB2hdr = (struct opthdr *) &TLIreq[1]; + + MIB2hdr->level = MIB2_IP; + MIB2hdr->name = 0; + MIB2hdr->len = 0; + + msgdata.buf = storage; + msgdata.len = sizeof (struct T_optmgmt_req) + sizeof (struct opthdr); + + flags = 0; + + if (putmsg (dev, &msgdata, NULL, flags) == -1) { + zlog_warn ("putmsg failed: %s", safe_strerror (errno)); + goto exit; + } + + MIB2hdr = (struct opthdr *) &TLIack[1]; + msgdata.maxlen = sizeof (storage); + + while (1) { + flags = 0; + retval = getmsg (dev, &msgdata, NULL, &flags); + + if (retval == -1) { + zlog_warn ("getmsg(ctl) failed: %s", safe_strerror (errno)); + goto exit; + } + + /* This is normal loop termination */ + if (retval == 0 && + (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 ((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) + ? TLIerr->UNIX_error : EPROTO)); + break; + } + + /* should dump more debugging info to the log statement, + like what GateD does in this instance, but not + critical yet. */ + if (retval != MOREDATA || + (size_t)msgdata.len < sizeof (struct T_optmgmt_ack) || + TLIack->PRIM_type != T_OPTMGMT_ACK || + TLIack->MGMT_flags != T_SUCCESS) { + errno = ENOMSG; + zlog_warn ("getmsg(ctl) returned bizarreness"); + break; + } + + /* MIB2_IP_21 is the the pseudo-MIB2 ipRouteTable + entry, see . "This isn't the MIB data + you're looking for." */ + process = (MIB2hdr->level == MIB2_IP && + MIB2hdr->name == MIB2_IP_21) ? 1 : 0; + + /* getmsg writes the data buffer out completely, not + to the closest smaller multiple. Unless reassembling + data structures across buffer boundaries is your idea + of a good time, set maxlen to the closest smaller + multiple of the size of the datastructure you're + retrieving. */ + msgdata.maxlen = sizeof (storage) - (sizeof (storage) + % sizeof (mib2_ipRouteEntry_t)); + + msgdata.len = 0; + flags = 0; + + do { + retval = getmsg (dev, NULL, &msgdata, &flags); + + if (retval == -1) { + zlog_warn ("getmsg(data) failed: %s", + safe_strerror (errno)); + goto exit; + } + + if (!(retval == 0 || retval == MOREDATA)) { + zlog_warn ("getmsg(data) returned %d", retval); + goto exit; + } + + if (process) { + if (msgdata.len % + sizeof (mib2_ipRouteEntry_t) != 0) { + zlog_warn ("getmsg(data) returned " +"msgdata.len = %d (%% sizeof (mib2_ipRouteEntry_t) != 0)", msgdata.len); + goto exit; + } + + routeEntry = (mib2_ipRouteEntry_t *) + msgdata.buf; + lastRouteEntry = (mib2_ipRouteEntry_t *) + (msgdata.buf + msgdata.len); + do { + handle_route_entry (routeEntry); + } while (++routeEntry < lastRouteEntry); + } + } while (retval == MOREDATA); + } + +exit: + close (dev); +} diff --git a/zebra/rtread_netlink.c b/zebra/rtread_netlink.c new file mode 100644 index 0000000..1f65864 --- /dev/null +++ b/zebra/rtread_netlink.c @@ -0,0 +1,31 @@ +/* + * Kernel routing table readup by netlink + * Copyright (C) 1998 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/zserv.h" +#include "rt_netlink.h" + +void route_read (struct zebra_vrf *zvrf) +{ + netlink_route_read (zvrf); +} diff --git a/zebra/rtread_sysctl.c b/zebra/rtread_sysctl.c new file mode 100644 index 0000000..385e150 --- /dev/null +++ b/zebra/rtread_sysctl.c @@ -0,0 +1,84 @@ +/* + * Kernel routing table read by sysctl function. + * 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 "memory.h" +#include "log.h" +#include "vrf.h" + +#include "zebra/zserv.h" +#include "zebra/rt.h" +#include "zebra/kernel_socket.h" + +/* Kernel routing table read up by sysctl function. */ +void +route_read (struct zebra_vrf *zvrf) +{ + caddr_t buf, end, ref; + size_t bufsiz; + struct rt_msghdr *rtm; + +#define MIBSIZ 6 + int mib[MIBSIZ] = + { + CTL_NET, + PF_ROUTE, + 0, + 0, + NET_RT_DUMP, + 0 + }; + + if (zvrf->vrf_id != VRF_DEFAULT) + return; + + /* Get buffer size. */ + if (sysctl (mib, MIBSIZ, NULL, &bufsiz, NULL, 0) < 0) + { + zlog_warn ("sysctl fail: %s", safe_strerror (errno)); + return; + } + + /* Allocate buffer. */ + ref = buf = XMALLOC (MTYPE_TMP, bufsiz); + + /* Read routing table information by calling sysctl(). */ + if (sysctl (mib, MIBSIZ, buf, &bufsiz, NULL, 0) < 0) + { + zlog_warn ("sysctl() fail by %s", safe_strerror (errno)); + return; + } + + for (end = buf + bufsiz; buf < end; buf += rtm->rtm_msglen) + { + rtm = (struct rt_msghdr *) buf; + /* We must set RTF_DONE here, so rtm_read() doesn't ignore the message. */ + SET_FLAG (rtm->rtm_flags, RTF_DONE); + rtm_read (rtm); + } + + /* Free buffer. */ + XFREE (MTYPE_TMP, ref); + + return; +} diff --git a/zebra/test_main.c b/zebra/test_main.c new file mode 100644 index 0000000..09f53ad --- /dev/null +++ b/zebra/test_main.c @@ -0,0 +1,396 @@ +/* main routine. + * Copyright (C) 1997, 98 Kunihiro Ishiguro + * + * 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 "getopt.h" +#include "command.h" +#include "thread.h" +#include "filter.h" +#include "memory.h" +#include "prefix.h" +#include "log.h" +#include "privs.h" +#include "sigevent.h" +#include "vrf.h" + +#include "zebra/rib.h" +#include "zebra/zserv.h" +#include "zebra/debug.h" +#include "zebra/router-id.h" +#include "zebra/interface.h" + +/* Zebra instance */ +struct zebra_t zebrad = +{ + .rtm_table_default = 0, +}; + +/* process id. */ +pid_t pid; + +/* zebra_rib's workqueue hold time. Private export for use by test code only */ +extern int rib_process_hold_time; + +/* Pacify zclient.o in libzebra, which expects this variable. */ +struct thread_master *master; + +/* Command line options. */ +struct option longopts[] = +{ + { "batch", no_argument, NULL, 'b'}, + { "daemon", no_argument, NULL, 'd'}, + { "config_file", required_argument, NULL, 'f'}, + { "help", no_argument, NULL, 'h'}, + { "vty_addr", required_argument, NULL, 'A'}, + { "vty_port", required_argument, NULL, 'P'}, + { "version", no_argument, NULL, 'v'}, + { "rib_hold", required_argument, NULL, 'r'}, + { 0 } +}; + +zebra_capabilities_t _caps_p [] = +{ + ZCAP_NET_ADMIN, + ZCAP_SYS_ADMIN, + ZCAP_NET_RAW, +}; + +/* Default configuration file path. */ +char config_default[] = SYSCONFDIR DEFAULT_CONFIG_FILE; + +/* Process ID saved for use by init system */ +const char *pid_file = PATH_ZEBRA_PID; + +/* Help information display. */ +static void +usage (char *progname, int status) +{ + if (status != 0) + fprintf (stderr, "Try `%s --help' for more information.\n", progname); + else + { + printf ("Usage : %s [OPTION...]\n\n"\ + "Daemon which manages kernel routing table management and "\ + "redistribution between different routing protocols.\n\n"\ + "-b, --batch Runs in batch mode\n"\ + "-d, --daemon Runs in daemon mode\n"\ + "-f, --config_file Set configuration file name\n"\ + "-A, --vty_addr Set vty's bind address\n"\ + "-P, --vty_port Set vty's port number\n"\ + "-r, --rib_hold Set rib-queue hold time\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 ifindex_t test_ifindex = 0; + +/* testrib commands */ +DEFUN (test_interface_state, + test_interface_state_cmd, + "state (up|down)", + "configure interface\n" + "up\n" + "down\n") +{ + struct interface *ifp; + if (argc < 1) + return CMD_WARNING; + + ifp = vty->index; + if (ifp->ifindex == IFINDEX_INTERNAL) + { + ifp->ifindex = ++test_ifindex; + ifp->mtu = 1500; + ifp->flags = IFF_BROADCAST|IFF_MULTICAST; + } + + switch (argv[0][0]) + { + case 'u': + SET_FLAG (ifp->flags, IFF_UP); + if_add_update (ifp); + printf ("up\n"); + break; + case 'd': + UNSET_FLAG (ifp->flags, IFF_UP); + if_delete_update (ifp); + printf ("down\n"); + break; + default: + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +static void +test_cmd_init (void) +{ + install_element (INTERFACE_NODE, &test_interface_state_cmd); +} + +/* SIGHUP handler. */ +static void +sighup (void) +{ + zlog_info ("SIGHUP received"); + + /* Reload of config file. */ + ; +} + +/* SIGINT handler. */ +static void +sigint (void) +{ + zlog_notice ("Terminating on signal"); + + exit (0); +} + +/* SIGUSR1 handler. */ +static void +sigusr1 (void) +{ + zlog_rotate (NULL); +} + +struct quagga_signal_t zebra_signals[] = +{ + { + .signal = SIGHUP, + .handler = &sighup, + }, + { + .signal = SIGUSR1, + .handler = &sigusr1, + }, + { + .signal = SIGINT, + .handler = &sigint, + }, + { + .signal = SIGTERM, + .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) +{ + char *p; + char *vty_addr = NULL; + int vty_port = 0; + int batch_mode = 0; + int daemon_mode = 0; + char *config_file = NULL; + char *progname; + struct thread thread; + + /* Set umask before anything for security */ + umask (0027); + + /* preserve my name */ + progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]); + + zlog_default = openzlog (progname, ZLOG_ZEBRA, + LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON); + + while (1) + { + int opt; + + opt = getopt_long (argc, argv, "bdf:hA:P:r:v", longopts, 0); + + if (opt == EOF) + break; + + switch (opt) + { + case 0: + break; + case 'b': + batch_mode = 1; + case 'd': + daemon_mode = 1; + break; + case 'f': + config_file = optarg; + break; + case 'A': + vty_addr = optarg; + break; + case 'P': + /* Deal with atoi() returning 0 on failure, and zebra not + listening on zebra port... */ + if (strcmp(optarg, "0") == 0) + { + vty_port = 0; + break; + } + vty_port = atoi (optarg); + break; + case 'r': + rib_process_hold_time = atoi(optarg); + break; + case 'v': + print_version (progname); + exit (0); + break; + case 'h': + usage (progname, 0); + break; + default: + usage (progname, 1); + break; + } + } + + /* port and conf file mandatory */ + if (!vty_port || !config_file) + { + fprintf (stderr, "Error: --vty_port and --config_file arguments" + " are both required\n"); + usage (progname, 1); + } + + /* Make master thread emulator. */ + zebrad.master = thread_master_create (); + + /* Vty related initialize. */ + signal_init (zebrad.master, array_size(zebra_signals), zebra_signals); + cmd_init (1); + vty_init (zebrad.master); + memory_init (); + zebra_debug_init (); + zebra_if_init (); + test_cmd_init (); + + /* Zebra related initialize. */ + rib_init (); + access_list_init (); + + /* Make kernel routing socket. */ + zebra_vrf_init (); + zebra_vty_init(); + + /* Configuration file read*/ + vty_read_config (config_file, config_default); + + /* Clean up rib. */ + rib_weed_tables (); + + /* Exit when zebra is working in batch mode. */ + if (batch_mode) + exit (0); + + /* Daemonize. */ + if (daemon_mode && daemon (0, 0) < 0) + { + perror("daemon start failed"); + exit (1); + } + + /* Needed for BSD routing socket. */ + pid = getpid (); + + /* Make vty server socket. */ + vty_serv_sock (vty_addr, vty_port, "/tmp/test_zebra"); + + /* Print banner. */ + zlog_notice ("Zebra %s starting: vty@%d", QUAGGA_VERSION, vty_port); + + while (thread_fetch (zebrad.master, &thread)) + thread_call (&thread); + + /* Not reached... */ + return 0; +} diff --git a/zebra/testrib.conf b/zebra/testrib.conf new file mode 100644 index 0000000..0df7dc2 --- /dev/null +++ b/zebra/testrib.conf @@ -0,0 +1,76 @@ +! +! Zebra configuration saved from vty +! 2007/04/01 17:46:48 +! +password foo +log stdout +service advanced-vty +! +debug zebra rib +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 +! + +! 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 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.conf.sample b/zebra/zebra.conf.sample new file mode 100644 index 0000000..a5d0732 --- /dev/null +++ b/zebra/zebra.conf.sample @@ -0,0 +1,25 @@ +! -*- zebra -*- +! +! zebra sample configuration file +! +! $Id: zebra.conf.sample,v 1.1 2002/12/13 20:15:30 paul Exp $ +! +hostname Router +password zebra +enable password zebra +! +! Interface's description. +! +!interface lo +! description test of desc. +! +!interface sit0 +! multicast + +! +! Static default route sample. +! +!ip route 0.0.0.0/0 203.181.89.241 +! + +!log file zebra.log diff --git a/zebra/zebra_fpm.c b/zebra/zebra_fpm.c new file mode 100644 index 0000000..22fc6ca --- /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 0000000..ecd23c7 --- /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 0000000..bd171c8 --- /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 0000000..175d351 --- /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 0000000..1c4fd4c --- /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 0000000..beef310 --- /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 new file mode 100644 index 0000000..9ca0290 --- /dev/null +++ b/zebra/zebra_rib.c @@ -0,0 +1,3347 @@ +/* Routing Information Base. + * Copyright (C) 1997, 98, 99, 2001 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 "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 "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; + +/* Hold time for RIB process, should be very minimal. + * it is useful to able to set it otherwise for testing, hence exported + * as global here for test-rig code. + */ +int rib_process_hold_time = 10; + +/* Each route type's string and default distance value. */ +static const struct +{ + int key; + int distance; +} route_info[ZEBRA_ROUTE_MAX] = +{ + [ZEBRA_ROUTE_SYSTEM] = {ZEBRA_ROUTE_SYSTEM, 0}, + [ZEBRA_ROUTE_KERNEL] = {ZEBRA_ROUTE_KERNEL, 0}, + [ZEBRA_ROUTE_CONNECT] = {ZEBRA_ROUTE_CONNECT, 0}, + [ZEBRA_ROUTE_STATIC] = {ZEBRA_ROUTE_STATIC, 1}, + [ZEBRA_ROUTE_RIP] = {ZEBRA_ROUTE_RIP, 120}, + [ZEBRA_ROUTE_RIPNG] = {ZEBRA_ROUTE_RIPNG, 120}, + [ZEBRA_ROUTE_OSPF] = {ZEBRA_ROUTE_OSPF, 110}, + [ZEBRA_ROUTE_OSPF6] = {ZEBRA_ROUTE_OSPF6, 110}, + [ZEBRA_ROUTE_ISIS] = {ZEBRA_ROUTE_ISIS, 115}, + [ZEBRA_ROUTE_BGP] = {ZEBRA_ROUTE_BGP, 20 /* IBGP is 200. */}, + [ZEBRA_ROUTE_BABEL] = {ZEBRA_ROUTE_BABEL, 95}, + [ZEBRA_ROUTE_NHRP] = {ZEBRA_ROUTE_NHRP, 10}, + /* no entry/default: 150 */ +}; + +/* RPF lookup behaviour */ +static enum multicast_mode ipv4_multicast_mode = MCAST_NO_CONFIG; + +static void __attribute__((format (printf, 4, 5))) +_rnode_zlog(const char *_func, struct route_node *rn, int priority, + const char *msgfmt, ...) +{ + char prefix[PREFIX_STRLEN], buf[256]; + char msgbuf[512]; + va_list ap; + + va_start(ap, msgfmt); + vsnprintf(msgbuf, sizeof(msgbuf), msgfmt, ap); + va_end(ap); + + if (rn) + { + rib_table_info_t *info = rn->table->info; + + 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}"); + } + + zlog (NULL, priority, "%s: %s: %s", _func, buf, msgbuf); +} + +#define rnode_debug(node, ...) \ + _rnode_zlog(__func__, node, LOG_DEBUG, __VA_ARGS__) +#define rnode_info(node, ...) \ + _rnode_zlog(__func__, node, LOG_INFO, __VA_ARGS__) + +/* Add nexthop to the end of a rib node's nexthop list */ +void +rib_nexthop_add (struct rib *rib, struct nexthop *nexthop) +{ + nexthop_add(&rib->nexthop, nexthop); + rib->nexthop_num++; +} + +/* Delete specified nexthop from the list. */ +static void +rib_nexthop_delete (struct rib *rib, struct nexthop *nexthop) +{ + if (nexthop->next) + nexthop->next->prev = nexthop->prev; + if (nexthop->prev) + nexthop->prev->next = nexthop->next; + else + rib->nexthop = nexthop->next; + rib->nexthop_num--; +} + +struct nexthop * +rib_nexthop_ifindex_add (struct rib *rib, ifindex_t ifindex) +{ + struct nexthop *nexthop; + + nexthop = nexthop_new (); + nexthop->type = NEXTHOP_TYPE_IFINDEX; + nexthop->ifindex = ifindex; + + rib_nexthop_add (rib, nexthop); + + return nexthop; +} + +struct nexthop * +rib_nexthop_ifname_add (struct rib *rib, char *ifname) +{ + struct nexthop *nexthop; + + nexthop = nexthop_new (); + nexthop->type = NEXTHOP_TYPE_IFNAME; + nexthop->ifname = XSTRDUP (MTYPE_TMP, ifname); + + rib_nexthop_add (rib, nexthop); + + return nexthop; +} + +struct nexthop * +rib_nexthop_ipv4_add (struct rib *rib, struct in_addr *ipv4, struct in_addr *src) +{ + struct nexthop *nexthop; + + nexthop = nexthop_new (); + nexthop->type = NEXTHOP_TYPE_IPV4; + nexthop->gate.ipv4 = *ipv4; + if (src) + nexthop->src.ipv4 = *src; + + rib_nexthop_add (rib, nexthop); + + return nexthop; +} + +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 = nexthop_new (); + nexthop->type = NEXTHOP_TYPE_IPV4_IFINDEX; + nexthop->gate.ipv4 = *ipv4; + if (src) + nexthop->src.ipv4 = *src; + nexthop->ifindex = ifindex; + + rib_nexthop_add (rib, nexthop); + + return nexthop; +} + +struct nexthop * +rib_nexthop_ipv6_add (struct rib *rib, struct in6_addr *ipv6) +{ + struct nexthop *nexthop; + + nexthop = nexthop_new (); + nexthop->type = NEXTHOP_TYPE_IPV6; + nexthop->gate.ipv6 = *ipv6; + + rib_nexthop_add (rib, nexthop); + + return nexthop; +} + +static struct nexthop * +rib_nexthop_ipv6_ifname_add (struct rib *rib, struct in6_addr *ipv6, + char *ifname) +{ + struct nexthop *nexthop; + + nexthop = nexthop_new (); + nexthop->type = NEXTHOP_TYPE_IPV6_IFNAME; + nexthop->gate.ipv6 = *ipv6; + nexthop->ifname = XSTRDUP (MTYPE_TMP, ifname); + + rib_nexthop_add (rib, nexthop); + + return nexthop; +} + +struct nexthop * +rib_nexthop_ipv6_ifindex_add (struct rib *rib, struct in6_addr *ipv6, + ifindex_t ifindex) +{ + struct nexthop *nexthop; + + nexthop = nexthop_new (); + nexthop->type = NEXTHOP_TYPE_IPV6_IFINDEX; + nexthop->gate.ipv6 = *ipv6; + nexthop->ifindex = ifindex; + + rib_nexthop_add (rib, nexthop); + + return nexthop; +} + +struct nexthop * +rib_nexthop_blackhole_add (struct rib *rib) +{ + struct nexthop *nexthop; + + nexthop = nexthop_new (); + nexthop->type = NEXTHOP_TYPE_BLACKHOLE; + SET_FLAG (rib->flags, ZEBRA_FLAG_BLACKHOLE); + + 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 +nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, + struct route_node *top) +{ + struct prefix_ipv4 p; + 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); + nexthops_free(nexthop->resolved); + nexthop->resolved = NULL; + rib->nexthop_mtu = 0; + } + + /* Make lookup prefix. */ + memset (&p, 0, sizeof (struct prefix_ipv4)); + p.family = AF_INET; + p.prefixlen = IPV4_MAX_PREFIXLEN; + p.prefix = nexthop->gate.ipv4; + + /* Lookup table. */ + table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, rib->vrf_id); + if (! table) + return 0; + + rn = route_node_match (table, (struct prefix *) &p); + while (rn) + { + route_unlock_node (rn); + + /* If lookup self prefix return immediately. */ + if (rn == top) + return 0; + + /* Pick up selected route. */ + RNODE_FOREACH_RIB (rn, match) + { + if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) + continue; + 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) + { + do { + rn = rn->parent; + } while (rn && rn->info == NULL); + if (rn) + route_lock_node (rn); + } + 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. */ + newhop = match->nexthop; + if (newhop && nexthop->type == NEXTHOP_TYPE_IPV4) + nexthop->ifindex = newhop->ifindex; + + return 1; + } + 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)) + { + if (set) + { + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE); + + 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) + { + resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX; + resolved_hop->gate.ipv4 = nexthop->gate.ipv4; + resolved_hop->ifindex = newhop->ifindex; + } + + nexthop_add(&nexthop->resolved, resolved_hop); + } + resolved = 1; + } + if (resolved && set) + rib->nexthop_mtu = match->mtu; + return resolved; + } + else + { + return 0; + } + } + } + return 0; +} + +/* If force flag is not set, do not modify falgs at all for uninstall + the route from FIB. */ +static int +nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, + struct route_node *top) +{ + struct prefix_ipv6 p; + 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); + nexthops_free(nexthop->resolved); + nexthop->resolved = NULL; + } + + /* Make lookup prefix. */ + memset (&p, 0, sizeof (struct prefix_ipv6)); + p.family = AF_INET6; + p.prefixlen = IPV6_MAX_PREFIXLEN; + p.prefix = nexthop->gate.ipv6; + + /* Lookup table. */ + table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, rib->vrf_id); + if (! table) + return 0; + + rn = route_node_match (table, (struct prefix *) &p); + while (rn) + { + route_unlock_node (rn); + + /* If lookup self prefix return immediately. */ + if (rn == top) + return 0; + + /* Pick up selected route. */ + RNODE_FOREACH_RIB (rn, match) + { + if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) + continue; + 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) + { + do { + rn = rn->parent; + } while (rn && rn->info == NULL); + if (rn) + route_lock_node (rn); + } + 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. */ + newhop = match->nexthop; + + if (newhop && nexthop->type == NEXTHOP_TYPE_IPV6) + nexthop->ifindex = newhop->ifindex; + + return 1; + } + 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)) + { + if (set) + { + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE); + + 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) + { + 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) + { + resolved_hop->flags |= NEXTHOP_FLAG_ONLINK; + resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX; + resolved_hop->gate.ipv6 = nexthop->gate.ipv6; + resolved_hop->ifindex = newhop->ifindex; + } + + nexthop_add(&nexthop->resolved, resolved_hop); + } + resolved = 1; + } + return resolved; + } + else + { + return 0; + } + } + } + return 0; +} + +struct rib * +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 route_table *table; + struct route_node *rn; + struct rib *match; + struct nexthop *newhop, *tnewhop; + int recursing; + + /* Lookup table. */ + table = zebra_vrf_table (AFI_IP, safi, vrf_id); + if (! table) + return 0; + + rn = route_node_match_ipv4 (table, &addr); + + while (rn) + { + route_unlock_node (rn); + + /* Pick up selected route. */ + RNODE_FOREACH_RIB (rn, match) + { + if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) + continue; + 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 || (skip_bgp && (match->type == ZEBRA_ROUTE_BGP))) + { + do { + rn = rn->parent; + } while (rn && rn->info == NULL); + if (rn) + route_lock_node (rn); + } + else + { + if (match->type != ZEBRA_ROUTE_CONNECT) + { + int found = 0; + for (ALL_NEXTHOPS_RO(match->nexthop, newhop, tnewhop, recursing)) + if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB)) + { + found = 1; + break; + } + if (!found) + return NULL; + } + + if (rn_out) + *rn_out = rn; + return match; + } + } + return NULL; +} + +struct rib * +rib_match_ipv4_multicast (struct in_addr addr, struct route_node **rn_out, + 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, *tnexthop; + int recursing; + + /* Lookup table. */ + table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, vrf_id); + if (! table) + return 0; + + rn = route_node_lookup (table, (struct prefix *) p); + + /* No route for this prefix. */ + if (! rn) + return NULL; + + /* Unlock node. */ + route_unlock_node (rn); + + RNODE_FOREACH_RIB (rn, match) + { + if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) + continue; + if (CHECK_FLAG (match->status, RIB_ENTRY_SELECTED_FIB)) + break; + } + + if (! match || match->type == ZEBRA_ROUTE_BGP) + return NULL; + + if (match->type == ZEBRA_ROUTE_CONNECT) + return match; + + for (ALL_NEXTHOPS_RO(match->nexthop, nexthop, tnexthop, recursing)) + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) + return match; + + return NULL; +} + +/* + * This clone function, unlike its original rib_lookup_ipv4(), checks + * if specified IPv4 route record (prefix/mask -> gate) exists in + * the whole RIB and has ZEBRA_FLAG_SELECTED set. + * + * Return values: + * -1: error + * 0: exact match found + * 1: a match was found with a different gate + * 2: connected route found + * 3: no matches found + */ +int +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, *tnexthop; + int recursing; + int nexthops_active; + + /* Lookup table. */ + table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, vrf_id); + if (! table) + return ZEBRA_RIB_LOOKUP_ERROR; + + /* Scan the RIB table for exactly matching RIB entry. */ + rn = route_node_lookup (table, (struct prefix *) p); + + /* No route for this prefix. */ + if (! rn) + return ZEBRA_RIB_NOTFOUND; + + /* Unlock node. */ + route_unlock_node (rn); + + /* Find out if a "selected" RR for the discovered RIB entry exists ever. */ + RNODE_FOREACH_RIB (rn, match) + { + if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) + continue; + if (CHECK_FLAG (match->status, RIB_ENTRY_SELECTED_FIB)) + break; + } + + /* None such found :( */ + if (!match) + return ZEBRA_RIB_NOTFOUND; + + if (match->type == ZEBRA_ROUTE_CONNECT) + return ZEBRA_RIB_FOUND_CONNECTED; + + /* Ok, we have a cood candidate, let's check it's nexthop list... */ + nexthops_active = 0; + for (ALL_NEXTHOPS_RO(match->nexthop, nexthop, tnexthop, recursing)) + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) + { + 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], 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; +} + +struct rib * +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, *tnewhop; + int recursing; + + /* Lookup table. */ + table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, vrf_id); + if (! table) + return 0; + + memset (&p, 0, sizeof (struct prefix_ipv6)); + p.family = AF_INET6; + p.prefixlen = IPV6_MAX_PREFIXLEN; + IPV6_ADDR_COPY (&p.prefix, addr); + + rn = route_node_match (table, (struct prefix *) &p); + + while (rn) + { + route_unlock_node (rn); + + /* Pick up selected route. */ + RNODE_FOREACH_RIB (rn, match) + { + if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED)) + continue; + 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) + { + do { + rn = rn->parent; + } while (rn && rn->info == NULL); + if (rn) + route_lock_node (rn); + } + else + { + if (match->type == ZEBRA_ROUTE_CONNECT) + /* Directly point connected route. */ + return match; + else + { + for (ALL_NEXTHOPS_RO(match->nexthop, newhop, tnewhop, recursing)) + if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB)) + return match; + return NULL; + } + } + } + return NULL; +} + +#define RIB_SYSTEM_ROUTE(R) \ + ((R)->type == ZEBRA_ROUTE_KERNEL || (R)->type == ZEBRA_ROUTE_CONNECT) + +/* This function verifies reachability of one given nexthop, which can be + * numbered or unnumbered, IPv4 or IPv6. The result is unconditionally stored + * in nexthop->flags field. If the 4th parameter, 'set', is non-zero, + * nexthop->ifindex will be updated appropriately as well. + * An existing route map can turn (otherwise active) nexthop into inactive, but + * not vice versa. + * + * The return value is the final value of 'ACTIVE' flag. + */ + +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]; + struct route_map *rmap; + int family; + + family = 0; + switch (nexthop->type) + { + case NEXTHOP_TYPE_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 + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + break; + case NEXTHOP_TYPE_IPV6_IFNAME: + family = AFI_IP6; + case NEXTHOP_TYPE_IFNAME: + ifp = if_lookup_by_name_vrf (nexthop->ifname, rib->vrf_id); + if (ifp && if_is_operative(ifp)) + { + if (set) + nexthop->ifindex = ifp->ifindex; + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + } + else + { + if (set) + nexthop->ifindex = 0; + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + } + break; + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + family = AFI_IP; + if (nexthop_active_ipv4 (rib, nexthop, set, rn)) + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + else + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + break; + case NEXTHOP_TYPE_IPV6: + family = AFI_IP6; + if (nexthop_active_ipv6 (rib, nexthop, set, rn)) + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + else + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + break; + case NEXTHOP_TYPE_IPV6_IFINDEX: + family = AFI_IP6; + if (IN6_IS_ADDR_LINKLOCAL (&nexthop->gate.ipv6)) + { + 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 + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + } + else + { + if (nexthop_active_ipv6 (rib, nexthop, set, rn)) + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + else + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + } + break; + case NEXTHOP_TYPE_BLACKHOLE: + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + break; + default: + break; + } + 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]) + rmap = route_map_lookup_by_name (proto_rm[family][rib->type]); + if (!rmap && proto_rm[family][ZEBRA_ROUTE_MAX]) + rmap = route_map_lookup_by_name (proto_rm[family][ZEBRA_ROUTE_MAX]); + if (rmap) { + struct nexthop_vrfid nh_vrf = {nexthop, rib->vrf_id}; + ret = route_map_apply(rmap, &rn->p, RMAP_ZEBRA, &nh_vrf); + } + + if (ret == RMAP_DENYMATCH) + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + return CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); +} + +/* 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 RIB_ENTRY_CHANGED. The 4th 'set' argument is + * transparently passed to nexthop_active_check(). + * + * Return value is the new number of active nexthops. + */ + +static int +nexthop_active_update (struct route_node *rn, struct rib *rib, int set) +{ + struct nexthop *nexthop; + unsigned int prev_active, new_active; + ifindex_t prev_index; + + rib->nexthop_active_num = 0; + + for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + { + prev_active = CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + prev_index = nexthop->ifindex; + if ((new_active = nexthop_active_check (rn, rib, nexthop, set))) + rib->nexthop_active_num++; + if (prev_active != new_active || + prev_index != nexthop->ifindex) + SET_FLAG (rib->status, RIB_ENTRY_CHANGED); + } + return rib->nexthop_active_num; +} + + + +static int +rib_update_kernel (struct route_node *rn, struct rib *old, struct rib *new) +{ + int ret = 0; + 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 && new) + { + for (ALL_NEXTHOPS_RO(new->nexthop, nexthop, tnexthop, recursing)) + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + } + else if (old && old != new) + { + for (ALL_NEXTHOPS_RO(old->nexthop, nexthop, tnexthop, recursing)) + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + } + + return ret; +} + +/* Uninstall the route from kernel. */ +static void +rib_uninstall (struct route_node *rn, struct rib *rib) +{ + 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_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 *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, *tnexthop; + int recursing; + rib_table_info_t *info; + + assert (rn); + + info = rn->table->info; + + RNODE_FOREACH_RIB (rn, rib) + { + UNSET_FLAG (rib->status, RIB_ENTRY_CHANGED); + + /* Currently installed rib. */ + if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) + { + assert (old_selected == NULL); + old_selected = rib; + } + if (CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB)) + { + 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)) + continue; + + /* Infinit distance. */ + if (rib->distance == DISTANCE_INFINITY) + continue; + + 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: + * 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. + */ + + /* 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)) + { + /* 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); + } + + /* 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); + } + + if (new_selected) + { + /* Install new or replace existing redistributed entry */ + SET_FLAG (new_selected->flags, ZEBRA_FLAG_SELECTED); + redistribute_add (&rn->p, new_selected); + } + } + + /* Remove all RIB entries queued for removal */ + RNODE_FOREACH_RIB_SAFE (rn, rib, next) + { + 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); + } + } + + if (IS_ZEBRA_DEBUG_RIB_Q) + 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 + * picked from it and processed by rib_process(). Don't process more, + * than one RN record; operate only in the specified sub-queue. + */ +static unsigned int +process_subq (struct list * subq, u_char qindex) +{ + struct listnode *lnode = listhead (subq); + struct route_node *rnode; + + if (!lnode) + return 0; + + rnode = listgetdata (lnode); + rib_process (rnode); + + if (rnode->info) + UNSET_FLAG (rib_dest_from_rnode (rnode)->flags, RIB_ROUTE_QUEUED (qindex)); + +#if 0 + else + { + zlog_debug ("%s: called for route_node (%p, %d) with no ribs", + __func__, rnode, rnode->lock); + zlog_backtrace(LOG_DEBUG); + } +#endif + route_unlock_node (rnode); + list_delete_node (subq, lnode); + 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. + */ +static wq_item_status +meta_queue_process (struct work_queue *dummy, void *data) +{ + struct meta_queue * mq = data; + unsigned i; + + for (i = 0; i < MQ_SIZE; i++) + if (process_subq (mq->subq[i], i)) + { + mq->size--; + break; + } + return mq->size ? WQ_REQUEUE : WQ_SUCCESS; +} + +/* + * 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, + [ZEBRA_ROUTE_CONNECT] = 0, + [ZEBRA_ROUTE_STATIC] = 1, + [ZEBRA_ROUTE_RIP] = 2, + [ZEBRA_ROUTE_RIPNG] = 2, + [ZEBRA_ROUTE_OSPF] = 2, + [ZEBRA_ROUTE_OSPF6] = 2, + [ZEBRA_ROUTE_ISIS] = 2, + [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, + * increasing the size for each data push done. + */ +static void +rib_meta_queue_add (struct meta_queue *mq, struct route_node *rn) +{ + struct rib *rib; + + 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 (rib_dest_from_rnode (rn)->flags, + RIB_ROUTE_QUEUED (qindex))) + { + if (IS_ZEBRA_DEBUG_RIB_Q) + rnode_debug (rn, "rn %p is already queued in sub-queue %u", + (void *)rn, qindex); + continue; + } + + 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) + rnode_debug (rn, "queued rn %p into sub-queue %u", + (void *)rn, qindex); + } +} + +/* Add route_node to work queue and schedule processing */ +static void +rib_queue_add (struct zebra_t *zebra, struct route_node *rn) +{ + assert (zebra && rn); + + /* Pointless to queue a route_node with no RIB entries to add or remove */ + if (!rnode_to_ribs (rn)) + { + 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); + + if (zebra->ribq == NULL) + { + zlog_err ("%s: work_queue does not exist!", __func__); + return; + } + + /* + * The RIB queue should normally be either empty or holding the only + * work_queue_item element. In the latter case this element would + * hold a pointer to the meta queue structure, which must be used to + * actually queue the route nodes to process. So create the MQ + * holder, if necessary, then push the work into it in any case. + * This semantics was introduced after 0.99.9 release. + */ + if (!zebra->ribq->items->count) + 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. + A destructor function doesn't seem to be necessary here. + */ +static struct meta_queue * +meta_queue_new (void) +{ + struct meta_queue *new; + unsigned i; + + new = XCALLOC (MTYPE_WORK_QUEUE, sizeof (struct meta_queue)); + assert(new); + + for (i = 0; i < MQ_SIZE; i++) + { + new->subq[i] = list_new (); + assert(new->subq[i]); + } + + return new; +} + +/* initialise zebra rib work queue */ +static void +rib_queue_init (struct zebra_t *zebra) +{ + assert (zebra); + + if (! (zebra->ribq = work_queue_new (zebra->master, + "route_node processing"))) + { + zlog_err ("%s: could not initialise work queue!", __func__); + return; + } + + /* 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. + * + * The queue length is bounded by the maximal size of the routing table, + * 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_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. + * + * Deleted RIBs are reaped during best-path selection. + * + * rib_addnode + * |-> rib_link or unset RIB_ENTRY_REMOVE |->Update kernel with + * |-------->| | best RIB, if required + * | | + * static_install->|->rib_addqueue...... -> rib_process + * | | + * |-------->| |-> rib_unlink + * |-> set RIB_ENTRY_REMOVE | + * rib_delnode (RIB freed) + * + * 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: + * - dest attached to route_node: + * - managed by: rib_link/rib_gc_dest + * - route_node processing queue + * - managed by: rib_addqueue, rib_process. + * + */ + +/* Add RIB to head of the route node. */ +static void +rib_link (struct route_node *rn, struct rib *rib) +{ + struct rib *head; + rib_dest_t *dest; + + assert (rib && rn); + + if (IS_ZEBRA_DEBUG_RIB) + rnode_debug (rn, "rn %p, rib %p", (void *)rn, (void *)rib); + + dest = rib_dest_from_rnode (rn); + if (!dest) + { + if (IS_ZEBRA_DEBUG_RIB) + 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; + } + rib->next = head; + dest->routes = rib; + rib_queue_add (&zebrad, rn); +} + +static void +rib_addnode (struct route_node *rn, struct rib *rib) +{ + /* RIB node has been un-removed before route-node is processed. + * route_node must hence already be on the queue for processing.. + */ + if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) + { + if (IS_ZEBRA_DEBUG_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) +{ + rib_dest_t *dest; + + assert (rn && rib); + + if (IS_ZEBRA_DEBUG_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; + + if (rib->prev) + rib->prev->next = rib->next; + else + { + dest->routes = rib->next; + } + + /* free RIB and nexthops */ + nexthops_free(rib->nexthop); + XFREE (MTYPE_RIB, rib); + +} + +static void +rib_delnode (struct route_node *rn, struct rib *rib) +{ + if (IS_ZEBRA_DEBUG_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); +} + +int +rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p, + struct in_addr *gate, struct in_addr *src, + 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; + struct route_table *table; + struct route_node *rn; + struct nexthop *nexthop; + + /* Lookup table. */ + table = zebra_vrf_table (AFI_IP, safi, vrf_id); + if (! table) + return 0; + + /* Make it sure prefixlen is applied to the prefix. */ + apply_mask_ipv4 (p); + + /* Set default distance by route type. */ + if (distance == 0) + { + if ((unsigned)type >= array_size(route_info)) + distance = 150; + else + distance = route_info[type].distance; + + /* iBGP distance is 200. */ + if (type == ZEBRA_ROUTE_BGP && CHECK_FLAG (flags, ZEBRA_FLAG_IBGP)) + distance = 200; + } + + /* 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, rib) + { + if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) + continue; + + if (rib->type != type) + continue; + if (rib->type != ZEBRA_ROUTE_CONNECT) + { + same = rib; + break; + } + /* Duplicate connected route comes in. */ + else if ((nexthop = rib->nexthop) && + nexthop->type == NEXTHOP_TYPE_IFINDEX && + nexthop->ifindex == ifindex && + !CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) + { + rib->refcnt++; + return 0 ; + } + } + + /* Allocate new rib structure. */ + rib = XCALLOC (MTYPE_RIB, sizeof (struct rib)); + rib->type = type; + rib->distance = distance; + rib->flags = flags; + rib->metric = metric; + rib->mtu = mtu; + rib->vrf_id = vrf_id; + rib->table = table_id; + rib->nexthop_num = 0; + rib->uptime = time (NULL); + + /* Nexthop settings. */ + if (gate) + { + if (ifindex) + rib_nexthop_ipv4_ifindex_add (rib, gate, src, ifindex); + else + rib_nexthop_ipv4_add (rib, gate, src); + } + else + 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) + for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + + /* Link new rib to node.*/ + if (IS_ZEBRA_DEBUG_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__, (void *)rn, (void *)rib); + rib_delnode (rn, same); + } + + route_unlock_node (rn); + return 0; +} + +/* This function dumps the contents of a given RIB entry into + * standard debug log. Calling function name and IP prefix in + * question are passed as 1st and 2nd arguments. + */ + +void _rib_dump (const char * func, + union prefix46constptr pp, const struct rib * rib) +{ + const struct prefix *p = pp.p; + char straddr[PREFIX_STRLEN]; + struct nexthop *nexthop, *tnexthop; + int recursing; + + 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", + func, + rib->refcnt, + (unsigned long) rib->uptime, + rib->type, + rib->table + ); + zlog_debug + ( + "%s: metric == %u, distance == %u, flags == %u, status == %u", + func, + rib->metric, + rib->distance, + rib->flags, + rib->status + ); + zlog_debug + ( + "%s: nexthop_num == %u, nexthop_active_num == %u, nexthop_fib_num == %u", + func, + rib->nexthop_num, + rib->nexthop_active_num, + rib->nexthop_fib_num + ); + + 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); +} + +/* This is an exported helper to rtm_read() to dump the strange + * RIB entry found by rib_lookup_ipv4_route() + */ + +void rib_lookup_and_dump (struct prefix_ipv4 * p) +{ + struct route_table *table; + struct route_node *rn; + struct rib *rib; + char prefix_buf[INET_ADDRSTRLEN]; + + /* Lookup table. */ + table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, VRF_DEFAULT); + if (! table) + { + zlog_err ("%s: zebra_vrf_table() returned NULL", __func__); + return; + } + + /* 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", __func__, + prefix2str((struct prefix*) p, prefix_buf, sizeof(prefix_buf))); + return; + } + + /* Unlock node. */ + route_unlock_node (rn); + + /* let's go */ + RNODE_FOREACH_RIB (rn, rib) + { + zlog_debug + ( + "%s: rn %p, rib %p: %s, %s", + __func__, + (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 (p, rib); + } +} + +int +rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib, safi_t safi) +{ + struct route_table *table; + struct route_node *rn; + struct rib *same; + struct nexthop *nexthop; + int ret = 0; + + /* Lookup table. */ + table = zebra_vrf_table (AFI_IP, safi, rib->vrf_id); + if (! table) + return 0; + + /* Make it sure prefixlen is applied to the prefix. */ + apply_mask_ipv4 (p); + + /* Set default distance by route type. */ + if (rib->distance == 0) + { + rib->distance = route_info[rib->type].distance; + + /* iBGP distance is 200. */ + if (rib->type == ZEBRA_ROUTE_BGP + && CHECK_FLAG (rib->flags, ZEBRA_FLAG_IBGP)) + rib->distance = 200; + } + + /* 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 && same->table == rib->table + && 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; + 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); + ret = -1; + } + + route_unlock_node (rn); + return ret; +} + +/* XXX factor with rib_delete_ipv6 */ +int +rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p, + 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, *tnexthop; + int recursing; + char buf1[PREFIX_STRLEN]; + char buf2[INET_ADDRSTRLEN]; + + /* Lookup table. */ + table = zebra_vrf_table (AFI_IP, safi, vrf_id); + if (! table) + return 0; + + /* Apply mask. */ + apply_mask_ipv4 (p); + + 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); + 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_INET, gate, buf2, INET_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) + { + if (nexthop->ifindex != ifindex) + continue; + if (rib->refcnt) + { + rib->refcnt--; + route_unlock_node (rn); + route_unlock_node (rn); + return 0; + } + same = rib; + break; + } + /* Make sure that the route found has the same gateway. */ + else + { + 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 && + 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 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 vrf %u ifindex %d type %d doesn't exist in rib", + prefix2str (p, buf1, sizeof(buf1)), vrf_id, + ifindex, + type); + } + route_unlock_node (rn); + return ZEBRA_ERR_RTNOEXIST; + } + } + + if (same) + rib_delnode (rn, same); + + route_unlock_node (rn); + return 0; +} + +/* Install static route into rib. */ +static void +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 = zebra_vrf_table (afi, safi, si->vrf_id); + if (! table) + return; + + /* Lookup existing route */ + rn = route_node_get (table, p); + RNODE_FOREACH_RIB (rn, rib) + { + if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) + continue; + + if (rib->type == ZEBRA_ROUTE_STATIC && rib->distance == si->distance) + break; + } + + 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: + 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); + } + 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->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: + 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) */ + rib->flags = si->flags; + + /* Link this rib to the tree. */ + rib_addnode (rn, rib); + } +} + +static int +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->addr.ipv4)) + return 1; + if (nexthop->type == NEXTHOP_TYPE_IFNAME + && si->type == STATIC_IPV4_IFNAME + && 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_route (afi_t afi, safi_t safi, struct prefix *p, struct static_route *si) +{ + struct route_node *rn; + struct rib *rib; + struct nexthop *nexthop; + struct route_table *table; + + /* Lookup table. */ + table = zebra_vrf_table (afi, safi, si->vrf_id); + if (! table) + return; + + /* Lookup existing route with type and distance. */ + rn = route_node_lookup (table, p); + if (! rn) + return; + + RNODE_FOREACH_RIB (rn, rib) + { + if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) + continue; + + if (rib->type == ZEBRA_ROUTE_STATIC && rib->distance == si->distance && + rib->tag == si->tag) + break; + } + + if (! rib) + { + route_unlock_node (rn); + return; + } + + /* Lookup nexthop. */ + for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + if (static_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); + rib_nexthop_delete (rib, nexthop); + nexthop_free (nexthop); + rib_queue_add (&zebrad, rn); + } + /* Unlock node. */ + route_unlock_node (rn); +} + +int +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_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]; + + if (! stable) + return -1; + + /* Lookup static route prefix. */ + rn = route_node_get (stable, p); + + /* Make flags. */ + if (gate) + type = STATIC_IPV4_GATEWAY; + else if (ifname) + type = STATIC_IPV4_IFNAME; + else + type = STATIC_IPV4_BLACKHOLE; + + /* Do nothing if there is a same static route. */ + for (si = rn->info; si; si = si->next) + { + if (type == si->type + && (! gate || IPV4_ADDR_SAME (gate, &si->addr.ipv4)) + && (! ifname || strcmp (ifname, si->ifname) == 0)) + { + if (distance == si->distance && + tag == si->tag) + { + route_unlock_node (rn); + return 0; + } + else + update = si; + } + } + + /* Distance or tag changed. */ + if (update) + static_delete_ipv4_safi (safi, p, gate, ifname, update->tag, update->distance, vrf_id); + + /* Make new static route structure. */ + 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->addr.ipv4 = *gate; + if (ifname) + si->ifname = XSTRDUP (MTYPE_TMP, ifname); + + /* Add new static route information to the tree with sort by + distance value and gateway address. */ + for (pp = NULL, cp = rn->info; cp; pp = cp, cp = cp->next) + { + if (si->distance < cp->distance) + break; + if (si->distance > cp->distance) + continue; + if (si->type == STATIC_IPV4_GATEWAY && cp->type == STATIC_IPV4_GATEWAY) + { + if (ntohl (si->addr.ipv4.s_addr) < ntohl (cp->addr.ipv4.s_addr)) + break; + if (ntohl (si->addr.ipv4.s_addr) > ntohl (cp->addr.ipv4.s_addr)) + continue; + } + } + + /* Make linked list. */ + if (pp) + pp->next = si; + else + rn->info = si; + if (cp) + cp->prev = si; + si->prev = pp; + si->next = cp; + + /* Install into rib. */ + static_install_route (AFI_IP, safi, p, si); + + return 1; +} + +int +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_route *si; + struct route_table *stable; + + /* Lookup table. */ + stable = zebra_vrf_static_table (AFI_IP, safi, vrf_id); + if (! stable) + return -1; + + /* Lookup static route prefix. */ + rn = route_node_lookup (stable, p); + if (! rn) + return 0; + + /* Make flags. */ + if (gate) + type = STATIC_IPV4_GATEWAY; + else if (ifname) + type = STATIC_IPV4_IFNAME; + else + type = STATIC_IPV4_BLACKHOLE; + + /* 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->addr.ipv4)) + && (! ifname || strcmp (ifname, si->ifname) == 0) + && (! tag || (tag == si->tag))) + break; + + /* Can't find static route. */ + if (! si) + { + route_unlock_node (rn); + return 0; + } + + /* Install into rib. */ + static_uninstall_route (AFI_IP, safi, p, si); + + /* Unlink static route from linked list. */ + if (si->prev) + si->prev->next = si->next; + else + rn->info = si->next; + if (si->next) + si->next->prev = si->prev; + route_unlock_node (rn); + + /* Free static route configuration. */ + if (ifname) + XFREE (0, si->ifname); + XFREE (MTYPE_STATIC_ROUTE, si); + + route_unlock_node (rn); + + return 1; +} + +int +rib_add_ipv6 (int type, int flags, struct prefix_ipv6 *p, + 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; + struct route_table *table; + struct route_node *rn; + struct nexthop *nexthop; + + /* Lookup table. */ + table = zebra_vrf_table (AFI_IP6, safi, vrf_id); + if (! table) + return 0; + + /* Make sure mask is applied. */ + apply_mask_ipv6 (p); + + /* Set default distance by route type. */ + if (!distance) + distance = route_info[type].distance; + + if (type == ZEBRA_ROUTE_BGP && CHECK_FLAG (flags, ZEBRA_FLAG_IBGP)) + distance = 200; + + /* 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, rib) + { + if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) + continue; + + if (rib->type != type) + continue; + if (rib->type != ZEBRA_ROUTE_CONNECT) + { + same = rib; + break; + } + else if ((nexthop = rib->nexthop) && + nexthop->type == NEXTHOP_TYPE_IFINDEX && + nexthop->ifindex == ifindex) + { + rib->refcnt++; + return 0; + } + } + + /* Allocate new rib structure. */ + rib = XCALLOC (MTYPE_RIB, sizeof (struct rib)); + + rib->type = type; + rib->distance = distance; + rib->flags = flags; + rib->metric = metric; + rib->mtu = mtu; + rib->vrf_id = vrf_id; + rib->table = table_id; + rib->nexthop_num = 0; + rib->uptime = time (NULL); + + /* Nexthop settings. */ + if (gate) + { + if (ifindex) + rib_nexthop_ipv6_ifindex_add (rib, gate, ifindex); + else + rib_nexthop_ipv6_add (rib, gate); + } + else + 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) + for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + + /* 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; +} + +int +rib_add_ipv6_multipath (struct prefix_ipv6 *p, struct rib *rib, safi_t safi) +{ + struct route_table *table; + struct route_node *rn; + struct rib *same = NULL; + struct nexthop *nexthop; + int ret = 0; + + if (!rib) + return 0; /* why are we getting called with NULL rib */ + + /* Lookup table. */ + table = zebra_vrf_table (AFI_IP6, safi, rib->vrf_id); + + if (! table) + return 0; + + /* Make sure mask is applied. */ + apply_mask_ipv6 (p); + + /* Set default distance by route type. */ + if (rib->distance == 0) + { + rib->distance = route_info[rib->type].distance; + + /* iBGP distance is 200. */ + if (rib->type == ZEBRA_ROUTE_BGP + && CHECK_FLAG (rib->flags, ZEBRA_FLAG_IBGP)) + rib->distance = 200; + } + + /* 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) + { + if (nexthop->ifindex != ifindex) + continue; + if (rib->refcnt) + { + rib->refcnt--; + route_unlock_node (rn); + route_unlock_node (rn); + return 0; + } + same = rib; + break; + } + /* Make sure that the route found has the same gateway. */ + 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 && + 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 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 vrf %u ifindex %d type %d doesn't exist in rib", + prefix2str (p, buf1, sizeof(buf1)), vrf_id, + ifindex, + type); + } + route_unlock_node (rn); + return ZEBRA_ERR_RTNOEXIST; + } + } + + if (same) + rib_delnode (rn, same); + + route_unlock_node (rn); + return 0; +} + +/* 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, route_tag_t tag, + u_char distance, vrf_id_t vrf_id) +{ + struct route_node *rn; + 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]; + + if (! stable) + return -1; + + if (!gate && + (type == STATIC_IPV6_GATEWAY || type == STATIC_IPV6_GATEWAY_IFNAME)) + return -1; + + if (!ifname && + (type == STATIC_IPV6_GATEWAY_IFNAME || type == STATIC_IPV6_IFNAME)) + return -1; + + /* Lookup static route prefix. */ + rn = route_node_get (stable, p); + + /* Do nothing if there is a same static route. */ + for (si = rn->info; si; si = si->next) + { + if (type == si->type + && tag == si->tag + && (! gate || IPV6_ADDR_SAME (gate, &si->addr.ipv6)) + && (! ifname || strcmp (ifname, si->ifname) == 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_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->addr.ipv6 = *gate; + break; + case STATIC_IPV6_IFNAME: + si->ifname = XSTRDUP (MTYPE_TMP, ifname); + break; + case STATIC_IPV6_GATEWAY_IFNAME: + si->addr.ipv6 = *gate; + si->ifname = XSTRDUP (MTYPE_TMP, ifname); + break; + } + + /* Add new static route information to the tree with sort by + distance value and gateway address. */ + for (pp = NULL, cp = rn->info; cp; pp = cp, cp = cp->next) + { + if (si->distance < cp->distance) + break; + if (si->distance > cp->distance) + continue; + } + + /* Make linked list. */ + if (pp) + pp->next = si; + else + rn->info = si; + if (cp) + cp->prev = si; + si->prev = pp; + si->next = cp; + + /* Install into rib. */ + static_install_route (AFI_IP6, SAFI_UNICAST, p, si); + + return 1; +} + +/* Delete static route from static route configuration. */ +int +static_delete_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate, + const char *ifname, route_tag_t tag, u_char distance, + vrf_id_t vrf_id) +{ + struct route_node *rn; + struct static_route *si; + struct route_table *stable; + + /* Lookup table. */ + stable = zebra_vrf_static_table (AFI_IP6, SAFI_UNICAST, vrf_id); + if (! stable) + return -1; + + /* Lookup static route prefix. */ + rn = route_node_lookup (stable, p); + if (! rn) + return 0; + + /* Find same static route is the tree */ + for (si = rn->info; si; si = si->next) + if (distance == si->distance + && type == si->type + && (! gate || IPV6_ADDR_SAME (gate, &si->addr.ipv6)) + && (! ifname || strcmp (ifname, si->ifname) == 0) + && (! tag || (tag == si->tag))) + break; + + /* Can't find static route. */ + if (! si) + { + route_unlock_node (rn); + return 0; + } + + /* Install into rib. */ + static_uninstall_route (AFI_IP6, SAFI_UNICAST, p, si); + + /* Unlink static route from linked list. */ + if (si->prev) + si->prev->next = si->next; + else + rn->info = si->next; + if (si->next) + si->next->prev = si->prev; + + /* Free static route configuration. */ + if (ifname) + XFREE (0, si->ifname); + XFREE (MTYPE_STATIC_ROUTE, si); + + return 1; +} + +/* RIB update function. */ +void +rib_update (vrf_id_t vrf_id) +{ + struct route_node *rn; + struct route_table *table; + + table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, vrf_id); + if (table) + for (rn = route_top (table); rn; rn = route_next (rn)) + if (rnode_to_ribs (rn)) + rib_queue_add (&zebrad, rn); + + table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, vrf_id); + if (table) + for (rn = route_top (table); rn; rn = route_next (rn)) + 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) +{ + struct route_node *rn; + struct rib *rib; + struct rib *next; + + if (table) + for (rn = route_top (table); rn; rn = route_next (rn)) + RNODE_FOREACH_RIB_SAFE (rn, rib, next) + { + if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) + continue; + + if (rib->table != zebrad.rtm_table_default && + rib->table != RT_TABLE_MAIN) + rib_delnode (rn, rib); + } +} + +/* Delete all routes from non main table. */ +void +rib_weed_tables (void) +{ + 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) +{ + struct route_node *rn; + struct rib *rib; + struct rib *next; + int ret = 0; + + if (table) + for (rn = route_top (table); rn; rn = route_next (rn)) + RNODE_FOREACH_RIB_SAFE (rn, 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_update_kernel (rn, rib, NULL); + if (! ret) + rib_delnode (rn, rib); + } + } +} +#endif + +/* Sweep all RIB tables. */ +void +rib_sweep_route (void) +{ + 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'. */ +static unsigned long +rib_score_proto_table (u_char proto, struct route_table *table) +{ + struct route_node *rn; + struct rib *rib; + struct rib *next; + unsigned long n = 0; + + if (table) + for (rn = route_top (table); rn; rn = route_next (rn)) + RNODE_FOREACH_RIB_SAFE (rn, rib, next) + { + if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) + continue; + if (rib->type == proto) + { + rib_delnode (rn, rib); + n++; + } + } + + return n; +} + +/* Remove specific by protocol routes. */ +unsigned long +rib_score_proto (u_char proto) +{ + 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. */ +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)) + RNODE_FOREACH_RIB (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); + } +} + +/* Close all RIB tables. */ +void +rib_close (void) +{ + 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_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 0000000..859b6d7 --- /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 0000000..574c95f --- /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 0000000..664667d --- /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 new file mode 100644 index 0000000..5a6a96b --- /dev/null +++ b/zebra/zebra_routemap.c @@ -0,0 +1,715 @@ +/* zebra routemap. + * Copyright (C) 2006 IBM Corporation + * + * 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 "memory.h" +#include "prefix.h" +#include "rib.h" +#include "routemap.h" +#include "command.h" +#include "filter.h" +#include "plist.h" +#include "vrf.h" +#include "nexthop.h" + +#include "zebra/zserv.h" + +/* Add zebra route map rule */ +static int +zebra_route_match_add(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg) +{ + int ret; + + ret = route_map_add_match (index, command, arg); + if (ret) + { + switch (ret) + { + case RMAP_RULE_MISSING: + vty_out (vty, "%% Zebra Can't find rule.%s", VTY_NEWLINE); + return CMD_WARNING; + case RMAP_COMPILE_ERROR: + vty_out (vty, "%% Zebra Argument is malformed.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + return CMD_SUCCESS; +} + +/* Delete zebra route map rule. */ +static int +zebra_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, "%% Zebra Can't find rule.%s", VTY_NEWLINE); + return CMD_WARNING; + case RMAP_COMPILE_ERROR: + vty_out (vty, "%% Zebra Argument is malformed.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + return CMD_SUCCESS; +} + +/* Add zebra route map rule. */ +static int +zebra_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, "%% Zebra Can't find rule.%s", VTY_NEWLINE); + return CMD_WARNING; + case RMAP_COMPILE_ERROR: + vty_out (vty, "%% Zebra Argument is malformed.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + return CMD_SUCCESS; +} + +/* Delete zebra route map rule. */ +static int +zebra_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, "%% Zebra Can't find rule.%s", VTY_NEWLINE); + return CMD_WARNING; + case RMAP_COMPILE_ERROR: + 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; + ifindex_t ifindex; + + if (type == RMAP_ZEBRA) + { + if (strcasecmp(ifname, "any") == 0) + return RMAP_MATCH; + nh_vrf = object; + if (!nh_vrf) + return RMAP_NOMATCH; + ifindex = ifname2ifindex_vrf (ifname, nh_vrf->vrf_id); + if (ifindex == 0) + return RMAP_NOMATCH; + nexthop = nh_vrf->nexthop; + if (!nexthop) + return RMAP_NOMATCH; + if (nexthop->ifindex == ifindex) + return RMAP_MATCH; + } + return RMAP_NOMATCH; +} + +/* Route map `match interface' match statement. `arg' is IFNAME value */ +static void * +route_match_interface_compile (const char *arg) +{ + return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); +} + +/* Free route map's compiled `match interface' value. */ +static void +route_match_interface_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for interface matching */ +struct route_map_rule_cmd route_match_interface_cmd = +{ + "interface", + route_match_interface, + route_match_interface_compile, + route_match_interface_free +}; + +DEFUN (match_interface, + match_interface_cmd, + "match interface WORD", + MATCH_STR + "match first hop interface of route\n" + "Interface name\n") +{ + return zebra_route_match_add (vty, vty->index, "interface", argv[0]); +} + +DEFUN (no_match_interface, + no_match_interface_cmd, + "no match interface", + NO_STR + MATCH_STR + "Match first hop interface of route\n") +{ + if (argc == 0) + return zebra_route_match_delete (vty, vty->index, "interface", NULL); + + return zebra_route_match_delete (vty, vty->index, "interface", argv[0]); +} + +ALIAS (no_match_interface, + no_match_interface_val_cmd, + "no match interface WORD", + NO_STR + MATCH_STR + "Match first hop interface of route\n" + "Interface name\n") + +DEFUN (match_ip_next_hop, + match_ip_next_hop_cmd, + "match ip next-hop (<1-199>|<1300-2699>|WORD)", + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "IP access-list number\n" + "IP access-list number (expanded range)\n" + "IP Access-list name\n") +{ + return zebra_route_match_add (vty, vty->index, "ip next-hop", argv[0]); +} + +DEFUN (no_match_ip_next_hop, + no_match_ip_next_hop_cmd, + "no match ip next-hop", + NO_STR + MATCH_STR + IP_STR + "Match next-hop address of route\n") +{ + if (argc == 0) + return zebra_route_match_delete (vty, vty->index, "ip next-hop", NULL); + + return zebra_route_match_delete (vty, vty->index, "ip next-hop", argv[0]); +} + +ALIAS (no_match_ip_next_hop, + no_match_ip_next_hop_val_cmd, + "no match ip next-hop (<1-199>|<1300-2699>|WORD)", + NO_STR + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "IP access-list number\n" + "IP access-list number (expanded range)\n" + "IP Access-list name\n") + +DEFUN (match_ip_next_hop_prefix_list, + match_ip_next_hop_prefix_list_cmd, + "match ip next-hop prefix-list WORD", + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") +{ + return zebra_route_match_add (vty, vty->index, "ip next-hop prefix-list", argv[0]); +} + +DEFUN (no_match_ip_next_hop_prefix_list, + no_match_ip_next_hop_prefix_list_cmd, + "no match ip next-hop prefix-list", + NO_STR + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "Match entries of prefix-lists\n") +{ + if (argc == 0) + return zebra_route_match_delete (vty, vty->index, "ip next-hop prefix-list", NULL); + + return zebra_route_match_delete (vty, vty->index, "ip next-hop prefix-list", argv[0]); +} + +ALIAS (no_match_ip_next_hop_prefix_list, + no_match_ip_next_hop_prefix_list_val_cmd, + "no match ip next-hop prefix-list WORD", + NO_STR + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") + +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 zebra_route_match_add (vty, vty->index, "ip address", argv[0]); +} + +DEFUN (no_match_ip_address, + no_match_ip_address_cmd, + "no match ip address", + NO_STR + MATCH_STR + IP_STR + "Match address of route\n") +{ + if (argc == 0) + return zebra_route_match_delete (vty, vty->index, "ip address", NULL); + + return zebra_route_match_delete (vty, vty->index, "ip address", argv[0]); +} + +ALIAS (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") + +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 zebra_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 zebra_route_match_delete (vty, vty->index, "ip address prefix-list", NULL); + + return zebra_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") + +/* set functions */ + +DEFUN (set_src, + set_src_cmd, + "set src A.B.C.D", + SET_STR + "src address for route\n" + "src address\n") +{ + struct in_addr src; + struct interface *pif = NULL; + vrf_iter_t iter; + + if (inet_pton(AF_INET, argv[0], &src) <= 0) + { + 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]); +} + +DEFUN (no_set_src, + no_set_src_cmd, + "no set src", + NO_STR + SET_STR + "Source address for route\n") +{ + if (argc == 0) + return zebra_route_set_delete (vty, vty->index, "src", NULL); + + return zebra_route_set_delete (vty, vty->index, "src", argv[0]); +} + +ALIAS (no_set_src, + no_set_src_val_cmd, + "no set src (A.B.C.D)", + NO_STR + SET_STR + "src address for route\n" + "src address\n") + +/*XXXXXXXXXXXXXXXXXXXXXXXXXXXX*/ + +/* `match ip next-hop IP_ACCESS_LIST' */ + +/* Match function return 1 if match is success else return zero. */ +static route_map_result_t +route_match_ip_next_hop (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct access_list *alist; + struct nexthop *nexthop; + struct nexthop_vrfid *nh_vrf; + struct prefix_ipv4 p; + + if (type == RMAP_ZEBRA) + { + 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: + case NEXTHOP_TYPE_IPV4: + p.family = AF_INET; + p.prefix = nexthop->gate.ipv4; + p.prefixlen = IPV4_MAX_BITLEN; + break; + default: + return RMAP_NOMATCH; + } + alist = access_list_lookup (AFI_IP, (char *) rule); + if (alist == NULL) + return RMAP_NOMATCH; + + return (access_list_apply (alist, &p) == FILTER_DENY ? + RMAP_NOMATCH : RMAP_MATCH); + } + return RMAP_NOMATCH; +} + +/* Route map `ip next-hop' match statement. `arg' should be + access-list name. */ +static void * +route_match_ip_next_hop_compile (const char *arg) +{ + return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); +} + +/* Free route map's compiled `. */ +static void +route_match_ip_next_hop_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for ip next-hop matching. */ +static struct route_map_rule_cmd route_match_ip_next_hop_cmd = +{ + "ip next-hop", + route_match_ip_next_hop, + 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 +route_match_ip_next_hop_prefix_list (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct prefix_list *plist; + struct nexthop *nexthop; + struct nexthop_vrfid *nh_vrf; + struct prefix_ipv4 p; + + if (type == RMAP_ZEBRA) + { + 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: + case NEXTHOP_TYPE_IPV4: + p.family = AF_INET; + p.prefix = nexthop->gate.ipv4; + p.prefixlen = IPV4_MAX_BITLEN; + break; + default: + return RMAP_NOMATCH; + } + plist = prefix_list_lookup (AFI_IP, (char *) rule); + if (plist == NULL) + return RMAP_NOMATCH; + + return (prefix_list_apply (plist, &p) == PREFIX_DENY ? + RMAP_NOMATCH : RMAP_MATCH); + } + return RMAP_NOMATCH; +} + +static void * +route_match_ip_next_hop_prefix_list_compile (const char *arg) +{ + return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); +} + +static void +route_match_ip_next_hop_prefix_list_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +static struct route_map_rule_cmd route_match_ip_next_hop_prefix_list_cmd = +{ + "ip next-hop prefix-list", + route_match_ip_next_hop_prefix_list, + 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 + zero. */ +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; + + if (type == RMAP_ZEBRA) + { + alist = access_list_lookup (AFI_IP, (char *) rule); + if (alist == NULL) + return RMAP_NOMATCH; + + return (access_list_apply (alist, prefix) == FILTER_DENY ? + RMAP_NOMATCH : RMAP_MATCH); + } + return RMAP_NOMATCH; +} + +/* Route map `ip address' match statement. `arg' should be + access-list name. */ +static void * +route_match_ip_address_compile (const char *arg) +{ + return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); +} + +/* Free route map's compiled `ip address' value. */ +static void +route_match_ip_address_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for ip address matching. */ +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 +}; + +/* `match ip address prefix-list PREFIX_LIST' */ + +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_ZEBRA) + { + plist = prefix_list_lookup (AFI_IP, (char *) rule); + if (plist == NULL) + return RMAP_NOMATCH; + + return (prefix_list_apply (plist, prefix) == PREFIX_DENY ? + RMAP_NOMATCH : 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); +} + +static 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 +}; + + +/* `set src A.B.C.D' */ + +/* Set src. */ +static route_map_result_t +route_set_src (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + if (type == RMAP_ZEBRA) + { + struct nexthop_vrfid *nh_vrf; + + nh_vrf = object; + nh_vrf->nexthop->src = *(union g_addr *)rule; + } + return RMAP_OKAY; +} + +/* set src compilation. */ +static void * +route_set_src_compile (const char *arg) +{ + union g_addr src, *psrc; + + if (inet_pton(AF_INET, arg, &src.ipv4) != 1 +#ifdef HAVE_IPV6 + && inet_pton(AF_INET6, arg, &src.ipv6) != 1 +#endif /* HAVE_IPV6 */ + ) + return NULL; + + psrc = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (union g_addr)); + *psrc = src; + + return psrc; +} + +/* Free route map's compiled `set src' value. */ +static void +route_set_src_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Set src rule structure. */ +static struct route_map_rule_cmd route_set_src_cmd = +{ + "src", + route_set_src, + route_set_src_compile, + route_set_src_free, +}; + +void +zebra_route_map_init () +{ + route_map_init (); + route_map_init_vty (); + + route_map_install_match (&route_match_interface_cmd); + route_map_install_match (&route_match_ip_next_hop_cmd); + route_map_install_match (&route_match_ip_next_hop_prefix_list_cmd); + route_map_install_match (&route_match_ip_address_cmd); + route_map_install_match (&route_match_ip_address_prefix_list_cmd); +/* */ + route_map_install_set (&route_set_src_cmd); +/* */ + 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_ip_next_hop_cmd); + install_element (RMAP_NODE, &no_match_ip_next_hop_cmd); + install_element (RMAP_NODE, &no_match_ip_next_hop_val_cmd); + install_element (RMAP_NODE, &match_ip_next_hop_prefix_list_cmd); + install_element (RMAP_NODE, &no_match_ip_next_hop_prefix_list_cmd); + install_element (RMAP_NODE, &no_match_ip_next_hop_prefix_list_val_cmd); + install_element (RMAP_NODE, &match_ip_address_cmd); + install_element (RMAP_NODE, &no_match_ip_address_cmd); + install_element (RMAP_NODE, &no_match_ip_address_val_cmd); + install_element (RMAP_NODE, &match_ip_address_prefix_list_cmd); + install_element (RMAP_NODE, &no_match_ip_address_prefix_list_cmd); + install_element (RMAP_NODE, &no_match_ip_address_prefix_list_val_cmd); +/* */ + install_element (RMAP_NODE, &set_src_cmd); + install_element (RMAP_NODE, &no_set_src_cmd); +} diff --git a/zebra/zebra_snmp.c b/zebra/zebra_snmp.c new file mode 100644 index 0000000..3d005aa --- /dev/null +++ b/zebra/zebra_snmp.c @@ -0,0 +1,582 @@ +/* FIB SNMP. + * 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. + */ + +/* + * Currently SNMP is only running properly for MIBs in the default VRF. + */ + +#include + +#ifdef HAVE_SNMP +#include +#include + +#include "if.h" +#include "log.h" +#include "prefix.h" +#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 */ +#define IPFORWARDDEST 1 +#define IPFORWARDMASK 2 +#define IPFORWARDPOLICY 3 +#define IPFORWARDNEXTHOP 4 +#define IPFORWARDIFINDEX 5 +#define IPFORWARDTYPE 6 +#define IPFORWARDPROTO 7 +#define IPFORWARDAGE 8 +#define IPFORWARDINFO 9 +#define IPFORWARDNEXTHOPAS 10 +#define IPFORWARDMETRIC1 11 +#define IPFORWARDMETRIC2 12 +#define IPFORWARDMETRIC3 13 +#define IPFORWARDMETRIC4 14 +#define IPFORWARDMETRIC5 15 + +/* ipCidrRouteTable */ +#define IPCIDRROUTEDEST 1 +#define IPCIDRROUTEMASK 2 +#define IPCIDRROUTETOS 3 +#define IPCIDRROUTENEXTHOP 4 +#define IPCIDRROUTEIFINDEX 5 +#define IPCIDRROUTETYPE 6 +#define IPCIDRROUTEPROTO 7 +#define IPCIDRROUTEAGE 8 +#define IPCIDRROUTEINFO 9 +#define IPCIDRROUTENEXTHOPAS 10 +#define IPCIDRROUTEMETRIC1 11 +#define IPCIDRROUTEMETRIC2 12 +#define IPCIDRROUTEMETRIC3 13 +#define IPCIDRROUTEMETRIC4 14 +#define IPCIDRROUTEMETRIC5 15 +#define IPCIDRROUTESTATUS 16 + +#define INTEGER32 ASN_INTEGER +#define GAUGE32 ASN_GAUGE +#define ENUMERATION ASN_INTEGER +#define ROWSTATUS ASN_INTEGER +#define IPADDRESS ASN_IPADDRESS +#define OBJECTIDENTIFIER ASN_OBJECT_ID + +extern struct zebra_t zebrad; + +oid ipfw_oid [] = { IPFWMIB }; + +/* Hook functions. */ +static u_char * ipFwNumber (struct variable *, oid [], size_t *, + int, size_t *, WriteMethod **); +static u_char * ipFwTable (struct variable *, oid [], size_t *, + int, size_t *, WriteMethod **); +static u_char * ipCidrNumber (struct variable *, oid [], size_t *, + int, size_t *, WriteMethod **); +static u_char * ipCidrTable (struct variable *, oid [], size_t *, + int, size_t *, WriteMethod **); + +struct variable zebra_variables[] = + { + {0, GAUGE32, RONLY, ipFwNumber, 1, {1}}, + {IPFORWARDDEST, IPADDRESS, RONLY, ipFwTable, 3, {2, 1, 1}}, + {IPFORWARDMASK, IPADDRESS, RONLY, ipFwTable, 3, {2, 1, 2}}, + {IPFORWARDPOLICY, INTEGER32, RONLY, ipFwTable, 3, {2, 1, 3}}, + {IPFORWARDNEXTHOP, IPADDRESS, RONLY, ipFwTable, 3, {2, 1, 4}}, + {IPFORWARDIFINDEX, INTEGER32, RONLY, ipFwTable, 3, {2, 1, 5}}, + {IPFORWARDTYPE, ENUMERATION, RONLY, ipFwTable, 3, {2, 1, 6}}, + {IPFORWARDPROTO, ENUMERATION, RONLY, ipFwTable, 3, {2, 1, 7}}, + {IPFORWARDAGE, INTEGER32, RONLY, ipFwTable, 3, {2, 1, 8}}, + {IPFORWARDINFO, OBJECTIDENTIFIER, RONLY, ipFwTable, 3, {2, 1, 9}}, + {IPFORWARDNEXTHOPAS, INTEGER32, RONLY, ipFwTable, 3, {2, 1, 10}}, + {IPFORWARDMETRIC1, INTEGER32, RONLY, ipFwTable, 3, {2, 1, 11}}, + {IPFORWARDMETRIC2, INTEGER32, RONLY, ipFwTable, 3, {2, 1, 12}}, + {IPFORWARDMETRIC3, INTEGER32, RONLY, ipFwTable, 3, {2, 1, 13}}, + {IPFORWARDMETRIC4, INTEGER32, RONLY, ipFwTable, 3, {2, 1, 14}}, + {IPFORWARDMETRIC5, INTEGER32, RONLY, ipFwTable, 3, {2, 1, 15}}, + {0, GAUGE32, RONLY, ipCidrNumber, 1, {3}}, + {IPCIDRROUTEDEST, IPADDRESS, RONLY, ipCidrTable, 3, {4, 1, 1}}, + {IPCIDRROUTEMASK, IPADDRESS, RONLY, ipCidrTable, 3, {4, 1, 2}}, + {IPCIDRROUTETOS, INTEGER32, RONLY, ipCidrTable, 3, {4, 1, 3}}, + {IPCIDRROUTENEXTHOP, IPADDRESS, RONLY, ipCidrTable, 3, {4, 1, 4}}, + {IPCIDRROUTEIFINDEX, INTEGER32, RONLY, ipCidrTable, 3, {4, 1, 5}}, + {IPCIDRROUTETYPE, ENUMERATION, RONLY, ipCidrTable, 3, {4, 1, 6}}, + {IPCIDRROUTEPROTO, ENUMERATION, RONLY, ipCidrTable, 3, {4, 1, 7}}, + {IPCIDRROUTEAGE, INTEGER32, RONLY, ipCidrTable, 3, {4, 1, 8}}, + {IPCIDRROUTEINFO, OBJECTIDENTIFIER, RONLY, ipCidrTable, 3, {4, 1, 9}}, + {IPCIDRROUTENEXTHOPAS, INTEGER32, RONLY, ipCidrTable, 3, {4, 1, 10}}, + {IPCIDRROUTEMETRIC1, INTEGER32, RONLY, ipCidrTable, 3, {4, 1, 11}}, + {IPCIDRROUTEMETRIC2, INTEGER32, RONLY, ipCidrTable, 3, {4, 1, 12}}, + {IPCIDRROUTEMETRIC3, INTEGER32, RONLY, ipCidrTable, 3, {4, 1, 13}}, + {IPCIDRROUTEMETRIC4, INTEGER32, RONLY, ipCidrTable, 3, {4, 1, 14}}, + {IPCIDRROUTEMETRIC5, INTEGER32, RONLY, ipCidrTable, 3, {4, 1, 15}}, + {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) +{ + static int result; + struct route_table *table; + struct route_node *rn; + struct rib *rib; + + if (smux_header_generic(v, objid, objid_len, exact, val_len, write_method) == MATCH_FAILED) + return NULL; + + 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)) + RNODE_FOREACH_RIB (rn, rib) + result++; + + return (u_char *)&result; +} + +static u_char * +ipCidrNumber (struct variable *v, oid objid[], size_t *objid_len, + int exact, size_t *val_len, WriteMethod **write_method) +{ + static int result; + struct route_table *table; + struct route_node *rn; + struct rib *rib; + + if (smux_header_generic(v, objid, objid_len, exact, val_len, write_method) == MATCH_FAILED) + return NULL; + + 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)) + RNODE_FOREACH_RIB (rn, rib) + result++; + + return (u_char *)&result; +} + +static int +in_addr_cmp(u_char *p1, u_char *p2) +{ + int i; + + for (i=0; i<4; i++) + { + if (*p1 < *p2) + return -1; + if (*p1 > *p2) + return 1; + p1++; p2++; + } + return 0; +} + +static int +in_addr_add(u_char *p, int num) +{ + int i, ip0; + + ip0 = *p; + p += 4; + for (i = 3; 0 <= i; i--) { + p--; + if (*p + num > 255) { + *p += num; + num = 1; + } else { + *p += num; + return 1; + } + } + if (ip0 > *p) { + /* ip + num > 0xffffffff */ + return 0; + } + + return 1; +} + +static int +proto_trans(int type) +{ + switch (type) + { + case ZEBRA_ROUTE_SYSTEM: + return 1; /* other */ + case ZEBRA_ROUTE_KERNEL: + return 1; /* other */ + case ZEBRA_ROUTE_CONNECT: + return 2; /* local interface */ + case ZEBRA_ROUTE_STATIC: + return 3; /* static route */ + case ZEBRA_ROUTE_RIP: + return 8; /* rip */ + case ZEBRA_ROUTE_RIPNG: + return 1; /* shouldn't happen */ + case ZEBRA_ROUTE_OSPF: + return 13; /* ospf */ + case ZEBRA_ROUTE_OSPF6: + return 1; /* shouldn't happen */ + case ZEBRA_ROUTE_BGP: + return 14; /* bgp */ + default: + return 1; /* other */ + } +} + +static void +check_replace(struct route_node *np2, struct rib *rib2, + struct route_node **np, struct rib **rib) +{ + int proto, proto2; + + if (!*np) + { + *np = np2; + *rib = rib2; + return; + } + + if (in_addr_cmp(&(*np)->p.u.prefix, &np2->p.u.prefix) < 0) + return; + if (in_addr_cmp(&(*np)->p.u.prefix, &np2->p.u.prefix) > 0) + { + *np = np2; + *rib = rib2; + return; + } + + proto = proto_trans((*rib)->type); + proto2 = proto_trans(rib2->type); + + if (proto2 > proto) + return; + if (proto2 < proto) + { + *np = np2; + *rib = rib2; + return; + } + + if (in_addr_cmp((u_char *)&(*rib)->nexthop->gate.ipv4, + (u_char *)&rib2->nexthop->gate.ipv4) <= 0) + return; + + *np = np2; + *rib = rib2; + return; +} + +static void +get_fwtable_route_node(struct variable *v, oid objid[], size_t *objid_len, + int exact, struct route_node **np, struct rib **rib) +{ + struct in_addr dest; + struct route_table *table; + struct route_node *np2; + struct rib *rib2; + int proto; + int policy; + struct in_addr nexthop; + u_char *pnt; + int i; + + /* Init index variables */ + + pnt = (u_char *) &dest; + for (i = 0; i < 4; i++) + *pnt++ = 0; + + pnt = (u_char *) &nexthop; + for (i = 0; i < 4; i++) + *pnt++ = 0; + + proto = 0; + policy = 0; + + /* Init return variables */ + + *np = NULL; + *rib = NULL; + + /* Short circuit exact matches of wrong length */ + + if (exact && (*objid_len != (unsigned) v->namelen + 10)) + return; + + table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, VRF_DEFAULT); + if (! table) + return; + + /* Get INDEX information out of OID. + * ipForwardDest, ipForwardProto, ipForwardPolicy, ipForwardNextHop + */ + + if (*objid_len > (unsigned) v->namelen) + oid2in_addr (objid + v->namelen, MIN(4, *objid_len - v->namelen), &dest); + + if (*objid_len > (unsigned) v->namelen + 4) + proto = objid[v->namelen + 4]; + + if (*objid_len > (unsigned) v->namelen + 5) + policy = objid[v->namelen + 5]; + + if (*objid_len > (unsigned) v->namelen + 6) + oid2in_addr (objid + v->namelen + 6, MIN(4, *objid_len - v->namelen - 6), + &nexthop); + + /* Apply GETNEXT on not exact search */ + + if (!exact && (*objid_len >= (unsigned) v->namelen + 10)) + { + if (! in_addr_add((u_char *) &nexthop, 1)) + return; + } + + /* For exact: search matching entry in rib table. */ + + if (exact) + { + if (policy) /* Not supported (yet?) */ + return; + for (*np = route_top (table); *np; *np = route_next (*np)) + { + if (!in_addr_cmp(&(*np)->p.u.prefix, (u_char *)&dest)) + { + RNODE_FOREACH_RIB (*np, *rib) + { + if (!in_addr_cmp((u_char *)&(*rib)->nexthop->gate.ipv4, + (u_char *)&nexthop)) + if (proto == proto_trans((*rib)->type)) + return; + } + } + } + return; + } + + /* Search next best entry */ + + for (np2 = route_top (table); np2; np2 = route_next (np2)) + { + + /* Check destination first */ + if (in_addr_cmp(&np2->p.u.prefix, (u_char *)&dest) > 0) + 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 */ + RNODE_FOREACH_RIB (np2, rib2) + { + int proto2, policy2; + + proto2 = proto_trans(rib2->type); + policy2 = 0; + + if ((policy < policy2) + || ((policy == policy2) && (proto < proto2)) + || ((policy == policy2) && (proto == proto2) + && (in_addr_cmp((u_char *)&rib2->nexthop->gate.ipv4, + (u_char *) &nexthop) >= 0) + )) + check_replace(np2, rib2, np, rib); + } + } + } + + if (!*rib) + return; + + policy = 0; + proto = proto_trans((*rib)->type); + + *objid_len = v->namelen + 10; + pnt = (u_char *) &(*np)->p.u.prefix; + for (i = 0; i < 4; i++) + objid[v->namelen + i] = *pnt++; + + objid[v->namelen + 4] = proto; + objid[v->namelen + 5] = policy; + + { + struct nexthop *nexthop; + + nexthop = (*rib)->nexthop; + if (nexthop) + { + pnt = (u_char *) &nexthop->gate.ipv4; + for (i = 0; i < 4; i++) + objid[i + v->namelen + 6] = *pnt++; + } + } + + return; +} + +static u_char * +ipFwTable (struct variable *v, oid objid[], size_t *objid_len, + int exact, size_t *val_len, WriteMethod **write_method) +{ + struct route_node *np; + struct rib *rib; + static int result; + static int resarr[2]; + 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; + + nexthop = rib->nexthop; + if (! nexthop) + return NULL; + + switch (v->magic) + { + case IPFORWARDDEST: + *val_len = 4; + return &np->p.u.prefix; + break; + case IPFORWARDMASK: + masklen2ip(np->p.prefixlen, &netmask); + *val_len = 4; + return (u_char *)&netmask; + break; + case IPFORWARDPOLICY: + result = 0; + *val_len = sizeof(int); + return (u_char *)&result; + break; + case IPFORWARDNEXTHOP: + *val_len = 4; + return (u_char *)&nexthop->gate.ipv4; + break; + case IPFORWARDIFINDEX: + *val_len = sizeof(int); + return (u_char *)&nexthop->ifindex; + break; + case IPFORWARDTYPE: + if (nexthop->type == NEXTHOP_TYPE_IFINDEX + || nexthop->type == NEXTHOP_TYPE_IFNAME) + result = 3; + else + result = 4; + *val_len = sizeof(int); + return (u_char *)&result; + break; + case IPFORWARDPROTO: + result = proto_trans(rib->type); + *val_len = sizeof(int); + return (u_char *)&result; + break; + case IPFORWARDAGE: + result = 0; + *val_len = sizeof(int); + return (u_char *)&result; + break; + case IPFORWARDINFO: + resarr[0] = 0; + resarr[1] = 0; + *val_len = 2 * sizeof(int); + return (u_char *)resarr; + break; + case IPFORWARDNEXTHOPAS: + result = -1; + *val_len = sizeof(int); + return (u_char *)&result; + break; + case IPFORWARDMETRIC1: + result = 0; + *val_len = sizeof(int); + return (u_char *)&result; + break; + case IPFORWARDMETRIC2: + result = 0; + *val_len = sizeof(int); + return (u_char *)&result; + break; + case IPFORWARDMETRIC3: + result = 0; + *val_len = sizeof(int); + return (u_char *)&result; + break; + case IPFORWARDMETRIC4: + result = 0; + *val_len = sizeof(int); + return (u_char *)&result; + break; + case IPFORWARDMETRIC5: + result = 0; + *val_len = sizeof(int); + return (u_char *)&result; + break; + default: + return NULL; + break; + } + return NULL; +} + +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: + break; + default: + return NULL; + break; + } + return NULL; +} + +void +zebra_snmp_init () +{ + smux_init (zebrad.master); + REGISTER_MIB("mibII/ipforward", zebra_variables, variable, ipfw_oid); +} +#endif /* HAVE_SNMP */ diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c new file mode 100644 index 0000000..0c08990 --- /dev/null +++ b/zebra/zebra_vty.c @@ -0,0 +1,5528 @@ +/* Zebra VTY functions + * Copyright (C) 2002 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 "memory.h" +#include "if.h" +#include "prefix.h" +#include "command.h" +#include "table.h" +#include "rib.h" +#include "vrf.h" +#include "nexthop.h" + +#include "zebra/zserv.h" +#include "zebra/zebra_rnh.h" + +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_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; + struct prefix p; + struct in_addr gate; + 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) + { + vty_out (vty, "%% Malformed address%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Cisco like mask notation. */ + if (mask_str) + { + ret = inet_aton (mask_str, &mask); + if (ret == 0) + { + vty_out (vty, "%% Malformed address%s", VTY_NEWLINE); + return CMD_WARNING; + } + p.prefixlen = ip_masklen (mask); + } + + /* Apply mask for given prefix. */ + apply_mask (&p); + + /* Administrative distance. */ + if (distance_str) + distance = atoi (distance_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)) + { + if (flag_str) + { + vty_out (vty, "%% can not have flag %s with Null0%s", flag_str, VTY_NEWLINE); + return CMD_WARNING; + } + if (add_cmd) + static_add_ipv4_safi (safi, &p, NULL, NULL, ZEBRA_FLAG_BLACKHOLE, tag, distance, vrf_id); + else + static_delete_ipv4_safi (safi, &p, NULL, NULL, tag, distance, vrf_id); + return CMD_SUCCESS; + } + + /* 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; + } + } + + if (gate_str == NULL) + { + if (add_cmd) + static_add_ipv4_safi (safi, &p, NULL, NULL, flag, tag, distance, vrf_id); + else + static_delete_ipv4_safi (safi, &p, NULL, NULL, tag, distance, vrf_id); + + return CMD_SUCCESS; + } + + /* When gateway is A.B.C.D format, gate is treated as nexthop + address other case gate is treated as interface name. */ + ret = inet_aton (gate_str, &gate); + if (ret) + ifname = NULL; + else + ifname = gate_str; + + if (add_cmd) + 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 + 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; +} + +/* Static route configuration. */ +DEFUN (ip_route, + ip_route_cmd, + "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0)", + 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") +{ + 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, + ip_route_flags_cmd, + "ip route A.B.C.D/M (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 gateway address\n" + "IP gateway interface name\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], 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, + ip_route_flags2_cmd, + "ip route A.B.C.D/M (reject|blackhole)", + 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, 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. */ +DEFUN (ip_route_mask, + ip_route_mask_cmd, + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0)", + 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, 1, argv[0], argv[1], + argv[2], NULL, NULL, NULL, NULL); +} + +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" + "Null interface\n" + "Set tag for this route\n" + "Tag value\n") + +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], + NULL, argv[3], NULL, NULL); +} + +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" + "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], argv[1], argv[2], + NULL, argv[3], NULL, argv[4]); +} + +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\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") +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], + argv[2], argv[3], NULL, NULL, NULL); +} + +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\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" + "Set tag for this route\n" + "Tag value\n") +{ + 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_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" + "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], argv[1], + argv[2], argv[3], argv[4], NULL, argv[5]); +} + +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" + "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], + NULL, argv[2], NULL, NULL, NULL); +} + +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" + "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], argv[1], NULL, + argv[2], argv[3], NULL, NULL); +} + +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\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_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], NULL, + argv[2], argv[3], NULL, argv[4]); +} + +/* 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") +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, + argv[1], NULL, NULL, argv[2], NULL); +} + +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" + "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_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, + argv[1], NULL, argv[2], argv[3], NULL); +} + +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 (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" + "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, argv[2], argv[3], 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>", + 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, 1, argv[0], NULL, argv[1], + argv[2], NULL, argv[3], NULL); +} + +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 (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_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], + argv[2], argv[3], argv[4], NULL); +} + +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" + "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_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, + NULL, argv[1], NULL, argv[2], NULL); +} + +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" + "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_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, + NULL, argv[1], argv[2], argv[3], NULL); +} + +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_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], + NULL, NULL, argv[3], NULL); +} + +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" + "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") +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], + argv[2], NULL, argv[3], argv[4], NULL); +} + +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" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], + argv[3], argv[4], argv[5], argv[6]); +} + +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_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], + argv[3], NULL, argv[4], 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>", + 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_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") +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], NULL, + argv[2], argv[3], argv[4], NULL); +} + +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_route, + no_ip_route_cmd, + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0)", + 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") +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, + argv[1], NULL, NULL, NULL, NULL); +} + +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); +} + +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) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], + NULL, argv[2], NULL, argv[3]); +} + +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); + 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_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; + } + + 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_show_ip_route_detail (vty, rn, 0); + + route_unlock_node (rn); + } + + return CMD_SUCCESS; +} + +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; + + 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]); + + return CMD_SUCCESS; +} + +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 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_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 ((zvrf = vrf_iter2info (iter)) == NULL || + (stable = zvrf->stable[AFI_IP][safi]) == NULL) + continue; + + 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); + + switch (si->type) + { + 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; + } + + /* 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->tag) + vty_out (vty, " tag %d", si->tag); + + 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;i", + 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); +} + +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 (ipv6_route_flags, + ipv6_route_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], NULL, argv[2], NULL, + NULL, NULL); +} + +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); +} + +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 (ipv6_route_ifname, + ipv6_route_ifname_cmd, + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE", + 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, 1, argv[0], argv[1], argv[2], NULL, NULL, + NULL, NULL); +} + +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); +} + +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]); +} + +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 (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 + "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], argv[2], argv[3], argv[4], NULL, NULL); +} + +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]); +} + +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 (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 + "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], NULL, NULL, argv[2], argv[3], argv[4]); +} + +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); +} + +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); +} + +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]); +} + +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 (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 + "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], argv[2], NULL, argv[3], argv[4], NULL); +} + +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]); +} + +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); +} + +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); +} + +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]); +} + +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); +} + +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); +} + +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) +{ + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, argv[2], NULL, argv[3]); +} + +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") + +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") + +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); +} + +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); +} + +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 + "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, 0, argv[0], argv[1], argv[2], NULL, argv[3], NULL, argv[4]); +} + +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") + +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") + +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); +} + +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") +{ + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, argv[2], argv[3], NULL); +} + +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) +{ + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, argv[2], argv[3], argv[4]); +} + +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>", + 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") +{ + /* We do not care about argv[2] */ + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, argv[2], NULL, argv[3], + NULL); +} + +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); +} + +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]); +} + +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); +} + +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); +} + +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]); +} + +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); +} + +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 (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" + "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, 0, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]); +} + +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" + VRF_CMD_HELP_STR) +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], NULL, NULL, + argv[3]); +} + +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" + VRF_CMD_HELP_STR) +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, NULL, NULL, + argv[3]); +} + +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" + VRF_CMD_HELP_STR) +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], NULL, NULL, + argv[4]); +} + +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" + VRF_CMD_HELP_STR) +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, NULL, argv[2], + argv[3]); +} + +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" + "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" + VRF_CMD_HELP_STR) +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], NULL, argv[3], + argv[4]); +} + +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" + VRF_CMD_HELP_STR) +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, NULL, argv[3], + argv[4]); +} + +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" + "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" + VRF_CMD_HELP_STR) +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], NULL, argv[4], + argv[5]); +} + +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" + VRF_CMD_HELP_STR) +{ + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, NULL, NULL, + (argc > 3) ? argv[3] : argv[2]); +} + +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" + "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" + VRF_CMD_HELP_STR) + +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" + VRF_CMD_HELP_STR) +{ + 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_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" + "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" + VRF_CMD_HELP_STR) + +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" + VRF_CMD_HELP_STR) +{ + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, NULL, argv[2], + argv[3]); +} + +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" + "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" + VRF_CMD_HELP_STR) +{ + /* We do not care about argv[2] */ + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, argv[2], NULL, argv[3], + argv[4]); +} + +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" + VRF_CMD_HELP_STR) +{ + 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_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" + "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" + VRF_CMD_HELP_STR) +{ + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], argv[3], NULL, argv[4], + argv[5]); +} + +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; + 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_IP6, SAFI_UNICAST, vrf_id); + if (! table) + return CMD_SUCCESS; + + /* 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; +} + +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_tag, + show_ipv6_route_tag_cmd, + "show ipv6 route tag <1-4294967295>", + SHOW_STR + IP_STR + "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; + + 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 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_V6_HEADER); + first = 0; + } + 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", + SHOW_STR + IP_STR + "IPv6 routing table\n" + "IPv6 prefix\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_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)) + 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; +} + +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, + SHOW_STR + IP_STR + "IP routing table\n" + QUAGGA_IP6_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_IP6, 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_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)) + 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; +} + +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", + SHOW_STR + IP_STR + "IPv6 routing table\n" + "IPv6 Address\n") +{ + int ret; + 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) + { + vty_out (vty, "Malformed IPv6 address%s", VTY_NEWLINE); + 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; + + 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_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", + SHOW_STR + IP_STR + "IPv6 routing table\n" + "IPv6 prefix\n") +{ + int ret; + 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) + { + vty_out (vty, "Malformed IPv6 prefix%s", VTY_NEWLINE); + 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; + + 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_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, + "show ipv6 route summary", + SHOW_STR + IP_STR + "IPv6 routing table\n" + "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 = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, vrf_id); + if (! table) + return CMD_SUCCESS; + + vty_show_ip_route_summary (vty, table); + + return CMD_SUCCESS; +} + +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 + "IPv6 routing table\n" + "Summary of all IPv6 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_IP6, SAFI_UNICAST, vrf_id); + if (! table) + return CMD_SUCCESS; + + 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. + */ + +DEFUN (show_ipv6_mroute, + show_ipv6_mroute_cmd, + "show ipv6 mroute", + SHOW_STR + IP_STR + "IPv6 Multicast routing table\n") +{ + struct route_table *table; + 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 = 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)) + 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; +} + +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_route *si; + int write; + char buf[BUFSIZ]; + struct route_table *stable; + struct zebra_vrf *zvrf; + vrf_iter_t iter; + + write = 0; + + 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", prefix2str (&rn->p, buf, sizeof buf)); + + 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_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); + + if (si->vrf_id != VRF_DEFAULT) + vty_out (vty, " vrf %u", si->vrf_id); + + vty_out (vty, "%s", VTY_NEWLINE); + + write = 1; + } + } + return write; +} + +/* Static ip route configuration write function. */ +static int +zebra_ip_config (struct vty *vty) +{ + int write = 0; + + 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 */ + + return write; +} + +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;i + +#include "prefix.h" +#include "command.h" +#include "if.h" +#include "thread.h" +#include "stream.h" +#include "memory.h" +#include "table.h" +#include "rib.h" +#include "network.h" +#include "sockunion.h" +#include "log.h" +#include "zclient.h" +#include "privs.h" +#include "network.h" +#include "buffer.h" +#include "vrf.h" +#include "nexthop.h" + +#include "zebra/zserv.h" +#include "zebra/router-id.h" +#include "zebra/redistribute.h" +#include "zebra/debug.h" +#include "zebra/ipforward.h" +#include "zebra/zebra_rnh.h" + +/* Event list of zebra. */ +enum event { ZEBRA_SERV, ZEBRA_READ, ZEBRA_WRITE }; + +extern struct zebra_t zebrad; + +static void zebra_event (enum event event, int sock, struct zserv *client); + +extern struct zebra_privs_t zserv_privs; + +static void zebra_client_close (struct zserv *client); + +static int +zserv_delayed_close(struct thread *thread) +{ + struct zserv *client = THREAD_ARG(thread); + + client->t_suicide = NULL; + zebra_client_close(client); + return 0; +} + +/* When client connects, it sends hello message + * with promise to send zebra routes of specific type. + * Zebra stores a socket fd of the client into + * this array. And use it to clean up routes that + * client didn't remove for some reasons after closing + * connection. + */ +static int route_type_oaths[ZEBRA_ROUTE_MAX]; + +static int +zserv_flush_data(struct thread *thread) +{ + struct zserv *client = THREAD_ARG(thread); + + client->t_write = NULL; + if (client->t_suicide) + { + zebra_client_close(client); + return -1; + } + switch (buffer_flush_available(client->wb, client->sock)) + { + case BUFFER_ERROR: + zlog_warn("%s: buffer_flush_available failed on zserv client fd %d, " + "closing", __func__, client->sock); + zebra_client_close(client); + break; + case BUFFER_PENDING: + client->t_write = thread_add_write(zebrad.master, zserv_flush_data, + client, client->sock); + break; + case BUFFER_EMPTY: + break; + } + + client->last_write_time = quagga_time(NULL); + return 0; +} + +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))) + { + case BUFFER_ERROR: + zlog_warn("%s: buffer_write failed to zserv client fd %d, closing", + __func__, client->sock); + /* Schedule a delayed close since many of the functions that call this + one do not check the return code. They do not allow for the + possibility that an I/O error may have caused the client to be + deleted. */ + client->t_suicide = thread_add_event(zebrad.master, zserv_delayed_close, + client, 0); + return -1; + case BUFFER_EMPTY: + THREAD_OFF(client->t_write); + break; + case BUFFER_PENDING: + THREAD_WRITE_ON(zebrad.master, client->t_write, + zserv_flush_data, client, client->sock); + break; + } + + client->last_write_time = quagga_time(NULL); + return 0; +} + +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: + * - in response to a 3-byte ZEBRA_INTERFACE_ADD request + * from the client. + * - at startup, when zebra figures out the available interfaces + * - when an interface is added (where support for + * RTM_IFANNOUNCE or AF_NETLINK sockets is available), or when + * an interface is marked IFF_UP (i.e., an RTM_IFINFO message is + * received) + */ +int +zsend_interface_add (struct zserv *client, struct interface *ifp) +{ + struct stream *s; + + /* Check this client need interface information. */ + if (! vrf_bitmap_check (client->ifinfo, ifp->vrf_id)) + return 0; + + s = client->obuf; + stream_reset (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); +} + +/* Interface deletion from zebra daemon. */ +int +zsend_interface_delete (struct zserv *client, struct interface *ifp) +{ + struct stream *s; + + /* Check this client need interface information. */ + 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_LINK_PARAMS, ifp->vrf_id); + + /* Add Interface Index */ + stream_putl (s, ifp->ifindex); + + /* 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); +} + +/* Interface address is added/deleted. Send ZEBRA_INTERFACE_ADDRESS_ADD or + * ZEBRA_INTERFACE_ADDRESS_DELETE to the client. + * + * A ZEBRA_INTERFACE_ADDRESS_ADD is sent in the following situations: + * - in response to a 3-byte ZEBRA_INTERFACE_ADD request + * from the client, after the ZEBRA_INTERFACE_ADD has been + * sent from zebra to the client + * - redistribute new address info to all clients in the following situations + * - at startup, when zebra figures out the available interfaces + * - when an interface is added (where support for + * RTM_IFANNOUNCE or AF_NETLINK sockets is available), or when + * an interface is marked IFF_UP (i.e., an RTM_IFINFO message is + * received) + * - for the vty commands "ip address A.B.C.D/M [|