From 2d33f157898e50c2855cd014a9f50696dca8a77b Mon Sep 17 00:00:00 2001 From: paul Date: Mon, 17 Mar 2003 01:10:58 +0000 Subject: Addition of OSPF-API - Amir Guindehi --- ospfclient/AUTHORS | 1 + ospfclient/COPYING | 339 ++++++ ospfclient/ChangeLog | 57 + ospfclient/INSTALL | 182 +++ ospfclient/Makefile.am | 19 + ospfclient/Makefile.in | 374 ++++++ ospfclient/NEWS | 1 + ospfclient/README | 4 + ospfclient/ospf_apiclient.c | 747 ++++++++++++ ospfclient/ospf_apiclient.h | 135 +++ ospfclient/ospfclient.c | 291 +++++ ospfd/ospf_api.c | 647 +++++++++++ ospfd/ospf_api.h | 357 ++++++ ospfd/ospf_apiserver.c | 2647 +++++++++++++++++++++++++++++++++++++++++++ ospfd/ospf_apiserver.h | 201 ++++ 15 files changed, 6002 insertions(+) create mode 100644 ospfclient/AUTHORS create mode 100644 ospfclient/COPYING create mode 100644 ospfclient/ChangeLog create mode 100644 ospfclient/INSTALL create mode 100644 ospfclient/Makefile.am create mode 100644 ospfclient/Makefile.in create mode 100644 ospfclient/NEWS create mode 100644 ospfclient/README create mode 100644 ospfclient/ospf_apiclient.c create mode 100644 ospfclient/ospf_apiclient.h create mode 100644 ospfclient/ospfclient.c create mode 100644 ospfd/ospf_api.c create mode 100644 ospfd/ospf_api.h create mode 100644 ospfd/ospf_apiserver.c create mode 100644 ospfd/ospf_apiserver.h diff --git a/ospfclient/AUTHORS b/ospfclient/AUTHORS new file mode 100644 index 00000000..b865c550 --- /dev/null +++ b/ospfclient/AUTHORS @@ -0,0 +1 @@ +Ralph Keller diff --git a/ospfclient/COPYING b/ospfclient/COPYING new file mode 100644 index 00000000..a43ea212 --- /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/ChangeLog b/ospfclient/ChangeLog new file mode 100644 index 00000000..275b14ef --- /dev/null +++ b/ospfclient/ChangeLog @@ -0,0 +1,57 @@ +2003-01-10 Ralph Keller + * New ChangeLog + +2003-01-08 Ralph Keller + * apiclient message passing bug resolved + * (proposed by Masahiko) + +2002-10-09 Ralph Keller + * Includes patch in OSPFd for ISM, NSM, neighbor + +2002-09-16 Ralph Keller + * Current patch vs. Zebra-0.93b + +2002-08-22 Ralph Keller + * Bugfix: LSA updates are now received only once even + * if multiple opaque types are registered + +2002-08-06 Ralph Keller + * Upgrade of OSPF API to Zebra-0.93a. + +2002-06-13 Ralph Keller + * Opaque LSA origination bug fixed by Masahiko. + * This code is based on 0.93-pre + +2002-05-03 Ralph Keller + * Upgrade to newest Zebra from CVS repository. ospf_flood_through + * function did not work after upgrade. Function + * ospf_apiserver_flood_opaque_lsa added. Also byte order problem + * with port numbers resolved. + +2002-05-02 Ralph Keller + + * new autoconf and automake files, now called configure.in + * instead of configure.ac + +2002-04-24 Ralph Keller + + * reverse channel now works (syncport and syncport+1) + * ospf_apiclient.c: standard includes removed (not needed) + * bzero replaced with memset + +2002-04-18 Ralph Keller + + * 2-way phase connection setup + +2002-04-05 Ralph Keller + + * Changes incorporated as proposed by Masahiko + +2002-04-05 Ralph Keller + + * autoconf and automake-style Makefile + +2002-04-04 Ralph Keller + + * 1st public release of OSPF API + diff --git a/ospfclient/INSTALL b/ospfclient/INSTALL new file mode 100644 index 00000000..b42a17ac --- /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 00000000..c345d1bd --- /dev/null +++ b/ospfclient/Makefile.am @@ -0,0 +1,19 @@ +## Automake.am for OSPF API client + +INCLUDES = -I../lib -I../ + +noinst_LIBRARIES = libospfapiclient.a +sbin_PROGRAMS = ospfclient + +libospfapiclient_a_SOURCES = \ + ospf_apiclient.c + +noinst_HEADERS = \ + ospf_apiclient.h + +ospfclient_SOURCES = \ + ospfclient.c $(libospfapiclient_a_SOURCES) + +ospfclient_LDADD = ../ospfd/libospf.a ../lib/libzebra.a + + diff --git a/ospfclient/Makefile.in b/ospfclient/Makefile.in new file mode 100644 index 00000000..4deac874 --- /dev/null +++ b/ospfclient/Makefile.in @@ -0,0 +1,374 @@ +# Makefile.in generated automatically by automake 1.4-p5 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AR = @AR@ +BGPD = @BGPD@ +CC = @CC@ +CPP = @CPP@ +CURSES = @CURSES@ +IF_METHOD = @IF_METHOD@ +IF_PROC = @IF_PROC@ +IPFORWARD = @IPFORWARD@ +KERNEL_METHOD = @KERNEL_METHOD@ +LIBPAM = @LIBPAM@ +LIB_IPV6 = @LIB_IPV6@ +LIB_REGEX = @LIB_REGEX@ +MAKEINFO = @MAKEINFO@ +MULTIPATH_NUM = @MULTIPATH_NUM@ +OSPF6D = @OSPF6D@ +OSPFCLIENT = @OSPFCLIENT@ +OSPFD = @OSPFD@ +OTHER_METHOD = @OTHER_METHOD@ +PACKAGE = @PACKAGE@ +RANLIB = @RANLIB@ +RIPD = @RIPD@ +RIPNGD = @RIPNGD@ +RTREAD_METHOD = @RTREAD_METHOD@ +RT_METHOD = @RT_METHOD@ +VERSION = @VERSION@ +VTYSH = @VTYSH@ +ZEBRA = @ZEBRA@ + +INCLUDES = -I../lib -I../ + +noinst_LIBRARIES = libospfapiclient.a +sbin_PROGRAMS = ospfclient + +libospfapiclient_a_SOURCES = \ + ospf_apiclient.c + + +noinst_HEADERS = \ + ospf_apiclient.h + + +ospfclient_SOURCES = \ + ospfclient.c $(libospfapiclient_a_SOURCES) + + +ospfclient_LDADD = ../ospfd/libospf.a ../lib/libzebra.a +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = ../config.h +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) + + +DEFS = @DEFS@ -I. -I$(srcdir) -I.. +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +libospfapiclient_a_LIBADD = +libospfapiclient_a_OBJECTS = ospf_apiclient.o +PROGRAMS = $(sbin_PROGRAMS) + +ospfclient_OBJECTS = ospfclient.o ospf_apiclient.o +ospfclient_DEPENDENCIES = ../ospfd/libospf.a ../lib/libzebra.a +ospfclient_LDFLAGS = +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ +HEADERS = $(noinst_HEADERS) + +DIST_COMMON = README AUTHORS COPYING ChangeLog INSTALL Makefile.am \ +Makefile.in NEWS + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = gtar +GZIP_ENV = --best +DEP_FILES = .deps/ospf_apiclient.P .deps/ospfclient.P +SOURCES = $(libospfapiclient_a_SOURCES) $(ospfclient_SOURCES) +OBJECTS = $(libospfapiclient_a_OBJECTS) $(ospfclient_OBJECTS) + +all: all-redirect +.SUFFIXES: +.SUFFIXES: .S .c .o .s +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --foreign ospfclient/Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status $(BUILT_SOURCES) + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + + +mostlyclean-noinstLIBRARIES: + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) + +distclean-noinstLIBRARIES: + +maintainer-clean-noinstLIBRARIES: + +.s.o: + $(COMPILE) -c $< + +.S.o: + $(COMPILE) -c $< + +mostlyclean-compile: + -rm -f *.o core *.core + +clean-compile: + +distclean-compile: + -rm -f *.tab.c + +maintainer-clean-compile: + +libospfapiclient.a: $(libospfapiclient_a_OBJECTS) $(libospfapiclient_a_DEPENDENCIES) + -rm -f libospfapiclient.a + $(AR) cru libospfapiclient.a $(libospfapiclient_a_OBJECTS) $(libospfapiclient_a_LIBADD) + $(RANLIB) libospfapiclient.a + +mostlyclean-sbinPROGRAMS: + +clean-sbinPROGRAMS: + -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS) + +distclean-sbinPROGRAMS: + +maintainer-clean-sbinPROGRAMS: + +install-sbinPROGRAMS: $(sbin_PROGRAMS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(sbindir) + @list='$(sbin_PROGRAMS)'; for p in $$list; do \ + if test -f $$p; then \ + echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`"; \ + $(INSTALL_PROGRAM) $$p $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \ + else :; fi; \ + done + +uninstall-sbinPROGRAMS: + @$(NORMAL_UNINSTALL) + list='$(sbin_PROGRAMS)'; for p in $$list; do \ + rm -f $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \ + done + +ospfclient: $(ospfclient_OBJECTS) $(ospfclient_DEPENDENCIES) + @rm -f ospfclient + $(LINK) $(ospfclient_LDFLAGS) $(ospfclient_OBJECTS) $(ospfclient_LDADD) $(LIBS) + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + here=`pwd` && cd $(srcdir) \ + && mkid -f$$here/ID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS) + +mostlyclean-tags: + +clean-tags: + +distclean-tags: + -rm -f TAGS ID + +maintainer-clean-tags: + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) + +subdir = ospfclient + +distdir: $(DISTFILES) + here=`cd $(top_builddir) && pwd`; \ + top_distdir=`cd $(top_distdir) && pwd`; \ + distdir=`cd $(distdir) && pwd`; \ + cd $(top_srcdir) \ + && $(AUTOMAKE) --include-deps --build-dir=$$here --srcdir-name=$(top_srcdir) --output-dir=$$top_distdir --foreign ospfclient/Makefile + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$d/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done + +DEPS_MAGIC := $(shell mkdir .deps > /dev/null 2>&1 || :) + +-include $(DEP_FILES) + +mostlyclean-depend: + +clean-depend: + +distclean-depend: + -rm -rf .deps + +maintainer-clean-depend: + +%.o: %.c + @echo '$(COMPILE) -c $<'; \ + $(COMPILE) -Wp,-MD,.deps/$(*F).pp -c $< + @-cp .deps/$(*F).pp .deps/$(*F).P; \ + tr ' ' '\012' < .deps/$(*F).pp \ + | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \ + >> .deps/$(*F).P; \ + rm .deps/$(*F).pp + +%.lo: %.c + @echo '$(LTCOMPILE) -c $<'; \ + $(LTCOMPILE) -Wp,-MD,.deps/$(*F).pp -c $< + @-sed -e 's/^\([^:]*\)\.o[ ]*:/\1.lo \1.o :/' \ + < .deps/$(*F).pp > .deps/$(*F).P; \ + tr ' ' '\012' < .deps/$(*F).pp \ + | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \ + >> .deps/$(*F).P; \ + rm -f .deps/$(*F).pp +info-am: +info: info-am +dvi-am: +dvi: dvi-am +check-am: all-am +check: check-am +installcheck-am: +installcheck: installcheck-am +install-exec-am: install-sbinPROGRAMS +install-exec: install-exec-am + +install-data-am: +install-data: install-data-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-am +uninstall-am: uninstall-sbinPROGRAMS +uninstall: uninstall-am +all-am: Makefile $(LIBRARIES) $(PROGRAMS) $(HEADERS) +all-redirect: all-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: + $(mkinstalldirs) $(DESTDIR)$(sbindir) + + +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-noinstLIBRARIES mostlyclean-compile \ + mostlyclean-sbinPROGRAMS mostlyclean-tags \ + mostlyclean-depend mostlyclean-generic + +mostlyclean: mostlyclean-am + +clean-am: clean-noinstLIBRARIES clean-compile clean-sbinPROGRAMS \ + clean-tags clean-depend clean-generic mostlyclean-am + +clean: clean-am + +distclean-am: distclean-noinstLIBRARIES distclean-compile \ + distclean-sbinPROGRAMS distclean-tags distclean-depend \ + distclean-generic clean-am + +distclean: distclean-am + +maintainer-clean-am: maintainer-clean-noinstLIBRARIES \ + maintainer-clean-compile maintainer-clean-sbinPROGRAMS \ + maintainer-clean-tags maintainer-clean-depend \ + maintainer-clean-generic distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-am + +.PHONY: mostlyclean-noinstLIBRARIES distclean-noinstLIBRARIES \ +clean-noinstLIBRARIES maintainer-clean-noinstLIBRARIES \ +mostlyclean-compile distclean-compile clean-compile \ +maintainer-clean-compile mostlyclean-sbinPROGRAMS \ +distclean-sbinPROGRAMS clean-sbinPROGRAMS maintainer-clean-sbinPROGRAMS \ +uninstall-sbinPROGRAMS install-sbinPROGRAMS tags mostlyclean-tags \ +distclean-tags clean-tags maintainer-clean-tags distdir \ +mostlyclean-depend distclean-depend clean-depend \ +maintainer-clean-depend info-am info dvi-am dvi check check-am \ +installcheck-am installcheck install-exec-am install-exec \ +install-data-am install-data install-am install uninstall-am uninstall \ +all-redirect all-am all installdirs mostlyclean-generic \ +distclean-generic clean-generic maintainer-clean-generic clean \ +mostlyclean distclean maintainer-clean + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/ospfclient/NEWS b/ospfclient/NEWS new file mode 100644 index 00000000..5b1ec4fd --- /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 00000000..894cd783 --- /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 00000000..3a62a423 --- /dev/null +++ b/ospfclient/ospf_apiclient.c @@ -0,0 +1,747 @@ +/* + * 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 "version.h" +#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 + * ----------------------------------------------------------- + */ + +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; + 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_SIN_LEN + myaddr_async.sin_len = size; +#endif /* HAVE_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", 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_SIN_LEN + myaddr_sync.sin_len = sizeof (struct sockaddr_in); +#endif /* HAVE_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_SIN_LEN + myaddr_sync.sin_len = sizeof (struct sockaddr_in); +#endif /* HAVE_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 = XMALLOC (MTYPE_OSPF_APICLIENT, sizeof (struct ospf_apiclient)); + memset (new, 0, 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 */ +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 + * ----------------------------------------------------------- + */ + +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); + } +} + +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); + } +} + +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); + } +} + +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); + } + +} + +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); + } +} + +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); +} + +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); +} + +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 00000000..0e74787a --- /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 0 + +/* 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 00000000..7321bf68 --- /dev/null +++ b/ospfclient/ospfclient.c @@ -0,0 +1,291 @@ +/* + * Simple main program to demonstrate how OSPF API can be used. + */ + +/* The following includes are needed in all OSPF API client + applications */ + +#include +#include "prefix.h" /* for ospf_asbr.h */ + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_opaque.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_api.h" +#include "ospf_apiclient.h" + +/* The following includes are specific to this main application. Here + main uses the thread functionality from libzebra (however an + application can use any thread library like pthreads) */ + +#include "thread.h" +#include "log.h" + +/* local portnumber for async channel */ +#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; + u_char data[4]; +}; + + +/* --------------------------------------------------------- + * Threads for asynchronous messages and LSA update/delete + * --------------------------------------------------------- + */ + +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; +} + +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 */ + 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 */ +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 + * --------------------------------------------------------- + */ + +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); + + ospf_lsa_header_dump (lsa); +} + +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); +} + +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); +} + +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)); +} + +void +del_if_callback (struct in_addr ifaddr) +{ + printf ("new_if_callback: ifaddr: %s\n ", inet_ntoa (ifaddr)); +} + +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)); +} + +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 + * --------------------------------------------------------- + */ + +int +main (int argc, char *argv[]) +{ + struct thread thread; + + args = argv; + + /* Main 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., 140 for experimental Active Networking) + * 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) + { + printf ("main: wrong number of arguments!\n"); + exit (1); + } + + /* Initialization */ + master = thread_master_create (); + + /* Open connection to OSPF daemon */ + oclient = ospf_apiclient_connect (args[1], ASYNCPORT); + if (!oclient) + { + printf ("main: connect failed!\n"); + 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/ospf_api.c b/ospfd/ospf_api.c new file mode 100644 index 00000000..cd69336c --- /dev/null +++ b/ospfd/ospf_api.c @@ -0,0 +1,647 @@ +/* + * 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 +#ifndef HAVE_OPAQUE_LSA +#error "Core Opaque-LSA module must be configured." +#endif /* HAVE_OPAQUE_LSA */ + +#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[0]; + }; + + 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_warn ("apiserver_lsa_print: opaquelen=%d\n", opaquelen); + + for (i = 0; i < opaquelen; i++) + { + zlog_warn ("0x%x ", olsa->mydata[i]); + } + zlog_warn ("\n"); +} + +/* ----------------------------------------------------------- + * Generic messages + * ----------------------------------------------------------- + */ + +struct msg * +msg_new (u_char msgtype, void *msgbody, u_int32_t seqnum, u_int16_t msglen) +{ + struct msg *new; + + new = XMALLOC (MTYPE_OSPF_API_MSG, sizeof (struct msg)); + memset (new, 0, 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 = sizeof (NameTab) / sizeof (NameTab[0]); + 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 = sizeof (NameTab) / sizeof (NameTab[0]); + 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_warn ("msg_print msg=NULL!\n"); + return; + } + +#ifdef ORIGINAL_CODING + zlog_warn + ("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_info + ("API-msg [%s]: type(%d),len(%d),seq(%lu),data(%p),size(%lu)", + 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 () +{ + struct msg_fifo *new; + + new = XMALLOC (MTYPE_OSPF_API_FIFO, sizeof (struct msg_fifo)); + memset (new, 0, sizeof (struct msg_fifo)); + + return new; +} + +/* 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; + char buf[OSPF_API_MAX_MSG_SIZE]; + int bodylen; + int rlen; + + /* Read message header */ + rlen = readn (fd, (char *) &hdr, sizeof (struct apimsghdr)); + + if (rlen < 0) + { + zlog_warn ("msg_read: readn %s", 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", 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", 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; + int 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; + 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; + int 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; + 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; + int omsglen; + char buf[OSPF_API_MAX_MSG_SIZE]; + + omsglen = sizeof (struct msg_originate_request) - sizeof (struct lsa_header) + + ntohs (data->length); + + omsg = (struct msg_originate_request *) buf; + omsg->ifaddr = ifaddr; + omsg->area_id = area_id; + memcpy (&omsg->data, data, ntohs (data->length)); + + 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; + int len; + + assert (data); + + nmsg = (struct msg_lsa_change_notify *) buf; + len = ntohs (data->length) + sizeof (struct msg_lsa_change_notify) + - sizeof (struct lsa_header); + nmsg->ifaddr = ifaddr; + nmsg->area_id = area_id; + nmsg->is_self_originated = is_self_originated; + memset (&nmsg->pad, 0, sizeof (nmsg->pad)); + memcpy (&nmsg->data, data, ntohs (data->length)); + + 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 00000000..e7867614 --- /dev/null +++ b/ospfd/ospf_api.h @@ -0,0 +1,357 @@ +/* + * 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. */ +struct msg *msg_new (u_char msgtype, void *msgbody, + u_int32_t seqnum, u_int16_t msglen); +struct msg *msg_dup (struct msg *msg); +void msg_print (struct msg *msg); /* XXX debug only */ +void msg_free (struct msg *msg); +struct msg *msg_read (int fd); +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 + +void msg_set_seq (struct msg *msg, u_int32_t seqnr); +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. */ +struct msg_fifo *msg_fifo_new (); +void msg_fifo_push (struct msg_fifo *, struct msg *msg); +struct msg *msg_fifo_pop (struct msg_fifo *fifo); +struct msg *msg_fifo_head (struct msg_fifo *fifo); +void msg_fifo_flush (struct msg_fifo *fifo); +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 +{ + 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. */ +void api_opaque_lsa_print (struct lsa_header *data); + +/* Messages sent by client */ +struct msg *new_msg_register_opaque_type (u_int32_t seqnum, u_char ltype, + u_char otype); +struct msg *new_msg_register_event (u_int32_t seqnum, + struct lsa_filter_type *filter); +struct msg *new_msg_sync_lsdb (u_int32_t seqnum, + struct lsa_filter_type *filter); +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 *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 */ +struct msg *new_msg_reply (u_int32_t seqnum, u_char rc); + +struct msg *new_msg_ready_notify (u_int32_t seqnr, u_char lsa_type, + u_char opaque_type, struct in_addr addr); + +struct msg *new_msg_new_if (u_int32_t seqnr, + struct in_addr ifaddr, struct in_addr area); + +struct msg *new_msg_del_if (u_int32_t seqnr, struct in_addr ifaddr); + +struct msg *new_msg_ism_change (u_int32_t seqnr, struct in_addr ifaddr, + struct in_addr area, u_char status); + +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 */ +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 */ +const char *ospf_api_errname (int errcode); +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 00000000..fc0713b0 --- /dev/null +++ b/ospfd/ospf_apiserver.c @@ -0,0 +1,2647 @@ +/* + * 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 +#ifndef HAVE_OPAQUE_LSA +#error "Core Opaque-LSA module must be configured." +#endif /* HAVE_OPAQUE_LSA */ + +#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. */ +list apiserver_list; + +/* ----------------------------------------------------------- + * Functions to lookup interfaces + * ----------------------------------------------------------- + */ + +struct ospf_interface * +ospf_apiserver_if_lookup_by_addr (struct in_addr address) +{ + listnode node; + struct ospf_interface *oi; + struct ospf *ospf; + + ospf = ospf_get (); + assert (ospf); + + for (node = listhead (ospf->oiflist); node; nextnode (node)) + { + if ((oi = getdata (node)) != NULL + && 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) +{ + listnode node; + struct ospf_interface *oi; + struct ospf *ospf; + + ospf = ospf_get (); + assert (ospf); + + for (node = listhead (ospf->oiflist); node; nextnode (node)) + { + if ((oi = getdata (node)) && 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) +{ + listnode node; + + /* Unregister wildcard [0/0] type */ + ospf_delete_opaque_functab (0 /* all LSAs */, + 0 /* all opaque types */); + + /* Free all client instances */ + for (node = listhead (apiserver_list); node; nextnode (node)) + { + struct ospf_apiserver *apiserv = + (struct ospf_apiserver *) getdata (node); + ospf_apiserver_free (apiserv); + } + + /* Free client list itself */ + list_delete (apiserver_list); + + /* Free wildcard list */ + /* XXX */ +} + +static struct ospf_apiserver * +lookup_apiserver (u_char lsa_type, u_char opaque_type) +{ + listnode n1, n2; + struct registered_opaque_type *r; + struct ospf_apiserver *apiserv, *found = NULL; + + for (n1 = listhead (apiserver_list); n1; nextnode (n1)) + { + apiserv = (struct ospf_apiserver *) getdata (n1); + + for (n2 = listhead (apiserv->opaque_types); n2; nextnode (n2)) + { + r = (struct registered_opaque_type *) getdata (n2); + + 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_info ("API: Put LSA(%p)[%s] into reserve, total=%ld", 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_info ("API: Get LSA(%p)[%s] from reserve, total=%ld", 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) +{ + struct thread *apiserver_serv_thread; + + switch (event) + { + case OSPF_APISERVER_ACCEPT: + apiserver_serv_thread = + 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) +{ + 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 = node->data; + + 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_info ("API: Delete apiserv(%p), total#(%d)", 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_info ("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_info ("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_info ("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_info ("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", + 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; + 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", 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", strerror (errno)); + close (new_sync_sock); + return -1; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_info ("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", 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", 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", 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_warn ("API: New apiserv(%p), total#(%d)", apiserv, apiserver_list->count); + + return 0; +} + + +/* ----------------------------------------------------------- + * Send reply with return code to client application + * ----------------------------------------------------------- + */ + +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 = + XMALLOC (MTYPE_OSPF_APISERVER, sizeof (struct registered_opaque_type)); + memset (regtype, 0, 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_info ("API: Add LSA-type(%d)/Opaque-type(%d) into apiserv(%p), total#(%d)", lsa_type, opaque_type, 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) +{ + listnode node; + + for (node = listhead (apiserv->opaque_types); node; nextnode (node)) + { + struct registered_opaque_type *regtype = node->data; + + /* 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_info ("API: Del LSA-type(%d)/Opaque-type(%d) from apiserv(%p), total#(%d)", lsa_type, opaque_type, 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; +} + + +int +apiserver_is_opaque_type_registered (struct ospf_apiserver *apiserv, + u_char lsa_type, u_char opaque_type) +{ + listnode node; + + for (node = listhead (apiserv->opaque_types); node; nextnode (node)) + { + struct registered_opaque_type *regtype = node->data; + + /* 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) +{ + listnode node; + listnode n2; + + for (node = listhead (ospf_top->oiflist); node; nextnode (node)) + { + struct ospf_interface *oi = (struct ospf_interface *) getdata (node); + + /* 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 */ + for (n2 = listhead (apiserv->opaque_types); n2; nextnode (n2)) + { + struct registered_opaque_type *r = + (struct registered_opaque_type *) getdata (n2); + 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) +{ + listnode node; + listnode n2; + + for (node = listhead (ospf_top->areas); node; nextnode (node)) + { + struct ospf_area *area = getdata (node); + + if (!ospf_apiserver_is_ready_type10 (area)) + { + continue; + } + + /* Check for registered opaque type 10 types */ + for (n2 = listhead (apiserv->opaque_types); n2; nextnode (n2)) + { + struct registered_opaque_type *r = + (struct registered_opaque_type *) getdata (n2); + 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) +{ + listnode n2; + + /* Can type 11 be originated? */ + if (!ospf_apiserver_is_ready_type11 (ospf_top)) + goto out;; + + /* Check for registered opaque type 11 types */ + for (n2 = listhead (apiserv->opaque_types); n2; nextnode (n2)) + { + struct registered_opaque_type *r = + (struct registered_opaque_type *) getdata (n2); + struct msg *msg; + struct in_addr noarea_id = { 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. + * ----------------------------------------------------------- + */ + +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 = { 0L }; + + /* Default interface for non Opaque9 LSAs */ + struct in_addr ifaddr = { 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) +{ + listnode node; + u_int32_t seqnum; + int rc = 0; + struct msg_sync_lsdb *smsg; + struct param_t + { + struct ospf_apiserver *apiserv; + struct lsa_filter_type *filter; + } + param; + u_int16_t mask; + + /* 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 (node = listhead (ospf_top->areas); node; nextnode (node)) + { + struct ospf_area *area = node->data; + 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]) + foreach_lsa (ROUTER_LSDB (area), (void *) ¶m, seqnum, + apiserver_sync_callback); + if (mask & Power2[OSPF_NETWORK_LSA]) + foreach_lsa (NETWORK_LSDB (area), (void *) ¶m, seqnum, + apiserver_sync_callback); + if (mask & Power2[OSPF_SUMMARY_LSA]) + foreach_lsa (SUMMARY_LSDB (area), (void *) ¶m, seqnum, + apiserver_sync_callback); + if (mask & Power2[OSPF_ASBR_SUMMARY_LSA]) + foreach_lsa (ASBR_SUMMARY_LSDB (area), (void *) ¶m, seqnum, + apiserver_sync_callback); + if (mask & Power2[OSPF_OPAQUE_LINK_LSA]) + foreach_lsa (OPAQUE_LINK_LSDB (area), (void *) ¶m, + seqnum, apiserver_sync_callback); + if (mask & Power2[OSPF_OPAQUE_AREA_LSA]) + foreach_lsa (OPAQUE_AREA_LSDB (area), (void *) ¶m, + seqnum, apiserver_sync_callback); + } + } + + /* For AS-external LSAs */ + if (ospf_top->lsdb) + { + if (mask & Power2[OSPF_AS_EXTERNAL_LSA]) + foreach_lsa (EXTERNAL_LSDB (ospf_top), (void *) ¶m, seqnum, + apiserver_sync_callback); + } + + /* For AS-external opaque LSAs */ + if (ospf_top->lsdb) + { + if (mask & Power2[OSPF_OPAQUE_AS_LSA]) + foreach_lsa (OPAQUE_AS_LSDB (ospf_top), (void *) ¶m, + seqnum, apiserver_sync_callback); + } + + /* 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; + + /* 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); +#ifdef HAVE_NSSA + options |= LSA_NSSA_GET (area); +#endif /* HAVE_NSSA */ + } + + options |= OSPF_OPTION_O; /* Don't forget to set option bit */ + + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + { + zlog_info ("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); + + /* 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_free (new); + new = NULL; + 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_opaque_capable_nbr_count (oi->nbrs, NSM_Full) > 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. */ + listnode node; + + for (node = listhead (area->oiflist); node; nextnode (node)) + { + struct ospf_interface *oi = getdata (node); + + /* 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. */ + listnode node; + + for (node = listhead (ospf->oiflist); node; nextnode (node)) + { + struct ospf_interface *oi = getdata (node); + + /* 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; + int lsa_type, opaque_type; + int ready = 0; + int rc = 0; + + /* 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 (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_top->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_top); + 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->top->lsa_originate_count++; + + /* Flood LSA through area. */ + ospf_flood_through_area (lsa->area, NULL /*nbr */ , lsa); + break; + case OSPF_OPAQUE_AS_LSA: + /* Increment counters? XXX */ + + /* Flood LSA through AS. */ + ospf_flood_through_as (NULL /*nbr */ , lsa); + break; + } +} + +int +ospf_apiserver_originate1 (struct ospf_lsa *lsa) +{ + /* Install this LSA into LSDB. */ + if (ospf_lsa_install (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. */ +void +ospf_apiserver_lsa_refresher (struct ospf_lsa *lsa) +{ + struct ospf_apiserver *apiserv; + struct ospf_lsa *new = NULL; + + 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 (new->oi, new) == NULL) + { + zlog_warn ("ospf_apiserver_lsa_refresher: ospf_lsa_install failed"); + ospf_lsa_free (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_info ("LSA[Type%d:%s]: Refresh Opaque LSA", + new->data->type, inet_ntoa (new->data->id)); + ospf_lsa_header_dump (new->data); + } + +out: + return; +} + + +/* ----------------------------------------------------------- + * 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; + + /* 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 (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_top->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 */ +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; + listnode node; + + /* Set parameter struct. */ + param.apiserv = apiserv; + param.lsa_type = lsa_type; + param.opaque_type = opaque_type; + +#ifdef ORIGINAL_CODING + /* Iterate over all areas */ + for (node = listhead (ospf_top->areas); node; nextnode (node)) + { + struct ospf_area *area = node->data; + + foreach_lsa (OPAQUE_LINK_LSDB (area), (void *) ¶m, 0, + apiserver_flush_opaque_type_callback); + foreach_lsa (OPAQUE_AREA_LSDB (area), (void *) ¶m, 0, + apiserver_flush_opaque_type_callback); + } + + /* For AS-external opaque LSAs */ + if (ospf_top->lsdb) + { + foreach_lsa (OPAQUE_AS_LSDB (ospf_top), (void *) ¶m, 0, + apiserver_flush_opaque_type_callback); + } +#else /* ORIGINAL_CODING */ + switch (lsa_type) + { + case OSPF_OPAQUE_LINK_LSA: + for (node = listhead (ospf_top->areas); node; nextnode (node)) + { + struct ospf_area *area = node->data; + foreach_lsa (OPAQUE_LINK_LSDB (area), (void *) ¶m, 0, + apiserver_flush_opaque_type_callback); + } + break; + case OSPF_OPAQUE_AREA_LSA: + for (node = listhead (ospf_top->areas); node; nextnode (node)) + { + struct ospf_area *area = node->data; + foreach_lsa (OPAQUE_AREA_LSDB (area), (void *) ¶m, 0, + apiserver_flush_opaque_type_callback); + } + break; + case OSPF_OPAQUE_AS_LSA: + foreach_lsa (OPAQUE_AS_LSDB (ospf_top), (void *) ¶m, 0, + apiserver_flush_opaque_type_callback); + break; + default: + break; + } + return; +#endif /* ORIGINAL_CODING */ +} + + +/* ----------------------------------------------------------- + * 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); + assert (oi); + + /* 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_info (" Added using OSPF API: %u octets of opaque data %s", + opaquelen, + VALID_OPAQUE_INFO_LEN (lsa-> + data) ? "" : "(Invalid length?)"); + zlog_info (" Opaque data: "); + + for (i = 0; i < opaquelen; i++) + { + zlog_info ("0x%x ", olsa->data[i]); + } + zlog_info ("\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) +{ + listnode node; + + /* Send message to all clients */ + for (node = listhead (apiserver_list); node; nextnode (node)) + { + struct ospf_apiserver *apiserv = + (struct ospf_apiserver *) getdata (node); + + 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) +{ + listnode node; + struct msg *msg; + + 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 (node = listhead (apiserver_list); node; nextnode (node)) + { + struct ospf_apiserver *apiserv = + (struct ospf_apiserver *) getdata (node); + listnode n2; + + for (n2 = listhead (apiserv->opaque_types); n2; nextnode (n2)) + { + struct registered_opaque_type *r = + (struct registered_opaque_type *) getdata (n2); + 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) +{ + listnode node; + struct msg *msg; + + assert (area); + + if (!ospf_apiserver_is_ready_type10 (area)) + { + zlog_warn ("Area not ready for type 10?"); + return; + } + + for (node = listhead (apiserver_list); node; nextnode (node)) + { + struct ospf_apiserver *apiserv = + (struct ospf_apiserver *) getdata (node); + listnode n2; + + for (n2 = listhead (apiserv->opaque_types); n2; nextnode (n2)) + { + struct registered_opaque_type *r = + (struct registered_opaque_type *) getdata (n2); + 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) +{ + listnode node; + struct msg *msg; + struct in_addr id_null = { 0L }; + + assert (top); + + if (!ospf_apiserver_is_ready_type11 (top)) + { + zlog_warn ("AS not ready for type 11?"); + return; + } + + for (node = listhead (apiserver_list); node; nextnode (node)) + { + struct ospf_apiserver *apiserv = + (struct ospf_apiserver *) getdata (node); + listnode n2; + + for (n2 = listhead (apiserv->opaque_types); n2; nextnode (n2)) + { + struct registered_opaque_type *r = + (struct registered_opaque_type *) getdata (n2); + 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 = { 0L }; + struct in_addr area_id = { 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->ifp->status); + 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 = { 0L }; + struct in_addr nbraddr = { 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); +} + +void +apiserver_clients_lsa_change_notify (u_char msgtype, struct ospf_lsa *lsa) +{ + struct msg *msg; + listnode node; + + /* Default area for AS-External and Opaque11 LSAs */ + struct in_addr area_id = { 0L }; + + /* Default interface for non Opaque9 LSAs */ + struct in_addr ifaddr = { 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 (node = listhead (apiserver_list); node; nextnode (node)) + { + struct ospf_apiserver *apiserv = (struct ospf_apiserver *) node->data; + 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 + * ------------------------------------------------------------- + */ + + +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 = { 0L }; + + /* default interface for non Opaque9 LSAs */ + struct in_addr ifaddr = { 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 00000000..c7145782 --- /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. */ + 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. + * ----------------------------------------------------------- + */ + +unsigned short ospf_apiserver_getport (void); +int ospf_apiserver_init (void); +void ospf_apiserver_term (void); +struct ospf_apiserver *ospf_apiserver_new (int fd_sync, int fd_async); +void ospf_apiserver_free (struct ospf_apiserver *apiserv); +void ospf_apiserver_event (enum event event, int fd, + struct ospf_apiserver *apiserv); +int ospf_apiserver_serv_sock_family (unsigned short port, int family); +int ospf_apiserver_accept (struct thread *thread); +int ospf_apiserver_read (struct thread *thread); +int ospf_apiserver_sync_write (struct thread *thread); +int ospf_apiserver_async_write (struct thread *thread); +int ospf_apiserver_send_reply (struct ospf_apiserver *apiserv, + u_int32_t seqnr, u_char rc); + +/* ----------------------------------------------------------- + * Followings are message handler functions + * ----------------------------------------------------------- + */ + +int ospf_apiserver_lsa9_originator (void *arg); +int ospf_apiserver_lsa10_originator (void *arg); +int ospf_apiserver_lsa11_originator (void *arg); + +void ospf_apiserver_clients_notify_all (struct msg *msg); + +void ospf_apiserver_clients_notify_ready_type9 (struct ospf_interface *oi); +void ospf_apiserver_clients_notify_ready_type10 (struct ospf_area *area); +void ospf_apiserver_clients_notify_ready_type11 (struct ospf *top); + +void ospf_apiserver_clients_notify_new_if (struct ospf_interface *oi); +void ospf_apiserver_clients_notify_del_if (struct ospf_interface *oi); +void ospf_apiserver_clients_notify_ism_change (struct ospf_interface *oi); +void ospf_apiserver_clients_notify_nsm_change (struct ospf_neighbor *nbr); + +int ospf_apiserver_is_ready_type9 (struct ospf_interface *oi); +int ospf_apiserver_is_ready_type10 (struct ospf_area *area); +int ospf_apiserver_is_ready_type11 (struct ospf *ospf); + +void ospf_apiserver_notify_ready_type9 (struct ospf_apiserver *apiserv); +void ospf_apiserver_notify_ready_type10 (struct ospf_apiserver *apiserv); +void ospf_apiserver_notify_ready_type11 (struct ospf_apiserver *apiserv); + +int ospf_apiserver_handle_msg (struct ospf_apiserver *apiserv, + struct msg *msg); +int ospf_apiserver_handle_register_opaque_type (struct ospf_apiserver + *apiserv, struct msg *msg); +int ospf_apiserver_handle_unregister_opaque_type (struct ospf_apiserver + *apiserv, struct msg *msg); +int ospf_apiserver_handle_register_event (struct ospf_apiserver *apiserv, + struct msg *msg); +int ospf_apiserver_handle_originate_request (struct ospf_apiserver *apiserv, + struct msg *msg); +int ospf_apiserver_handle_delete_request (struct ospf_apiserver *apiserv, + struct msg *msg); +int ospf_apiserver_handle_sync_lsdb (struct ospf_apiserver *apiserv, + struct msg *msg); + + +/* ----------------------------------------------------------- + * Followings are functions for LSA origination/deletion + * ----------------------------------------------------------- + */ + +int ospf_apiserver_register_opaque_type (struct ospf_apiserver *apiserver, + u_char lsa_type, u_char opaque_type); +int ospf_apiserver_unregister_opaque_type (struct ospf_apiserver *apiserver, + u_char lsa_type, + u_char opaque_type); +struct ospf_lsa *ospf_apiserver_opaque_lsa_new (struct ospf_area *area, + struct ospf_interface *oi, + struct lsa_header *protolsa); +struct ospf_interface *ospf_apiserver_if_lookup_by_addr (struct in_addr + address); +struct ospf_interface *ospf_apiserver_if_lookup_by_ifp (struct interface + *ifp); +int ospf_apiserver_originate1 (struct ospf_lsa *lsa); +void ospf_apiserver_flood_opaque_lsa (struct ospf_lsa *lsa); + + +/* ----------------------------------------------------------- + * Followings are callback functions to handle opaque types + * ----------------------------------------------------------- + */ + +int ospf_apiserver_new_if (struct interface *ifp); +int ospf_apiserver_del_if (struct interface *ifp); +void ospf_apiserver_ism_change (struct ospf_interface *oi, int old_status); +void ospf_apiserver_nsm_change (struct ospf_neighbor *nbr, int old_status); +void ospf_apiserver_config_write_router (struct vty *vty); +void ospf_apiserver_config_write_if (struct vty *vty, struct interface *ifp); +void ospf_apiserver_show_info (struct vty *vty, struct ospf_lsa *lsa); +int ospf_ospf_apiserver_lsa_originator (void *arg); +void ospf_apiserver_lsa_refresher (struct ospf_lsa *lsa); +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 */ + +int ospf_apiserver_lsa_update (struct ospf_lsa *lsa); +int ospf_apiserver_lsa_delete (struct ospf_lsa *lsa); + +void ospf_apiserver_clients_lsa_change_notify (u_char msgtype, + struct ospf_lsa *lsa); + +#endif /* _OSPF_APISERVER_H */ -- cgit v1.2.1