From 83f7687a91638a399621f7fbb5b832d84d53c57f Mon Sep 17 00:00:00 2001 From: p4u Date: Thu, 4 Apr 2013 13:56:25 +0200 Subject: [PATCH 1/1] First commit with the existing packages --- packages/bmx6-luci/COPYING | 339 ++ packages/bmx6-luci/Makefile | 62 + packages/bmx6-luci/files/etc/config/luci-bmx6 | 7 + .../usr/lib/lua/luci/controller/bmx6.lua | 329 ++ .../files/usr/lib/lua/luci/model/bmx6json.lua | 219 ++ .../lib/lua/luci/model/cbi/bmx6/advanced.lua | 74 + .../usr/lib/lua/luci/model/cbi/bmx6/hna.lua | 47 + .../lua/luci/model/cbi/bmx6/interfaces.lua | 77 + .../usr/lib/lua/luci/model/cbi/bmx6/main.lua | 108 + .../lib/lua/luci/model/cbi/bmx6/plugins.lua | 50 + .../lib/lua/luci/model/cbi/bmx6/tunnels.lua | 75 + .../admin_status/index/neighbours_simple.htm | 108 + .../files/usr/lib/lua/luci/view/bmx6/chat.htm | 35 + .../usr/lib/lua/luci/view/bmx6/error.htm | 10 + .../usr/lib/lua/luci/view/bmx6/gateways_j.htm | 120 + .../usr/lib/lua/luci/view/bmx6/graph.htm | 110 + .../usr/lib/lua/luci/view/bmx6/interfaces.htm | 59 + .../usr/lib/lua/luci/view/bmx6/links.htm | 55 + .../usr/lib/lua/luci/view/bmx6/neighbours.htm | 89 + .../lib/lua/luci/view/bmx6/neighbours_j.htm | 188 + .../usr/lib/lua/luci/view/bmx6/nodes.htm | 87 + .../usr/lib/lua/luci/view/bmx6/nodes_j.htm | 193 + .../usr/lib/lua/luci/view/bmx6/status.htm | 69 + .../usr/lib/lua/luci/view/bmx6/status_j.htm | 114 + .../usr/lib/lua/luci/view/bmx6/tunnels_j.htm | 107 + .../usr/lib/lua/luci/view/bmx6/wireless.htm | 7 + .../bmx6-luci/files/www/cgi-bin/bmx6-info | 112 + .../luci-static/resources/bmx6/bmx6logo.png | Bin 0 -> 4986 bytes .../resources/bmx6/js/Curry-1.0.1.js | 29 + .../resources/bmx6/js/dracula_algorithms.js | 599 ++++ .../resources/bmx6/js/dracula_graffle.js | 106 + .../resources/bmx6/js/dracula_graph.js | 527 +++ .../resources/bmx6/js/jquery-1.4.2.min.js | 154 + .../luci-static/resources/bmx6/js/polling.js | 81 + .../resources/bmx6/js/raphael-min.js | 7 + .../resources/bmx6/js/seedrandom.js | 266 ++ .../www/luci-static/resources/bmx6/link.png | Bin 0 -> 2910 bytes .../www/luci-static/resources/bmx6/style.css | 22 + .../www/luci-static/resources/bmx6/wifi.png | Bin 0 -> 3551 bytes .../www/luci-static/resources/bmx6/world.png | Bin 0 -> 1885 bytes .../resources/bmx6/world_small.png | Bin 0 -> 923 bytes packages/bmx6-qmp/Makefile | 157 + packages/bmx6-qmp/files/etc/config/bmx6 | 82 + packages/bmx6-qmp/files/etc/init.d/bmx6 | 41 + packages/qmp-quagga/Makefile | 312 ++ packages/qmp-quagga/files/quagga | 335 ++ packages/qmp-quagga/files/quagga.conf | 7 + packages/qmp-quagga/files/quagga.init | 11 + .../patches/110-fix_ipctl_forwarding.patch | 25 + .../qmp-quagga/patches/120-quagga_manet.patch | 243 ++ .../qmp-quagga/patches/121-quagga-bmx6.patch | 127 + packages/qmp-quagga/patches/130-fix_cpp.patch | 11 + .../patches/140-holdtimer-set.patch | 22 + .../patches/150-no-cross-fs-link.patch | 40 + packages/qmp-quagga/patches/160-pgbgp.patch | 3104 +++++++++++++++++ .../qmp-quagga/patches/161-pgbgp-addon.patch | 318 ++ .../patches/170-use-supported-pagers.patch | 29 + 57 files changed, 9505 insertions(+) create mode 100644 packages/bmx6-luci/COPYING create mode 100644 packages/bmx6-luci/Makefile create mode 100644 packages/bmx6-luci/files/etc/config/luci-bmx6 create mode 100644 packages/bmx6-luci/files/usr/lib/lua/luci/controller/bmx6.lua create mode 100644 packages/bmx6-luci/files/usr/lib/lua/luci/model/bmx6json.lua create mode 100644 packages/bmx6-luci/files/usr/lib/lua/luci/model/cbi/bmx6/advanced.lua create mode 100644 packages/bmx6-luci/files/usr/lib/lua/luci/model/cbi/bmx6/hna.lua create mode 100644 packages/bmx6-luci/files/usr/lib/lua/luci/model/cbi/bmx6/interfaces.lua create mode 100644 packages/bmx6-luci/files/usr/lib/lua/luci/model/cbi/bmx6/main.lua create mode 100644 packages/bmx6-luci/files/usr/lib/lua/luci/model/cbi/bmx6/plugins.lua create mode 100644 packages/bmx6-luci/files/usr/lib/lua/luci/model/cbi/bmx6/tunnels.lua create mode 100644 packages/bmx6-luci/files/usr/lib/lua/luci/view/admin_status/index/neighbours_simple.htm create mode 100644 packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/chat.htm create mode 100644 packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/error.htm create mode 100644 packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/gateways_j.htm create mode 100644 packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/graph.htm create mode 100644 packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/interfaces.htm create mode 100644 packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/links.htm create mode 100644 packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/neighbours.htm create mode 100644 packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/neighbours_j.htm create mode 100644 packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/nodes.htm create mode 100644 packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/nodes_j.htm create mode 100644 packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/status.htm create mode 100644 packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/status_j.htm create mode 100644 packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/tunnels_j.htm create mode 100644 packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/wireless.htm create mode 100644 packages/bmx6-luci/files/www/cgi-bin/bmx6-info create mode 100644 packages/bmx6-luci/files/www/luci-static/resources/bmx6/bmx6logo.png create mode 100644 packages/bmx6-luci/files/www/luci-static/resources/bmx6/js/Curry-1.0.1.js create mode 100644 packages/bmx6-luci/files/www/luci-static/resources/bmx6/js/dracula_algorithms.js create mode 100644 packages/bmx6-luci/files/www/luci-static/resources/bmx6/js/dracula_graffle.js create mode 100644 packages/bmx6-luci/files/www/luci-static/resources/bmx6/js/dracula_graph.js create mode 100644 packages/bmx6-luci/files/www/luci-static/resources/bmx6/js/jquery-1.4.2.min.js create mode 100644 packages/bmx6-luci/files/www/luci-static/resources/bmx6/js/polling.js create mode 100644 packages/bmx6-luci/files/www/luci-static/resources/bmx6/js/raphael-min.js create mode 100644 packages/bmx6-luci/files/www/luci-static/resources/bmx6/js/seedrandom.js create mode 100644 packages/bmx6-luci/files/www/luci-static/resources/bmx6/link.png create mode 100644 packages/bmx6-luci/files/www/luci-static/resources/bmx6/style.css create mode 100644 packages/bmx6-luci/files/www/luci-static/resources/bmx6/wifi.png create mode 100644 packages/bmx6-luci/files/www/luci-static/resources/bmx6/world.png create mode 100644 packages/bmx6-luci/files/www/luci-static/resources/bmx6/world_small.png create mode 100644 packages/bmx6-qmp/Makefile create mode 100644 packages/bmx6-qmp/files/etc/config/bmx6 create mode 100755 packages/bmx6-qmp/files/etc/init.d/bmx6 create mode 100644 packages/qmp-quagga/Makefile create mode 100644 packages/qmp-quagga/files/quagga create mode 100644 packages/qmp-quagga/files/quagga.conf create mode 100644 packages/qmp-quagga/files/quagga.init create mode 100644 packages/qmp-quagga/patches/110-fix_ipctl_forwarding.patch create mode 100644 packages/qmp-quagga/patches/120-quagga_manet.patch create mode 100644 packages/qmp-quagga/patches/121-quagga-bmx6.patch create mode 100644 packages/qmp-quagga/patches/130-fix_cpp.patch create mode 100644 packages/qmp-quagga/patches/140-holdtimer-set.patch create mode 100644 packages/qmp-quagga/patches/150-no-cross-fs-link.patch create mode 100644 packages/qmp-quagga/patches/160-pgbgp.patch create mode 100644 packages/qmp-quagga/patches/161-pgbgp-addon.patch create mode 100644 packages/qmp-quagga/patches/170-use-supported-pagers.patch diff --git a/packages/bmx6-luci/COPYING b/packages/bmx6-luci/COPYING new file mode 100644 index 0000000..d511905 --- /dev/null +++ b/packages/bmx6-luci/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/packages/bmx6-luci/Makefile b/packages/bmx6-luci/Makefile new file mode 100644 index 0000000..4fd86b8 --- /dev/null +++ b/packages/bmx6-luci/Makefile @@ -0,0 +1,62 @@ +# Copyright (C) 2011 Pau Escrich +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# The full GNU General Public License is included in this distribution in +# the file called "COPYING". + +include $(TOPDIR)/rules.mk + +PKG_NAME:=bmx6-luci +PKG_RELEASE:=2 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(INCLUDE_DIR)/package.mk + +define Package/bmx6-luci + SECTION:=luci + CATEGORY:=LuCI + SUBMENU:=3. Applications + TITLE:= Bmx6 configuration, status and visualization module +# DEPENDS:=+bmx6 +bmx6-uci-config + DEPENDS:=+luci-lib-json +luci-mod-admin-core +luci-lib-httpclient +endef + +define Package/bmx6-luci/description + bmx6 web module for LuCi web interface +endef + +define Package/bmx6-luci/conffiles + /etc/config/luci-bmx6 +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/bmx6-luci/install + $(CP) ./files/* $(1)/ + chmod 755 $(1)/www/cgi-bin/bmx6-info +endef + +$(eval $(call BuildPackage,bmx6-luci)) + diff --git a/packages/bmx6-luci/files/etc/config/luci-bmx6 b/packages/bmx6-luci/files/etc/config/luci-bmx6 new file mode 100644 index 0000000..f70205b --- /dev/null +++ b/packages/bmx6-luci/files/etc/config/luci-bmx6 @@ -0,0 +1,7 @@ +config 'bmx6' 'luci' + option ignore '0' + #option place 'admin status Bmx6' + option place 'qmp Mesh' + option position '3' + #option json 'http://127.0.0.1/cgi-bin/bmx6-info?' + option json 'exec:/www/cgi-bin/bmx6-info -s' diff --git a/packages/bmx6-luci/files/usr/lib/lua/luci/controller/bmx6.lua b/packages/bmx6-luci/files/usr/lib/lua/luci/controller/bmx6.lua new file mode 100644 index 0000000..cb5642e --- /dev/null +++ b/packages/bmx6-luci/files/usr/lib/lua/luci/controller/bmx6.lua @@ -0,0 +1,329 @@ +--[[ + Copyright (C) 2011 Pau Escrich + Contributors Jo-Philipp Wich + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". +--]] + +local bmx6json = require("luci.model.bmx6json") + +module("luci.controller.bmx6", package.seeall) + +function index() + local place = {} + local ucim = require "luci.model.uci" + local uci = ucim.cursor() + + -- checking if ignore is on + if uci:get("luci-bmx6","luci","ignore") == "1" then + return nil + end + + -- getting value from uci database + local uci_place = uci:get("luci-bmx6","luci","place") + + -- default values + if uci_place == nil then + place = {"bmx6"} + else + local util = require "luci.util" + place = util.split(uci_place," ") + end + + -- getting position of menu + local uci_position = uci:get("luci-bmx6","luci","position") + + --------------------------- + -- Starting with the pages + --------------------------- + + --- status (default) + entry(place,call("action_nodes_j"),place[#place],tonumber(uci_position)) + + table.insert(place,"Status") + entry(place,call("action_status_j"),"Status",0) + table.remove(place) + + -- not visible + table.insert(place,"nodes_nojs") + entry(place, call("action_nodes"), nil) + table.remove(place) + + --- nodes + table.insert(place,"Nodes") + entry(place,call("action_nodes_j"),"Nodes",1) + table.remove(place) + + --- links + table.insert(place,"Links") + entry(place,call("action_links"),"Links",2).leaf = true + table.remove(place) + + -- Tunnels + table.insert(place,"Tunnels") + entry(place,call("action_tunnels_j"), "Tunnels", 3).leaf = true + table.remove(place) + + -- Gateways (deprecated) + --table.insert(place,"Gateways") + --entry(place,call("action_gateways_j"),"Gateways").leaf = true + --table.remove(place) + + --- Chat + table.insert(place,"Chat") + entry(place,call("action_chat"),"Chat",5) + table.remove(place) + + --- Graph + table.insert(place,"Graph") + entry(place, template("bmx6/graph"), "Graph",4) + table.remove(place) + + --- Topology (hidden) + table.insert(place,"topology") + entry(place, call("action_topology"), nil) + table.remove(place) + + --- configuration (CBI) + table.insert(place,"Configuration") + entry(place, cbi("bmx6/main"), "Configuration",6).dependent=false + + table.insert(place,"General") + entry(place, cbi("bmx6/main"), "General",1) + table.remove(place) + + table.insert(place,"Advanced") + entry(place, cbi("bmx6/advanced"), "Advanced",5) + table.remove(place) + + table.insert(place,"Interfaces") + entry(place, cbi("bmx6/interfaces"), "Interfaces",2) + table.remove(place) + + table.insert(place,"Tunnels") + entry(place, cbi("bmx6/tunnels"), "Tunnels",3) + table.remove(place) + + table.insert(place,"Plugins") + entry(place, cbi("bmx6/plugins"), "Plugins",6) + table.remove(place) + + table.insert(place,"HNAv6") + entry(place, cbi("bmx6/hna"), "HNAv6",4) + table.remove(place) + + table.remove(place) + +end + +function action_status() + local status = bmx6json.get("status").status or nil + local interfaces = bmx6json.get("interfaces").interfaces or nil + + if status == nil or interfaces == nil then + luci.template.render("bmx6/error", {txt="Cannot fetch data from bmx6 json"}) + else + luci.template.render("bmx6/status", {status=status,interfaces=interfaces}) + end +end + +function action_status_j() + luci.template.render("bmx6/status_j", {}) +end + + +function action_nodes() + local orig_list = bmx6json.get("originators").originators or nil + + if orig_list == nil then + luci.template.render("bmx6/error", {txt="Cannot fetch data from bmx6 json"}) + return nil + end + + local originators = {} + local desc = nil + local orig = nil + local name = "" + local ipv4 = "" + + for _,o in ipairs(orig_list) do + orig = bmx6json.get("originators/"..o.name) or {} + desc = bmx6json.get("descriptions/"..o.name) or {} + + if string.find(o.name,'.') then + name = luci.util.split(o.name,'.')[1] + else + name = o.name + end + + table.insert(originators,{name=name,orig=orig,desc=desc}) + end + + luci.template.render("bmx6/nodes", {originators=originators}) +end + +function action_nodes_j() + local http = require "luci.http" + local link_non_js = "/cgi-bin/luci" .. http.getenv("PATH_INFO") .. '/nodes_nojs' + + luci.template.render("bmx6/nodes_j", {link_non_js=link_non_js}) +end + +function action_gateways_j() + luci.template.render("bmx6/gateways_j", {}) +end + +function action_tunnels_j() + luci.template.render("bmx6/tunnels_j", {}) +end + + +function action_links(host) + local links = bmx6json.get("links", host) + local devlinks = {} + local _,l + + if links ~= nil then + links = links.links + for _,l in ipairs(links) do + devlinks[l.viaDev] = {} + end + for _,l in ipairs(links) do + l.globalId = luci.util.split(l.globalId,'.')[1] + table.insert(devlinks[l.viaDev],l) + end + end + + luci.template.render("bmx6/links", {links=devlinks}) +end + +function action_topology() + local originators = bmx6json.get("originators/all") + local o,i,l,i2 + local first = true + local topology = '[ ' + local cache = '/tmp/bmx6-topology.json' + local offset = 60 + + local cachefd = io.open(cache,r) + local update = false + + if cachefd ~= nil then + local lastupdate = tonumber(cachefd:read("*line")) or 0 + if os.time() >= lastupdate + offset then + update = true + else + topology = cachefd:read("*all") + end + cachefd:close() + end + + if cachefd == nil or update then + for i,o in ipairs(originators) do + local links = bmx6json.get("links",o.primaryIp) + if links then + if first then + first = false + else + topology = topology .. ', ' + end + + topology = topology .. '{ "globalId": "%s", "links": [' %o.globalId:match("^[^%.]+") + + local first2 = true + + for i2,l in ipairs(links.links) do + if first2 then + first2 = false + else + topology = topology .. ', ' + end + + topology = topology .. '{ "globalId": "%s", "rxRate": %s, "txRate": %s }' + %{ l.globalId:match("^[^%.]+"), l.rxRate, l.txRate } + + end + + topology = topology .. ']}' + end + + end + + topology = topology .. ' ]' + + -- Upgrading the content of the cache file + cachefd = io.open(cache,'w+') + cachefd:write(os.time()..'\n') + cachefd:write(topology) + cachefd:close() + end + + luci.http.prepare_content("application/json") + luci.http.write(topology) +end + + +function action_chat() + local sms_dir = "/var/run/bmx6/sms" + local rcvd_dir = sms_dir .. "/rcvdSms" + local send_file = sms_dir .. "/sendSms/chat" + local sms_list = bmx6json.get("rcvdSms") + local sender = "" + local sms_file = "" + local chat = {} + local to_send = nil + local sent = "" + local fd = nil + + if luci.sys.call("test -d " .. sms_dir) ~= 0 then + luci.template.render("bmx6/error", {txt="sms plugin disabled or some problem with directory " .. sms_dir}) + return nil + end + + sms_list = luci.util.split(luci.util.exec("ls "..rcvd_dir.."/*:chat")) + + for _,sms_path in ipairs(sms_list) do + if #sms_path > #rcvd_dir then + sms_file = luci.util.split(sms_path,'/') + sms_file = sms_file[#sms_file] + sender = luci.util.split(sms_file,':')[1] + + -- Trying to clean the name + if string.find(sender,".") ~= nil then + sender = luci.util.split(sender,".")[1] + end + + fd = io.open(sms_path,"r") + chat[sender] = fd:read() + fd:close() + end + end + + to_send = luci.http.formvalue("toSend") + if to_send ~= nil and #to_send > 1 then + fd = io.open(send_file,"w") + fd:write(to_send) + fd:close() + sent = to_send + else + sent = luci.util.exec("cat "..send_file) + end + + luci.template.render("bmx6/chat", {chat=chat,sent=sent}) +end + diff --git a/packages/bmx6-luci/files/usr/lib/lua/luci/model/bmx6json.lua b/packages/bmx6-luci/files/usr/lib/lua/luci/model/bmx6json.lua new file mode 100644 index 0000000..dfe9ab1 --- /dev/null +++ b/packages/bmx6-luci/files/usr/lib/lua/luci/model/bmx6json.lua @@ -0,0 +1,219 @@ +--[[ + Copyright (C) 2011 Pau Escrich + Contributors Jo-Philipp Wich + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". +--]] + +local ltn12 = require("luci.ltn12") +local json = require("luci.json") +local util = require("luci.util") +local uci = require("luci.model.uci") +local sys = require("luci.sys") +local template = require("luci.template") +local http = require("luci.http") +local string = require("string") +local table = require("table") +local nixio = require("nixio") +local nixiofs = require("nixio.fs") +local ipairs = ipairs + +module "luci.model.bmx6json" + +-- Returns a LUA object from bmx6 JSON daemon + +function get(field, host) + local url + if host ~= nil then + if host:match(":") then + url = 'http://[%s]/cgi-bin/bmx6-info?' % host + else + url = 'http://%s/cgi-bin/bmx6-info?' % host + end + else + url = uci.cursor():get("luci-bmx6","luci","json") + end + + if url == nil then + print_error("bmx6 json url not configured, cannot fetch bmx6 daemon data",true) + return nil + end + + local json_url = util.split(url,":") + local raw = "" + + if json_url[1] == "http" then + raw,err = wget(url..field,1000) + else + + if json_url[1] == "exec" then + raw = sys.exec(json_url[2]..' '..field) + else + print_error("bmx6 json url not recognized, cannot fetch bmx6 daemon data. Use http: or exec:",true) + return nil + end + + end + + local data = nil + + if raw and raw:len() > 10 then + local decoder = json.Decoder() + ltn12.pump.all(ltn12.source.string(raw), decoder:sink()) + data = decoder:get() +-- else +-- print_error("Cannot get data from bmx6 daemon",true) +-- return nil + end + + return data +end + +function print_error(txt,popup) + util.perror(txt) + sys.call("logger -t bmx6json " .. txt) + + if popup then + http.write('') + else + http.write("

Dammit! some error detected

") + http.write("bmx6-luci: " .. txt) + http.write('

') + end + +end + +function text2html(txt) + txt = string.gsub(txt,"<","{") + txt = string.gsub(txt,">","}") + txt = util.striptags(txt) + return txt +end + + +function wget(url, timeout) + local rfd, wfd = nixio.pipe() + local pid = nixio.fork() + if pid == 0 then + rfd:close() + nixio.dup(wfd, nixio.stdout) + + local candidates = { "/usr/bin/wget", "/bin/wget" } + local _, bin + for _, bin in ipairs(candidates) do + if nixiofs.access(bin, "x") then + nixio.exec(bin, "-q", "-O", "-", url) + end + end + return + else + wfd:close() + rfd:setblocking(false) + + local buffer = { } + local err1, err2 + + while true do + local ready = nixio.poll({{ fd = rfd, events = nixio.poll_flags("in") }}, timeout) + if not ready then + nixio.kill(pid, nixio.const.SIGKILL) + err1 = "timeout" + break + end + + local rv = rfd:read(4096) + if rv then + -- eof + if #rv == 0 then + break + end + + buffer[#buffer+1] = rv + else + -- error + if nixio.errno() ~= nixio.const.EAGAIN and + nixio.errno() ~= nixio.const.EWOULDBLOCK then + err1 = "error" + err2 = nixio.errno() + end + end + end + + nixio.waitpid(pid, "nohang") + if not err1 then + return table.concat(buffer) + else + return nil, err1, err2 + end + end +end + +function getOptions(name) + -- Getting json and Checking if bmx6-json is avaiable + local options = get("options") + if options == nil or options.OPTIONS == nil then + m.message = "bmx6-json plugin is not running or some mistake in luci-bmx6 configuration, check /etc/config/luci-bmx6" + return nil + else + options = options.OPTIONS + end + + -- Filtering by the option name + local i,_ + local namedopt = nil + if name ~= nil then + for _,i in ipairs(options) do + if i.name == name and i.CHILD_OPTIONS ~= nil then + namedopt = i.CHILD_OPTIONS + break + end + end + end + + return namedopt +end + +-- Rturns a help string formated to be used in HTML scope +function getHtmlHelp(opt) + if opt == nil then return nil end + + local help = "" + if opt.help ~= nil then + help = text2html(opt.help) + end + if opt.syntax ~= nil then + help = help .. "
Syntax: " .. text2html(opt.syntax) + end + + return help +end + +function testandreload() + local test = sys.call('bmx6 -c --test > /tmp/bmx6-luci.err.tmp') + if test ~= 0 then + return sys.exec("cat /tmp/bmx6-luci.err.tmp") + end + + local err = sys.call('bmx6 -c --configReload > /tmp/bmx6-luci.err.tmp') + if err ~= 0 then + return sys.exec("cat /tmp/bmx6-luci.err.tmp") + end + + return nil +end + diff --git a/packages/bmx6-luci/files/usr/lib/lua/luci/model/cbi/bmx6/advanced.lua b/packages/bmx6-luci/files/usr/lib/lua/luci/model/cbi/bmx6/advanced.lua new file mode 100644 index 0000000..9510214 --- /dev/null +++ b/packages/bmx6-luci/files/usr/lib/lua/luci/model/cbi/bmx6/advanced.lua @@ -0,0 +1,74 @@ +--[[ + Copyright (C) 2011 Pau Escrich + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". +--]] + +m = Map("bmx6", "bmx6") + +local bmx6json = require("luci.model.bmx6json") +local util = require("luci.util") +local http = require("luci.http") +local sys = require("luci.sys") + +local options = bmx6json.get("options") +if options == nil or options.OPTIONS == nil then + m.message = "bmx6-json plugin is not running or some mistake in luci-bmx6 configuration, check /etc/config/luci-bmx6" + options = {} +else + options = options.OPTIONS +end + +local general = m:section(NamedSection,"general","general","General Options") + +local name = "" +local help = "" +local value = nil +local _,o + +for _,o in ipairs(options) do + if o.name ~= nil and o.CHILD_OPTIONS == nil and o.configurable == 1 then + help = "" + name = o.name + + if o.help ~= nil then + help = bmx6json.text2html(o.help) + end + + if o.syntax ~= nil then + help = help .. "
Syntax: " .. bmx6json.text2html(o.syntax) + end + + if o.def ~= nil then + help = help .. " Default: " .. o.def + end + + value = general:option(Value,name,name,help) + + end +end + +function m.on_commit(self,map) + local err = sys.call('bmx6 -c --configReload > /tmp/bmx6-luci.err.tmp') + if err ~= 0 then + m.message = sys.exec("cat /tmp/bmx6-luci.err.tmp") + end +end + +return m + diff --git a/packages/bmx6-luci/files/usr/lib/lua/luci/model/cbi/bmx6/hna.lua b/packages/bmx6-luci/files/usr/lib/lua/luci/model/cbi/bmx6/hna.lua new file mode 100644 index 0000000..db98ae6 --- /dev/null +++ b/packages/bmx6-luci/files/usr/lib/lua/luci/model/cbi/bmx6/hna.lua @@ -0,0 +1,47 @@ +--[[ + Copyright (C) 2011 Pau Escrich + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". +--]] + +local sys = require("luci.sys") + +m = Map("bmx6", "bmx6") + +local hna = m:section(TypedSection,"unicastHna","IPv6 HNA") +hna.addremove = true +hna.anonymous = true +local hna_option = hna:option(Value,"unicastHna", "IPv6 Host Network Announcement. Syntax /") + +--function hna_option:validate(value) +-- local err = sys.call('bmx6 -c --test -a ' .. value) +-- if err ~= 0 then +-- return nil +-- end +-- return value +--end + +function m.on_commit(self,map) + local err = sys.call('bmx6 -c --configReload > /tmp/bmx6-luci.err.tmp') + if err ~= 0 then + m.message = sys.exec("cat /tmp/bmx6-luci.err.tmp") + end +end + +return m + diff --git a/packages/bmx6-luci/files/usr/lib/lua/luci/model/cbi/bmx6/interfaces.lua b/packages/bmx6-luci/files/usr/lib/lua/luci/model/cbi/bmx6/interfaces.lua new file mode 100644 index 0000000..fb1261b --- /dev/null +++ b/packages/bmx6-luci/files/usr/lib/lua/luci/model/cbi/bmx6/interfaces.lua @@ -0,0 +1,77 @@ +--[[ + Copyright (C) 2011 Pau Escrich + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". +--]] + +local sys = require("luci.sys") +local bmx6json = require("luci.model.bmx6json") +local m = Map("bmx6", "bmx6") + +local eth_int = sys.net.devices() +local interfaces = m:section(TypedSection,"dev","Devices","") +interfaces.addremove = true +interfaces.anonymous = true + +local intlv = interfaces:option(ListValue,"dev","Device") + +for _,i in ipairs(eth_int) do + intlv:value(i,i) +end + +-- Getting json and looking for device section +local json = bmx6json.get("options") + +if json == nil or json.OPTIONS == nil then + m.message = "bmx6-json plugin is not running or some mistake in luci-bmx6 configuration, check /etc/config/luci-bmx6" + json = {} +else + json = json.OPTIONS +end + +local dev = {} +for _,j in ipairs(json) do + if j.name == "dev" and j.CHILD_OPTIONS ~= nil then + dev = j.CHILD_OPTIONS + break + end +end + +local help = "" +local name = "" + +for _,o in ipairs(dev) do + if o.name ~= nil then + help = "" + name = o.name + if o.help ~= nil then + help = bmx6json.text2html(o.help) + end + + if o.syntax ~= nil then + help = help .. "
Syntax: " .. bmx6json.text2html(o.syntax) + end + + value = interfaces:option(Value,name,name,help) + value.optional = true + end +end + + +return m + diff --git a/packages/bmx6-luci/files/usr/lib/lua/luci/model/cbi/bmx6/main.lua b/packages/bmx6-luci/files/usr/lib/lua/luci/model/cbi/bmx6/main.lua new file mode 100644 index 0000000..8c114bf --- /dev/null +++ b/packages/bmx6-luci/files/usr/lib/lua/luci/model/cbi/bmx6/main.lua @@ -0,0 +1,108 @@ +--[[ + Copyright (C) 2011 Pau Escrich + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". +--]] + +local sys = require("luci.sys") +local bmx6json = require("luci.model.bmx6json") + +m = Map("bmx6", "bmx6") + +-- Getting json and Checking if bmx6-json is avaiable +local options = bmx6json.get("options") +if options == nil or options.OPTIONS == nil then + m.message = "bmx6-json plugin is not running or some mistake in luci-bmx6 configuration, check /etc/config/luci-bmx6" +else + options = options.OPTIONS +end + +-- Getting a list of interfaces +local eth_int = luci.sys.net.devices() + +-- Getting the most important options from general +local general = m:section(NamedSection,"general","general","General") +general.addremove = false +general:option(Value,"globalPrefix","Global ip prefix","Specify global prefix for interfaces: NETADDR/LENGTH. If you are using IPv6 leave blank to let bmx6 autoassign an ULA IPv6 address.") + +if m:get("ipVersion","ipVersion") == "6" then + general:option(Value,"tun4Address","IPv4 address or range","specify default IPv4 tunnel address and announced range") +end + +-- IP section +-- ipVersion section is important, we are allways showing it +local ipV = m:section(NamedSection,"ipVersion","ipVersion","IP options") +ipV.addremove = false +local lipv = ipV:option(ListValue,"ipVersion","IP version") +lipv:value("4","4") +lipv:value("6","6") +lipv.default = "6" + +-- rest of ip options are optional, getting them from json +local ipoptions = {} +for _,o in ipairs(options) do + if o.name == "ipVersion" and o.CHILD_OPTIONS ~= nil then + ipoptions = o.CHILD_OPTIONS + break + end +end + +local help = "" +local name = "" +local value = nil + +for _,o in ipairs(ipoptions) do + if o.name ~= nil then + help = "" + name = o.name + if o.help ~= nil then + help = bmx6json.text2html(o.help) + end + + if o.syntax ~= nil then + help = help .. "
Syntax: " .. bmx6json.text2html(o.syntax) + end + + if o.def ~= nil then + help = help .. "
Default: " .. bmx6json.text2html(o.def) + end + + value = ipV:option(Value,name,name,help) + value.optional = true + end +end + +-- Interfaces section +local interfaces = m:section(TypedSection,"dev","Devices","") +interfaces.addremove = true +interfaces.anonymous = true +local intlv = interfaces:option(ListValue,"dev","Device") + +for _,i in ipairs(eth_int) do + intlv:value(i,i) +end + +function m.on_commit(self,map) + local err = sys.call('bmx6 -c --configReload > /tmp/bmx6-luci.err.tmp') + if err ~= 0 then + m.message = sys.exec("cat /tmp/bmx6-luci.err.tmp") + end +end + +return m + diff --git a/packages/bmx6-luci/files/usr/lib/lua/luci/model/cbi/bmx6/plugins.lua b/packages/bmx6-luci/files/usr/lib/lua/luci/model/cbi/bmx6/plugins.lua new file mode 100644 index 0000000..518864e --- /dev/null +++ b/packages/bmx6-luci/files/usr/lib/lua/luci/model/cbi/bmx6/plugins.lua @@ -0,0 +1,50 @@ +--[[ + Copyright (C) 2011 Pau Escrich + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". +--]] +local sys = require("luci.sys") + +m = Map("bmx6", "bmx6") +plugins_dir = {"/usr/lib/","/var/lib","/lib"} + +plugin = m:section(TypedSection,"plugin","Plugin") +plugin.addremove = true +plugin.anonymous = true +plv = plugin:option(ListValue,"plugin", "Plugin") + +for _,d in ipairs(plugins_dir) do + pl = luci.sys.exec("cd "..d..";ls bmx6_*") + if #pl > 6 then + for _,v in ipairs(luci.util.split(pl,"\n")) do + plv:value(v,v) + end + end +end + + +function m.on_commit(self,map) + local err = sys.call('/etc/init.d/bmx6 restart') + if err ~= 0 then + m.message = sys.exec("Cannot restart bmx6") + end +end + + +return m + diff --git a/packages/bmx6-luci/files/usr/lib/lua/luci/model/cbi/bmx6/tunnels.lua b/packages/bmx6-luci/files/usr/lib/lua/luci/model/cbi/bmx6/tunnels.lua new file mode 100644 index 0000000..7a6bfd3 --- /dev/null +++ b/packages/bmx6-luci/files/usr/lib/lua/luci/model/cbi/bmx6/tunnels.lua @@ -0,0 +1,75 @@ +--[[ + Copyright (C) 2011 Pau Escrich + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". +--]] + +local sys = require("luci.sys") +local bmx6json = require("luci.model.bmx6json") + +m = Map("bmx6", "bmx6") + +-- tunOut +local tunnelsOut = m:section(TypedSection,"tunOut",translate("Networks to fetch"),translate("Tunnel announcements to fetch if possible")) +tunnelsOut.addremove = true +tunnelsOut.anonymous = true +tunnelsOut:option(Value,"tunOut","Name") +tunnelsOut:option(Value,"network", translate("Network to fetch")) + +local tunoptions = bmx6json.getOptions("tunOut") +local _,o +for _,o in ipairs(tunoptions) do + if o.name ~= nil and o.name ~= "network" then + help = bmx6json.getHtmlHelp(o) + value = tunnelsOut:option(Value,o.name,o.name,help) + value.optional = true + end +end + + +--tunIn +local tunnelsIn = m:section(TypedSection,"tunInNet",translate("Networks to offer"),translate("Tunnels to announce in the network")) +tunnelsIn.addremove = true +tunnelsIn.anonymous = true + +local net = tunnelsIn:option(Value,"tunInNet", translate("Network to offer")) +net.default = "10.0.0.0/8" + +local bwd = tunnelsIn:option(Value,"bandwidth",translate("Bandwidth (Bytes)")) +bwd.default = "1000000" + +local tuninoptions = bmx6json.getOptions("tunInNet") +local _,o +for _,o in ipairs(tuninoptions) do + if o.name ~= nil and o.name ~= "tunInNet" and o.name ~= "bandwidth" then + help = bmx6json.getHtmlHelp(o) + value = tunnelsIn:option(Value,o.name,o.name,help) + value.optional = true + end +end + +function m.on_commit(self,map) + --Not working. If test returns error the changes are still commited + local msg = bmx6json.testandreload() + if msg ~= nil then + m.message = msg + end +end + +return m + diff --git a/packages/bmx6-luci/files/usr/lib/lua/luci/view/admin_status/index/neighbours_simple.htm b/packages/bmx6-luci/files/usr/lib/lua/luci/view/admin_status/index/neighbours_simple.htm new file mode 100644 index 0000000..97d6e0e --- /dev/null +++ b/packages/bmx6-luci/files/usr/lib/lua/luci/view/admin_status/index/neighbours_simple.htm @@ -0,0 +1,108 @@ + + +
+ +
+ <%:Mesh nodes%> + + + + + + + + + + + + + +
<%:Hostname%><%:Primary IP%><%:Via Device%><%:Metric%><%:Last Desc%><%:Last Ref%><%:Blocked%>

<%:Collecting data...%>
+
+ +
+ + diff --git a/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/chat.htm b/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/chat.htm new file mode 100644 index 0000000..8e25b11 --- /dev/null +++ b/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/chat.htm @@ -0,0 +1,35 @@ +<%+header%> + +

<%:Chat%>

+

This is sms a chat where all bmx6 nodes can participate. The data is replayed using routing packets, so there is a limit of 2040 bytes. Use it only to send short messages.

+

Each participant can only send one sms at same time.

+
+ +Received SMS +
+
+<% for orig,sms in pairs(chat) do %>
+         <%=orig%>:<%=sms%>
+<% end %>
+
+ +
+ +
+ +
+ +
+ + +
+ +
+ + + + + +
Your last sms:
<%=sent%>
+<%+footer%> + diff --git a/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/error.htm b/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/error.htm new file mode 100644 index 0000000..78de7b9 --- /dev/null +++ b/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/error.htm @@ -0,0 +1,10 @@ +<%+header%> +

<%:ERROR%>

+Some error has occurred +
+
+	<%=txt%>
+
+
+<%+footer%> + diff --git a/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/gateways_j.htm b/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/gateways_j.htm new file mode 100644 index 0000000..12bfcd4 --- /dev/null +++ b/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/gateways_j.htm @@ -0,0 +1,120 @@ +<%+header%> + + + + + +
+ +

Originators

+
+
+ <%:Mesh gateways%> + + + + + + + + + + +
<%:Node%><%:Network%><%:Bandwidth%>

<%:Collecting data...%>
+
+ +
+ +<%+footer%> + diff --git a/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/graph.htm b/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/graph.htm new file mode 100644 index 0000000..a4dabb7 --- /dev/null +++ b/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/graph.htm @@ -0,0 +1,110 @@ +<%# +Copyright (C) 2011 Pau Escrich +Contributors Jo-Philip + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +The full GNU General Public License is included in this distribution in +the file called "COPYING". +-%> + +<% + luci.http.prepare_content("text/html") + + local location = { unpack(luci.dispatcher.context.path) } + location[#location] = "topology" +%> + +<%+header%> + + + + + + + + +
+

+ +<%:Collecting data...%> + +
+ +
+ + + + +<%+footer%> diff --git a/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/interfaces.htm b/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/interfaces.htm new file mode 100644 index 0000000..70935ea --- /dev/null +++ b/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/interfaces.htm @@ -0,0 +1,59 @@ +<%+header%> + + + +

<%:Interfaces%>

+Interfaces where bmx6 is running +
+
+ + + + + + + + + + + +<% for i,v in ipairs(data) do %> + + + + + + + + + + +<%end%> +
NameStateTypeRate (Min/Max)Local IPGlobal IPMulticast IPPrimary
<%=v.devName%><%=v.state%><%=v.type%><%=v.rateMin%>/<%=v.rateMax%><%=v.llocalIp%><%=v.globalIp%><%=v.multicastIp%><%=v.primary%>
+ +
+<%+footer%> diff --git a/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/links.htm b/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/links.htm new file mode 100644 index 0000000..65e62d7 --- /dev/null +++ b/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/links.htm @@ -0,0 +1,55 @@ +<%+header%> + +

<%:Links%>

+
+ + +
+
+<%+footer%> diff --git a/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/neighbours.htm b/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/neighbours.htm new file mode 100644 index 0000000..6474116 --- /dev/null +++ b/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/neighbours.htm @@ -0,0 +1,89 @@ +<%+header%> + + +

<%:Neighbours%>

+ + + + + + + + + + + + + +<% for i,o in ipairs(originators) do %> + + + + + + + + + + + +<%end%> +
NameIPv4IPv6Via DevVia IPRoutesMetricLast DescLast Ref
<%=o.name%><%=o.ipv4%><%=o.orig.primaryIp%><%=o.orig.viaDev%><%=o.orig.viaIp%><%=o.orig.routes%><%=o.orig.metric%><%=o.orig.lastDesc%><%=o.orig.lastRef%>
+ + + + + + + +<% for i,o in ipairs(originators) do %> + + + + +<% end %> +
NodeAnnounced networks
<%=o.name%> + <% if o.desc.DESC_ADV ~= nil then %> + <% for j,h in ipairs(o.desc.DESC_ADV.extensions[2].HNA6_EXTENSION) do %> + <%=h.address%>     + <% end %> + <% end %> +
+ +
+<%+footer%> diff --git a/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/neighbours_j.htm b/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/neighbours_j.htm new file mode 100644 index 0000000..14f5597 --- /dev/null +++ b/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/neighbours_j.htm @@ -0,0 +1,188 @@ +<%+header%> + + + +
+ +

Originators

+
+
+
+ Click to the icon to see individual node information +
+
+ <%:Mesh nodes%> + + + + + + + + + + + + + + +
<%:Hostname%><%:Primary IP%><%:Via Device%><%:Metric%><%:Last Desc%><%:Last Ref%><%:Blocked%>

<%:Collecting data...%>
+
+ +
+Go to non JavaScript view + + +<%+footer%> + diff --git a/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/nodes.htm b/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/nodes.htm new file mode 100644 index 0000000..18e5cc9 --- /dev/null +++ b/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/nodes.htm @@ -0,0 +1,87 @@ +<%+header%> + + +

<%:Nodes%>

+ + + + + + + + + + + + +<% for i,o in ipairs(originators) do %> + + + + + + + + + + +<%end%> +
NameIPv6Via DevVia IPRoutesMetricLast DescLast Ref
<%=o.name%><%=o.orig.primaryIp%><%=o.orig.viaDev%><%=o.orig.viaIp%><%=o.orig.routes%><%=o.orig.metric%><%=o.orig.lastDesc%><%=o.orig.lastRef%>
+ + + + + + + +<% for i,o in ipairs(originators) do %> + + + + +<% end %> +
NodeAnnounced networks
<%=o.name%> + <% if o.desc.DESC_ADV ~= nil then %> + <% for j,h in ipairs(o.desc.DESC_ADV.extensions[2].HNA6_EXTENSION) do %> + <%=h.address%>     + <% end %> + <% end %> +
+ +
+<%+footer%> diff --git a/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/nodes_j.htm b/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/nodes_j.htm new file mode 100644 index 0000000..0435655 --- /dev/null +++ b/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/nodes_j.htm @@ -0,0 +1,193 @@ +<%# + Copyright (C) 2011 Pau Escrich + Contributors Lluis Esquerda + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". +-%> + +<%+header%> + + + + + +
+ +

Node originators

+
+
+
+
+ Click icon to see individual node information +
+
+
+ <%:Mesh nodes%> + + + + + + + + + + + + + + +
<%:Hostname%><%:Primary IP%><%:Via Device%><%:Metric%><%:Last Desc%><%:Last Ref%><%:Blocked%>

<%:Collecting data...%>
+
+ +
+ +Go to non JavaScript view + + + +<%+footer%> + diff --git a/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/status.htm b/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/status.htm new file mode 100644 index 0000000..11e9682 --- /dev/null +++ b/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/status.htm @@ -0,0 +1,69 @@ +<%+header%> + + + + +Bmx6 is a routing protocol for Linux based operating systems. Visit bmx6.net for more info. + +
+
+ +

Status of bmx6

+ + + + + + + + + + + + + + + + + + + + + + + + +
VersionCompatibilityCodeVersionGlobal IdPrimary IpLocal IdUptimeCPUNodes
<%=status.version%><%=status.compatibility%><%=status.codeVersion%><%=status.globalId%>/<%=status.rateMax%><%=status.primaryIp%><%=status.myLocalId%><%=status.uptime%><%=status.cpu%><%=status.nodes%>
+ +
+
+ +

Status of interfaces

+ + + + + + + + + + + +<% for i,v in ipairs(interfaces) do %> + + + + + + + + + + +<%end%> +
NameStateTypeRate (Min/Max)Local IPGlobal IPMulticast IPPrimary
<%=v.devName%><%=v.state%><%=v.type%><%=v.rateMin%>/<%=v.rateMax%><%=v.llocalIp%><%=v.globalIp%><%=v.multicastIp%><%=v.primary%>
+ +
+ +<%+footer%> diff --git a/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/status_j.htm b/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/status_j.htm new file mode 100644 index 0000000..5e51f09 --- /dev/null +++ b/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/status_j.htm @@ -0,0 +1,114 @@ +<%+header%> + + + + + +
+
+ +
+
+a mesh routing protocol for Linux devices.
+Visit bmx6.net for more info. +
+
+
+ +

status

+
+
+ <%:status%> + + + + + + + + + + + + + + + + + +
<%:Version%><%:Compat%><%:Code Version%><%:Global ID%><%:Primary IP%><%:Tun6Address%><%:Tun4Address%><%:Local ID%><%:Uptime%><%:Cpu load%><%:Nodes seen%>

<%:Collecting data...%>
+
+ +
+ + <%:Network devices%> + + + + + + + + + + + + + + +
<%:Name%><%:State%><%:Type%><%:Rate%><%:Local IP%><%:Global IP%><%:Multicast IP%><%:is Primary%>

<%:Collecting data...%>
+
+ +
+ + + + +<%+footer%> + diff --git a/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/tunnels_j.htm b/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/tunnels_j.htm new file mode 100644 index 0000000..1b7ce42 --- /dev/null +++ b/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/tunnels_j.htm @@ -0,0 +1,107 @@ +<%# + Copyright (C) 2011 Pau Escrich + Contributors Lluis Esquerda + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". +-%> + + +<%+header%> + + + + + +
+ +

Gateways tunnel announcements

+
+
+ <%:Mesh gateways%> + + + + + + + + + + + + + + + + + + +
<%:Tunnel%><%:Node%><%:Network%><%:Bandwidth%><%:SearchNet%><%:Type%><%:Path Metric%><%:IP metric%><%:Tun metric%><%:Bonus%><%:search id%>

<%:Collecting data...%>
+
+ +
+ + + +<%+footer%> + diff --git a/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/wireless.htm b/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/wireless.htm new file mode 100644 index 0000000..aa7e19d --- /dev/null +++ b/packages/bmx6-luci/files/usr/lib/lua/luci/view/bmx6/wireless.htm @@ -0,0 +1,7 @@ +<%+header%> +

<%:Wireless%>

+
+

Wireless

+<%=data%> +
+<%+footer%> diff --git a/packages/bmx6-luci/files/www/cgi-bin/bmx6-info b/packages/bmx6-luci/files/www/cgi-bin/bmx6-info new file mode 100644 index 0000000..931cbf6 --- /dev/null +++ b/packages/bmx6-luci/files/www/cgi-bin/bmx6-info @@ -0,0 +1,112 @@ +#!/bin/sh +# Copyright (C) 2011 Pau Escrich +# Contributors Jo-Philipp Wich +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# The full GNU General Public License is included in this distribution in +# the file called "COPYING". +# +# This script gives information about bmx6 +# Can be executed from a linux shell: ./bmx6-info -s links +# Or from web interfae (with cgi enabled): http://host/cgi-bin/bmx6-info?links +# If you ask for a directory you wil get the directory contents in JSON forman + +BMX6_DIR="$(uci get bmx6.general.runtimeDir 2>/dev/null)" || BMX6_DIR="/var/run/bmx6/json" + +#Checking if shell mode or cgi-bin mode +if [ "$1" == "-s" ]; then + QUERY="$2" +else + QUERY="${QUERY_STRING%%=*}" + echo "Content-type: application/json" + echo "" + +fi + +check_path() { + [ -d "$1" ] && path=$(cd $1; pwd) + [ -f "$1" ] && path=$(cd $1/..; pwd) + [ $(echo "$path" | grep -c "^$BMX6_DIR") -ne 1 ] && exit 1 +} + +print_query() { + # If the query is a directory + [ -d "$BMX6_DIR/$1" ] && + { + # If /all has not been specified + [ -z "$QALL" ] && + { + total=$(ls $BMX6_DIR/$1 | wc -w) + i=1 + echo -n "{ \"$1\": [ " + for f in $(ls $BMX6_DIR/$1); do + echo -n "{ \"name\": \"$f\" }" + [ $i -lt $total ] && echo -n ',' + i=$(( $i + 1 )) + done + echo -n " ] }" + + # If /all has been specified, printing all the files together + } || { + comma="" + echo -n "[ " + for entry in "$BMX6_DIR/$1/"*; do + [ -f "$entry" ] && + { + ${comma:+echo "$comma"} + tr -d '\n' < "$entry" + comma="," + } + done + echo -n " ]" + } + } + + # If the query is a file, just printing the file + [ -f "$BMX6_DIR/$QUERY" ] && cat "$BMX6_DIR/$QUERY"; +} + +if [ "${QUERY##*/}" == "all" ]; then + QUERY="${QUERY%/all}" + QALL=1 +fi + + +if [ "$QUERY" == '$neighbours' ]; then + QALL=1 + echo '{ "neighbours": [ ' + echo '{ "originators": ' + print_query originators + echo '}, ' + echo '{ "descriptions": ' + print_query descriptions + echo "} ] }" + exit 0 + +else if [ "$QUERY" == '$tunnels' ]; then + bmx6 -c --jshow tunnels + exit 0 + +else + check_path "$BMX6_DIR/$QUERY" + print_query $QUERY + exit 0 +fi +fi + +ls -1F "$BMX6_DIR" +exit 0 + diff --git a/packages/bmx6-luci/files/www/luci-static/resources/bmx6/bmx6logo.png b/packages/bmx6-luci/files/www/luci-static/resources/bmx6/bmx6logo.png new file mode 100644 index 0000000000000000000000000000000000000000..12f752624ec550c23ad914ed6a78b7bd45483483 GIT binary patch literal 4986 zcmV-=6NT)FP)Fxv=K#|25cTkVuI1`6q ziDCqyl_WSKFd&Gs9A#V&s3Qm{dW3){iV;mZKtM1sg5!cCIOsT{0zn`~L|LRe9nxJ( z*ZO|*$9+X!y}GZus+TNh_@47lcisE$@BZq&-~HX+c1u*17xDia&aP(QMeKUKCo8v7w-a|Uc^p@xGoP@tGv?l0*wNR1myFiRJA;-42YZ#TKGVB$^cH|P z5cQ&G&36=$0ooG)fC1nfU{FLp3w%>mOEbvJfxst#Q##=1QW1IAO!6^Pcy{&QWxD2+ zj&_t2JUB||TS%lMF9elbPLngutcVO^grJT`Fe~uD4#<>%wZMg{Iz9y+4)uBHWKJ6o z`>E>4G-ym88zi>9+<(4^ehQcu=YKlkL}-?lYbCc~CktkDW~95j=mFiY0= zK#RUjM0*RZuJ70B2I&*ht<3l3fO@^7r{wf%Z#8T7m z*o4V59N}B#hho$?06E}Jj0N>KtO78Ci4D98I0BQWxMh}ko)jYT5#VSV1wesO;N-EP zx$``g8wqJ{fzM4>0HAfmaCa1fjlffNLrqo3MdS>O>Ypt5Gy=R$1BodSS%%sFcNs+F z%@{Y=W(ORrs^eor{kH-8hekfwliPavOz;rt9jH>XweL!(>Xxvj?teZ5Xyp@^fcI!9 zGpVX0s`|4TXS7ZD;qyCa7JvfA{d{PAsQ)9t@uB`2a2jW7WhVG&T zd$3NcgmF1{iksIid|Tvf>~_FXRV`)L_w)|u@aiOAmrZv_?s2LaCk z_xkrgpsHERnEAjVq2`)@e@D+qM7l6x3^&wO0KTrOch>Jj`_8gZc%o7y~iaE*vu4qR!QS47?qd^MjIEo06w;jL7IxH7WN#Wc#H)H|C@dC{L+I{1 z0KB+^!&oc}L;WMD{#bDCEG1>*z~FE$iYakjMv6uHgAv1c98xJB%H{Mm0CVTcfz>LX z1CCQA?RnHZkCZBos?i*dL&b3@I}Ry8RC!8-RhbOe4G(KH10wGL*3qa5D!{(Lmoe^9 zchvX*3c$~ScdBaDr=7&8-nY}d`fSK!V)q}euW}LD2eU1Ot@r})epPKN*d!w7V2r`= zci9HKLRE+B4Srl8>=uG>q^h$`R6l{}Nwj@rK(-=WQOG7&?S1Ccg{EHDW$VNQeZgwu zcpQ>RewRwIq*UTk5zeeup;964wc#^}Kr9CFI0eT+9fyx+vwDsFUO5i)qJj@ufgb~J zp?U9Yg#sq)_in#}=@*#jaVIqLz@4gka)8IBn8;$7P7Zjrs{XkZK@oWq#$dJC0mrE7 zef5U%4ZZWVk{HI=ML{U=BaHS7qm1z~CaVd3%n^>CvwZ8L!Ny1zQ3SVK=6^ho(&nKyKCvZ#Y|<>4jA8Jhl`!el5+>Vc(=Hj{ zXc75mOc6*}0NSD90?S0?1v@}h@4{p;^PzjYfICGbK?Fpk9}~@vN(|lCaDO%aa+(DI z%riOK|7Vk`UV!M)>eLSC-8|aWJ8xc>c+SBJ9{~P0f}NLp%CEMs9az#p*mc=9F~PjB zCP<~ATBTen89!A7l1a+G%1>id{&PT|2uaVQ;CVQe3SD7sAp)rs#aN8T^LgH^suMf< z(i@0_yX_@@HOD~0UC#4f-!SK zG^&_@&GG=B34i`Yp?eF!H&pff5D$omgE2hcK%>830)D2d$A-s@4-KqT8Z(*!+#@(& z>RB;zmsx7j7#r$88@L*~Ky8Eg8+%v$?N1OP1R@ZR<5enVmHOL58Mw=FSPN{b8-F4q z=`{O04vR(jC*ZtF1*cqYo;;nV7>jXFKF@Kg>iG}qc;MSK@3~+E;5)#1w!#h(*&Czs z4+wST*s=E(Fa;PFs^uV!PvgIVmxj6uz+zQ>GH~w!7;hxJuNQz5RrT%=?JF>~3_*KD zF+%zZwbVzB5A}aq;q&2kL{I8nK6-0YdkbtI>VLO@eT?Yud*j;r$4|OYCRdr%QpUmg=u3Sz(Cc>eK1dnueQIya|Q!0`6Jl>Yc z5OjZ{_OT+cMpZv(699kiXMvB=tk&x##A21nM{p zr&1i4&+8rSOq|W?!}&a~7vaarB%x#vl*^<=xNN}!slOyr2UEp>RaYW>0z!3I$z`3gWU<+QXLQr#Jqb;l)CmLahe18p%T81#MsxfC=Ess=BSsxqcNv zXd*FP z>ZsaSAj0c;5K0Gvm&eUQSqjzJVK1tP6Oxrd-wU1n(t@N|OS&7(gR;~h0YRiZV42wmxP zM=!!MftFNxIvh(-1yzcwEQ**v0d#WswCZ2ep^>9mkXjzTQPt})#i32wE`S2CvYr@f zi6vUa#4*A%?KSDxR=8L_keh5>g6?G%0{^9rZQG_6hC0T>5do!IwaP;5#;Mmy8aVHe|K0P52$7c8fJRKP1HBCiL&fIS`%L<%@uM9yo6ZbclsYplAzwY^=KB9rFOyUK4Py#voz)12_@f9ltO`rh>jTUwh)nSOlC0b3g$79!mvx%jrnzx*G)#d|KyQpP)qPjIB{y+ z8Wh$J?2Y9-rU!R8m~3DZs#P+;m;6lFlu)bjg<6ebn0_n+a_4 zsZ8p+@&*x3E0>#n?L>kJ5x(Vr+@pj*L_UBy_!|CRS)l%d6<@@Jp@VS;0s3g;_junL**Lf7S?UhkgLqDp5GY*360NpOAeGB>c^+3}GV-QQCd_1HUlG=o%f!N4K_Y>Q z@aJ6ag)@MAj1Y*(B213nmKi)j)%f^+eBvaUvzuMOaem!oGzPUS!WS$IoS0qT(?2!l zIWp8T>iLz+Erp;~gGz<8<8Ytr%GV}SWg;T3D`$!Dc(uw)>*?q^90w8!sv?{d2;{Wn zVRwOuq_Nup1>j4njaI#1GdPGn+jy;CdLD_M<)haK5;hW(HBE(&rca9OO#_DrFXU=` z$S3PbsmNeaI&JC!DixfG2@;;izq&44Tvx7kT{*L-N8akXvXAS^Vcp$wN_V$h>bm?M z_-d&{SG^+Xg((HnX-dE)*=)m+u-#q&d|ZdAunr1#6;mmEiEV(Y{sdDI6&8fLebB6iC-X#pYMDOjH zH}8YNds>}J{-xt^YgZSgxL;9MtwOns6pOfp0-1asDHKQ-i*%RE#44fmi(i*mOQk3Q z=jQYJ!KR6Ja~DxWUWzGCv)KXf^(&Ji;g`QJqdCds0`K=r?jzB=eDp_xb0Wt3sAIn1 z{bX!?|Hrp}YhaFT+;iXE%N<)ca1-d2>{#qWJ#*(D#nR0U@q#!Yu~=i-qVLChxtyNj zx-yhZa&02Pf@+nnTFqz%8~l??I1V@t)kFdX{!4@lvRQpN;(PA40^s8gn(crIOmXSm zmiwEas(%rYf1&yKnG38Fk;STNP4xowtQh{m*wDZcpldLPSOMZFmnG8r$?Ze^w+lR~ zs(%xQ#VBt~$K)_}2=2I|m&{!;YSlb(Rlc80@~Uu$2)6^|^ZEl3ImmTckw`F@Nboiw zsY+QD52M;V#~~&{7x26)n=vu)ujg|5&?L{u!$@kd1gcZPsQP2-G=%2Vayv9ML?edz zA*-Oz?_i2pKH3R!RlQ9_ZUj!Iacjt6j@6%R9q6NX#mH}1Er*SF_x+oiAFf*Hcg+!V zpcfGd=Q1Zy*2I#DOS%W2jr`R(=``CaA~DxxG3E%`fC&AnjCmf9rqevyY5Li>x|h*t zpKMXpwyT07XgsbR{0O!G!%^>~Q2= fn.length ? fn.apply(self,args) : function(){ + return master.apply( self, args.concat(curry.args(arguments)) ); + }; + }; +}; + +curry.args = function( args ){ + return Array.prototype.slice.call(args); +}; + +Function.prototype.curry = function(){ + return curry(this); +}; \ No newline at end of file diff --git a/packages/bmx6-luci/files/www/luci-static/resources/bmx6/js/dracula_algorithms.js b/packages/bmx6-luci/files/www/luci-static/resources/bmx6/js/dracula_algorithms.js new file mode 100644 index 0000000..0fbb085 --- /dev/null +++ b/packages/bmx6-luci/files/www/luci-static/resources/bmx6/js/dracula_algorithms.js @@ -0,0 +1,599 @@ +/* + * Various algorithms and data structures, licensed under the MIT-license. + * (c) 2010 by Johann Philipp Strathausen + * http://strathausen.eu + * + */ + + + +/* + Bellman-Ford + + Path-finding algorithm, finds the shortest paths from one node to all nodes. + + + Complexity + + O( |E| · |V| ), where E = edges and V = vertices (nodes) + + + Constraints + + Can run on graphs with negative edge weights as long as they do not have + any negative weight cycles. + + */ +function bellman_ford(g, source) { + + /* STEP 1: initialisation */ + for(var n in g.nodes) + g.nodes[n].distance = Infinity; + /* predecessors are implicitly null */ + source.distance = 0; + + step("Initially, all distances are infinite and all predecessors are null."); + + /* STEP 2: relax each edge (this is at the heart of Bellman-Ford) */ + /* repeat this for the number of nodes minus one */ + for(var i = 1; i < g.nodes.length; i++) + /* for each edge */ + for(var e in g.edges) { + var edge = g.edges[e]; + if(edge.source.distance + edge.weight < edge.target.distance) { + step("Relax edge between " + edge.source.id + " and " + edge.target.id + "."); + edge.target.distance = edge.source.distance + edge.weight; + edge.target.predecessor = edge.source; + } + //Added by Jake Stothard (Needs to be tested) + if(!edge.style.directed) { + if(edge.target.distance + edge.weight < edge.source.distance) { + g.snapShot("Relax edge between "+edge.target.id+" and "+edge.source.id+"."); + edge.source.distance = edge.target.distance + edge.weight; + edge.source.predecessor = edge.target; + } + } + } + step("Ready."); + + /* STEP 3: TODO Check for negative cycles */ + /* For now we assume here that the graph does not contain any negative + weights cycles. (this is left as an excercise to the reader[tm]) */ +} + + + +/* + Path-finding algorithm Dijkstra + + - worst-case running time is O((|E| + |V|) · log |V| ) thus better than + Bellman-Ford for sparse graphs (with less edges), but cannot handle + negative edge weights + */ +function dijkstra(g, source) { + + /* initially, all distances are infinite and all predecessors are null */ + for(var n in g.nodes) + g.nodes[n].distance = Infinity; + /* predecessors are implicitly null */ + + g.snapShot("Initially, all distances are infinite and all predecessors are null."); + + source.distance = 0; + /* set of unoptimized nodes, sorted by their distance (but a Fibonacci heap + would be better) */ + var q = new BinaryMinHeap(g.nodes, "distance"); + + /* pointer to the node in focus */ + var node; + + /* get the node with the smallest distance + as long as we have unoptimized nodes. q.min() can have O(log n). */ + while(q.min() != undefined) { + /* remove the latest */ + node = q.extractMin(); + node.optimized = true; + + /* no nodes accessible from this one, should not happen */ + if(node.distance == Infinity) + throw "Orphaned node!"; + + /* for each neighbour of node */ + for(e in node.edges) { + var other = (node == node.edges[e].target) ? node.edges[e].source : node.edges[e].target; + + if(other.optimized) + continue; + + /* look for an alternative route */ + var alt = node.distance + node.edges[e].weight; + + /* update distance and route if a better one has been found */ + if (alt < other.distance) { + + /* update distance of neighbour */ + other.distance = alt; + + /* update priority queue */ + q.heapify(); + + /* update path */ + other.predecessor = node; + g.snapShot("Enhancing node.") + } + } + } +} + + +/* All-Pairs-Shortest-Paths */ +/* Runs at worst in O(|V|³) and at best in Omega(|V|³) :-) + complexity Sigma(|V|²) */ +/* This implementation is not yet ready for general use, but works with the + Dracula graph library. */ +function floyd_warshall(g, source) { + + /* Step 1: initialising empty path matrix (second dimension is implicit) */ + var path = []; + var next = []; + var n = g.nodes.length; + + /* construct path matrix, initialize with Infinity */ + for(j in g.nodes) { + path[j] = []; + next[j] = []; + for(i in g.nodes) + path[j][i] = j == i ? 0 : Infinity; + } + + /* initialize path with edge weights */ + for(e in g.edges) + path[g.edges[e].source.id][g.edges[e].target.id] = g.edges[e].weight; + + /* Note: Usually, the initialisation is done by getting the edge weights + from a node matrix representation of the graph, not by iterating through + a list of edges as done here. */ + + /* Step 2: find best distances (the heart of Floyd-Warshall) */ + for(k in g.nodes){ + for(i in g.nodes) { + for(j in g.nodes) + if(path[i][j] > path[i][k] + path[k][j]) { + path[i][j] = path[i][k] + path[k][j]; + /* Step 2.b: remember the path */ + next[i][j] = k; + } + } + } + + /* Step 3: Path reconstruction, get shortest path */ + function getPath(i, j) { + if(path[i][j] == Infinity) + throw "There is no path."; + var intermediate = next[i][j]; + if(intermediate == undefined) + return null; + else + return getPath(i, intermediate) + .concat([intermediate]) + .concat(getPath(intermediate, j)); + } + + /* TODO use the knowledge, e.g. mark path in graph */ +} + +/* + Ford-Fulkerson + + Max-Flow-Min-Cut Algorithm finding the maximum flow through a directed + graph from source to sink. + + + Complexity + + O(E * max(f)), max(f) being the maximum flow + + + Description + + As long as there is an open path through the residual graph, send the + minimum of the residual capacities on the path. + + + Constraints + + The algorithm works only if all weights are integers. Otherwise it is + possible that the Ford–Fulkerson algorithm will not converge to the maximum + value. + + + Input + + g - Graph object + s - Source ID + t - Target (sink) ID + + + Output + + Maximum flow from Source s to Target t + + */ +/* + Edmonds-Karp + + Max-Flow-Min-Cut Algorithm finding the maximum flow through a directed + graph from source to sink. An implementation of the Ford-Fulkerson + algorithm. + + + Complexity + + O(|V|*|E|²) + + + Input + + g - Graph object (with node and edge lists, capacity is a property of edge) + s - source ID + t - sink ID + + */ +function edmonds_karp(g, s, t) { + +} + +/* + A simple binary min-heap serving as a priority queue + - takes an array as the input, with elements having a key property + - elements will look like this: + { + key: "... key property ...", + value: "... element content ..." + } + - provides insert(), min(), extractMin() and heapify() + - example usage (e.g. via the Firebug or Chromium console): + var x = {foo: 20, hui: "bla"}; + var a = new BinaryMinHeap([x,{foo:3},{foo:10},{foo:20},{foo:30},{foo:6},{foo:1},{foo:3}],"foo"); + console.log(a.extractMin()); + console.log(a.extractMin()); + x.foo = 0; // update key + a.heapify(); // call this always after having a key updated + console.log(a.extractMin()); + console.log(a.extractMin()); + - can also be used on a simple array, like [9,7,8,5] + */ +function BinaryMinHeap(array, key) { + + /* Binary tree stored in an array, no need for a complicated data structure */ + var tree = []; + + var key = key || 'key'; + + /* Calculate the index of the parent or a child */ + var parent = function(index) { return Math.floor((index - 1)/2); }; + var right = function(index) { return 2 * index + 2; }; + var left = function(index) { return 2 * index + 1; }; + + /* Helper function to swap elements with their parent + as long as the parent is bigger */ + function bubble_up(i) { + var p = parent(i); + while((p >= 0) && (tree[i][key] < tree[p][key])) { + /* swap with parent */ + tree[i] = tree.splice(p, 1, tree[i])[0]; + /* go up one level */ + i = p; + p = parent(i); + } + } + + /* Helper function to swap elements with the smaller of their children + as long as there is one */ + function bubble_down(i) { + var l = left(i); + var r = right(i); + + /* as long as there are smaller children */ + while(tree[l] && (tree[i][key] > tree[l][key]) || tree[r] && (tree[i][key] > tree[r][key])) { + + /* find smaller child */ + var child = tree[l] ? tree[r] ? tree[l][key] > tree[r][key] ? r : l : l : l; + + /* swap with smaller child with current element */ + tree[i] = tree.splice(child, 1, tree[i])[0]; + + /* go up one level */ + i = child; + l = left(i); + r = right(i); + } + } + + /* Insert a new element with respect to the heap property + 1. Insert the element at the end + 2. Bubble it up until it is smaller than its parent */ + this.insert = function(element) { + + /* make sure there's a key property */ + (element[key] == undefined) && (element = {key:element}); + + /* insert element at the end */ + tree.push(element); + + /* bubble up the element */ + bubble_up(tree.length - 1); + } + + /* Only show us the minimum */ + this.min = function() { + return tree.length == 1 ? undefined : tree[0]; + } + + /* Return and remove the minimum + 1. Take the root as the minimum that we are looking for + 2. Move the last element to the root (thereby deleting the root) + 3. Compare the new root with both of its children, swap it with the + smaller child and then check again from there (bubble down) + */ + this.extractMin = function() { + var result = this.min(); + + /* move the last element to the root or empty the tree completely */ + /* bubble down the new root if necessary */ + (tree.length == 1) && (tree = []) || (tree[0] = tree.pop()) && bubble_down(0); + + return result; + } + + /* currently unused, TODO implement */ + this.changeKey = function(index, key) { + throw "function not implemented"; + } + + this.heapify = function() { + for(var start = Math.floor((tree.length - 2) / 2); start >= 0; start--) { + bubble_down(start); + } + } + + /* insert the input elements one by one only when we don't have a key property (TODO can be done more elegant) */ + for(i in (array || [])) + this.insert(array[i]); +} + + + +/* + Quick Sort: + 1. Select some random value from the array, the median. + 2. Divide the array in three smaller arrays according to the elements + being less, equal or greater than the median. + 3. Recursively sort the array containg the elements less than the + median and the one containing elements greater than the median. + 4. Concatenate the three arrays (less, equal and greater). + 5. One or no element is always sorted. + TODO: This could be implemented more efficiently by using only one array object and several pointers. +*/ +function quickSort(arr) { + /* recursion anchor: one element is always sorted */ + if(arr.length <= 1) return arr; + /* randomly selecting some value */ + var median = arr[Math.floor(Math.random() * arr.length)]; + var arr1 = [], arr2 = [], arr3 = []; + for(var i in arr) { + arr[i] < median && arr1.push(arr[i]); + arr[i] == median && arr2.push(arr[i]); + arr[i] > median && arr3.push(arr[i]); + } + /* recursive sorting and assembling final result */ + return quickSort(arr1).concat(arr2).concat(quickSort(arr3)); +} + +/* + Selection Sort: + 1. Select the minimum and remove it from the array + 2. Sort the rest recursively + 3. Return the minimum plus the sorted rest + 4. An array with only one element is already sorted +*/ +function selectionSort(arr) { + /* recursion anchor: one element is always sorted */ + if(arr.length == 1) return arr; + var minimum = Infinity; + var index; + for(var i in arr) { + if(arr[i] < minimum) { + minimum = arr[i]; + index = i; /* remember the minimum index for later removal */ + } + } + /* remove the minimum */ + arr.splice(index, 1); + /* assemble result and sort recursively (could be easily done iteratively as well)*/ + return [minimum].concat(selectionSort(arr)); +} + +/* + Merge Sort: + 1. Cut the array in half + 2. Sort each of them recursively + 3. Merge the two sorted arrays + 4. An array with only one element is considered sorted + +*/ +function mergeSort(arr) { + /* merges two sorted arrays into one sorted array */ + function merge(a, b) { + /* result set */ + var c = []; + /* as long as there are elements in the arrays to be merged */ + while(a.length > 0 || b.length > 0){ + /* are there elements to be merged, if yes, compare them and merge */ + var n = a.length > 0 && b.length > 0 ? a[0] < b[0] ? a.shift() : b.shift() : b.length > 0 ? b.shift() : a.length > 0 ? a.shift() : null; + /* always push the smaller one onto the result set */ + n != null && c.push(n); + } + return c; + } + /* this mergeSort implementation cuts the array in half, wich should be fine with randomized arrays, but introduces the risk of a worst-case scenario */ + median = Math.floor(arr.length / 2); + var part1 = arr.slice(0, median); /* for some reason it doesn't work if inserted directly in the return statement (tried so with firefox) */ + var part2 = arr.slice(median - arr.length); + return arr.length <= 1 ? arr : merge( + mergeSort(part1), /* first half */ + mergeSort(part2) /* second half */ + ); +} + +/* Balanced Red-Black-Tree */ +function RedBlackTree(arr) { + +} + +function BTree(arr) { + +} + +function NaryTree(n, arr) { + +} + +/** + * Knuth-Morris-Pratt string matching algorithm - finds a pattern in a text. + * FIXME: Doesn't work correctly yet. + */ +function kmp(p, t) { + + /** + * PREFIX, OVERLAP or FALIURE function for KMP. Computes how many iterations + * the algorithm can skip after a mismatch. + * + * @input p - pattern (string) + * @result array of skippable iterations + */ + function prefix(p) { + /* pi contains the computed skip marks */ + var pi = [0], k = 0; + for(q = 1; q < p.length; q++) { + while(k > 0 && (p.charAt(k) != p.charAt(q))) + k = pi[k-1]; + + (p.charAt(k) == p.charAt(q)) && k++; + + pi[q] = k; + } + return pi; + } + + /* The actual KMP algorithm starts here. */ + + var pi = prefix(p), q = 0, result = []; + + for(var i = 0; i < t.length; i++) { + /* jump forward as long as the character doesn't match */ + while((q > 0) && (p.charAt(q) != t.charAt(i))) + q = pi[q]; + + (p.charAt(q) == t.charAt(i)) && q++; + + (q == p.length) && result.push(i - p.length) && (q = pi[q]); + } + + return result; +} + +/* step for algorithm visualisation */ +function step(comment, funct) { + //wait for input + //display comment (before or after waiting) +// next.wait(); + /* execute callback function */ + funct(); +} + +/** + * Curry - Function currying + * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com + * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php) + * Date: 10/4/2008 + * + * @author Ariel Flesler + * @version 1.0.1 + */ +function curry( fn ){ + return function(){ + var args = curry.args(arguments), + master = arguments.callee, + self = this; + + return args.length >= fn.length ? fn.apply(self,args) : function(){ + return master.apply( self, args.concat(curry.args(arguments)) ); + }; + }; +}; + +curry.args = function( args ){ + return Array.prototype.slice.call(args); +}; + +Function.prototype.curry = function(){ + return curry(this); +}; + +/** + * Topological Sort + * + * Sort a directed graph based on incoming edges + * + * Coded by Jake Stothard + */ +function topological_sort(g) { + //Mark nodes as "deleted" instead of actually deleting them + //That way we don't have to copy g + + for(i in g.nodes) + g.nodes[i].deleted = false; + + var ret = topological_sort_helper(g); + + //Cleanup: Remove the deleted property + for(i in g.nodes) + delete g.nodes[i].deleted + + return ret; +} +function topological_sort_helper(g) { + //Find node with no incoming edges + var node; + for(i in g.nodes) { + if(g.nodes[i].deleted) + continue; //Bad style, meh + + var incoming = false; + for(j in g.nodes[i].edges) { + if(g.nodes[i].edges[j].target == g.nodes[i] + && g.nodes[i].edges[j].source.deleted == false) { + incoming = true; + break; + } + } + if(!incoming) { + node = g.nodes[i]; + break; + } + } + + // Either unsortable or done. Either way, GTFO + if(node == undefined) + return []; + + //"Delete" node from g + node.deleted = true; + + var tail = topological_sort_helper(g); + + tail.unshift(node); + + return tail; +} diff --git a/packages/bmx6-luci/files/www/luci-static/resources/bmx6/js/dracula_graffle.js b/packages/bmx6-luci/files/www/luci-static/resources/bmx6/js/dracula_graffle.js new file mode 100644 index 0000000..ddf171d --- /dev/null +++ b/packages/bmx6-luci/files/www/luci-static/resources/bmx6/js/dracula_graffle.js @@ -0,0 +1,106 @@ +/** + * Originally grabbed from the official RaphaelJS Documentation + * http://raphaeljs.com/graffle.html + * Adopted (arrows) and commented by Philipp Strathausen http://blog.ameisenbar.de + * Licenced under the MIT licence. + */ + +/** + * Usage: + * connect two shapes + * parameters: + * source shape [or connection for redrawing], + * target shape, + * style with { fg : linecolor, bg : background color, directed: boolean } + * returns: + * connection { draw = function() } + */ +Raphael.fn.connection = function (obj1, obj2, style) { + var selfRef = this; + /* create and return new connection */ + var edge = {/* + from : obj1, + to : obj2, + style : style,*/ + draw : function() { + /* get bounding boxes of target and source */ + var bb1 = obj1.getBBox(); + var bb2 = obj2.getBBox(); + var off1 = 0; + var off2 = 0; + /* coordinates for potential connection coordinates from/to the objects */ + var p = [ + {x: bb1.x + bb1.width / 2, y: bb1.y - off1}, /* NORTH 1 */ + {x: bb1.x + bb1.width / 2, y: bb1.y + bb1.height + off1}, /* SOUTH 1 */ + {x: bb1.x - off1, y: bb1.y + bb1.height / 2}, /* WEST 1 */ + {x: bb1.x + bb1.width + off1, y: bb1.y + bb1.height / 2}, /* EAST 1 */ + {x: bb2.x + bb2.width / 2, y: bb2.y - off2}, /* NORTH 2 */ + {x: bb2.x + bb2.width / 2, y: bb2.y + bb2.height + off2}, /* SOUTH 2 */ + {x: bb2.x - off2, y: bb2.y + bb2.height / 2}, /* WEST 2 */ + {x: bb2.x + bb2.width + off2, y: bb2.y + bb2.height / 2} /* EAST 2 */ + ]; + + /* distances between objects and according coordinates connection */ + var d = {}, dis = []; + + /* + * find out the best connection coordinates by trying all possible ways + */ + /* loop the first object's connection coordinates */ + for (var i = 0; i < 4; i++) { + /* loop the seond object's connection coordinates */ + for (var j = 4; j < 8; j++) { + var dx = Math.abs(p[i].x - p[j].x), + dy = Math.abs(p[i].y - p[j].y); + if ((i == j - 4) || (((i != 3 && j != 6) || p[i].x < p[j].x) && ((i != 2 && j != 7) || p[i].x > p[j].x) && ((i != 0 && j != 5) || p[i].y > p[j].y) && ((i != 1 && j != 4) || p[i].y < p[j].y))) { + dis.push(dx + dy); + d[dis[dis.length - 1].toFixed(3)] = [i, j]; + } + } + } + var res = dis.length == 0 ? [0, 4] : d[Math.min.apply(Math, dis).toFixed(3)]; + /* bezier path */ + var x1 = p[res[0]].x, + y1 = p[res[0]].y, + x4 = p[res[1]].x, + y4 = p[res[1]].y, + dx = Math.max(Math.abs(x1 - x4) / 2, 10), + dy = Math.max(Math.abs(y1 - y4) / 2, 10), + x2 = [x1, x1, x1 - dx, x1 + dx][res[0]].toFixed(3), + y2 = [y1 - dy, y1 + dy, y1, y1][res[0]].toFixed(3), + x3 = [0, 0, 0, 0, x4, x4, x4 - dx, x4 + dx][res[1]].toFixed(3), + y3 = [0, 0, 0, 0, y1 + dy, y1 - dy, y4, y4][res[1]].toFixed(3); + /* assemble path and arrow */ + var path = ["M", x1.toFixed(3), y1.toFixed(3), "C", x2, y2, x3, y3, x4.toFixed(3), y4.toFixed(3)].join(","); + /* arrow */ + if(style && style.directed) { + /* magnitude, length of the last path vector */ + var mag = Math.sqrt((y4 - y3) * (y4 - y3) + (x4 - x3) * (x4 - x3)); + /* vector normalisation to specified length */ + var norm = function(x,l){return (-x*(l||5)/mag);}; + /* calculate array coordinates (two lines orthogonal to the path vector) */ + var arr = [ + {x:(norm(x4-x3)+norm(y4-y3)+x4).toFixed(3), y:(norm(y4-y3)+norm(x4-x3)+y4).toFixed(3)}, + {x:(norm(x4-x3)-norm(y4-y3)+x4).toFixed(3), y:(norm(y4-y3)-norm(x4-x3)+y4).toFixed(3)} + ]; + path = path + ",M"+arr[0].x+","+arr[0].y+",L"+x4+","+y4+",L"+arr[1].x+","+arr[1].y; + } + /* function to be used for moving existent path(s), e.g. animate() or attr() */ + var move = "attr"; + /* applying path(s) */ + edge.fg && edge.fg[move]({path:path}) + || (edge.fg = selfRef.path(path).attr({stroke: style && style.stroke || "#000", fill: "none"}).toBack()); + edge.bg && edge.bg[move]({path:path}) + || style && style.fill && (edge.bg = style.fill.split && selfRef.path(path).attr({stroke: style.fill.split("|")[0], fill: "none", "stroke-width": style.fill.split("|")[1] || 3}).toBack()); + /* setting label */ + style && style.label + && (edge.label && edge.label.attr({x:(x1+x4)/2, y:(y1+y4)/2}) + || (edge.label = selfRef.text((x1+x4)/2, (y1+y4)/2, style.label).attr({fill: "#000", "font-size": style["font-size"] || "12px"}))); + style && style.label && style["label-style"] && edge.label && edge.label.attr(style["label-style"]); + style && style.callback && style.callback(edge); + } + } + edge.draw(); + return edge; +}; +//Raphael.prototype.set.prototype.dodo=function(){console.log("works");}; diff --git a/packages/bmx6-luci/files/www/luci-static/resources/bmx6/js/dracula_graph.js b/packages/bmx6-luci/files/www/luci-static/resources/bmx6/js/dracula_graph.js new file mode 100644 index 0000000..f3e43e1 --- /dev/null +++ b/packages/bmx6-luci/files/www/luci-static/resources/bmx6/js/dracula_graph.js @@ -0,0 +1,527 @@ +/* + * Dracula Graph Layout and Drawing Framework 0.0.3alpha + * (c) 2010 Philipp Strathausen , http://strathausen.eu + * Contributions by Jake Stothard . + * + * based on the Graph JavaScript framework, version 0.0.1 + * (c) 2006 Aslak Hellesoy + * (c) 2006 Dave Hoover + * + * Ported from Graph::Layouter::Spring in + * http://search.cpan.org/~pasky/Graph-Layderer-0.02/ + * The algorithm is based on a spring-style layouter of a Java-based social + * network tracker PieSpy written by Paul Mutton . + * + * This code is freely distributable under the MIT license. Commercial use is + * hereby granted without any cost or restriction. + * + * Links: + * + * Graph Dracula JavaScript Framework: + * http://graphdracula.net + * + /*--------------------------------------------------------------------------*/ + +/* + * Edge Factory + */ +var AbstractEdge = function() { +} +AbstractEdge.prototype = { + hide: function() { + this.connection.fg.hide(); + this.connection.bg && this.bg.connection.hide(); + } +}; +var EdgeFactory = function() { + this.template = new AbstractEdge(); + this.template.style = new Object(); + this.template.style.directed = false; + this.template.weight = 1; +}; +EdgeFactory.prototype = { + build: function(source, target) { + var e = jQuery.extend(true, {}, this.template); + e.source = source; + e.target = target; + return e; + } +}; + +/* + * Graph + */ +var Graph = function() { + this.nodes = {}; + this.edges = []; + this.snapshots = []; // previous graph states TODO to be implemented + this.edgeFactory = new EdgeFactory(); +}; +Graph.prototype = { + /* + * add a node + * @id the node's ID (string or number) + * @content (optional, dictionary) can contain any information that is + * being interpreted by the layout algorithm or the graph + * representation + */ + addNode: function(id, content) { + /* testing if node is already existing in the graph */ + if(this.nodes[id] == undefined) { + this.nodes[id] = new Graph.Node(id, content); + } + return this.nodes[id]; + }, + + addEdge: function(source, target, style) { + var s = this.addNode(source); + var t = this.addNode(target); + var edge = this.edgeFactory.build(s, t); + jQuery.extend(edge.style,style); + s.edges.push(edge); + this.edges.push(edge); + // NOTE: Even directed edges are added to both nodes. + t.edges.push(edge); + }, + + /* TODO to be implemented + * Preserve a copy of the graph state (nodes, positions, ...) + * @comment a comment describing the state + */ + snapShot: function(comment) { + /* FIXME + var graph = new Graph(); + graph.nodes = jQuery.extend(true, {}, this.nodes); + graph.edges = jQuery.extend(true, {}, this.edges); + this.snapshots.push({comment: comment, graph: graph}); + */ + }, + removeNode: function(id) { + delete this.nodes[id]; + for(var i = 0; i < this.edges.length; i++) { + if (this.edges[i].source.id == id || this.edges[i].target.id == id) { + this.edges.splice(i, 1); + i--; + } + } + } +}; + +/* + * Node + */ +Graph.Node = function(id, node){ + node = node || {}; + node.id = id; + node.edges = []; + node.hide = function() { + this.hidden = true; + this.shape && this.shape.hide(); /* FIXME this is representation specific code and should be elsewhere */ + for(i in this.edges) + (this.edges[i].source.id == id || this.edges[i].target == id) && this.edges[i].hide && this.edges[i].hide(); + }; + node.show = function() { + this.hidden = false; + this.shape && this.shape.show(); + for(i in this.edges) + (this.edges[i].source.id == id || this.edges[i].target == id) && this.edges[i].show && this.edges[i].show(); + }; + return node; +}; +Graph.Node.prototype = { +}; + +/* + * Renderer base class + */ +Graph.Renderer = {}; + +/* + * Renderer implementation using RaphaelJS + */ +Graph.Renderer.Raphael = function(element, graph, width, height) { + this.width = width || 400; + this.height = height || 400; + var selfRef = this; + this.r = Raphael(element, this.width, this.height); + this.radius = 40; /* max dimension of a node */ + this.graph = graph; + this.mouse_in = false; + + /* TODO default node rendering function */ + if(!this.graph.render) { + this.graph.render = function() { + return; + } + } + + /* + * Dragging + */ + this.isDrag = false; + this.dragger = function (e) { + this.dx = e.clientX; + this.dy = e.clientY; + selfRef.isDrag = this; + this.set && this.set.animate({"fill-opacity": .1}, 200) && this.set.toFront(); + e.preventDefault && e.preventDefault(); + }; + + var d = document.getElementById(element); + d.onmousemove = function (e) { + e = e || window.event; + if (selfRef.isDrag) { + var bBox = selfRef.isDrag.set.getBBox(); + // TODO round the coordinates here (eg. for proper image representation) + var newX = e.clientX - selfRef.isDrag.dx + (bBox.x + bBox.width / 2); + var newY = e.clientY - selfRef.isDrag.dy + (bBox.y + bBox.height / 2); + /* prevent shapes from being dragged out of the canvas */ + var clientX = e.clientX - (newX < 20 ? newX - 20 : newX > selfRef.width - 20 ? newX - selfRef.width + 20 : 0); + var clientY = e.clientY - (newY < 20 ? newY - 20 : newY > selfRef.height - 20 ? newY - selfRef.height + 20 : 0); + selfRef.isDrag.set.translate(clientX - Math.round(selfRef.isDrag.dx), clientY - Math.round(selfRef.isDrag.dy)); + // console.log(clientX - Math.round(selfRef.isDrag.dx), clientY - Math.round(selfRef.isDrag.dy)); + for (var i in selfRef.graph.edges) { + selfRef.graph.edges[i].connection && selfRef.graph.edges[i].connection.draw(); + } + //selfRef.r.safari(); + selfRef.isDrag.dx = clientX; + selfRef.isDrag.dy = clientY; + } + }; + d.onmouseup = function () { + selfRef.isDrag && selfRef.isDrag.set.animate({"fill-opacity": .6}, 500); + selfRef.isDrag = false; + }; + this.draw(); +}; +Graph.Renderer.Raphael.prototype = { + translate: function(point) { + return [ + (point[0] - this.graph.layoutMinX) * this.factorX + this.radius, + (point[1] - this.graph.layoutMinY) * this.factorY + this.radius + ]; + }, + + rotate: function(point, length, angle) { + var dx = length * Math.cos(angle); + var dy = length * Math.sin(angle); + return [point[0]+dx, point[1]+dy]; + }, + + draw: function() { + this.factorX = (this.width - 2 * this.radius) / (this.graph.layoutMaxX - this.graph.layoutMinX); + this.factorY = (this.height - 2 * this.radius) / (this.graph.layoutMaxY - this.graph.layoutMinY); + for (i in this.graph.nodes) { + this.drawNode(this.graph.nodes[i]); + } + for (var i = 0; i < this.graph.edges.length; i++) { + this.drawEdge(this.graph.edges[i]); + } + }, + + drawNode: function(node) { + var point = this.translate([node.layoutPosX, node.layoutPosY]); + node.point = point; + + /* if node has already been drawn, move the nodes */ + if(node.shape) { + var oBBox = node.shape.getBBox(); + var opoint = { x: oBBox.x + oBBox.width / 2, y: oBBox.y + oBBox.height / 2}; + node.shape.translate(Math.round(point[0] - opoint.x), Math.round(point[1] - opoint.y)); + this.r.safari(); + return node; + }/* else, draw new nodes */ + + var shape; + + /* if a node renderer function is provided by the user, then use it + or the default render function instead */ + if(!node.render) { + node.render = function(r, node) { + /* the default node drawing */ + var color = Raphael.getColor(); + var ellipse = r.ellipse(0, 0, 30, 20).attr({fill: color, stroke: color, "stroke-width": 2}); + /* set DOM node ID */ + ellipse.node.id = node.label || node.id; + shape = r.set(). + push(ellipse). + push(r.text(0, 30, node.label || node.id)); + return shape; + } + } + /* or check for an ajax representation of the nodes */ + if(node.shapes) { + // TODO ajax representation evaluation + } + + shape = node.render(this.r, node).hide(); + + shape.attr({"fill-opacity": .6}); + /* re-reference to the node an element belongs to, needed for dragging all elements of a node */ + shape.items.forEach(function(item){ item.set = shape; item.node.style.cursor = "move"; }); + shape.mousedown(this.dragger); + + var box = shape.getBBox(); + shape.translate(Math.round(point[0]-(box.x+box.width/2)),Math.round(point[1]-(box.y+box.height/2))) + //console.log(box,point); + node.hidden || shape.show(); + node.shape = shape; + }, + drawEdge: function(edge) { + /* if this edge already exists the other way around and is undirected */ + if(edge.backedge) + return; + if(edge.source.hidden || edge.target.hidden) { + edge.connection && edge.connection.fg.hide() | edge.connection.bg && edge.connection.bg.hide(); + return; + } + /* if edge already has been drawn, only refresh the edge */ + if(!edge.connection) { + edge.style && edge.style.callback && edge.style.callback(edge); // TODO move this somewhere else + edge.connection = this.r.connection(edge.source.shape, edge.target.shape, edge.style); + return; + } + //FIXME showing doesn't work well + edge.connection.fg.show(); + edge.connection.bg && edge.connection.bg.show(); + edge.connection.draw(); + } +}; +Graph.Layout = {}; +Graph.Layout.Spring = function(graph) { + this.graph = graph; + this.iterations = 500; + this.maxRepulsiveForceDistance = 6; + this.k = 2; + this.c = 0.01; + this.maxVertexMovement = 0.5; + this.layout(); +}; +Graph.Layout.Spring.prototype = { + layout: function() { + this.layoutPrepare(); + for (var i = 0; i < this.iterations; i++) { + this.layoutIteration(); + } + this.layoutCalcBounds(); + }, + + layoutPrepare: function() { + for (i in this.graph.nodes) { + var node = this.graph.nodes[i]; + node.layoutPosX = 0; + node.layoutPosY = 0; + node.layoutForceX = 0; + node.layoutForceY = 0; + } + + }, + + layoutCalcBounds: function() { + var minx = Infinity, maxx = -Infinity, miny = Infinity, maxy = -Infinity; + + for (i in this.graph.nodes) { + var x = this.graph.nodes[i].layoutPosX; + var y = this.graph.nodes[i].layoutPosY; + + if(x > maxx) maxx = x; + if(x < minx) minx = x; + if(y > maxy) maxy = y; + if(y < miny) miny = y; + } + + this.graph.layoutMinX = minx; + this.graph.layoutMaxX = maxx; + this.graph.layoutMinY = miny; + this.graph.layoutMaxY = maxy; + }, + + layoutIteration: function() { + // Forces on nodes due to node-node repulsions + + var prev = new Array(); + for(var c in this.graph.nodes) { + var node1 = this.graph.nodes[c]; + for (var d in prev) { + var node2 = this.graph.nodes[prev[d]]; + this.layoutRepulsive(node1, node2); + + } + prev.push(c); + } + + // Forces on nodes due to edge attractions + for (var i = 0; i < this.graph.edges.length; i++) { + var edge = this.graph.edges[i]; + this.layoutAttractive(edge); + } + + // Move by the given force + for (i in this.graph.nodes) { + var node = this.graph.nodes[i]; + var xmove = this.c * node.layoutForceX; + var ymove = this.c * node.layoutForceY; + + var max = this.maxVertexMovement; + if(xmove > max) xmove = max; + if(xmove < -max) xmove = -max; + if(ymove > max) ymove = max; + if(ymove < -max) ymove = -max; + + node.layoutPosX += xmove; + node.layoutPosY += ymove; + node.layoutForceX = 0; + node.layoutForceY = 0; + } + }, + + layoutRepulsive: function(node1, node2) { + if (typeof node1 == 'undefined' || typeof node2 == 'undefined') + return; + var dx = node2.layoutPosX - node1.layoutPosX; + var dy = node2.layoutPosY - node1.layoutPosY; + var d2 = dx * dx + dy * dy; + if(d2 < 0.01) { + dx = 0.1 * Math.random() + 0.1; + dy = 0.1 * Math.random() + 0.1; + var d2 = dx * dx + dy * dy; + } + var d = Math.sqrt(d2); + if(d < this.maxRepulsiveForceDistance) { + var repulsiveForce = this.k * this.k / d; + node2.layoutForceX += repulsiveForce * dx / d; + node2.layoutForceY += repulsiveForce * dy / d; + node1.layoutForceX -= repulsiveForce * dx / d; + node1.layoutForceY -= repulsiveForce * dy / d; + } + }, + + layoutAttractive: function(edge) { + var node1 = edge.source; + var node2 = edge.target; + + var dx = node2.layoutPosX - node1.layoutPosX; + var dy = node2.layoutPosY - node1.layoutPosY; + var d2 = dx * dx + dy * dy; + if(d2 < 0.01) { + dx = 0.1 * Math.random() + 0.1; + dy = 0.1 * Math.random() + 0.1; + var d2 = dx * dx + dy * dy; + } + var d = Math.sqrt(d2); + if(d > this.maxRepulsiveForceDistance) { + d = this.maxRepulsiveForceDistance; + d2 = d * d; + } + var attractiveForce = (d2 - this.k * this.k) / this.k; + if(edge.attraction == undefined) edge.attraction = 1; + attractiveForce *= Math.log(edge.attraction) * 0.5 + 1; + + node2.layoutForceX -= attractiveForce * dx / d; + node2.layoutForceY -= attractiveForce * dy / d; + node1.layoutForceX += attractiveForce * dx / d; + node1.layoutForceY += attractiveForce * dy / d; + } +}; + +Graph.Layout.Ordered = function(graph, order) { + this.graph = graph; + this.order = order; + this.layout(); +}; +Graph.Layout.Ordered.prototype = { + layout: function() { + this.layoutPrepare(); + this.layoutCalcBounds(); + }, + + layoutPrepare: function(order) { + for (i in this.graph.nodes) { + var node = this.graph.nodes[i]; + node.layoutPosX = 0; + node.layoutPosY = 0; + } + var counter = 0; + for (i in this.order) { + var node = this.order[i]; + node.layoutPosX = counter; + node.layoutPosY = Math.random(); + counter++; + } + }, + + layoutCalcBounds: function() { + var minx = Infinity, maxx = -Infinity, miny = Infinity, maxy = -Infinity; + + for (i in this.graph.nodes) { + var x = this.graph.nodes[i].layoutPosX; + var y = this.graph.nodes[i].layoutPosY; + + if(x > maxx) maxx = x; + if(x < minx) minx = x; + if(y > maxy) maxy = y; + if(y < miny) miny = y; + } + + this.graph.layoutMinX = minx; + this.graph.layoutMaxX = maxx; + + this.graph.layoutMinY = miny; + this.graph.layoutMaxY = maxy; + } +}; + +/* + * usefull JavaScript extensions, + */ + +function log(a) {console.log&&console.log(a);} + +/* + * Raphael Tooltip Plugin + * - attaches an element as a tooltip to another element + * + * Usage example, adding a rectangle as a tooltip to a circle: + * + * paper.circle(100,100,10).tooltip(paper.rect(0,0,20,30)); + * + * If you want to use more shapes, you'll have to put them into a set. + * + */ +Raphael.el.tooltip = function (tp) { + this.tp = tp; + this.tp.o = {x: 0, y: 0}; + this.tp.hide(); + this.hover( + function(event){ + this.mousemove(function(event){ + this.tp.translate(event.clientX - + this.tp.o.x,event.clientY - this.tp.o.y); + this.tp.o = {x: event.clientX, y: event.clientY}; + }); + this.tp.show().toFront(); + }, + function(event){ + this.tp.hide(); + this.unmousemove(); + }); + return this; +}; + +/* For IE */ +if (!Array.prototype.forEach) +{ + Array.prototype.forEach = function(fun /*, thisp*/) + { + var len = this.length; + if (typeof fun != "function") + throw new TypeError(); + + var thisp = arguments[1]; + for (var i = 0; i < len; i++) + { + if (i in this) + fun.call(thisp, this[i], i, this); + } + }; +} diff --git a/packages/bmx6-luci/files/www/luci-static/resources/bmx6/js/jquery-1.4.2.min.js b/packages/bmx6-luci/files/www/luci-static/resources/bmx6/js/jquery-1.4.2.min.js new file mode 100644 index 0000000..7c24308 --- /dev/null +++ b/packages/bmx6-luci/files/www/luci-static/resources/bmx6/js/jquery-1.4.2.min.js @@ -0,0 +1,154 @@ +/*! + * jQuery JavaScript Library v1.4.2 + * http://jquery.com/ + * + * Copyright 2010, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2010, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Sat Feb 13 22:33:48 2010 -0500 + */ +(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/, +Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&& +(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this, +a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b=== +"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this, +function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b
a"; +var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected, +parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent= +false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n= +s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true, +applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando]; +else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this, +a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b=== +w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i, +cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected= +c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed"); +a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g, +function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split("."); +k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a), +C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B=0){a.type= +e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&& +f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive; +if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data", +e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a, +"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a, +d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, +e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift(); +t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D|| +g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()}, +CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m, +g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)}, +text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}}, +setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return hl[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h= +h[3];l=0;for(m=h.length;l=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m=== +"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g, +h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&& +q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML=""; +if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="

";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}(); +(function(){var g=s.createElement("div");g.innerHTML="
";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}: +function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f0)for(var j=d;j0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j= +{},i;if(f&&a.length){e=0;for(var o=a.length;e-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a=== +"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode", +d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")? +a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType=== +1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/"},F={option:[1,""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div
","
"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d= +c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this}, +wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})}, +prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b, +this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild); +return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja, +""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]); +return this}else{e=0;for(var j=d.length;e0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["", +""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]===""&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e= +c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]? +c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja= +function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter= +Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a, +"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f= +a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b= +a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=//gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!== +"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("
").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this}, +serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), +function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href, +global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&& +e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)? +"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache=== +false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B= +false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since", +c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E|| +d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x); +g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status=== +1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b=== +"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional; +if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration=== +"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]|| +c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start; +this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now= +this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem, +e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b
"; +a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b); +c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a, +d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top- +f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset": +"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in +e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window); diff --git a/packages/bmx6-luci/files/www/luci-static/resources/bmx6/js/polling.js b/packages/bmx6-luci/files/www/luci-static/resources/bmx6/js/polling.js new file mode 100644 index 0000000..8f81443 --- /dev/null +++ b/packages/bmx6-luci/files/www/luci-static/resources/bmx6/js/polling.js @@ -0,0 +1,81 @@ +/* + Copyright (C) 2011 Pau Escrich + Contributors Lluis Esquerda + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". +*/ + + +/* + Table pooler is a function to easy call XHR poller. + + new TablePooler(5,"/cgi-bin/bmx6-info", {'status':''}, "status_table", function(st){ + var table = Array() + table.push(st.first,st.second) + return table + } + Parameters are: + polling_time: time between pollings + json_url: the json url to fetch the data + json_call: the json call + output_table_id: the table where javascript will put the data + callback_function: the function that will be executed each polling_time + + The callback_function must return an array of arrays (matrix). + In the code st is the data obtained from the json call +*/ + + function TablePooler (time, jsonurl, getparams, table_id, callback) { + this.table = document.getElementById(table_id); + this.callback = callback; + this.jsonurl = jsonurl; + this.getparams = getparams; + this.time = time; + + this.clear = function(){ + /* clear all rows */ + while( this.table.rows.length > 1 ) this.table.deleteRow(1); + } + this.start = function(){ + XHR.poll(this.time, this.jsonurl, this.getparams, function(x, st){ + var data = this.callback(st); + var content, tr, td; + this.clear(); + for (var i = 0; i < data.length; i++){ + tr = this.table.insertRow(-1); + tr.className = 'cbi-section-table-row cbi-rowstyle-' + ((i % 2) + 1); + + for (var j = 0; j < data[i].length; j++){ + td = tr.insertCell(-1); + if (data[i][j].length == 2) { + td.colSpan = data[i][j][1]; + content = data[i][j][0]; + } + else content = data[i][j]; + td.innerHTML = content; + } + } + }.bind(this)); + } + + + this.start(); + } + + + diff --git a/packages/bmx6-luci/files/www/luci-static/resources/bmx6/js/raphael-min.js b/packages/bmx6-luci/files/www/luci-static/resources/bmx6/js/raphael-min.js new file mode 100644 index 0000000..8718b5b --- /dev/null +++ b/packages/bmx6-luci/files/www/luci-static/resources/bmx6/js/raphael-min.js @@ -0,0 +1,7 @@ +/* + * Raphael 1.3.1 - JavaScript Vector Library + * + * Copyright (c) 2008 - 2009 Dmitry Baranovskiy (http://raphaeljs.com) + * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. + */ +Raphael=(function(){var a=/[, ]+/,aO=/^(circle|rect|path|ellipse|text|image)$/,L=document,au=window,l={was:"Raphael" in au,is:au.Raphael},an=function(){if(an.is(arguments[0],"array")){var d=arguments[0],e=w[aW](an,d.splice(0,3+an.is(d[0],al))),S=e.set();for(var R=0,a0=d[m];R

";if(ag.childNodes[m]!=2){return null;}}an.svg=!(an.vml=an.type=="VML");aT[aY]=an[aY];an._id=0;an._oid=0;an.fn={};an.is=function(e,d){d=aZ.call(d);return((d=="object"||d=="undefined")&&typeof e==d)||(e==null&&d=="null")||aZ.call(aw.call(e).slice(8,-1))==d;};an.setWindow=function(d){au=d;L=au.document;};var aD=function(e){if(an.vml){var d=/^\s+|\s+$/g;aD=aj(function(R){var S;R=(R+at)[aP](d,at);try{var a0=new ActiveXObject("htmlfile");a0.write("");a0.close();S=a0.body;}catch(a2){S=createPopup().document.body;}var i=S.createTextRange();try{S.style.color=R;var a1=i.queryCommandValue("ForeColor");a1=((a1&255)<<16)|(a1&65280)|((a1&16711680)>>>16);return"#"+("000000"+a1[aA](16)).slice(-6);}catch(a2){return"none";}});}else{var E=L.createElement("i");E.title="Rapha\xebl Colour Picker";E.style.display="none";L.body[aL](E);aD=aj(function(i){E.style.color=i;return L.defaultView.getComputedStyle(E,at).getPropertyValue("color");});}return aD(e);};an.hsb2rgb=aj(function(a3,a1,a7){if(an.is(a3,"object")&&"h" in a3&&"s" in a3&&"b" in a3){a7=a3.b;a1=a3.s;a3=a3.h;}var R,S,a8;if(a7==0){return{r:0,g:0,b:0,hex:"#000"};}if(a3>1||a1>1||a7>1){a3/=255;a1/=255;a7/=255;}var a0=~~(a3*6),a4=(a3*6)-a0,E=a7*(1-a1),e=a7*(1-(a1*a4)),a9=a7*(1-(a1*(1-a4)));R=[a7,e,E,E,a9,a7,a7][a0];S=[a9,a7,a7,e,E,E,a9][a0];a8=[E,E,a9,a7,a7,e,E][a0];R*=255;S*=255;a8*=255;var a5={r:R,g:S,b:a8},d=(~~R)[aA](16),a2=(~~S)[aA](16),a6=(~~a8)[aA](16);d=d[aP](aU,"0");a2=a2[aP](aU,"0");a6=a6[aP](aU,"0");a5.hex="#"+d+a2+a6;return a5;},an);an.rgb2hsb=aj(function(d,e,a1){if(an.is(d,"object")&&"r" in d&&"g" in d&&"b" in d){a1=d.b;e=d.g;d=d.r;}if(an.is(d,"string")){var a3=an.getRGB(d);d=a3.r;e=a3.g;a1=a3.b;}if(d>1||e>1||a1>1){d/=255;e/=255;a1/=255;}var a0=g(d,e,a1),i=aI(d,e,a1),R,E,S=a0;if(i==a0){return{h:0,s:0,b:a0};}else{var a2=(a0-i);E=a2/a0;if(d==a0){R=(e-a1)/a2;}else{if(e==a0){R=2+((a1-d)/a2);}else{R=4+((d-e)/a2);}}R/=6;R<0&&R++;R>1&&R--;}return{h:R,s:E,b:S};},an);var aE=/,?([achlmqrstvxz]),?/gi;an._path2string=function(){return this.join(",")[aP](aE,"$1");};function aj(E,e,d){function i(){var R=Array[aY].slice.call(arguments,0),a0=R[az]("\u25ba"),S=i.cache=i.cache||{},a1=i.count=i.count||[];if(S[Q](a0)){return d?d(S[a0]):S[a0];}a1[m]>=1000&&delete S[a1.shift()];a1[f](a0);S[a0]=E[aW](e,R);return d?d(S[a0]):S[a0];}return i;}an.getRGB=aj(function(d){if(!d||!!((d=d+at).indexOf("-")+1)){return{r:-1,g:-1,b:-1,hex:"none",error:1};}if(d=="none"){return{r:-1,g:-1,b:-1,hex:"none"};}!(({hs:1,rg:1})[Q](d.substring(0,2))||d.charAt()=="#")&&(d=aD(d));var S,i,E,a2,a3,a0=d.match(x);if(a0){if(a0[2]){a2=G(a0[2].substring(5),16);E=G(a0[2].substring(3,5),16);i=G(a0[2].substring(1,3),16);}if(a0[3]){a2=G((a3=a0[3].charAt(3))+a3,16);E=G((a3=a0[3].charAt(2))+a3,16);i=G((a3=a0[3].charAt(1))+a3,16);}if(a0[4]){a0=a0[4][z](/\s*,\s*/);i=W(a0[0]);E=W(a0[1]);a2=W(a0[2]);}if(a0[5]){a0=a0[5][z](/\s*,\s*/);i=W(a0[0])*2.55;E=W(a0[1])*2.55;a2=W(a0[2])*2.55;}if(a0[6]){a0=a0[6][z](/\s*,\s*/);i=W(a0[0]);E=W(a0[1]);a2=W(a0[2]);return an.hsb2rgb(i,E,a2);}if(a0[7]){a0=a0[7][z](/\s*,\s*/);i=W(a0[0])*2.55;E=W(a0[1])*2.55;a2=W(a0[2])*2.55;return an.hsb2rgb(i,E,a2);}a0={r:i,g:E,b:a2};var e=(~~i)[aA](16),R=(~~E)[aA](16),a1=(~~a2)[aA](16);e=e[aP](aU,"0");R=R[aP](aU,"0");a1=a1[aP](aU,"0");a0.hex="#"+e+R+a1;return a0;}return{r:-1,g:-1,b:-1,hex:"none",error:1};},an);an.getColor=function(e){var i=this.getColor.start=this.getColor.start||{h:0,s:1,b:e||0.75},d=this.hsb2rgb(i.h,i.s,i.b);i.h+=0.075;if(i.h>1){i.h=0;i.s-=0.2;i.s<=0&&(this.getColor.start={h:0,s:1,b:i.b});}return d.hex;};an.getColor.reset=function(){delete this.start;};an.parsePathString=aj(function(d){if(!d){return null;}var i={a:7,c:6,h:1,l:2,m:2,q:4,s:4,t:2,v:1,z:0},e=[];if(an.is(d,"array")&&an.is(d[0],"array")){e=av(d);}if(!e[m]){(d+at)[aP](/([achlmqstvz])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?\s*,?\s*)+)/ig,function(R,E,a1){var a0=[],S=aZ.call(E);a1[aP](/(-?\d*\.?\d*(?:e[-+]?\d+)?)\s*,?\s*/ig,function(a3,a2){a2&&a0[f](+a2);});while(a0[m]>=i[S]){e[f]([E][aS](a0.splice(0,i[S])));if(!i[S]){break;}}});}e[aA]=an._path2string;return e;});an.findDotsAtSegment=function(e,d,be,bc,a0,R,a2,a1,a8){var a6=1-a8,a5=aM(a6,3)*e+aM(a6,2)*3*a8*be+a6*3*a8*a8*a0+aM(a8,3)*a2,a3=aM(a6,3)*d+aM(a6,2)*3*a8*bc+a6*3*a8*a8*R+aM(a8,3)*a1,ba=e+2*a8*(be-e)+a8*a8*(a0-2*be+e),a9=d+2*a8*(bc-d)+a8*a8*(R-2*bc+d),bd=be+2*a8*(a0-be)+a8*a8*(a2-2*a0+be),bb=bc+2*a8*(R-bc)+a8*a8*(a1-2*R+bc),a7=(1-a8)*e+a8*be,a4=(1-a8)*d+a8*bc,E=(1-a8)*a0+a8*a2,i=(1-a8)*R+a8*a1,S=(90-ab.atan((ba-bd)/(a9-bb))*180/ab.PI);(ba>bd||a91){bi=ab.sqrt(by)*bi;bg=ab.sqrt(by)*bg;}var E=bi*bi,br=bg*bg,bt=(a4==S?-1:1)*ab.sqrt(ab.abs((E*br-E*bn*bn-br*bo*bo)/(E*bn*bn+br*bo*bo))),bd=bt*bi*bn/bg+(a9+a8)/2,bc=bt*-bg*bo/bi+(bE+bD)/2,a3=ab.asin(((bE-bc)/bg).toFixed(7)),a2=ab.asin(((bD-bc)/bg).toFixed(7));a3=a9a2){a3=a3-R*2;}if(!S&&a2>a3){a2=a2-R*2;}}else{a3=bb[0];a2=bb[1];bd=bb[2];bc=bb[3];}var a7=a2-a3;if(ab.abs(a7)>bf){var be=a2,bh=a8,a5=bD;a2=a3+bf*(S&&a2>a3?1:-1);a8=bd+bi*ab.cos(a2);bD=bc+bg*ab.sin(a2);bm=K(a8,bD,bi,bg,ba,0,S,bh,a5,[a2,be,bd,bc]);}a7=a2-a3;var a1=ab.cos(a3),bC=ab.sin(a3),a0=ab.cos(a2),bB=ab.sin(a2),bp=ab.tan(a7/4),bs=4/3*bi*bp,bq=4/3*bg*bp,bz=[a9,bE],bx=[a9+bs*bC,bE-bq*a1],bw=[a8+bs*bB,bD-bq*a0],bu=[a8,bD];bx[0]=2*bz[0]-bx[0];bx[1]=2*bz[1]-bx[1];if(bb){return[bx,bw,bu][aS](bm);}else{bm=[bx,bw,bu][aS](bm)[az]()[z](",");var bk=[];for(var bv=0,bl=bm[m];bv1000000000000&&(a0=0.5);ab.abs(S)>1000000000000&&(S=0.5);if(a0>0&&a0<1){e=M(i,d,R,E,a9,a8,a5,a2,a0);a6[f](e.x);a3[f](e.y);}if(S>0&&S<1){e=M(i,d,R,E,a9,a8,a5,a2,S);a6[f](e.x);a3[f](e.y);}a7=(a8-2*E+d)-(a2-2*a8+E);a4=2*(E-d)-2*(a8-E);a1=d-E;a0=(-a4+ab.sqrt(a4*a4-4*a7*a1))/2/a7;S=(-a4-ab.sqrt(a4*a4-4*a7*a1))/2/a7;ab.abs(a0)>1000000000000&&(a0=0.5);ab.abs(S)>1000000000000&&(S=0.5);if(a0>0&&a0<1){e=M(i,d,R,E,a9,a8,a5,a2,a0);a6[f](e.x);a3[f](e.y);}if(S>0&&S<1){e=M(i,d,R,E,a9,a8,a5,a2,S);a6[f](e.x);a3[f](e.y);}return{min:{x:aI[aW](0,a6),y:aI[aW](0,a3)},max:{x:g[aW](0,a6),y:g[aW](0,a3)}};}),H=aj(function(a9,a4){var R=r(a9),a5=a4&&r(a4),a6={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},d={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},a0=function(ba,bb){var i,bc;if(!ba){return["C",bb.x,bb.y,bb.x,bb.y,bb.x,bb.y];}!(ba[0] in {T:1,Q:1})&&(bb.qx=bb.qy=null);switch(ba[0]){case"M":bb.X=ba[1];bb.Y=ba[2];break;case"A":ba=["C"][aS](K[aW](0,[bb.x,bb.y][aS](ba.slice(1))));break;case"S":i=bb.x+(bb.x-(bb.bx||bb.x));bc=bb.y+(bb.y-(bb.by||bb.y));ba=["C",i,bc][aS](ba.slice(1));break;case"T":bb.qx=bb.x+(bb.x-(bb.qx||bb.x));bb.qy=bb.y+(bb.y-(bb.qy||bb.y));ba=["C"][aS](aK(bb.x,bb.y,bb.qx,bb.qy,ba[1],ba[2]));break;case"Q":bb.qx=ba[1];bb.qy=ba[2];ba=["C"][aS](aK(bb.x,bb.y,ba[1],ba[2],ba[3],ba[4]));break;case"L":ba=["C"][aS](aX(bb.x,bb.y,ba[1],ba[2]));break;case"H":ba=["C"][aS](aX(bb.x,bb.y,ba[1],bb.y));break;case"V":ba=["C"][aS](aX(bb.x,bb.y,bb.x,ba[1]));break;case"Z":ba=["C"][aS](aX(bb.x,bb.y,bb.X,bb.Y));break;}return ba;},e=function(ba,bb){if(ba[bb][m]>7){ba[bb].shift();var bc=ba[bb];while(bc[m]){ba.splice(bb++,0,["C"][aS](bc.splice(0,6)));}ba.splice(bb,1);a7=g(R[m],a5&&a5[m]||0);}},E=function(be,bd,bb,ba,bc){if(be&&bd&&be[bc][0]=="M"&&bd[bc][0]!="M"){bd.splice(bc,0,["M",ba.x,ba.y]);bb.bx=0;bb.by=0;bb.x=be[bc][1];bb.y=be[bc][2];a7=g(R[m],a5&&a5[m]||0);}};for(var a2=0,a7=g(R[m],a5&&a5[m]||0);a23){return{container:1,x:arguments[0],y:arguments[1],width:arguments[2],height:arguments[3]};}}},aG=function(d,i){var e=this;for(var E in i){if(i[Q](E)&&!(E in d)){switch(typeof i[E]){case"function":(function(R){d[E]=d===e?R:function(){return R[aW](e,arguments);};})(i[E]);break;case"object":d[E]=d[E]||{};aG.call(this,d[E],i[E]);break;default:d[E]=i[E];break;}}}},ak=function(d,e){d==e.top&&(e.top=d.prev);d==e.bottom&&(e.bottom=d.next);d.next&&(d.next.prev=d.prev);d.prev&&(d.prev.next=d.next);},Y=function(d,e){if(e.top===d){return;}ak(d,e);d.next=null;d.prev=e.top;e.top.next=d;e.top=d;},k=function(d,e){if(e.bottom===d){return;}ak(d,e);d.next=e.bottom;d.prev=null;e.bottom.prev=d;e.bottom=d;},A=function(e,d,i){ak(e,i);d==i.top&&(i.top=e);d.next&&(d.next.prev=e);e.next=d.next;e.prev=d;d.next=e;},aq=function(e,d,i){ak(e,i);d==i.bottom&&(i.bottom=e);d.prev&&(d.prev.next=e);e.prev=d.prev;d.prev=e;e.next=d;},s=function(d){return function(){throw new Error("Rapha\xebl: you are calling to method \u201c"+d+"\u201d of removed object");};},ar=/^r(?:\(([^,]+?)\s*,\s*([^\)]+?)\))?/;if(an.svg){aT[aY].svgns="http://www.w3.org/2000/svg";aT[aY].xlink="http://www.w3.org/1999/xlink";var O=function(d){return +d+(~~d===d)*0.5;},V=function(S){for(var e=0,E=S[m];e0.5)*2-1);aM(a1-0.5,2)+aM(S-0.5,2)>0.25&&(S=ab.sqrt(0.25-aM(a1-0.5,2))*ba+0.5)&&S!=0.5&&(S=S.toFixed(5)-0.00001*ba);}return at;});a7=a7[z](/\s*\-\s*/);if(a4=="linear"){var a0=a7.shift();a0=-W(a0);if(isNaN(a0)){return null;}var R=[0,0,ab.cos(a0*ab.PI/180),ab.sin(a0*ab.PI/180)],a6=1/(g(ab.abs(R[2]),ab.abs(R[3]))||1);R[2]*=a6;R[3]*=a6;if(R[2]<0){R[0]=-R[2];R[2]=0;}if(R[3]<0){R[1]=-R[3];R[3]=0;}}var a3=p(a7);if(!a3){return null;}var e=aJ(a4+"Gradient");e.id="r"+(an._id++)[aA](36);aJ(e,a4=="radial"?{fx:a1,fy:S}:{x1:R[0],y1:R[1],x2:R[2],y2:R[3]});d.defs[aL](e);for(var a2=0,a8=a3[m];a2a1.height)&&(a1.height=a0.y+a0.height-a1.y);(a0.x+a0.width-a1.x>a1.width)&&(a1.width=a0.x+a0.width-a1.x);}}E&&this.hide();return a1;};ax[aY].attr=function(){if(this.removed){return this;}if(arguments[m]==0){var R={};for(var E in this.attrs){if(this.attrs[Q](E)){R[E]=this.attrs[E];}}this._.rt.deg&&(R.rotation=this.rotate());(this._.sx!=1||this._.sy!=1)&&(R.scale=this.scale());R.gradient&&R.fill=="none"&&(R.fill=R.gradient)&&delete R.gradient;return R;}if(arguments[m]==1&&an.is(arguments[0],"string")){if(arguments[0]=="translation"){return t.call(this);}if(arguments[0]=="rotation"){return this.rotate();}if(arguments[0]=="scale"){return this.scale();}if(arguments[0]=="fill"&&this.attrs.fill=="none"&&this.attrs.gradient){return this.attrs.gradient;}return this.attrs[arguments[0]];}if(arguments[m]==1&&an.is(arguments[0],"array")){var d={};for(var e in arguments[0]){if(arguments[0][Q](e)){d[arguments[0][e]]=this.attrs[arguments[0][e]];}}return d;}if(arguments[m]==2){var S={};S[arguments[0]]=arguments[1];aa(this,S);}else{if(arguments[m]==1&&an.is(arguments[0],"object")){aa(this,arguments[0]);}}return this;};ax[aY].toFront=function(){if(this.removed){return this;}this.node.parentNode[aL](this.node);var d=this.paper;d.top!=this&&Y(this,d);return this;};ax[aY].toBack=function(){if(this.removed){return this;}if(this.node.parentNode.firstChild!=this.node){this.node.parentNode.insertBefore(this.node,this.node.parentNode.firstChild);k(this,this.paper);var d=this.paper;}return this;};ax[aY].insertAfter=function(d){if(this.removed){return this;}var e=d.node;if(e.nextSibling){e.parentNode.insertBefore(this.node,e.nextSibling);}else{e.parentNode[aL](this.node);}A(this,d,this.paper);return this;};ax[aY].insertBefore=function(d){if(this.removed){return this;}var e=d.node;e.parentNode.insertBefore(this.node,e);aq(this,d,this.paper);return this;};var P=function(e,d,S,R){d=O(d);S=O(S);var E=aJ("circle");e.canvas&&e.canvas[aL](E);var i=new ax(E,e);i.attrs={cx:d,cy:S,r:R,fill:"none",stroke:"#000"};i.type="circle";aJ(E,i.attrs);return i;};var aF=function(i,d,a1,e,S,a0){d=O(d);a1=O(a1);var R=aJ("rect");i.canvas&&i.canvas[aL](R);var E=new ax(R,i);E.attrs={x:d,y:a1,width:e,height:S,r:a0||0,rx:a0||0,ry:a0||0,fill:"none",stroke:"#000"};E.type="rect";aJ(R,E.attrs);return E;};var ai=function(e,d,a0,S,R){d=O(d);a0=O(a0);var E=aJ("ellipse");e.canvas&&e.canvas[aL](E);var i=new ax(E,e);i.attrs={cx:d,cy:a0,rx:S,ry:R,fill:"none",stroke:"#000"};i.type="ellipse";aJ(E,i.attrs);return i;};var o=function(i,a0,d,a1,e,S){var R=aJ("image");aJ(R,{x:d,y:a1,width:e,height:S,preserveAspectRatio:"none"});R.setAttributeNS(i.xlink,"href",a0);i.canvas&&i.canvas[aL](R);var E=new ax(R,i);E.attrs={x:d,y:a1,width:e,height:S,src:a0};E.type="image";return E;};var X=function(e,d,S,R){var E=aJ("text");aJ(E,{x:d,y:S,"text-anchor":"middle"});e.canvas&&e.canvas[aL](E);var i=new ax(E,e);i.attrs={x:d,y:S,"text-anchor":"middle",text:R,font:j.font,stroke:"none",fill:"#000"};i.type="text";aa(i,i.attrs);return i;};var aV=function(e,d){this.width=e||this.width;this.height=d||this.height;this.canvas[v]("width",this.width);this.canvas[v]("height",this.height);return this;};var w=function(){var E=ao[aW](null,arguments),i=E&&E.container,e=E.x,a0=E.y,R=E.width,d=E.height;if(!i){throw new Error("SVG container not found.");}var S=aJ("svg");R=R||512;d=d||342;aJ(S,{xmlns:"http://www.w3.org/2000/svg",version:1.1,width:R,height:d});if(i==1){S.style.cssText="position:absolute;left:"+e+"px;top:"+a0+"px";L.body[aL](S);}else{if(i.firstChild){i.insertBefore(S,i.firstChild);}else{i[aL](S);}}i=new aT;i.width=R;i.height=d;i.canvas=S;aG.call(i,i,an.fn);i.clear();return i;};aT[aY].clear=function(){var d=this.canvas;while(d.firstChild){d.removeChild(d.firstChild);}this.bottom=this.top=null;(this.desc=aJ("desc"))[aL](L.createTextNode("Created with Rapha\xebl"));d[aL](this.desc);d[aL](this.defs=aJ("defs"));};aT[aY].remove=function(){this.canvas.parentNode&&this.canvas.parentNode.removeChild(this.canvas);for(var d in this){this[d]=s(d);}};}if(an.vml){var aH=function(a8){var a5=/[ahqstv]/ig,a0=r;(a8+at).match(a5)&&(a0=H);a5=/[clmz]/g;if(a0==r&&!(a8+at).match(a5)){var e={M:"m",L:"l",C:"c",Z:"x",m:"t",l:"r",c:"v",z:"x"},R=/([clmz]),?([^clmz]*)/gi,S=/-?[^,\s-]+/g;var a4=(a8+at)[aP](R,function(a9,bb,i){var ba=[];i[aP](S,function(bc){ba[f](O(bc));});return e[bb]+ba;});return a4;}var a6=a0(a8),E,a4=[],d;for(var a2=0,a7=a6[m];a21&&(e=1);a7.opacity=e;}a8.fill&&(a7.on=true);if(a7.on==null||a8.fill=="none"){a7.on=false;}if(a7.on&&a8.fill){var i=a8.fill.match(c);if(i){a7.src=i[1];a7.type="tile";}else{a7.color=an.getRGB(a8.fill).hex;a7.src=at;a7.type="solid";if(an.getRGB(a8.fill).error&&(bd.type in {circle:1,ellipse:1}||(a8.fill+at).charAt()!="r")&&b(bd,a8.fill)){a9.fill="none";a9.gradient=a8.fill;}}}ba&&a6[aL](a7);var R=(a6.getElementsByTagName("stroke")&&a6.getElementsByTagName("stroke")[0]),bb=false;!R&&(bb=R=ah("stroke"));if((a8.stroke&&a8.stroke!="none")||a8["stroke-width"]||a8["stroke-opacity"]!=null||a8["stroke-dasharray"]||a8["stroke-miterlimit"]||a8["stroke-linejoin"]||a8["stroke-linecap"]){R.on=true;}(a8.stroke=="none"||R.on==null||a8.stroke==0||a8["stroke-width"]==0)&&(R.on=false);R.on&&a8.stroke&&(R.color=an.getRGB(a8.stroke).hex);var e=((+a9["stroke-opacity"]+1||2)-1)*((+a9.opacity+1||2)-1),a4=(W(a8["stroke-width"])||1)*0.75;e<0&&(e=0);e>1&&(e=1);a8["stroke-width"]==null&&(a4=a9["stroke-width"]);a8["stroke-width"]&&(R.weight=a4);a4&&a4<1&&(e*=a4)&&(R.weight=1);R.opacity=e;a8["stroke-linejoin"]&&(R.joinstyle=a8["stroke-linejoin"]||"miter");R.miterlimit=a8["stroke-miterlimit"]||8;a8["stroke-linecap"]&&(R.endcap=a8["stroke-linecap"]=="butt"?"flat":a8["stroke-linecap"]=="square"?"square":"round");if(a8["stroke-dasharray"]){var a5={"-":"shortdash",".":"shortdot","-.":"shortdashdot","-..":"shortdashdotdot",". ":"dot","- ":"dash","--":"longdash","- .":"dashdot","--.":"longdashdot","--..":"longdashdotdot"};R.dashstyle=a5[Q](a8["stroke-dasharray"])?a5[a8["stroke-dasharray"]]:at;}bb&&a6[aL](R);}if(bd.type=="text"){var a0=bd.paper.span.style;a9.font&&(a0.font=a9.font);a9["font-family"]&&(a0.fontFamily=a9["font-family"]);a9["font-size"]&&(a0.fontSize=a9["font-size"]);a9["font-weight"]&&(a0.fontWeight=a9["font-weight"]);a9["font-style"]&&(a0.fontStyle=a9["font-style"]);bd.node.string&&(bd.paper.span.innerHTML=(bd.node.string+at)[aP](/"));bd.W=a9.w=bd.paper.span.offsetWidth;bd.H=a9.h=bd.paper.span.offsetHeight;bd.X=a9.x;bd.Y=a9.y+O(bd.H/2);switch(a9["text-anchor"]){case"start":bd.node.style["v-text-align"]="left";bd.bbx=O(bd.W/2);break;case"end":bd.node.style["v-text-align"]="right";bd.bbx=-O(bd.W/2);break;default:bd.node.style["v-text-align"]="center";break;}}};var b=function(d,a1){d.attrs=d.attrs||{};var a2=d.attrs,a4=d.node.getElementsByTagName("fill"),S="linear",a0=".5 .5";d.attrs.gradient=a1;a1=(a1+at)[aP](ar,function(a6,a7,i){S="radial";if(a7&&i){a7=W(a7);i=W(i);aM(a7-0.5,2)+aM(i-0.5,2)>0.25&&(i=ab.sqrt(0.25-aM(a7-0.5,2))*((i>0.5)*2-1)+0.5);a0=a7+am+i;}return at;});a1=a1[z](/\s*\-\s*/);if(S=="linear"){var e=a1.shift();e=-W(e);if(isNaN(e)){return null;}}var R=p(a1);if(!R){return null;}d=d.shape||d.node;a4=a4[0]||ah("fill");if(R[m]){a4.on=true;a4.method="none";a4.type=(S=="radial")?"gradientradial":"gradient";a4.color=R[0].color;a4.color2=R[R[m]-1].color;var a5=[];for(var E=0,a3=R[m];E');};}catch(af){ah=function(d){return L.createElement("<"+d+' xmlns="urn:schemas-microsoft.com:vml" class="rvml">');};}var w=function(){var i=ao[aW](null,arguments),d=i.container,a2=i.height,a3,e=i.width,a1=i.x,a0=i.y;if(!d){throw new Error("VML container not found.");}var R=new aT,S=R.canvas=L.createElement("div"),E=S.style;e=e||512;a2=a2||342;e==+e&&(e+="px");a2==+a2&&(a2+="px");R.width=1000;R.height=1000;R.coordsize="1000 1000";R.coordorigin="0 0";R.span=L.createElement("span");R.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;";S[aL](R.span);E.cssText=an.format("width:{0};height:{1};position:absolute;clip:rect(0 {0} {1} 0);overflow:hidden",e,a2);if(d==1){L.body[aL](S);E.left=a1+"px";E.top=a0+"px";}else{d.style.width=e;d.style.height=a2;if(d.firstChild){d.insertBefore(S,d.firstChild);}else{d[aL](S);}}aG.call(R,R,an.fn);return R;};aT[aY].clear=function(){this.canvas.innerHTML=at;this.span=L.createElement("span");this.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;";this.canvas[aL](this.span);this.bottom=this.top=null;};aT[aY].remove=function(){this.canvas.parentNode.removeChild(this.canvas);for(var d in this){this[d]=s(d);}};}if((/^Apple|^Google/).test(navigator.vendor)&&!(navigator.userAgent.indexOf("Version/4.0")+1)){aT[aY].safari=function(){var d=this.rect(-99,-99,this.width+99,this.height+99);setTimeout(function(){d.remove();});};}else{aT[aY].safari=function(){};}var ae=(function(){if(L.addEventListener){return function(R,i,e,d){var E=function(S){return e.call(d,S);};R.addEventListener(i,E,false);return function(){R.removeEventListener(i,E,false);return true;};};}else{if(L.attachEvent){return function(S,E,i,e){var R=function(a0){return i.call(e,a0||au.event);};S.attachEvent("on"+E,R);var d=function(){S.detachEvent("on"+E,R);return true;};return d;};}}})();for(var ac=F[m];ac--;){(function(d){ax[aY][d]=function(e){if(an.is(e,"function")){this.events=this.events||[];this.events.push({name:d,f:e,unbind:ae(this.shape||this.node,d,e,this)});}return this;};ax[aY]["un"+d]=function(E){var i=this.events,e=i[m];while(e--){if(i[e].name==d&&i[e].f==E){i[e].unbind();i.splice(e,1);!i.length&&delete this.events;return this;}}return this;};})(F[ac]);}ax[aY].hover=function(e,d){return this.mouseover(e).mouseout(d);};ax[aY].unhover=function(e,d){return this.unmouseover(e).unmouseout(d);};aT[aY].circle=function(d,i,e){return P(this,d||0,i||0,e||0);};aT[aY].rect=function(d,R,e,i,E){return aF(this,d||0,R||0,e||0,i||0,E||0);};aT[aY].ellipse=function(d,E,i,e){return ai(this,d||0,E||0,i||0,e||0);};aT[aY].path=function(d){d&&!an.is(d,"string")&&!an.is(d[0],"array")&&(d+=at);return q(an.format[aW](an,arguments),this);};aT[aY].image=function(E,d,R,e,i){return o(this,E||"about:blank",d||0,R||0,e||0,i||0);};aT[aY].text=function(d,i,e){return X(this,d||0,i||0,e||at);};aT[aY].set=function(d){arguments[m]>1&&(d=Array[aY].splice.call(arguments,0,arguments[m]));return new T(d);};aT[aY].setSize=aV;aT[aY].top=aT[aY].bottom=null;aT[aY].raphael=an;function u(){return this.x+am+this.y;}ax[aY].scale=function(a6,a5,E,e){if(a6==null&&a5==null){return{x:this._.sx,y:this._.sy,toString:u};}a5=a5||a6;!+a5&&(a5=a6);var ba,a8,a9,a7,bm=this.attrs;if(a6!=0){var a4=this.getBBox(),a1=a4.x+a4.width/2,R=a4.y+a4.height/2,bl=a6/this._.sx,bk=a5/this._.sy;E=(+E||E==0)?E:a1;e=(+e||e==0)?e:R;var a3=~~(a6/ab.abs(a6)),a0=~~(a5/ab.abs(a5)),be=this.node.style,bo=E+(a1-E)*bl,bn=e+(R-e)*bk;switch(this.type){case"rect":case"image":var a2=bm.width*a3*bl,bd=bm.height*a0*bk;this.attr({height:bd,r:bm.r*aI(a3*bl,a0*bk),width:a2,x:bo-a2/2,y:bn-bd/2});break;case"circle":case"ellipse":this.attr({rx:bm.rx*a3*bl,ry:bm.ry*a0*bk,r:bm.r*aI(a3*bl,a0*bk),cx:bo,cy:bn});break;case"path":var bg=ad(bm.path),bh=true;for(var bj=0,bc=bg[m];bjS){if(e&&!a8.start){a6=an.findDotsAtSegment(a5,a4,E[1],E[2],E[3],E[4],E[5],E[6],(S-a3)/a1);R+=["C",a6.start.x,a6.start.y,a6.m.x,a6.m.y,a6.x,a6.y];if(a0){return R;}a8.start=R;R=["M",a6.x,a6.y+"C",a6.n.x,a6.n.y,a6.end.x,a6.end.y,E[5],E[6]][az]();a3+=a1;a5=+E[5];a4=+E[6];continue;}if(!d&&!e){a6=an.findDotsAtSegment(a5,a4,E[1],E[2],E[3],E[4],E[5],E[6],(S-a3)/a1);return{x:a6.x,y:a6.y,alpha:a6.alpha};}}a3+=a1;a5=+E[5];a4=+E[6];}R+=E;}a8.end=R;a6=d?a3:e?a8:an.findDotsAtSegment(a5,a4,E[1],E[2],E[3],E[4],E[5],E[6],1);a6.alpha&&(a6={x:a6.x,y:a6.y,alpha:a6.alpha});return a6;};},n=aj(function(E,d,a0,S,a6,a5,a4,a3){var R={x:0,y:0},a2=0;for(var a1=0;a1<1.01;a1+=0.01){var e=M(E,d,a0,S,a6,a5,a4,a3,a1);a1&&(a2+=ab.sqrt(aM(R.x-e.x,2)+aM(R.y-e.y,2)));R=e;}return a2;});var ap=aB(1),C=aB(),J=aB(0,1);ax[aY].getTotalLength=function(){if(this.type!="path"){return;}return ap(this.attrs.path);};ax[aY].getPointAtLength=function(d){if(this.type!="path"){return;}return C(this.attrs.path,d);};ax[aY].getSubpath=function(i,e){if(this.type!="path"){return;}if(ab.abs(this.getTotalLength()-e)<0.000001){return J(this.attrs.path,i).end;}var d=J(this.attrs.path,e,1);return i?J(d,i).end:d;};an.easing_formulas={linear:function(d){return d;},"<":function(d){return aM(d,3);},">":function(d){return aM(d-1,3)+1;},"<>":function(d){d=d*2;if(d<1){return aM(d,3)/2;}d-=2;return(aM(d,3)+2)/2;},backIn:function(e){var d=1.70158;return e*e*((d+1)*e-d);},backOut:function(e){e=e-1;var d=1.70158;return e*e*((d+1)*e+d)+1;},elastic:function(i){if(i==0||i==1){return i;}var e=0.3,d=e/4;return aM(2,-10*i)*ab.sin((i-d)*(2*ab.PI)/e)+1;},bounce:function(E){var e=7.5625,i=2.75,d;if(E<(1/i)){d=e*E*E;}else{if(E<(2/i)){E-=(1.5/i);d=e*E*E+0.75;}else{if(E<(2.5/i)){E-=(2.25/i);d=e*E*E+0.9375;}else{E-=(2.625/i);d=e*E*E+0.984375;}}}return d;}};var I={length:0},aR=function(){var a2=+new Date;for(var be in I){if(be!="length"&&I[Q](be)){var bj=I[be];if(bj.stop){delete I[be];I[m]--;continue;}var a0=a2-bj.start,bb=bj.ms,ba=bj.easing,bf=bj.from,a7=bj.diff,E=bj.to,a6=bj.t,a9=bj.prev||0,a1=bj.el,R=bj.callback,a8={},d;if(a0255?255:(d<0?0:d);},t=function(d,i){if(d==null){return{x:this._.tx,y:this._.ty,toString:u};}this._.tx+=+d;this._.ty+=+i;switch(this.type){case"circle":case"ellipse":this.attr({cx:+d+this.attrs.cx,cy:+i+this.attrs.cy});break;case"rect":case"image":case"text":this.attr({x:+d+this.attrs.x,y:+i+this.attrs.y});break;case"path":var e=ad(this.attrs.path);e[0][1]+=+d;e[0][2]+=+i;this.attr({path:e});break;}return this;};ax[aY].animateWith=function(e,i,d,R,E){I[e.id]&&(i.start=I[e.id].start);return this.animate(i,d,R,E);};ax[aY].animateAlong=ay();ax[aY].animateAlongBack=ay(1);function ay(d){return function(E,i,e,S){var R={back:d};an.is(e,"function")?(S=e):(R.rot=e);E&&E.constructor==ax&&(E=E.attrs.path);E&&(R.along=E);return this.animate(R,i,S);};}ax[aY].onAnimation=function(d){this._run=d||0;return this;};ax[aY].animate=function(be,a5,a4,E){if(an.is(a4,"function")||!a4){E=a4||null;}var a9={},e={},a2={};for(var a6 in be){if(be[Q](a6)){if(Z[Q](a6)){a9[a6]=this.attr(a6);(a9[a6]==null)&&(a9[a6]=j[a6]);e[a6]=be[a6];switch(Z[a6]){case"along":var bc=ap(be[a6]),a7=C(be[a6],bc*!!be.back),R=this.getBBox();a2[a6]=bc/a5;a2.tx=R.x;a2.ty=R.y;a2.sx=a7.x;a2.sy=a7.y;e.rot=be.rot;e.back=be.back;e.len=bc;be.rot&&(a2.r=W(this.rotate())||0);break;case"number":a2[a6]=(e[a6]-a9[a6])/a5;break;case"colour":a9[a6]=an.getRGB(a9[a6]);var a8=an.getRGB(e[a6]);a2[a6]={r:(a8.r-a9[a6].r)/a5,g:(a8.g-a9[a6].g)/a5,b:(a8.b-a9[a6].b)/a5};break;case"path":var S=H(a9[a6],e[a6]);a9[a6]=S[0];var a3=S[1];a2[a6]=[];for(var bb=0,a1=a9[a6][m];bb +// +// Math.seedrandom('yipee'); Sets Math.random to a function that is +// initialized using the given explicit seed. +// +// Math.seedrandom(); Sets Math.random to a function that is +// seeded using the current time, dom state, +// and other accumulated local entropy. +// The generated seed string is returned. +// +// Math.seedrandom('yowza', true); +// Seeds using the given explicit seed mixed +// together with accumulated entropy. +// +// +// Seeds using physical random bits downloaded +// from random.org. +// +// Examples: +// +// Math.seedrandom("hello"); // Use "hello" as the seed. +// document.write(Math.random()); // Always 0.5463663768140734 +// document.write(Math.random()); // Always 0.43973793770592234 +// var rng1 = Math.random; // Remember the current prng. +// +// var autoseed = Math.seedrandom(); // New prng with an automatic seed. +// document.write(Math.random()); // Pretty much unpredictable. +// +// Math.random = rng1; // Continue "hello" prng sequence. +// document.write(Math.random()); // Always 0.554769432473455 +// +// Math.seedrandom(autoseed); // Restart at the previous seed. +// document.write(Math.random()); // Repeat the 'unpredictable' value. +// +// Notes: +// +// Each time seedrandom('arg') is called, entropy from the passed seed +// is accumulated in a pool to help generate future seeds for the +// zero-argument form of Math.seedrandom, so entropy can be injected over +// time by calling seedrandom with explicit data repeatedly. +// +// On speed - This javascript implementation of Math.random() is about +// 3-10x slower than the built-in Math.random() because it is not native +// code, but this is typically fast enough anyway. Seeding is more expensive, +// especially if you use auto-seeding. Some details (timings on Chrome 4): +// +// Our Math.random() - avg less than 0.002 milliseconds per call +// seedrandom('explicit') - avg less than 0.5 milliseconds per call +// seedrandom('explicit', true) - avg less than 2 milliseconds per call +// seedrandom() - avg about 38 milliseconds per call +// +// LICENSE (BSD): +// +// Copyright 2010 David Bau, all rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of this module nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/** + * All code is in an anonymous closure to keep the global namespace clean. + * + * @param {number=} overflow + * @param {number=} startdenom + */ +(function (pool, math, width, chunks, significance, overflow, startdenom) { + + +// +// seedrandom() +// This is the seedrandom function described above. +// +math['seedrandom'] = function seedrandom(seed, use_entropy) { + var key = []; + var arc4; + + // Flatten the seed string or build one from local entropy if needed. + seed = mixkey(flatten( + use_entropy ? [seed, pool] : + arguments.length ? seed : + [new Date().getTime(), pool, window], 3), key); + + // Use the seed to initialize an ARC4 generator. + arc4 = new ARC4(key); + + // Mix the randomness into accumulated entropy. + mixkey(arc4.S, pool); + + // Override Math.random + + // This function returns a random double in [0, 1) that contains + // randomness in every bit of the mantissa of the IEEE 754 value. + + math['random'] = function random() { // Closure to return a random double: + var n = arc4.g(chunks); // Start with a numerator n < 2 ^ 48 + var d = startdenom; // and denominator d = 2 ^ 48. + var x = 0; // and no 'extra last byte'. + while (n < significance) { // Fill up all significant digits by + n = (n + x) * width; // shifting numerator and + d *= width; // denominator and generating a + x = arc4.g(1); // new least-significant-byte. + } + while (n >= overflow) { // To avoid rounding up, before adding + n /= 2; // last byte, shift everything + d /= 2; // right using integer math until + x >>>= 1; // we have exactly the desired bits. + } + return (n + x) / d; // Form the number within [0, 1). + }; + + // Return the seed that was used + return seed; +}; + +// +// ARC4 +// +// An ARC4 implementation. The constructor takes a key in the form of +// an array of at most (width) integers that should be 0 <= x < (width). +// +// The g(count) method returns a pseudorandom integer that concatenates +// the next (count) outputs from ARC4. Its return value is a number x +// that is in the range 0 <= x < (width ^ count). +// +/** @constructor */ +function ARC4(key) { + var t, u, me = this, keylen = key.length; + var i = 0, j = me.i = me.j = me.m = 0; + me.S = []; + me.c = []; + + // The empty key [] is treated as [0]. + if (!keylen) { key = [keylen++]; } + + // Set up S using the standard key scheduling algorithm. + while (i < width) { me.S[i] = i++; } + for (i = 0; i < width; i++) { + t = me.S[i]; + j = lowbits(j + t + key[i % keylen]); + u = me.S[j]; + me.S[i] = u; + me.S[j] = t; + } + + // The "g" method returns the next (count) outputs as one number. + me.g = function getnext(count) { + var s = me.S; + var i = lowbits(me.i + 1); var t = s[i]; + var j = lowbits(me.j + t); var u = s[j]; + s[i] = u; + s[j] = t; + var r = s[lowbits(t + u)]; + while (--count) { + i = lowbits(i + 1); t = s[i]; + j = lowbits(j + t); u = s[j]; + s[i] = u; + s[j] = t; + r = r * width + s[lowbits(t + u)]; + } + me.i = i; + me.j = j; + return r; + }; + // For robust unpredictability discard an initial batch of values. + // See http://www.rsa.com/rsalabs/node.asp?id=2009 + me.g(width); +} + +// +// flatten() +// Converts an object tree to nested arrays of strings. +// +/** @param {Object=} result + * @param {string=} prop */ +function flatten(obj, depth, result, prop) { + result = []; + if (depth && typeof(obj) == 'object') { + for (prop in obj) { + if (prop.indexOf('S') < 5) { // Avoid FF3 bug (local/sessionStorage) + try { result.push(flatten(obj[prop], depth - 1)); } catch (e) {} + } + } + } + return result.length ? result : '' + obj; +} + +// +// mixkey() +// Mixes a string seed into a key that is an array of integers, and +// returns a shortened string seed that is equivalent to the result key. +// +/** @param {number=} smear + * @param {number=} j */ +function mixkey(seed, key, smear, j) { + seed += ''; // Ensure the seed is a string + smear = 0; + for (j = 0; j < seed.length; j++) { + key[lowbits(j)] = + lowbits((smear ^= key[lowbits(j)] * 19) + seed.charCodeAt(j)); + } + seed = ''; + for (j in key) { seed += String.fromCharCode(key[j]); } + return seed; +} + +// +// lowbits() +// A quick "n mod width" for width a power of 2. +// +function lowbits(n) { return n & (width - 1); } + +// +// The following constants are related to IEEE 754 limits. +// +startdenom = math.pow(width, chunks); +significance = math.pow(2, significance); +overflow = significance * 2; + +// +// When seedrandom.js is loaded, we immediately mix a few bits +// from the built-in RNG into the entropy pool. Because we do +// not want to intefere with determinstic PRNG state later, +// seedrandom will not call math.random on its own again after +// initialization. +// +mixkey(math.random(), pool); + +// End anonymous scope, and pass initial values. +})( + [], // pool: entropy pool starts empty + Math, // math: package containing random, pow, and seedrandom + 256, // width: each RC4 output is 0 <= x < 256 + 6, // chunks: at least six RC4 outputs for each double + 52 // significance: there are 52 significant digits in a double +); diff --git a/packages/bmx6-luci/files/www/luci-static/resources/bmx6/link.png b/packages/bmx6-luci/files/www/luci-static/resources/bmx6/link.png new file mode 100644 index 0000000000000000000000000000000000000000..58977ffbb2f76f4fa7eab89c4d833c024c4a9da3 GIT binary patch literal 2910 zcmV-k3!(IhP)}27>jaztz!)zUr~`NcrXw@&WPTbhNQ*@#2T$7tf^AY5V&3bXsT_ zK(vDCKt!KoSLf|Ou+CVZjY$At&3b-4=y^R!&8jp~UIbkhCQweN$xVBtwf`(GUa6nH zc=6b=bosUb8~<|O=7w)hoZR!}!gccDzd3eqBHC2jTB5aJJP>e+suRyvh~HFQMkxTu z9_K<*n}TVMESima(XwV2%AtH&Z65pA;S<-LT(fI;t1cM>KmW$({`(IA*6iB7RPke- zzuNn$pZ(rx!5;eRPacg$sMH=eX1NGOR4VIEKtzRQ#)Yf|72(MM;1K2#*Qt|$QB*8a zZgRIlNv$$dG}}rQ?jQMwEpL!(sagN$`$rt9ls&oU%NGEEUI$=2xL)m^ICb6fEzcc& zSP|~Bu5i$V9OzVJEb?HjV@`Fw@Z`0c@7yDNbx`=~pyoUG2v1(ioa%ZH);dMTBFKTx zu5i#)gu86Zb4MRer-cRpYWKt`#)Ipx>q-Ey;_>}$y2@9+^Dj^TBArfax%N;`ai;vG zuCC;1B_1;1iGItfueKWAJ&dxrq)IVCr8u}31^ft?SeQsKd`Gm@>dsw0cfw4TDGa?S zQ@OM-U;Vh;XP*7U+K&3S(rL4I^dUqxPpuw9{l4h?100sb>rmXZyY;4!~cx&;ObIF<@_Xmw|-VCnAH7np6%pATfsmidCo#vS=Ja2n38swbrcO zU#!jjYS;LS|js93zZEC99MHDYtsW(%G zp@IPcLJ&019WkDN_HXg(E3aYqzwO3~#qBW99We+&kO2Y1P*JHjQ$+ESn(B7N!fKN@ z8vQ&HiH0ZSWT?ZK_k`VZqX?;Bk;Fh!lQcIJ;I$~{axB!M7@m+4l24+p=c%fyC>&hn z+*PgoC8Uxm001QvkM7tGD}M=`yDA4)RaH@3&r>CzL_HxTLM@8Hxf~1dT2#{9P+}md zQNbcNQXnKh5GysprWOnZJtycAp?e+@vd}}IPSE9o`a;NzQdw|8g%Btd=b}CTE zs}3OqASfl2${y!%xuAmZpz!pNN1TL9gibs)Xf~8k%&vzoOMc*g9Uu~OtXiv19Nl3& zz!Zu2j!1xbK>?yrN{C~7G9{o?A_%sxz{}s3m?UDjTySaDFpK*B3Ej)PKBT; z6JK|UuiJHJ85PvL0pJk({bI3pZ%=#g+cs^;s-{gZRLp@?Yp5W=XetqPCN5<4XvClf zY8dSAK_IdgUZfuYaNkG!VO8caekl(VA-YhmlYyn3j(|id_oRi2*{W*VL{mtz=IBzf zn7P*>_WL(dpqR>`s^zT$FDuL?bjjUqCvDT#?53{~0>We_+S?-h!sQtvo0qV0-5}1N zJ`S$Nz||Pe{pJKVuKfsD?IJE*o+0gR5zb^L2m!I1zGj=Yw&d=%lguR)ysT6$ZUIDCJnn~$U@nu%C49jJAdpPN{N6ZlW}yYI9X^H3WDZT&hb>f0 z7aMr>@OwC0ila9UBoi?o5by;TnM^JbHi9{L+y{W!s@oxiuN~j_`SB0j1)k4e&t$UO zC0THv3%~EK#PLRx<4R^OOhGw^FIKnA*AuvyraQK3P@++CSaHGl5cUR)L&xJ3^f@d<>?VitH|G-@U05hi!o@iUW{jUk3ZZN3V zC5fo9XsC2Gn_pD7EY`smq-~p5k$@`Wx5s~s9>c`iMLIStRj|5ShR%yvIj|h9Em54m zkRj)$>#SV0=)kh>w-uR|d>{4fOtG(6s`ZV({AW7>@DDfuz;j&b`N`?R1C@F$?1MIS zjg|VBcAv0p7adBGu2{YTKR$W{R?8ZZZ+(zY@lH(U3YeRp!~GjJLZTEa23De7DN@UJ z(Z96&gs!oY58ABMYvIZ1!UK*gJrBV90|x+L_SD#!&U+qRQmQnzr6R$M#AGOn%$KN> zo!dt4Q#kkW^W*PfqS}t2J4bu80!Huu7`AQQ21$}|@cVC|tFsdwZ7G&9tSR04_m+L{jUTnIdHkW7`SMUOAQcRmxr_9f!3Rc0F?R5K zD4HGy;wE`y^D1n8@Im<8!`K^Rc>CQmDA|%I&Sl8x$SAIyKcy7C#6+P|3H)gm>`do-cgqb~@6X0A@}eJk-D86V6CK z-8!^+2<37a!$YGm%_H!_r*Nv$&~`De&@)c1qNyh>NjAQ62+NI%Fw1^WHV>k z`FZixUw&if-`z=v;X~q2dU9VG+59OKiv`r{b-cWHH)^#i7Mu_-lc-sjSkvM2VaIV5 z&-Ue7y`ejr!G@x8JAP!t7*5kf~t2ilS;#9HF$>+OZAYS48Zw%s75 zQYn}((cBaV2vZLz{>3-PiZM3!vxb_WMwPC!c(BS$A)5dor2Ae|+cLD3>eM zGZPcTQwEdqBq|@n@fQT!qil2-fLAhM2 zXS3OFUOPGi*B%{!ZkncY`0$al0F-N|ssunuBocuQn>ReL`XhrMf9tI`$&T%hL6%wk zg%@5}`OGuV{Q5%y0s;Apj_W$R_k7(G0+_s#-3nm#+T6-Dk=r?U-C94N&o>W^9oiiW z#Re8HmHzA7`(6b=n8Mg!0QjpvstZ1(ZP^{*y_?=1{=dZk0CEu>=Ej2Zb^rhX07*qo IM6N<$g2srGw*UYD literal 0 HcmV?d00001 diff --git a/packages/bmx6-luci/files/www/luci-static/resources/bmx6/style.css b/packages/bmx6-luci/files/www/luci-static/resources/bmx6/style.css new file mode 100644 index 0000000..d8191a7 --- /dev/null +++ b/packages/bmx6-luci/files/www/luci-static/resources/bmx6/style.css @@ -0,0 +1,22 @@ + table { + width:90%; + border-top:1px solid #e5eaf8; + border-right:1px solid #e5eaf8; + margin:1em auto; + border-collapse:collapse; + } + + td { + color:#678197; + border-bottom:1px solid #e6eff8; + border-left:1px solid #e6eff8; + padding:.3em 1em; + text-align:center; + } + th { + background:#f4f9fe; + text-align:center; + font:bold 1.2em/2em Century Gothic,Trebuchet MS,Arial,Helvetica,sans-serif; + color:#66a3d3; + } + diff --git a/packages/bmx6-luci/files/www/luci-static/resources/bmx6/wifi.png b/packages/bmx6-luci/files/www/luci-static/resources/bmx6/wifi.png new file mode 100644 index 0000000000000000000000000000000000000000..4195b0872f4813e5bf5911b3b8179d29b823c76a GIT binary patch literal 3551 zcmZ{mc{tSF-^Yhcmh2?i-C44Xv1A+BiIJg@m_lO~#$YBhNY=7cG9!tStVT*RaJS}Dc0##Q5Fx+rdSTfD`e-K|5CRq!Vyz|1m6%BFOxQC zT>6$d8ZuhXIRE=zu$xei7!qjU5Sg1)O6oV<2%p>UOC$Eh0zG$s&A`IL!x?4QYF8-} z6Nr12>LaoKB3!W|Rba=!k_MfgQKUl-2CxykY2e1~%VOh#*0GxEtXSiC>Fz3uX9Ah>E&wUJV`(XEIGD z&F&sz=foK2+SVtm3-^kv#qUgej06s)VfF~DYLBu1mG^Q$EHAJ}lsM`IZkXS%3_1+~^RvB+s^0d9(tYA^K`C}X z%&+H?mlZAFN%I@)v&zBt>NkU;8?T@IVkuss!^`M4^>EzyMw7i)r7QcbgSiWYp@o#2 z$qzcFbVqOF*4vWpjXrajtHjp9u;F+omecwRx{wJpZBk+z0*UP-k|mBWWBI^kR(8$D|*fA-*`p!G2jvDLg)pE%C);^Zx{ zuRU%OhcDdqjt4G*YJW7S6*?*BR<~%0JREf|ac=gi@kjR#SA*T^BMz=!$t~YV{qAot zn3~DQ-UW~}k$%JKqR-yK=6&pOQ}CS-ww>@FmrXKX9U=<^`OfdF1KtKrk!MyZ6cBxQ zYxtQx90!$8F3M4Fg2VUvjE(mRgeg#M1~(iqcKt*!>xZZ-t%|dyjZqkxz{y#Z5f&Z= zYgM=UybukXMXoFk3P0?Wq)hpKf`cpR`7imgMO$R+-CPoG+p;AvM-kd*8rW;vSI{o= z@s50Ss}KhwQYR0{afa}ugU>KVAm%6kiZ_Do)UhF!ahA4ZuKiWrx82hIV-op~yVp)p zX}T^h_>xZ1$^n7N{DACpce6FwM|d~;OiNi)j~x(8vREYS>rf-yD-kNqKm8>p*3A~s zHa@dwP3AvdfB&ddu1mL~s!+j4;YV-h-r1S=qC@#nV3z)Pp0LDnLbfVGD z4+C)X??og+G~zuxU6;CZ;XSU#`QG?Rqv5uCV(Y`X2FRJ%M_KD#de~v4pXviJ#;{f5-nu@x-@DotSdDMUCS@!O3Nqd0!vdbZbqWYbc+vRG*5tB(XF4w6>?k zI*oOi%8^7tmMC~hek4Tm7rew>x{^Lytx-qcrdsZ=7WY83e01VWr2ekGcwkiOyLZwV zb$sDID5tykF=0}#=WiQ$gRSV^YQCUqI8A0dhA-3 zHT%J*lc5Sl6XnCHpW5?laIPsHP&#Z1is#*%XP4!7_$Y?Y7KLp%2kO#&d~F|RK_}SN zDhHK^MLpXT<5i~rdiot+hJpF0*Xl}W0U2Gy;sR=<uJkH06F4dS5-sE&E4PNs8@Y6v8nJn2XFYj#g7q0MvK%s@uRmQ&ZL7|bD znO1rRBJ6UKYne?+{X5^tO!K06+n_#mwAZboa_Os((warC^G39r8-sTh`xZzI_PFrT zj~^d-`}t?_ELJ-~a=qiVKWAPoh7Z_gKb}b`kM_oKSXgTpSM+Q;=&75QpM%xp0hjbd zW$zaQm7sEtB;8<*YoPMx^Je5IxWbDs?lYql4c;2zXSOheZ?w~C3@!gw-!kSYM0QfD=cceGx2;Y& zO4+|FUEO$q{i*q2)QI5YMiHjcnf zZC-6@eqo|2BEEGV|KZCQCq|ZGXue9yuk3!0?am_JAL@O(+T=Gz4b~&nhF?y@jRqEnvO4_Q@ zv@c)pCmFM*j-MA}9y`ul8OwQ@csQk_533uHANUk1LZiggo!GUo@am69os*1v4vdCj ze^;paL80_h1Iat|`0zH!3iW;6WxGh>`nUBjOv+u(nccU6WGbZC{G_Lcx-ioEjh6UB zZMe2(GPoqZ%})<3=Pkcl^R$8e@c@72Kx?59T!R!4DrJn+sTZ9H)r)NU0jIE$=sYeG9YEH#NK2GBr1%ZdTlI_V_{SpzrlRrTx9mA!*@+7dJ z^B*i5f-+6pcMWhEV6l~)lxrL*Bk}RI^DMDWPgJ z+!2Y(22Wo-m-mt5xXXqs3t1Qapu_;Q_U!}-?ie83LT#^Uy&~Inj)*>;QIl0e8Xu0? zOZ_$)rSS|(Jh{@$Pal@5AXjOVQI})OB~9D^+;peBuenHdTz)3bOP)6+n*uLvm5~F^+@sIoY2|oGEj)R-vAlH#Z0sW+K&&5JoZNkCQ zka(n^2nFn+DzDTOg6MH9Y$~n#vTYa~xZBq2p>1_R?T(Cj?qlTSs4N}dGWA5y4wrlA z#fbc{5M=O{`S%&8?EQ3JfNq4%be8Z*Mr_i%jg?#QCwBV8Psxb8+CGwfjpleC;=Wse zU$2%{{oTbatrRUa|+4ab2$UhqJb^d7Wv zjOkHjMVP@0jhqDKSc*B zDl=d7yd+B(l4&5x8y^4&Mqtstp4c)bQ4Js91@^@v(f&j<5$qWPA|k+A zYU*J3Xm}H|yZlji6oQ0?{KKIEn(7+rAazZUrk0b24n#`_ahY zVnY6Zpx9aG1{2UcI>Gke0D%8yMEk1+V{upl8ihst6YyvUz7*)m4C;Zx`ysIY#s`c2 zO#1W@?TE*af|>UO1P2GJVf}GLB!YlG;?h^oi!n_S|C#~=kZ6EC0qrki?u#YJmYFXn%V48)F%uZO6FY{O|1}j5A ZVr7ts0F-WxFpc3m7qncN-I?rX?NYW zDvNH4+EsT|yXXpagStQs5)>5#X#gQoNE`>SlQ_msY>z#@&&@Oc{r|eD5|AKS_Uw*6 z>3#I-J9^I%?osPX<*rwo(xu#TA)CFgwH<`68=)Cu*!k3S9Qcw+W~#evY1~c6___zB zlr;_A)uuP)`cXdAMUT;O4UIUZh~tPLhIY##>*-q`%(s3#wmUOZ5V`eLdh&Ik_vhB1 zcM|CrYFq8mQg4QooxtftO2-hT6dFnyS|oZxV`$$@p^k z>czS0Q@I_M-Pc`InS?Hlx(fn28cKmCKpc?FBn63WNCHpcxq{~io-1@bh2w$mns|Z6++2-9 z(v`?3 z9Y6>pg<%-@mZDI|lExz$CDht>Ud9)jef@>>-;`3{7m@h3qkn(8`kR^?@5~pI(rB*{ zh$izZEmX>&?szm@m!{KZqqas6wHX;MGPJv!(#|e)JA-TKBr+K!^d+4#m66M{SPPOs z?zW>)shxj%m`x2aFZbu zpUzP1%@Ndm_Us;Dp|Zw>i;ExzNh61*KsP)1orp{itk*hpS`H=}l}*LuTqL(0U7DDX zrS%$~W)WH%*KchzK0eN|BL~v?SRV47KWK(ZKF;q)x&{9hp9KVxOKBa zyWK+9H8ipC0uRUYDeNq>RokZB?x2Z*9(d>-?+z$ZAuH7tj6|N12M#bfyUfV`1BeQE z_x;OcQYjwzMv{rkSIFhkJaqgJ!^3$lUaT@XInC!ctLSD-v0A0E-3DLB_anl%30mt8 zsFcrQ8}lqy7Re@4n6VK0F;kP*Fg2l7-)3)Povy6K^o>QXd~%H_5(Wnc**|uGu|vnH zH7XQ)Q_L^daGVf#+rzXvlm=3FK)*Qij5;^|2`jNrJ{i*A-OZ&7K z*I(kZ$t83ngRW^*Dx1vRe1{;6C=cW~b@~J;D?@Kz21}G0-l62gSW)}?Z~q6n-$p-m zY|M`QDvx|~A20p&k4RUZP(Z`P#pC?>YmD9>GtgINYH9}Cwu$i306UeWuRO%`^<^$! zo&sl^tf?e@bhy&<@ZOuZBS=J+zkB-eKO7wDX}t3C+qB!;1VKO;`uLuUia^9*V&XG0 zxh@_&`XCz{Yec?_3Vk+LD`f063%4prU+)ry5r@YP5;Q~V8+D#|>@hx{zDiHdR#_uvS3aqR zdo1 z9qiq^3qOt+=q*yt8q!``Re3!5=?}mC_Q`{#GhdnB<+QwZ=0_)f_2>ipRJge$VSSUJ zwMpn~q1-yktE0Rcde~y(<8$oY)y=ctdl+wRiGm?~^v0X2q;Jc~BO@>_6Z7kjS(#>@=EbU>XKiGJy!}8yck0-OcRGbq*gKC7ZO+y(TA*j;;Uf z=_5Ptc{5y?+0-0A_+`HD8Ft>cpEqB9i+Zg=+RR{T5VTv|KRQZpA;*;~moQ9&LRXfx z^(DS_{4hqbt9Wj5!8?6$cjB&&KD;{Dv(f0R&s3Wx9qMJ5i$H z2KZrwp_^p8vQ(F=AcAR`7^Xqwx>&l2W(XNOK}mS`%Zsuz3 z@JPcA3+*8AT;H?8AjWZhbOg;Z34;I?$7Jm!QRtED%9u&RjN>?*DCSe%(LFuO{6Ffy XJC$YshjJ?x00000NkvXXu0mjf;7FEt literal 0 HcmV?d00001 diff --git a/packages/bmx6-luci/files/www/luci-static/resources/bmx6/world_small.png b/packages/bmx6-luci/files/www/luci-static/resources/bmx6/world_small.png new file mode 100644 index 0000000000000000000000000000000000000000..f5f31056c65084460d38d309afba7a848d37caf0 GIT binary patch literal 923 zcmV;M17!S(P)SkJX=+>gNTc|mwMr`$#Fi=*>c*`IiVOb* z7cL4eT!~w8rzqHf+NeZmlaQ%NNSfwh(>x}LnVCDU`UVWzP`IXKfdiobEy>7swr0+Fu!7XW$9o-h=Y{ zJAQD|a7vMx6+;>UY&Shh)he#nBApQsPYA7BM^8Xg%TU~MZvjLBexBa=aDKPWwRd0T z!OXe{qYAqXlR`dApt?-XZW6RZqH8fs8E0{ROUlFQwb`v&CJHb zsRB+IVQivET-5;;K>$_+m#Apkbsla;z3+dk$OwSl`_czb<)7(auR0{-8j}-C=v_H# zb(7P>$M8bI#Qga=6{Ql<@ z`Ft;@hh9KLTkMqU*i|WYO^Vls2ZSP}6xT1FNMCw!kh!tHFpUTyYd3g#6;88*i`AF5tEr(sNDPjwyW~79Xx~X6P(V zt%WK&H0w1fU6T_z!Oe@uZwn#X3V4@>|swPtrUJxA8- xrf_W!O;2Jqnq)Hxvgwr4*PStYvJrpJ{~uzmj|OZihQ9y+002ovPDHLkV1ltKp)CLa literal 0 HcmV?d00001 diff --git a/packages/bmx6-qmp/Makefile b/packages/bmx6-qmp/Makefile new file mode 100644 index 0000000..e92483e --- /dev/null +++ b/packages/bmx6-qmp/Makefile @@ -0,0 +1,157 @@ +# Copyright (C) 2011 Fundacio Privada per a la Xarxa Oberta, Lliure i Neutral guifi.net +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# The full GNU General Public License is included in this distribution in +# the file called "COPYING". +# +# Contibutors: +# Axel Neumann, Simó Albert i Beltran, Pau Escrich +# + + +include $(TOPDIR)/rules.mk + +PKG_NAME:=bmx6-qmp + +PKG_SOURCE_PROTO:=git + +Public Sources: +#PKG_SOURCE_URL:=git://git.bmx6.net/bmx6.git +PKG_SOURCE_URL:=git://github.com/axn/bmx6.git + +PKG_REV:=7219010098ea67f8ea08a06a68e7a765b114ca16 + +Private Sources: +#PKG_SOURCE_URL:=file:///usr/src/bmx6/bmx6-private.git + +PKG_VERSION:=r2013022001 + +PKG_RELEASE:=4 +#PKG_INSTALL:=1 # this tries to install straight to /usr/sbin/bmx6 + +PKG_SOURCE_VERSION:=$(PKG_REV) +PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION) +PKG_SOURCE:=$(PKG_SOURCE_SUBDIR).tar.gz +PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_SOURCE_SUBDIR) + +include $(INCLUDE_DIR)/package.mk + + +TARGET_CFLAGS += $(FPIC) + +#-DNO_TRAFFIC_DUMP -DNO_DYN_PLUGIN -DNO_DEBUG_DUMP -DNO_DEBUG_ALL -DNO_DEBUG_TRACK -DNO_DEBUG_SYS + +MAKE_ARGS += \ + EXTRA_CFLAGS="$(TARGET_CFLAGS) -I. -I$(STAGING_DIR)/usr/include -DNO_DEBUG_ALL -DNO_DEBUG_DUMP" \ + EXTRA_LDFLAGS="-L$(STAGING_DIR)/usr/lib " \ + REVISION_VERSION="$(PKG_REV)" \ + CC="$(TARGET_CC)" \ + INSTALL_DIR="$(PKG_INSTALL_DIR)" \ + STRIP="/bin/false" \ + build_all + + +define Package/bmx6-qmp/Default + SECTION:=net + CATEGORY:=qMp + TITLE:=BMX6 layer 3 routing daemon (QMP version) + URL:=http://bmx6.net/ + MAINTAINER:=Axel Neumann +endef + +define Package/bmx6-qmp/description +BMX6 layer 3 routing daemon (QMP version) supporting IPv4, IPv6, and IPv4 over IPv6 - http://www.bmx6.net +endef + +define Package/bmx6-qmp + $(call Package/bmx6-qmp/Default) + MENU:=1 +endef + +define Package/bmx6-qmp-uci-config + $(call Package/bmx6-qmp/Default) + DEPENDS:=bmx6-qmp +libuci + TITLE:=configuration plugin based on uci (recommended!) +endef + + +define Package/bmx6-qmp-json + $(call Package/bmx6-qmp/Default) + DEPENDS:=bmx6-qmp +libjson + TITLE:=josn plugin based on jsonc +endef + +define Package/bmx6-qmp-sms + $(call Package/bmx6-qmp/Default) + DEPENDS:=bmx6-qmp + TITLE:=sms plugin +endef + +define Package/bmx6-qmp-quagga + $(call Package/bmx6-qmp/Default) + DEPENDS:=bmx6-qmp +qmp-quagga + TITLE:=bmx6 quagga plugin to redistribute/export routes (needs manet/bmx6 patched quagga 0.99.21) +endef + +define Build/Configure + mkdir -p $(PKG_INSTALL_DIR) +endef + +define Build/Compile + $(MAKE) -C $(PKG_BUILD_DIR) $(MAKE_ARGS) +endef + + +define Package/bmx6-qmp/install + $(INSTALL_DIR) $(1)/usr/sbin $(1)/etc/config $(1)/etc/init.d + $(INSTALL_BIN) $(PKG_BUILD_DIR)/bmx6 $(1)/usr/sbin/bmx6 +endef + + +define Package/bmx6-qmp-uci-config/conffiles +/etc/config/bmx6 +endef + + +define Package/bmx6-qmp-uci-config/install + $(INSTALL_DIR) $(1)/usr/lib $(1)/etc/config $(1)/etc/init.d + $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/bmx6_uci_config/bmx6_config.so $(1)/usr/lib/bmx6_config.so + $(INSTALL_BIN) ./files/etc/init.d/bmx6 $(1)/etc/init.d/bmx6 + $(INSTALL_DATA) ./files/etc/config/bmx6 $(1)/etc/config/bmx6 +endef + +define Package/bmx6-qmp-json/install + $(INSTALL_DIR) $(1)/usr/lib + $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/bmx6_json/bmx6_json.so $(1)/usr/lib/bmx6_json.so +endef + +define Package/bmx6-qmp-sms/install + $(INSTALL_DIR) $(1)/usr/lib + $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/bmx6_sms/bmx6_sms.so $(1)/usr/lib/bmx6_sms.so +endef + +define Package/bmx6-qmp-quagga/install + $(INSTALL_DIR) $(1)/usr/lib + $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/bmx6_quagga/bmx6_quagga.so $(1)/usr/lib/bmx6_quagga.so +endef + +$(eval $(call BuildPackage,bmx6-qmp)) +$(eval $(call BuildPackage,bmx6-qmp-uci-config)) +$(eval $(call BuildPackage,bmx6-qmp-json)) +$(eval $(call BuildPackage,bmx6-qmp-sms)) +$(eval $(call BuildPackage,bmx6-qmp-quagga)) + + diff --git a/packages/bmx6-qmp/files/etc/config/bmx6 b/packages/bmx6-qmp/files/etc/config/bmx6 new file mode 100644 index 0000000..884fd12 --- /dev/null +++ b/packages/bmx6-qmp/files/etc/config/bmx6 @@ -0,0 +1,82 @@ + +# for more information: +# http://bmx6.net/projects/bmx6/wiki +# options execute: bmx6 --help + +config 'bmx6' 'general' +# option 'runtimeDir' '/var/run/bmx6' +# option 'tun4Address' '10.202.0.116/32' +# option 'tun4Address' '10.254.10.0/32' +# option 'tun6Address' '2012:0:0:1000::1/64' + +#config 'ipVersion' 'ipVersion' +# option 'ipVersion' '6' # default is 4 +# option 'throwRules' '0' + + +#config 'plugin' +# option 'plugin' 'bmx6_config.so' + + + +#config 'plugin' +# option 'plugin' 'bmx6_json.so' + + + +#config 'plugin' +# option 'plugin' 'bmx6_sms.so' + + +config 'dev' 'mesh_1' + option 'dev' 'eth0.12' + +config 'dev' 'mesh_2' + option 'dev' 'ath0.12' + + + +#config 'hna' 'my_global_prefix' +# option 'hna' '2012:0:0:74:0:0:0:0/64' + + +#config 'tunOut' +# option 'tunOut' 'ip6' +# option 'network' '2012::/16' +# option 'exportDistance' '0' + +#config 'tunOut' +# option 'tunOut' 'ip4' +# option 'network' '10.254.0.0/16' +# option 'exportDistance' '0' # requires quagga plugin ! +# option 'minPrefixLen' '27' + + + +#config 'plugin' +# option 'plugin' 'bmx6_quagga.so' + + + +#config 'redistribute' +# option 'redistribute' 'ospf6' +# option 'network' '10.0.0.0/8' +# option 'minPrefixLen' '10' +# option 'bandwidth' '10000000' +# option 'ospf6' '1' +# option 'aggregatePrefixLen' '16' + +#config 'redistribute' +# option 'redistribute' 'bgp' +# option 'network' '0.0.0.0/0' +# option 'minPrefixLen' '0' +# option 'maxPrefixLen' '24' +# option 'bandwidth' '10000000' +# option 'bgp' '1' +# option 'aggregatePrefixLen' '8' + + + + + + diff --git a/packages/bmx6-qmp/files/etc/init.d/bmx6 b/packages/bmx6-qmp/files/etc/init.d/bmx6 new file mode 100755 index 0000000..2b54386 --- /dev/null +++ b/packages/bmx6-qmp/files/etc/init.d/bmx6 @@ -0,0 +1,41 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2011 Fundacio Privada per a la Xarxa Oberta, Lliure i Neutral guifi.net +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# The full GNU General Public License is included in this distribution in +# the file called "COPYING". + +START=91 + +BIN=/usr/sbin/bmx6 +CONF=/etc/config/bmx6 +PID=/var/run/bmx6/pid + + +start() { + cd /root/ + ulimit -c 20000 + $BIN -f $CONF -d0 > /dev/null & +# start-stop-daemon -b -x $BIN -S -- -f $CONF +} + +stop() { + start-stop-daemon -p $PID -K +} + +restart() { + stop; sleep 3; start +} diff --git a/packages/qmp-quagga/Makefile b/packages/qmp-quagga/Makefile new file mode 100644 index 0000000..2401827 --- /dev/null +++ b/packages/qmp-quagga/Makefile @@ -0,0 +1,312 @@ +# +# Copyright (C) 2006-2012 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# +# Contributors: +# Simó Albert i Beltran +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=qmp-quagga +PKG_VERSION:=0.99.21 +PKG_RELEASE:=2 +PKG_MD5SUM:=99840adbe57047c90dfba6b6ed9aec7f + +PKG_SOURCE:=quagga-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=http://www.quagga.net/download/ \ + http://www.de.quagga.net/download/ \ + http://www.uk.quagga.net/download/ + +PKG_BUILD_DIR:=$(BUILD_DIR)/quagga-$(PKG_VERSION) + +PKG_CONFIG_DEPENDS:= \ + CONFIG_IPV6 \ + CONFIG_PACKAGE_qmp-quagga-watchquagga \ + CONFIG_PACKAGE_qmp-quagga-zebra \ + CONFIG_PACKAGE_qmp-quagga-libzebra \ + CONFIG_PACKAGE_qmp-quagga-libospf \ + CONFIG_PACKAGE_qmp-quagga-bgpd \ + CONFIG_PACKAGE_qmp-quagga-isisd \ + CONFIG_PACKAGE_qmp-quagga-ospf6d \ + CONFIG_PACKAGE_qmp-quagga-ripd \ + CONFIG_PACKAGE_qmp-quagga-ripngd \ + CONFIG_PACKAGE_qmp-quagga-babeld \ + CONFIG_PACKAGE_qmp-quagga-vtysh +PKG_BUILD_PARALLEL:=1 +PKG_FIXUP:=autoreconf +PKG_INSTALL:=1 + +include $(INCLUDE_DIR)/package.mk + +define Package/qmp-quagga/Default + SECTION:=qMp + CATEGORY:=qMp + SUBMENU:=Routing and Redirection + DEPENDS:=qmp-quagga + TITLE:=The Quagga Software Routing Suite + URL:=http://www.quagga.net + MAINTAINER:=Vasilis Tsiligiannis +endef + +define Package/qmp-quagga + $(call Package/qmp-quagga/Default) + DEPENDS:=+qmp-quagga-vtysh +qmp-quagga-bgpd + MENU:=1 +endef + +define Package/qmp-quagga/description + A routing software package that provides TCP/IP based routing services + with routing protocols support such as RIPv1, RIPv2, RIPng, OSPFv2, + OSPFv3, BGP-4, and BGP-4+ +endef + +define Package/qmp-quagga-watchquagga + $(call Package/qmp-quagga/Default) + TITLE:=Quagga watchdog + DEPENDS+=+qmp-quagga-libzebra + DEFAULT:=y if PACKAGE_qmp-quagga +endef + +define Package/qmp-quagga-zebra + $(call Package/qmp-quagga/Default) + TITLE:=Zebra daemon + DEPENDS+=+qmp-quagga-libzebra + DEFAULT:=y if PACKAGE_qmp-quagga +endef + +define Package/qmp-quagga-libzebra + $(call Package/qmp-quagga/Default) + DEPENDS+=+librt + TITLE:=zebra library +endef + +define Package/qmp-quagga-libospf + $(call Package/qmp-quagga/Default) + TITLE:=OSPF library +endef + +define Package/qmp-quagga-bgpd + $(call Package/qmp-quagga/Default) + DEPENDS+=+qmp-quagga-libzebra + TITLE:=BGPv4, BGPv4+, BGPv4- routing engine +endef + +define Package/qmp-quagga-isisd + $(call Package/qmp-quagga/Default) + DEPENDS+=+qmp-quagga-libzebra + TITLE:=IS-IS routing engine +endef + +define Package/qmp-quagga-ospfd + $(call Package/qmp-quagga/Default) + DEPENDS+=+qmp-quagga-libospf +qmp-quagga-libzebra + TITLE:=OSPFv2 routing engine +endef + +define Package/qmp-quagga-ospf6d + $(call Package/qmp-quagga/Default) + DEPENDS+=+qmp-quagga-libospf +qmp-quagga-libzebra @IPV6 + TITLE:=OSPFv3 routing engine +endef + +define Package/qmp-quagga-ripd + $(call Package/qmp-quagga/Default) + DEPENDS+=+qmp-quagga-libzebra + TITLE:=RIP routing engine +endef + +define Package/qmp-quagga-ripngd + $(call Package/qmp-quagga/Default) + DEPENDS+=+qmp-quagga-libzebra @IPV6 + TITLE:=RIPNG routing engine +endef + +define Package/qmp-quagga-babeld + $(call Package/qmp-quagga/Default) + DEPENDS+=+qmp-quagga-libzebra + TITLE:=Babel routing engine +endef + +define Package/qmp-quagga-vtysh + $(call Package/qmp-quagga/Default) + DEPENDS+=+qmp-quagga-libzebra +libreadline +libncurses + TITLE:=integrated shell for Quagga routing software +endef + +define Package/qmp-quagga-zebra/conffiles +/etc/quagga/zebra.conf +endef + +define Package/qmp-quagga-bgpd/conffiles +/etc/quagga/bgpd.conf +endef + +define Package/qmp-quagga-isisd/conffiles +/etc/quagga/isisd.conf +endef + +define Package/qmp-quagga-ospfd/conffiles +/etc/quagga/ospfd.conf +endef + +define Package/qmp-quagga-ospf6d/conffiles +/etc/quagga/ospf6d.conf +endef + +define Package/qmp-quagga-ripd/conffiles +/etc/quagga/ripd.conf +endef + +define Package/qmp-quagga-ripngd/conffiles +/etc/quagga/ripngd.conf +endef + +define Package/qmp-quagga-babeld/conffiles +/etc/quagga/babeld.conf +endef + +ifneq ($(SDK),) +CONFIG_PACKAGE_qmp-quagga-libzebra:=m +CONFIG_PACKAGE_qmp-quagga-libospf:=m +CONFIG_PACKAGE_qmp-quagga-watchquagga:=m +CONFIG_PACKAGE_qmp-quagga-zebra:=m +CONFIG_PACKAGE_qmp-quagga-bgpd:=m +CONFIG_PACKAGE_qmp-quagga-isisd:=m +CONFIG_PACKAGE_qmp-quagga-ospf6d:=m +CONFIG_PACKAGE_qmp-quagga-ripd:=m +CONFIG_PACKAGE_qmp-quagga-ripngd:=m +CONFIG_PACKAGE_qmp-quagga-babeld:=m +CONFIG_PACKAGE_qmp-quagga-vtysh:=m +endif + +CONFIGURE_ARGS+= \ + --localstatedir=/var/run/quagga \ + --sysconfdir=/etc/quagga/ \ + --enable-shared \ + --disable-static \ + --enable-user=network \ + --enable-group=network \ + --enable-pie=no \ + --enable-multipath=8 \ + --disable-ospfclient \ + --disable-capabilities \ + $(call autoconf_bool,CONFIG_PACKAGE_qmp-quagga-libzebra,zebra) \ + $(call autoconf_bool,CONFIG_PACKAGE_qmp-quagga-libospf,ospfd) \ + $(call autoconf_bool,CONFIG_PACKAGE_qmp-quagga-bgpd,bgpd) \ + $(call autoconf_bool,CONFIG_PACKAGE_qmp-quagga-isisd,isisd) \ + $(call autoconf_bool,CONFIG_PACKAGE_qmp-quagga-ospf6d,ospf6d) \ + $(call autoconf_bool,CONFIG_PACKAGE_qmp-quagga-ripd,ripd) \ + $(call autoconf_bool,CONFIG_PACKAGE_qmp-quagga-ripngd,ripngd) \ + $(call autoconf_bool,CONFIG_PACKAGE_qmp-quagga-babeld,babeld) \ + $(call autoconf_bool,CONFIG_PACKAGE_qmp-quagga-vtysh,vtysh) \ + +MAKE_FLAGS += \ + CFLAGS="$(TARGET_CFLAGS) -std=gnu99" + +define Package/qmp-quagga/install + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) ./files/quagga $(1)/usr/sbin/quagga.init + $(INSTALL_DIR) $(1)/etc/init.d + $(INSTALL_BIN) ./files/quagga.init $(1)/etc/init.d/quagga +endef + +define Package/qmp-quagga-watchquagga/install + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/watchquagga $(1)/usr/sbin/ +endef + +define Package/qmp-quagga-zebra/install + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/zebra $(1)/usr/sbin/ + $(INSTALL_DIR) $(1)/etc/quagga + chmod 0750 $(1)/etc/quagga + $(INSTALL_CONF) ./files/quagga.conf $(1)/etc/quagga/zebra.conf +endef + +define Package/qmp-quagga-bgpd/install + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/bgpd $(1)/usr/sbin/ + $(INSTALL_DIR) $(1)/etc/quagga + chmod 0750 $(1)/etc/quagga + $(INSTALL_CONF) ./files/quagga.conf $(1)/etc/quagga/bgpd.conf +endef + +define Package/qmp-quagga-isisd/install + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/isisd $(1)/usr/sbin/ + $(INSTALL_DIR) $(1)/etc/quagga + chmod 0750 $(1)/etc/quagga + $(INSTALL_CONF) ./files/quagga.conf $(1)/etc/quagga/isisd.conf +endef + +define Package/qmp-quagga-ospfd/install + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/ospfd $(1)/usr/sbin/ + $(INSTALL_DIR) $(1)/etc/quagga + chmod 0750 $(1)/etc/quagga + $(INSTALL_CONF) ./files/quagga.conf $(1)/etc/quagga/ospfd.conf +endef + +define Package/qmp-quagga-ospf6d/install + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/ospf6d $(1)/usr/sbin/ + $(INSTALL_DIR) $(1)/etc/quagga + chmod 0750 $(1)/etc/quagga + $(INSTALL_CONF) ./files/quagga.conf $(1)/etc/quagga/ospf6d.conf +endef + +define Package/qmp-quagga-ripd/install + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/ripd $(1)/usr/sbin/ + $(INSTALL_DIR) $(1)/etc/quagga + chmod 0750 $(1)/etc/quagga + $(INSTALL_CONF) ./files/quagga.conf $(1)/etc/quagga/ripd.conf +endef + +define Package/qmp-quagga-ripngd/install + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/ripngd $(1)/usr/sbin/ + $(INSTALL_DIR) $(1)/etc/quagga + chmod 0750 $(1)/etc/quagga + $(INSTALL_CONF) ./files/quagga.conf $(1)/etc/quagga/ripngd.conf +endef + +define Package/qmp-quagga-babeld/install + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/babeld $(1)/usr/sbin/ + $(INSTALL_DIR) $(1)/etc/quagga + chmod 0750 $(1)/etc/quagga + $(INSTALL_CONF) ./files/quagga.conf $(1)/etc/quagga/babeld.conf +endef + +define Package/qmp-quagga-vtysh/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/vtysh $(1)/usr/bin/ +endef + +define Package/qmp-quagga-libospf/install + $(INSTALL_DIR) $(1)/usr/lib + $(CP) $(PKG_INSTALL_DIR)/usr/lib/libospf.so.* $(1)/usr/lib/ +endef + +define Package/qmp-quagga-libzebra/install + $(INSTALL_DIR) $(1)/usr/lib + $(CP) $(PKG_INSTALL_DIR)/usr/lib/libzebra.so.* $(1)/usr/lib/ +endef + +$(eval $(call BuildPackage,qmp-quagga)) +$(eval $(call BuildPackage,qmp-quagga-libzebra)) +$(eval $(call BuildPackage,qmp-quagga-libospf)) +$(eval $(call BuildPackage,qmp-quagga-watchquagga)) +$(eval $(call BuildPackage,qmp-quagga-zebra)) +$(eval $(call BuildPackage,qmp-quagga-bgpd)) +$(eval $(call BuildPackage,qmp-quagga-isisd)) +$(eval $(call BuildPackage,qmp-quagga-ospfd)) +$(eval $(call BuildPackage,qmp-quagga-ospf6d)) +$(eval $(call BuildPackage,qmp-quagga-ripd)) +$(eval $(call BuildPackage,qmp-quagga-ripngd)) +$(eval $(call BuildPackage,qmp-quagga-babeld)) +$(eval $(call BuildPackage,qmp-quagga-vtysh)) diff --git a/packages/qmp-quagga/files/quagga b/packages/qmp-quagga/files/quagga new file mode 100644 index 0000000..0ccfb1c --- /dev/null +++ b/packages/qmp-quagga/files/quagga @@ -0,0 +1,335 @@ +#!/bin/sh +# +# quagga Starts/stops quagga daemons and watchquagga. +# Create a daemon.conf file to have that routing daemon +# started/stopped automagically when using this script +# without any daemon names as args. +# If watchquagga is available, it will also be +# started/stopped if the script is called without +# any daemon names. +# + +ME=$(basename $0) + +usage() { + echo "Usage: ${ME} {start|stop|restart} [daemon ...]" + exit 2 +} + +if [ -z "$1" ] +then + usage +else + COMMAND=$1 +fi +shift +ARG_DAEMONS=$* +BINDIR=/usr/sbin +CONFDIR=/etc/quagga +STATEDIR=/var/run/quagga +RUNUSER=network +RUNGROUP=$RUNUSER +DAEMONS="zebra ripd ripngd ospfd ospf6d bgpd" +DAEMON_FLAGS=-d +WATCHQUAGGA_FLAGS="-d -z -T 60 -R" +WATCHQUAGGA_CMD="$0 watchrestart" +if [ ${COMMAND} != "watchrestart" ] +then + DAEMONS="${DAEMONS} watchquagga" +fi +DAEMONS_STARTSEQ=${DAEMONS} + +reverse() +{ + local revlist r + revlist= + for r + do + revlist="$r $revlist" + done + echo $revlist +} + +DAEMONS_STOPSEQ=$(reverse ${DAEMONS_STARTSEQ}) + +#pidof() { +# ps ax | awk 'match($5, "(^|/)'"$1"'$") > 0 { printf " %s", $1 }' +#} + +quit() { + echo "${ME}: $1" + exit 0 +} + +die() { + echo "${ME}: $1" + exit 1 +} + +is_in() { + local i + for i in $2 + do + [ "$1" = "$i" ] && return 0 + done + return 1 +} + +select_subset() { + local unknown i j + unknown= + RESULT= + for i in $1 + do + is_in $i "$2" || unknown="$unknown $i" + done + if [ -n "$unknown" ] + then + RESULT=$unknown + return 1 + else + for j in $2 + do + is_in $j "$1" && RESULT="$RESULT $j" + done + return 0 + fi +} + +# check command + +case ${COMMAND} +in + start|stop|restart) + ;; + watchrestart) + if [ -n "$ARG_DAEMONS" ] + then + echo "${ME}: watchrestart mode is only for use by watchquagga" + exit 2 + fi + ;; + *) + usage + ;; +esac + +# select daemons to start + +case ${COMMAND} +in + start|restart|watchrestart) + START_DAEMONS= + for d in ${DAEMONS_STARTSEQ} + do + [ -x "${BINDIR}/${d}" -a -f "${CONFDIR}/${d}.conf" ] \ + && START_DAEMONS="${START_DAEMONS}${d} " + done + WATCHQUAGGA_DAEMONS=${START_DAEMONS} + if is_in watchquagga "${DAEMONS_STARTSEQ}" + then + START_DAEMONS="${START_DAEMONS} watchquagga" + fi + if [ -n "${ARG_DAEMONS}" ] + then + if select_subset "${ARG_DAEMONS}" "${DAEMONS}" + then + if select_subset "${ARG_DAEMONS}" "${START_DAEMONS}" + then + START_DAEMONS=${RESULT} + else + die "these daemons are not startable:${RESULT}." + fi + else + die "unknown daemons:${RESULT}; choose from: ${DAEMONS}." + fi + fi + ;; +esac + +# select daemons to stop + +case ${COMMAND} +in + stop|restart|watchrestart) + STOP_DAEMONS=${DAEMONS_STOPSEQ} + if [ -n "${ARG_DAEMONS}" ] + then + if select_subset "${ARG_DAEMONS}" "${STOP_DAEMONS}" + then + STOP_DAEMONS=${RESULT} + else + die "unknown daemons:${RESULT}; choose from: ${DAEMONS}." + fi + fi + stop_daemons= + for d in ${STOP_DAEMONS} + do + pidfile=${STATEDIR}/${d}.pid + if [ -f "${pidfile}" -o -n "$(pidof ${d})" ] + then + stop_daemons="${stop_daemons}${d} " + elif [ -n "${ARG_DAEMONS}" ] + then + echo "${ME}: found no ${d} process running." + fi + done + STOP_DAEMONS=${stop_daemons} + ;; +esac + +# stop daemons + +for d in $STOP_DAEMONS +do + echo -n "${ME}: Stopping ${d} ... " + pidfile=${STATEDIR}/${d}.pid + if [ -f "${pidfile}" ] + then + file_pid=$(cat ${pidfile}) + if [ -z "${file_pid}" ] + then + echo -n "no pid file entry found ... " + fi + else + file_pid= + echo -n "no pid file found ... " + fi + proc_pid=$(pidof ${d}) + if [ -z "${proc_pid}" ] + then + echo -n "found no ${d} process running ... " + else + count=0 + notinpidfile= + for p in ${proc_pid} + do + count=$((${count}+1)) + if kill ${p} + then + echo -n "killed ${p} ... " + else + echo -n "failed to kill ${p} ... " + fi + [ "${p}" = "${file_pid}" ] \ + || notinpidfile="${notinpidfile} ${p}" + done + [ ${count} -le 1 ] \ + || echo -n "WARNING: ${count} ${d} processes were found running ... " + for n in ${notinpidfile} + do + echo -n "WARNING: process ${n} was not in pid file ... " + done + fi + count=0 + survivors=$(pidof ${d}) + while [ -n "${survivors}" ] + do + sleep 1 + count=$((${count}+1)) + survivors=$(pidof ${d}) + [ -z "${survivors}" -o ${count} -gt 5 ] && break + for p in ${survivors} + do + sleep 1 + echo -n "${p} " + kill ${p} + done + done + survivors=$(pidof ${d}) + [ -n "${survivors}" ] && \ + if kill -KILL ${survivors} + then + echo -n "KILLed ${survivors} ... " + else + echo -n "failed to KILL ${survivors} ... " + fi + sleep 1 + survivors=$(pidof ${d}) + if [ -z "${survivors}" ] + then + echo -n "done." + if [ -f "${pidfile}" ] + then + rm -f ${pidfile} \ + || echo -n " Failed to remove pidfile." + fi + else + echo -n "failed to stop ${survivors} - giving up." + if [ "${survivors}" != "${file_pid}" ] + then + if echo "${survivors}" > ${pidfile} + then + chown ${RUNUSER}:${RUNGROUP} ${pidfile} + echo -n " Wrote ${survivors} to pidfile." + else + echo -n " Failed to write ${survivors} to pidfile." + fi + fi + fi + echo +done + +# start daemons + +if [ -n "$START_DAEMONS" ] +then + [ -d ${CONFDIR} ] \ + || quit "${ME}: no config directory ${CONFDIR} - exiting." + chown -R ${RUNUSER}:${RUNGROUP} ${CONFDIR} + [ -d ${STATEDIR} ] || mkdir -p ${STATEDIR} \ + || die "${ME}: could not create state directory ${STATEDIR} - exiting." + chown -R ${RUNUSER}:${RUNGROUP} ${STATEDIR} + + for d in $START_DAEMONS + do + echo -n "${ME}: Starting ${d} ... " + proc_pid=$(pidof ${d}) + pidfile=${STATEDIR}/${d}.pid + file_pid= + if [ -f "${pidfile}" ] + then + file_pid=$(cat ${pidfile}) + if [ -n "${file_pid}" ] + then + echo -n "found old pid file entry ${file_pid} ... " + fi + fi + if [ -n "${proc_pid}" ] + then + echo -n "found ${d} running (${proc_pid}) - skipping ${d}." + if [ "${proc_pid}" != "${file_pid}" ] + then + if echo "${proc_pid}" > ${pidfile} + then + chown ${RUNUSER}:${RUNGROUP} ${pidfile} + echo -n " Wrote ${proc_pid} to pidfile." + else + echo -n " Failed to write ${proc_pid} to pidfile." + fi + fi + elif rm -f "${pidfile}" + then + if [ "${d}" = "watchquagga" ] + then + "${BINDIR}/${d}" \ + ${WATCHQUAGGA_FLAGS} \ + "${WATCHQUAGGA_CMD}" \ + ${WATCHQUAGGA_DAEMONS} + status=$? + else + "${BINDIR}/${d}" ${DAEMON_FLAGS} + status=$? + fi + if [ $status -eq 0 ] + then + echo -n "done." + else + echo -n "failed." + fi + else + echo -n " failed to remove pidfile." + fi + echo + done +fi diff --git a/packages/qmp-quagga/files/quagga.conf b/packages/qmp-quagga/files/quagga.conf new file mode 100644 index 0000000..fb7a54e --- /dev/null +++ b/packages/qmp-quagga/files/quagga.conf @@ -0,0 +1,7 @@ +password zebra +! +access-list vty permit 127.0.0.0/8 +access-list vty deny any +! +line vty + access-class vty diff --git a/packages/qmp-quagga/files/quagga.init b/packages/qmp-quagga/files/quagga.init new file mode 100644 index 0000000..21fbf2c --- /dev/null +++ b/packages/qmp-quagga/files/quagga.init @@ -0,0 +1,11 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2006 OpenWrt.org + +START=60 +start() { + /usr/sbin/quagga.init start +} + +stop() { + /usr/sbin/quagga.init stop +} diff --git a/packages/qmp-quagga/patches/110-fix_ipctl_forwarding.patch b/packages/qmp-quagga/patches/110-fix_ipctl_forwarding.patch new file mode 100644 index 0000000..d757312 --- /dev/null +++ b/packages/qmp-quagga/patches/110-fix_ipctl_forwarding.patch @@ -0,0 +1,25 @@ +Add definitions for IPCTL_FORWARDING and IP6CTL_FORWARDING. + +Inspired from +http://svn.gnumonks.org/trunk/grouter/build/src/quagga/quagga/quagga-0.99.1-forward_sysctl-2.6.14.patch + +Signed-off-by: Thomas Petazzoni + +--- a/zebra/ipforward_sysctl.c ++++ b/zebra/ipforward_sysctl.c +@@ -31,6 +31,15 @@ + + #define MIB_SIZ 4 + ++/* Fix for recent (2.6.14) kernel headers */ ++#ifndef IPCTL_FORWARDING ++#define IPCTL_FORWARDING NET_IPV4_FORWARD ++#endif ++ ++#ifndef IP6CTL_FORWARDING ++#define IP6CTL_FORWARDING NET_IPV6_FORWARDING ++#endif ++ + extern struct zebra_privs_t zserv_privs; + + /* IPv4 forwarding control MIB. */ diff --git a/packages/qmp-quagga/patches/120-quagga_manet.patch b/packages/qmp-quagga/patches/120-quagga_manet.patch new file mode 100644 index 0000000..bf6d056 --- /dev/null +++ b/packages/qmp-quagga/patches/120-quagga_manet.patch @@ -0,0 +1,243 @@ +--- a/lib/log.c ++++ b/lib/log.c +@@ -929,13 +929,19 @@ proto_redistnum(int afi, const char *s) + return ZEBRA_ROUTE_STATIC; + else if (strncmp (s, "r", 1) == 0) + return ZEBRA_ROUTE_RIP; +- else if (strncmp (s, "o", 1) == 0) ++ else if (strncmp (s, "os", 2) == 0) + return ZEBRA_ROUTE_OSPF; + else if (strncmp (s, "i", 1) == 0) + return ZEBRA_ROUTE_ISIS; + else if (strncmp (s, "bg", 2) == 0) + return ZEBRA_ROUTE_BGP; +- else if (strncmp (s, "ba", 2) == 0) ++ else if (strncmp (s, "h", 1) == 0) ++ return ZEBRA_ROUTE_HSLS; ++ else if (strncmp (s, "ol", 2) == 0) ++ return ZEBRA_ROUTE_OLSR; ++ else if (strncmp (s, "bat", 3) == 0) ++ return ZEBRA_ROUTE_BATMAN; ++ else if (strncmp (s, "bab", 3) == 0) + return ZEBRA_ROUTE_BABEL; + } + if (afi == AFI_IP6) +@@ -948,13 +954,19 @@ proto_redistnum(int afi, const char *s) + return ZEBRA_ROUTE_STATIC; + else if (strncmp (s, "r", 1) == 0) + return ZEBRA_ROUTE_RIPNG; +- else if (strncmp (s, "o", 1) == 0) ++ else if (strncmp (s, "os", 2) == 0) + return ZEBRA_ROUTE_OSPF6; + else if (strncmp (s, "i", 1) == 0) + return ZEBRA_ROUTE_ISIS; + else if (strncmp (s, "bg", 2) == 0) + return ZEBRA_ROUTE_BGP; +- else if (strncmp (s, "ba", 2) == 0) ++ else if (strncmp (s, "h", 1) == 0) ++ return ZEBRA_ROUTE_HSLS; ++ else if (strncmp (s, "ol", 2) == 0) ++ return ZEBRA_ROUTE_OLSR; ++ else if (strncmp (s, "bat", 3) == 0) ++ return ZEBRA_ROUTE_BATMAN; ++ else if (strncmp (s, "bab", 3) == 0) + return ZEBRA_ROUTE_BABEL; + } + return -1; +--- a/lib/route_types.txt ++++ b/lib/route_types.txt +@@ -51,13 +51,9 @@ ZEBRA_ROUTE_OSPF, ospf, ospfd + ZEBRA_ROUTE_OSPF6, ospf6, ospf6d, 'O', 0, 1, "OSPFv6" + ZEBRA_ROUTE_ISIS, isis, isisd, 'I', 1, 1, "IS-IS" + ZEBRA_ROUTE_BGP, bgp, bgpd, 'B', 1, 1, "BGP" +-# HSLS and OLSR both are AFI independent (so: 1, 1), however +-# we want to disable for them for general Quagga distribution. +-# This at least makes it trivial for users of these protocols +-# to 'switch on' redist support (direct numeric entry remaining +-# possible). +-ZEBRA_ROUTE_HSLS, hsls, hslsd, 'H', 0, 0, "HSLS" +-ZEBRA_ROUTE_OLSR, olsr, olsrd, 'o', 0, 0, "OLSR" ++ZEBRA_ROUTE_HSLS, hsls, hslsd, 'H', 1, 1, "HSLS" ++ZEBRA_ROUTE_OLSR, olsr, olsrd, 'o', 1, 1, "OLSR" ++ZEBRA_ROUTE_BATMAN, batman, batmand,'b', 1, 1, "BATMAN" + ZEBRA_ROUTE_BABEL, babel, babeld, 'A', 1, 1, "Babel" + + ## help strings +@@ -72,5 +68,6 @@ ZEBRA_ROUTE_OSPF6, "Open Shortest Path + ZEBRA_ROUTE_ISIS, "Intermediate System to Intermediate System (IS-IS)" + ZEBRA_ROUTE_BGP, "Border Gateway Protocol (BGP)" + ZEBRA_ROUTE_HSLS, "Hazy-Sighted Link State Protocol (HSLS)" +-ZEBRA_ROUTE_OLSR, "Optimised Link State Routing (OLSR)" ++ZEBRA_ROUTE_OLSR, "Optimized Link State Routing (OLSR)" ++ZEBRA_ROUTE_BATMAN, "Better Approach to Mobile Ad-Hoc Networking (BATMAN)" + ZEBRA_ROUTE_BABEL, "Babel routing protocol (Babel)" +--- a/ripd/rip_zebra.c ++++ b/ripd/rip_zebra.c +@@ -206,9 +206,12 @@ static struct { + {ZEBRA_ROUTE_KERNEL, 1, "kernel"}, + {ZEBRA_ROUTE_CONNECT, 1, "connected"}, + {ZEBRA_ROUTE_STATIC, 1, "static"}, +- {ZEBRA_ROUTE_OSPF, 1, "ospf"}, ++ {ZEBRA_ROUTE_OSPF, 2, "ospf"}, + {ZEBRA_ROUTE_BGP, 2, "bgp"}, +- {ZEBRA_ROUTE_BABEL, 2, "babel"}, ++ {ZEBRA_ROUTE_HSLS, 1, "hsls"}, ++ {ZEBRA_ROUTE_OLSR, 2, "olsr"}, ++ {ZEBRA_ROUTE_BATMAN, 3, "batman"}, ++ {ZEBRA_ROUTE_BABEL, 3, "babel"}, + {0, 0, NULL} + }; + +--- a/ripngd/ripng_zebra.c ++++ b/ripngd/ripng_zebra.c +@@ -216,9 +216,12 @@ static struct { + {ZEBRA_ROUTE_KERNEL, 1, "kernel"}, + {ZEBRA_ROUTE_CONNECT, 1, "connected"}, + {ZEBRA_ROUTE_STATIC, 1, "static"}, +- {ZEBRA_ROUTE_OSPF6, 1, "ospf6"}, ++ {ZEBRA_ROUTE_OSPF6, 2, "ospf6"}, + {ZEBRA_ROUTE_BGP, 2, "bgp"}, +- {ZEBRA_ROUTE_BABEL, 2, "babel"}, ++ {ZEBRA_ROUTE_HSLS, 1, "hsls"}, ++ {ZEBRA_ROUTE_OLSR, 2, "olsr"}, ++ {ZEBRA_ROUTE_BATMAN, 3, "batman"}, ++ {ZEBRA_ROUTE_BABEL, 3, "babel"}, + {0, 0, NULL} + }; + +--- a/zebra/rt_netlink.c ++++ b/zebra/rt_netlink.c +@@ -1623,6 +1623,9 @@ netlink_route_multipath (int cmd, struct + addattr_l (&req.n, sizeof req, RTA_PREFSRC, + &nexthop->src.ipv4, bytelen); + ++ if (rib->type == ZEBRA_ROUTE_OLSR) ++ req.r.rtm_scope = RT_SCOPE_LINK; ++ + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("netlink_route_multipath() (single hop): " + "nexthop via if %u", nexthop->ifindex); +--- a/zebra/zebra_rib.c ++++ b/zebra/zebra_rib.c +@@ -67,6 +67,9 @@ static const struct + [ZEBRA_ROUTE_OSPF6] = {ZEBRA_ROUTE_OSPF6, 110}, + [ZEBRA_ROUTE_ISIS] = {ZEBRA_ROUTE_ISIS, 115}, + [ZEBRA_ROUTE_BGP] = {ZEBRA_ROUTE_BGP, 20 /* IBGP is 200. */}, ++ [ZEBRA_ROUTE_HSLS] = {ZEBRA_ROUTE_HSLS, 0}, ++ [ZEBRA_ROUTE_OLSR] = {ZEBRA_ROUTE_OLSR, 0}, ++ [ZEBRA_ROUTE_BATMAN] = {ZEBRA_ROUTE_BATMAN, 0}, + [ZEBRA_ROUTE_BABEL] = {ZEBRA_ROUTE_BABEL, 95}, + /* no entry/default: 150 */ + }; +@@ -403,6 +406,18 @@ nexthop_active_ipv4 (struct rib *rib, st + } + return 0; + } ++ else if (match->type == ZEBRA_ROUTE_OLSR) ++ { ++ for (newhop = match->nexthop; newhop; newhop = newhop->next) ++ if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB) ++ && newhop->type == NEXTHOP_TYPE_IFINDEX) ++ { ++ if (nexthop->type == NEXTHOP_TYPE_IPV4) ++ nexthop->ifindex = newhop->ifindex; ++ return 1; ++ } ++ return 0; ++ } + else + { + return 0; +@@ -507,6 +522,18 @@ nexthop_active_ipv6 (struct rib *rib, st + } + return 0; + } ++ else if (match->type == ZEBRA_ROUTE_OLSR) ++ { ++ for (newhop = match->nexthop; newhop; newhop = newhop->next) ++ if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB) ++ && newhop->type == NEXTHOP_TYPE_IFINDEX) ++ { ++ if (nexthop->type == NEXTHOP_TYPE_IPV6) ++ nexthop->ifindex = newhop->ifindex; ++ return 1; ++ } ++ return 0; ++ } + else + { + return 0; +@@ -1236,6 +1263,8 @@ static const u_char meta_queue_map[ZEBRA + [ZEBRA_ROUTE_ISIS] = 2, + [ZEBRA_ROUTE_BGP] = 3, + [ZEBRA_ROUTE_HSLS] = 4, ++ [ZEBRA_ROUTE_OLSR] = 4, ++ [ZEBRA_ROUTE_BATMAN] = 4, + [ZEBRA_ROUTE_BABEL] = 2, + }; + +--- a/zebra/zebra_snmp.c ++++ b/zebra/zebra_snmp.c +@@ -251,6 +251,12 @@ proto_trans(int type) + return 1; /* shouldn't happen */ + case ZEBRA_ROUTE_BGP: + return 14; /* bgp */ ++ case ZEBRA_ROUTE_HSLS: ++ return 1; /* other */ ++ case ZEBRA_ROUTE_OLSR: ++ return 1; /* other */ ++ case ZEBRA_ROUTE_BATMAN: ++ return 1; /* other */ + default: + return 1; /* other */ + } +--- a/zebra/zebra_vty.c ++++ b/zebra/zebra_vty.c +@@ -558,7 +558,10 @@ vty_show_ip_route_detail (struct vty *vt + || rib->type == ZEBRA_ROUTE_OSPF + || rib->type == ZEBRA_ROUTE_BABEL + || rib->type == ZEBRA_ROUTE_ISIS +- || rib->type == ZEBRA_ROUTE_BGP) ++ || rib->type == ZEBRA_ROUTE_BGP ++ || rib->type == ZEBRA_ROUTE_HSLS ++ || rib->type == ZEBRA_ROUTE_OLSR ++ || rib->type == ZEBRA_ROUTE_BATMAN) + { + time_t uptime; + struct tm *tm; +@@ -777,7 +780,10 @@ vty_show_ip_route (struct vty *vty, stru + || rib->type == ZEBRA_ROUTE_OSPF + || rib->type == ZEBRA_ROUTE_BABEL + || rib->type == ZEBRA_ROUTE_ISIS +- || rib->type == ZEBRA_ROUTE_BGP) ++ || rib->type == ZEBRA_ROUTE_BGP ++ || rib->type == ZEBRA_ROUTE_HSLS ++ || rib->type == ZEBRA_ROUTE_OLSR ++ || rib->type == ZEBRA_ROUTE_BATMAN) + { + time_t uptime; + struct tm *tm; +@@ -1536,7 +1542,10 @@ vty_show_ipv6_route_detail (struct vty * + || rib->type == ZEBRA_ROUTE_OSPF6 + || rib->type == ZEBRA_ROUTE_BABEL + || rib->type == ZEBRA_ROUTE_ISIS +- || rib->type == ZEBRA_ROUTE_BGP) ++ || rib->type == ZEBRA_ROUTE_BGP ++ || rib->type == ZEBRA_ROUTE_HSLS ++ || rib->type == ZEBRA_ROUTE_OLSR ++ || rib->type == ZEBRA_ROUTE_BATMAN) + { + time_t uptime; + struct tm *tm; +@@ -1716,7 +1725,10 @@ vty_show_ipv6_route (struct vty *vty, st + || rib->type == ZEBRA_ROUTE_OSPF6 + || rib->type == ZEBRA_ROUTE_BABEL + || rib->type == ZEBRA_ROUTE_ISIS +- || rib->type == ZEBRA_ROUTE_BGP) ++ || rib->type == ZEBRA_ROUTE_BGP ++ || rib->type == ZEBRA_ROUTE_HSLS ++ || rib->type == ZEBRA_ROUTE_OLSR ++ || rib->type == ZEBRA_ROUTE_BATMAN) + { + time_t uptime; + struct tm *tm; diff --git a/packages/qmp-quagga/patches/121-quagga-bmx6.patch b/packages/qmp-quagga/patches/121-quagga-bmx6.patch new file mode 100644 index 0000000..ec81afb --- /dev/null +++ b/packages/qmp-quagga/patches/121-quagga-bmx6.patch @@ -0,0 +1,127 @@ +--- a/lib/log.c ++++ b/lib/log.c +@@ -941,6 +941,8 @@ proto_redistnum(int afi, const char *s) + return ZEBRA_ROUTE_OLSR; + else if (strncmp (s, "bat", 3) == 0) + return ZEBRA_ROUTE_BATMAN; ++ else if (strncmp (s, "bmx", 3) == 0) ++ return ZEBRA_ROUTE_BMX6; + else if (strncmp (s, "bab", 3) == 0) + return ZEBRA_ROUTE_BABEL; + } +@@ -966,6 +968,8 @@ proto_redistnum(int afi, const char *s) + return ZEBRA_ROUTE_OLSR; + else if (strncmp (s, "bat", 3) == 0) + return ZEBRA_ROUTE_BATMAN; ++ else if (strncmp (s, "bmx", 3) == 0) ++ return ZEBRA_ROUTE_BMX6; + else if (strncmp (s, "bab", 3) == 0) + return ZEBRA_ROUTE_BABEL; + } +--- a/lib/route_types.txt ++++ b/lib/route_types.txt +@@ -54,6 +54,7 @@ ZEBRA_ROUTE_BGP, bgp, bgpd, + ZEBRA_ROUTE_HSLS, hsls, hslsd, 'H', 1, 1, "HSLS" + ZEBRA_ROUTE_OLSR, olsr, olsrd, 'o', 1, 1, "OLSR" + ZEBRA_ROUTE_BATMAN, batman, batmand,'b', 1, 1, "BATMAN" ++ZEBRA_ROUTE_BMX6, bmx6, bmx6, 'x', 1, 1, "BMX6" + ZEBRA_ROUTE_BABEL, babel, babeld, 'A', 1, 1, "Babel" + + ## help strings +@@ -70,4 +71,5 @@ ZEBRA_ROUTE_BGP, "Border Gateway Prot + ZEBRA_ROUTE_HSLS, "Hazy-Sighted Link State Protocol (HSLS)" + ZEBRA_ROUTE_OLSR, "Optimized Link State Routing (OLSR)" + ZEBRA_ROUTE_BATMAN, "Better Approach to Mobile Ad-Hoc Networking (BATMAN)" ++ZEBRA_ROUTE_BMX6, "BMX6 networking protocol" + ZEBRA_ROUTE_BABEL, "Babel routing protocol (Babel)" +--- a/ripd/rip_zebra.c ++++ b/ripd/rip_zebra.c +@@ -211,6 +211,7 @@ static struct { + {ZEBRA_ROUTE_HSLS, 1, "hsls"}, + {ZEBRA_ROUTE_OLSR, 2, "olsr"}, + {ZEBRA_ROUTE_BATMAN, 3, "batman"}, ++ {ZEBRA_ROUTE_BMX6, 3, "bmx6"}, + {ZEBRA_ROUTE_BABEL, 3, "babel"}, + {0, 0, NULL} + }; +--- a/ripngd/ripng_zebra.c ++++ b/ripngd/ripng_zebra.c +@@ -221,6 +221,7 @@ static struct { + {ZEBRA_ROUTE_HSLS, 1, "hsls"}, + {ZEBRA_ROUTE_OLSR, 2, "olsr"}, + {ZEBRA_ROUTE_BATMAN, 3, "batman"}, ++ {ZEBRA_ROUTE_BMX6, 3, "bmx6"}, + {ZEBRA_ROUTE_BABEL, 3, "babel"}, + {0, 0, NULL} + }; +--- a/zebra/zebra_rib.c ++++ b/zebra/zebra_rib.c +@@ -70,6 +70,7 @@ static const struct + [ZEBRA_ROUTE_HSLS] = {ZEBRA_ROUTE_HSLS, 0}, + [ZEBRA_ROUTE_OLSR] = {ZEBRA_ROUTE_OLSR, 0}, + [ZEBRA_ROUTE_BATMAN] = {ZEBRA_ROUTE_BATMAN, 0}, ++ [ZEBRA_ROUTE_BMX6] = {ZEBRA_ROUTE_BMX6, 0}, + [ZEBRA_ROUTE_BABEL] = {ZEBRA_ROUTE_BABEL, 95}, + /* no entry/default: 150 */ + }; +@@ -1265,6 +1266,7 @@ static const u_char meta_queue_map[ZEBRA + [ZEBRA_ROUTE_HSLS] = 4, + [ZEBRA_ROUTE_OLSR] = 4, + [ZEBRA_ROUTE_BATMAN] = 4, ++ [ZEBRA_ROUTE_BMX6] = 4, + [ZEBRA_ROUTE_BABEL] = 2, + }; + +--- a/zebra/zebra_snmp.c ++++ b/zebra/zebra_snmp.c +@@ -257,6 +257,8 @@ proto_trans(int type) + return 1; /* other */ + case ZEBRA_ROUTE_BATMAN: + return 1; /* other */ ++ case ZEBRA_ROUTE_BMX6: ++ return 1; /* other */ + default: + return 1; /* other */ + } +--- a/zebra/zebra_vty.c ++++ b/zebra/zebra_vty.c +@@ -561,7 +561,8 @@ vty_show_ip_route_detail (struct vty *vt + || rib->type == ZEBRA_ROUTE_BGP + || rib->type == ZEBRA_ROUTE_HSLS + || rib->type == ZEBRA_ROUTE_OLSR +- || rib->type == ZEBRA_ROUTE_BATMAN) ++ || rib->type == ZEBRA_ROUTE_BATMAN ++ || rib->type == ZEBRA_ROUTE_BMX6) + { + time_t uptime; + struct tm *tm; +@@ -783,7 +784,8 @@ vty_show_ip_route (struct vty *vty, stru + || rib->type == ZEBRA_ROUTE_BGP + || rib->type == ZEBRA_ROUTE_HSLS + || rib->type == ZEBRA_ROUTE_OLSR +- || rib->type == ZEBRA_ROUTE_BATMAN) ++ || rib->type == ZEBRA_ROUTE_BATMAN ++ || rib->type == ZEBRA_ROUTE_BMX6) + { + time_t uptime; + struct tm *tm; +@@ -1545,7 +1547,8 @@ vty_show_ipv6_route_detail (struct vty * + || rib->type == ZEBRA_ROUTE_BGP + || rib->type == ZEBRA_ROUTE_HSLS + || rib->type == ZEBRA_ROUTE_OLSR +- || rib->type == ZEBRA_ROUTE_BATMAN) ++ || rib->type == ZEBRA_ROUTE_BATMAN ++ || rib->type == ZEBRA_ROUTE_BMX6) + { + time_t uptime; + struct tm *tm; +@@ -1728,7 +1731,8 @@ vty_show_ipv6_route (struct vty *vty, st + || rib->type == ZEBRA_ROUTE_BGP + || rib->type == ZEBRA_ROUTE_HSLS + || rib->type == ZEBRA_ROUTE_OLSR +- || rib->type == ZEBRA_ROUTE_BATMAN) ++ || rib->type == ZEBRA_ROUTE_BATMAN ++ || rib->type == ZEBRA_ROUTE_BMX6) + { + time_t uptime; + struct tm *tm; diff --git a/packages/qmp-quagga/patches/130-fix_cpp.patch b/packages/qmp-quagga/patches/130-fix_cpp.patch new file mode 100644 index 0000000..23991c3 --- /dev/null +++ b/packages/qmp-quagga/patches/130-fix_cpp.patch @@ -0,0 +1,11 @@ +--- a/vtysh/extract.pl.in ++++ b/vtysh/extract.pl.in +@@ -63,7 +63,7 @@ $ignore{'"show history"'} = "ignore"; + foreach (@ARGV) { + $file = $_; + +- open (FH, "cpp -DHAVE_CONFIG_H -DVTYSH_EXTRACT_PL -DHAVE_IPV6 -I@top_builddir@ -I@srcdir@/ -I@srcdir@/.. -I@top_srcdir@/lib -I@top_srcdir@/isisd/topology @SNMP_INCLUDES@ @CPPFLAGS@ $file |"); ++ open (FH, "@CPP@ -DHAVE_CONFIG_H -DVTYSH_EXTRACT_PL -DHAVE_IPV6 -I@top_builddir@ -I@srcdir@/ -I@srcdir@/.. -I@top_srcdir@/lib -I@top_srcdir@/isisd/topology @SNMP_INCLUDES@ @CPPFLAGS@ $file |"); + local $/; undef $/; + $line = ; + close (FH); diff --git a/packages/qmp-quagga/patches/140-holdtimer-set.patch b/packages/qmp-quagga/patches/140-holdtimer-set.patch new file mode 100644 index 0000000..6f0d79a --- /dev/null +++ b/packages/qmp-quagga/patches/140-holdtimer-set.patch @@ -0,0 +1,22 @@ +--- a/bgpd/bgp_network.c ++++ b/bgpd/bgp_network.c +@@ -193,8 +193,7 @@ bgp_accept (struct thread *thread) + peer->fd = bgp_sock; + peer->status = Active; + peer->local_id = peer1->local_id; +- peer->v_holdtime = peer1->v_holdtime; +- peer->v_keepalive = peer1->v_keepalive; ++ peer->v_holdtime = BGP_LARGE_HOLDTIME; + + /* Make peer's address string. */ + sockunion2str (&su, buf, SU_ADDRSTRLEN); +--- a/bgpd/bgpd.h ++++ b/bgpd/bgpd.h +@@ -718,6 +718,7 @@ struct bgp_nlri + /* BGP timers default value. */ + #define BGP_INIT_START_TIMER 5 + #define BGP_ERROR_START_TIMER 30 ++#define BGP_LARGE_HOLDTIME 240 + #define BGP_DEFAULT_HOLDTIME 180 + #define BGP_DEFAULT_KEEPALIVE 60 + #define BGP_DEFAULT_ASORIGINATE 15 diff --git a/packages/qmp-quagga/patches/150-no-cross-fs-link.patch b/packages/qmp-quagga/patches/150-no-cross-fs-link.patch new file mode 100644 index 0000000..c3f29c3 --- /dev/null +++ b/packages/qmp-quagga/patches/150-no-cross-fs-link.patch @@ -0,0 +1,40 @@ +--- a/lib/command.c ++++ b/lib/command.c +@@ -2601,6 +2601,13 @@ DEFUN (config_write_file, + VTY_NEWLINE); + goto finished; + } ++ ++#if 0 ++ /* This code fails on UNION MOUNTs and similar filesystems if the ++ * config file is still on the RO layer. Hardlinks across layers ++ * will not work and cause quagga to fail saving the configuration... ++ * should use rename() to move files around... ++ */ + if (link (config_file, config_file_sav) != 0) + { + vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav, +@@ -2614,7 +2621,23 @@ DEFUN (config_write_file, + VTY_NEWLINE); + goto finished; + } ++#else ++ /* And this is the code that hopefully does work */ ++ if (rename (config_file, config_file_sav) != 0) ++ { ++ vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav, ++ VTY_NEWLINE); ++ goto finished; ++ } ++ sync (); ++#endif ++ ++#if 0 ++ /* same here. Please no cross-filesystem hardlinks... */ + if (link (config_file_tmp, config_file) != 0) ++#else ++ if (rename (config_file_tmp, config_file) != 0) ++#endif + { + vty_out (vty, "Can't save configuration file %s.%s", config_file, + VTY_NEWLINE); diff --git a/packages/qmp-quagga/patches/160-pgbgp.patch b/packages/qmp-quagga/patches/160-pgbgp.patch new file mode 100644 index 0000000..fb01ee1 --- /dev/null +++ b/packages/qmp-quagga/patches/160-pgbgp.patch @@ -0,0 +1,3104 @@ +From: Josh Karlin +Date: Mon, 18 Aug 2008 13:17:21 +0000 (+0100) +Subject: [bgp] Add support for Pretty-Good BGP +X-Git-Url: http://git.ozo.com/?p=quagga-pgbg.git;a=commitdiff_plain;h=c2ee55705cad607f4b86ff143f7af92d538dc946 + +[bgp] Add support for Pretty-Good BGP + +2008-7-7 Josh Karlin + + * bgpd/bgp_pgbgp.c: Added file + * bgpd/bgp_pgbgp.h: Added file + * bgpd/Makefile.am: Added bgp_pgbgp.h and bgp_pgbgp.c + * bgpd/bgp_aspath.h: Externed the hash of as paths (ashash) + * bgpd/bgp_route.c: . Added PGBGP depref check to decision process. + . Informs PGBGP of new updates and selected routes + . Added anomaly status for show ip bgp + . Added PGBGP commands + * bgpd/bgp_route.h: Added suspicious route flags + * bgpd/bgp_table.h: Added PGBGP history pointer to struct bgp_node + * bgpd/bgpd.h: Defined BGP_CONFIG_PGBGP + * lib/hash.c: Added "hash_iterate_until" to be able to break out + * lib/hash.h: Definition for "hash_iterate_until" + * lib/memtypes.c: Added PGBGP memory types +--- + +--- a/bgpd/Makefile.am ++++ b/bgpd/Makefile.am +@@ -15,14 +15,14 @@ libbgp_a_SOURCES = \ + bgp_debug.c bgp_route.c bgp_zebra.c bgp_open.c bgp_routemap.c \ + bgp_packet.c bgp_network.c bgp_filter.c bgp_regex.c bgp_clist.c \ + bgp_dump.c bgp_snmp.c bgp_ecommunity.c bgp_mplsvpn.c bgp_nexthop.c \ +- bgp_damp.c bgp_table.c bgp_advertise.c bgp_vty.c bgp_mpath.c ++ bgp_damp.c bgp_table.c bgp_advertise.c bgp_vty.c bgp_mpath.c bgp_pgbgp.c + + noinst_HEADERS = \ + bgp_aspath.h bgp_attr.h bgp_community.h bgp_debug.h bgp_fsm.h \ + bgp_network.h bgp_open.h bgp_packet.h bgp_regex.h bgp_route.h \ + bgpd.h bgp_filter.h bgp_clist.h bgp_dump.h bgp_zebra.h \ + bgp_ecommunity.h bgp_mplsvpn.h bgp_nexthop.h bgp_damp.h bgp_table.h \ +- bgp_advertise.h bgp_snmp.h bgp_vty.h bgp_mpath.h ++ bgp_advertise.h bgp_snmp.h bgp_vty.h bgp_mpath.h bgp_pgbgp.h + + bgpd_SOURCES = bgp_main.c + bgpd_LDADD = libbgp.a ../lib/libzebra.la @LIBCAP@ @LIBM@ +--- /dev/null ++++ b/bgpd/bgp_pgbgp.c +@@ -0,0 +1,2401 @@ ++/* ++ BGP Pretty Good BGP ++ Copyright (C) 2008 University of New Mexico (Josh Karlin) ++ ++This file is part of GNU Zebra. ++ ++GNU Zebra is free software; you can redistribute it and/or modify it ++under the terms of the GNU General Public License as published by the ++Free Software Foundation; either version 2, or (at your option) any ++later version. ++ ++GNU Zebra is distributed in the hope that it will be useful, but ++WITHOUT ANY WARRANTY; without even the implied warranty of ++MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++General Public License for more details. ++ ++You should have received a copy of the GNU General Public License ++along with GNU Zebra; see the file COPYING. If not, write to the Free ++Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA ++02111-1307, USA. ++*/ ++ ++/* ++ Quagga based Pretty Good BGP: ++ ++ Summary ++ ------- ++ Pretty Good BGP (PGBGP) is a soft security enhancement to BGP. ++ It uses independently collected (therefore completely distributed) ++ historical routing information to determine network topology and ++ prefix ownership. Abberations to the historical database are considered ++ anomalous and avoided when possible. ++ ++ What PGBGP can protect against: prefix hijacks, sub-prefix hijacks, and ++ spoofed edges. ++ ++ Further reading is available at http://cs.unm.edu/~karlinjf/pgbgp/ ++ ++ Route updates are forwarded to PGBGP, which determines if the route ++ is anomalous. Anomalous routes are flagged as suspicious and ++ avoided where feasible for 24 hours. If the anomalous ++ characteristic is still in the RIB after 24 hours, consider it valid ++ and enter it into the normal database. ++ ++ Cases for anomalous routes ++ -------------------------- ++ case 1) New origin AS - prefix pair (one not recently seen in the RIB): ++ response) label the route with BGP_INFO_SUSPICIOUS_O and avoid for 24 hours if possible ++ ++ case 2) New edge in path (one not recently seen in the RIB): ++ response) label the route with BGP_INFO_SUSPICIOUS_E and avoid for 24 hours ++ if possible ++ ++ case 3) New prefix that is a sub-prefix of a prefix in recent history ++ and that path differs from the current less-specific's path ++ response) label the sub-prefix routes with BGP_INFO_IGNORED_P and ++ prevent it from entering FIB for 24 hours ++ response) label the super-net routes from the same next-hop as BGP_INFO_SUSPICIOUS_P ++ and try to avoid it for 24 hours if possible ++ response) while no super-net route is selected, remove the BGP_INFO_IGNORED_P flags ++ ++ ++ Normal Database (history) ++ ------------------------- ++ Recently Seen) A route characteristic (edge, prefix/origin pair, prefix) ++ that has resided within the RIB within the last X hours ++ where X is user defined for each characteristic. ++ Storage) Prefix and Origin history are stored in bgp_node structs with the ++ "hist" pointer. ++ Edge information is stored in a separate hash table, where the edge ++ is the key to the hash. ++ Updates) The history's primary function is the keep track of when each route ++ characteristic was last seen. For each route announcement, update ++ the history's 'last seen' time. Periodically run the garbage collector ++ which updates 'last seen' times for objects currently in the RIB. ++ ++ Garbage Collection ++ ------------------ ++ Periodically the garbage collector (gc) is called to remove stale history ++ information and update the lastSeen time of objects that reside in the RIB ++ at the time of collection. This is relatively expensive as it walks ++ the RIB as well as the list of AS paths. ++ ++ What is removed) Objects that have not been seen in the RIB within a user-defined ++ time. ++ Suspicious objcets that are 24 hours old that have not been in the RIB ++ since the last collection. ++ ++ Reuse Priority Queue ++ -------------------- ++ After 24 hours, routes that are flagged as suspicious have the flags removed. ++ This is not run on a timer. Instead, for each update that PGBGP is informed of, ++ it checks the reuse queue to determine if any routes need to be updated. ++ ++*/ ++ ++ ++/* ++ Things that must be ensured: ++ . GC updates lastSeen so it must be called at least twice as often as the lowest BUFFER_TIME ++ . GC should be called at least twice per day ++ . Delay times must be shorter than history window lengths ++*/ ++ ++ ++/* ++ Changes made to original PGBGP thinking ++ . Don't check for things in the RIB all of the time, periodically ++ update the lastSeen values and just use lastSeen ++*/ ++ ++/* ++ Changes made to original protocol ++ . sub-prefixes are only ignored while the super-net has a selected ++ route and it's non-anomalous (not to a neighbor that announced ++ the sub-prefix) ++ ++ . At point of reuse, don't delete the item if it's not in the RIB. ++ delete it if it hasn't been in the RIB since the last storage. ++ This saves a lot of processing time for new edges ++ ++ . Changed heuristic from "if new sub-prefix and trusted AS on path ++ then it's okay" to "if new sub-prefix and same path is used to reach ++ super-prefix, then it's okay". Might be better to change to "if old ++ path is prefix of new path, then okay" ++*/ ++ ++#include ++#include ++ ++#include "prefix.h" ++#include "memory.h" ++#include "command.h" ++#include "log.h" ++#include "pqueue.h" ++#include "table.h" ++#include "hash.h" ++#include "str.h" ++ ++#include "bgpd/bgpd.h" ++#include "bgpd/bgp_aspath.h" ++#include "bgpd/bgp_pgbgp.h" ++#include "bgpd/bgp_table.h" ++#include "bgpd/bgp_route.h" ++#include "bgpd/bgp_attr.h" ++#include "bgpd/bgp_advertise.h" ++ ++ ++#define true 1 ++#define false 0 ++ ++struct hash * ashash; ++ ++static void *edge_hash_alloc (void *arg); ++static unsigned int edge_key_make (void *p); ++static int edge_cmp (const void *arg1, const void *args); ++ ++// Helper Functions ++static struct bgp_pgbgp_pathSet bgp_pgbgp_pathOrigin (struct aspath *); ++static int bgp_pgbgp_pathLength (struct aspath *asp); ++static int bgp_pgbgp_gc (struct bgp_table *); ++static int bgp_pgbgp_clean (struct bgp_table *); ++static int bgp_pgbgp_reuse (time_t); ++static struct bgp_node *findSuper (struct bgp_table *table, struct prefix *p, ++ time_t t_now); ++static int bgp_pgbgp_store (struct bgp_table *table); ++static int bgp_pgbgp_restore (void); ++static struct bgp_info *bgp_pgbgp_selected (struct bgp_node *node); ++static int originInRIB (struct bgp_node *node, struct bgp_pgbgp_origin *origin); ++static int prefixInRIB (struct bgp_node *node, struct bgp_pgbgp_prefix *prefix); ++static int edgeInRIB (struct bgp_pgbgp_edge *e); ++ ++// MOAS Functions ++static void bgp_pgbgp_logOriginAnomaly (as_t asn, struct bgp_node *rn, ++ struct attr *); ++static int bgp_pgbgp_reuseOrigin (struct bgp_pgbgp_r_origin); ++static void bgp_pgbgp_cleanHistTable (struct bgp_table *); ++static int bgp_pgbgp_garbageCollectHistTable (struct bgp_table *); ++static void bgp_pgbgp_storeHistTable (struct bgp_table *table, FILE * file); ++static int bgp_pgbgp_updateOrigin (struct bgp_pgbgp_hist *, struct bgp_info *, ++ struct attr *, struct bgp_node *, time_t, int); ++ ++ ++// Sub-Prefix Hijack Detector Functions ++static int bgp_pgbgp_shouldIgnore (struct bgp_node *super, struct bgp_info *selected); ++static void bgp_pgbgp_logSubprefixAnomaly (as_t asn, struct bgp_node *rn, ++ struct attr *, struct bgp_node *super); ++static int bgp_pgbgp_reusePrefix (struct bgp_pgbgp_r_prefix); ++static int bgp_pgbgp_updatePrefix (struct bgp_pgbgp_hist *hist, struct bgp_node *, ++ struct bgp_info *, struct attr *, ++ struct bgp_node *, time_t, int); ++ ++ ++// Spoofed Edge Detector Functions ++static void bgp_pgbgp_cleanEdges (void); ++static void bgp_pgbgp_logEdgeAnomaly (struct bgp_node *rn, struct attr *, ++ struct edge *edge); ++static int bgp_pgbgp_reuseEdge (struct bgp_pgbgp_r_edge); ++static void bgp_pgbgp_storeEdges (struct bgp_table *, FILE *); ++static int bgp_pgbgp_garbageCollectEdges (struct bgp_table *); ++static int bgp_pgbgp_updateEdge (struct bgp_pgbgp_hist *hist, struct bgp_info *, ++ struct attr *, struct bgp_node *, time_t, int); ++static int bgp_pgbgp_restoreEdge (FILE * file); ++static void bgp_pgbgp_storeEdges (struct bgp_table *table, FILE * file); ++ ++ ++ ++// New Peer Detector Functions ++static int bgp_pgbgp_updatePeer (struct bgp_info *binfo, time_t now); ++ ++ ++/* --------------- Global Variables ------------------ */ ++struct bgp_pgbgp_config bgp_pgbgp_cfg; ++struct bgp_pgbgp_config *pgbgp = &bgp_pgbgp_cfg; ++/*! --------------- Global Variables ------------------ !*/ ++ ++/* --------------- VTY (others exist in bgp_route.c) ------------------ */ ++ ++struct nsearch ++{ ++ struct vty *pvty; ++ time_t time; ++ as_t asn; ++}; ++ ++static void ++edge_neighbor_iterator (struct hash_backet *backet, struct nsearch *pns) ++{ ++ struct bgp_pgbgp_edge *hedge = backet->data; ++ if ((hedge->e.a == pns->asn || hedge->e.b == pns->asn) ++ && hedge->e.a != hedge->e.b) ++ { ++ struct vty *vty = pns->pvty; ++ if (hedge->deprefUntil > pns->time) ++ vty_out (pns->pvty, "Untrusted: %d -- %d%s", hedge->e.a, hedge->e.b, ++ VTY_NEWLINE); ++ else ++ vty_out (pns->pvty, "Trusted: %d -- %d%s", hedge->e.a, hedge->e.b, ++ VTY_NEWLINE); ++ } ++} ++ ++static int ++bgp_pgbgp_stats_neighbors (struct vty *vty, afi_t afi, safi_t safi, as_t asn) ++{ ++ struct nsearch ns; ++ ns.pvty = vty; ++ ns.time = time (NULL); ++ ns.asn = asn; ++ ++ hash_iterate (pgbgp->edgeT, ++ (void (*)(struct hash_backet *, void *)) ++ edge_neighbor_iterator, &ns); ++ return CMD_SUCCESS; ++} ++ ++static int ++bgp_pgbgp_stats_origins (struct vty *vty, afi_t afi, safi_t safi, ++ const char *prefix) ++{ ++ struct bgp *bgp; ++ struct bgp_table *table; ++ time_t t_now = time (NULL); ++ bgp = bgp_get_default (); ++ if (bgp == NULL) ++ return CMD_WARNING; ++ if (bgp->rib == NULL) ++ return CMD_WARNING; ++ table = bgp->rib[afi][safi]; ++ if (table == NULL) ++ return CMD_WARNING; ++ ++ struct prefix p; ++ str2prefix (prefix, &p); ++ struct bgp_node *rn = bgp_node_match (table, &p); ++ vty_out (vty, "%s%s", prefix, VTY_NEWLINE); ++ if (rn) ++ { ++ if (rn->hist) ++ { ++ for (struct bgp_pgbgp_origin * cur = rn->hist->o; cur != NULL; ++ cur = cur->next) ++ { ++ if (cur->deprefUntil > t_now) ++ vty_out (vty, "Untrusted Origin AS: %d%s", cur->originAS, ++ VTY_NEWLINE); ++ else ++ vty_out (vty, "Trusted Origin AS: %d%s", cur->originAS, ++ VTY_NEWLINE); ++ } ++ } ++ bgp_unlock_node (rn); ++ } ++ return CMD_SUCCESS; ++} ++ ++static int ++bgp_pgbgp_stats (struct vty *vty, afi_t afi, safi_t safi) ++{ ++ struct bgp *bgp; ++ struct bgp_table *table; ++ ++ ++ bgp = bgp_get_default (); ++ if (bgp == NULL) ++ return CMD_WARNING; ++ if (bgp->rib == NULL) ++ return CMD_WARNING; ++ table = bgp->rib[afi][safi]; ++ if (table == NULL) ++ return CMD_WARNING; ++ ++ // bgp_pgbgp_store(table); ++ ++ // Print out the number of anomalous routes ++ int anomalous = 0; ++ int routes = 0; ++ int num_selected = 0; ++ int num_origin = 0; ++ int num_super = 0; ++ int num_ignored = 0; ++ int num_edge = 0; ++ ++ for (struct bgp_node * rn = bgp_table_top (table); rn; ++ rn = bgp_route_next (rn)) ++ { ++ for (struct bgp_info * ri = rn->info; ri; ri = ri->next) ++ { ++ routes += 1; ++ if (ANOMALOUS (ri->flags)) ++ { ++ anomalous += 1; ++ if (CHECK_FLAG (ri->flags, BGP_INFO_SELECTED)) ++ num_selected += 1; ++ ++ if (CHECK_FLAG (ri->flags, BGP_INFO_SUSPICIOUS_O)) ++ num_origin += 1; ++ if (CHECK_FLAG (ri->flags, BGP_INFO_SUSPICIOUS_E)) ++ num_edge += 1; ++ if (CHECK_FLAG (ri->flags, BGP_INFO_SUSPICIOUS_P)) ++ num_super += 1; ++ if (CHECK_FLAG (ri->flags, BGP_INFO_IGNORED_P)) ++ num_ignored += 1; ++ } ++ } ++ } ++ ++ vty_out (vty, "%-30s: %10d%s", "Routes in the RIB", routes, VTY_NEWLINE); ++ vty_out (vty, "%-30s: %10d%s", "Anomalous routes in RIB", anomalous, ++ VTY_NEWLINE); ++ vty_out (vty, "%-30s: %10d%s", "Selected anomalous routes", num_selected, ++ VTY_NEWLINE); ++ vty_out (vty, "-----------------------------%s", VTY_NEWLINE); ++ vty_out (vty, "%-30s: %10d%s", "Routes with anomalous origins", num_origin, ++ VTY_NEWLINE); ++ vty_out (vty, "%-30s: %10d%s", "Routes with anomalous edges", num_edge, ++ VTY_NEWLINE); ++ vty_out (vty, "%-30s: %10d%s", "Routes ignored for sub-prefix", num_ignored, ++ VTY_NEWLINE); ++ vty_out (vty, "%-30s: %10d%s", "Less specific routes to avoid", num_super, ++ VTY_NEWLINE); ++ /* ++ vty_out (vty, "There are %d routes in the RIB.%s", routes, VTY_NEWLINE); ++ vty_out (vty, "%d are anomalous.%s", anomalous, VTY_NEWLINE); ++ vty_out (vty, "%d anomalous routes are selected.%s", num_selected, VTY_NEWLINE); ++ vty_out (vty, "%s", VTY_NEWLINE); ++ vty_out (vty, "Anomaly breakdown:%s", VTY_NEWLINE); ++ vty_out (vty, "%d contain anomalous origins%s", num_origin, VTY_NEWLINE); ++ vty_out (vty, "%d contain anomalous edges.%s", num_edge, VTY_NEWLINE); ++ vty_out (vty, "%d are for ignored sub-prefixes.%s", num_ignored, VTY_NEWLINE); ++ vty_out (vty, "%d are super-net routes through peers that announced anomalous sub-prefixes.%s", num_super, VTY_NEWLINE); ++ */ ++ return CMD_SUCCESS; ++} ++ ++ ++DEFUN (show_ip_bgp_pgbgp, ++ show_ip_bgp_pgbgp_cmd, ++ "show ip bgp pgbgp", ++ SHOW_STR IP_STR BGP_STR "Display PGBGP statistics\n") ++{ ++ return bgp_pgbgp_stats (vty, AFI_IP, SAFI_UNICAST); ++} ++ ++DEFUN (show_ip_bgp_pgbgp_neighbors, ++ show_ip_bgp_pgbgp_neighbors_cmd, ++ "show ip bgp pgbgp neighbors WORD", ++ SHOW_STR ++ IP_STR ++ BGP_STR ++ "BGP pgbgp\n" ++ "BGP pgbgp neighbors\n" "ASN whos neighbors should be displayed\n") ++{ ++ return bgp_pgbgp_stats_neighbors (vty, AFI_IP, SAFI_UNICAST, ++ atoi (argv[0])); ++} ++ ++DEFUN (show_ip_bgp_pgbgp_origins, ++ show_ip_bgp_pgbgp_origins_cmd, ++ "show ip bgp pgbgp origins A.B.C.D/M", ++ SHOW_STR ++ IP_STR ++ BGP_STR ++ "BGP pgbgp\n" ++ "BGP pgbgp neighbors\n" "Prefix to look up origin ASes of\n") ++{ ++ return bgp_pgbgp_stats_origins (vty, AFI_IP, SAFI_UNICAST, argv[0]); ++} ++ ++ ++ ++ ++/*! --------------- VTY (others exist in bgp_route.c) ------------------ !*/ ++ ++ ++ ++ ++ ++ ++ ++/* --------------- Helper Functions ------------------ */ ++/* ++ If the origin hasn't been seen/verified lately, look for it in the RIB ++*/ ++int ++originInRIB (struct bgp_node *node, struct bgp_pgbgp_origin *origin) ++{ ++ for (struct bgp_info * ri = node->info; ri; ri = ri->next) ++ { ++ struct bgp_pgbgp_pathSet pathOrigins; ++ pathOrigins = bgp_pgbgp_pathOrigin (ri->attr->aspath); ++ for (int i = 0; i < pathOrigins.length; ++i) ++ { ++ if (pathOrigins.ases[i] == origin->originAS) ++ { ++ return true; ++ } ++ } ++ } ++ return false; ++} ++ ++ ++/* ++ If the prefix hasn't been seen/verified lately, look for it in the RIB ++*/ ++int ++prefixInRIB (struct bgp_node *node, struct bgp_pgbgp_prefix *prefix) ++{ ++ if (node->info) ++ return true; ++ return false; ++} ++ ++static int ++edge_inRIB_iterator (struct hash_backet *backet, struct bgp_pgbgp_edge *hedge) ++{ ++ struct aspath *p = backet->data; ++ char first = true; ++ struct edge curEdge; ++ curEdge.a = 0; ++ curEdge.b = 0; ++ ++ struct assegment *seg; ++ ++ for (seg = p->segments; seg; seg = seg->next) ++ { ++ for (int i = 0; i < seg->length; i++) ++ { ++ curEdge.a = curEdge.b; ++ curEdge.b = seg->as[i]; ++ if (first) ++ { ++ first = false; ++ continue; ++ } ++ // Is this the edge we're looking for? ++ if (curEdge.a == hedge->e.a && curEdge.b == hedge->e.b) ++ { ++ hedge->lastSeen = time (NULL); ++ return false; ++ } ++ } ++ } ++ ++ return true; ++} ++ ++/* ++ If the edge hasn't been seen/verified lately, look for it in the AS path list ++ This function is expensive, use sparingly ++*/ ++int ++edgeInRIB (struct bgp_pgbgp_edge *e) ++{ ++ int completed; ++ completed = hash_iterate_until (ashash, ++ (int (*)(struct hash_backet *, void *)) ++ edge_inRIB_iterator, e); ++ if (completed) ++ return false; ++ ++ return true; ++} ++ ++ ++ ++/* ++ Return the selected route for the given route node ++ */ ++ ++struct bgp_info * ++bgp_pgbgp_selected (struct bgp_node *node) ++{ ++ for (struct bgp_info * ri = node->info; ri; ri = ri->next) ++ { ++ if (CHECK_FLAG (ri->flags, BGP_INFO_SELECTED)) ++ return ri; ++ } ++ return NULL; ++} ++ ++static int ++reuse_cmp (void *node1, void *node2) ++{ ++ struct bgp_pgbgp_reuse *a; ++ struct bgp_pgbgp_reuse *b; ++ a = (struct bgp_pgbgp_reuse *) node1; ++ b = (struct bgp_pgbgp_reuse *) node2; ++ return a->deprefUntil - b->deprefUntil; ++} ++ ++int ++bgp_pgbgp_pathLength (struct aspath *asp) ++{ ++ struct assegment *seg; ++ if ((asp == NULL) || (asp->segments == NULL)) ++ return 0; ++ int count = 0; ++ seg = asp->segments; ++ while (seg->next != NULL) ++ { ++ count += seg->length; ++ seg = seg->next; ++ } ++ return count; ++} ++ ++ ++ ++/* Find the origin(s) of the path ++ All ASes in the final set are considered origins */ ++static struct bgp_pgbgp_pathSet ++bgp_pgbgp_pathOrigin (struct aspath *asp) ++{ ++ struct assegment *seg, *last; ++ struct bgp_pgbgp_pathSet tmp; ++ tmp.length = 0; ++ tmp.ases = NULL; ++ ++ assert (asp != NULL && asp->segments != NULL); ++ ++ /* if ( (asp == NULL) || (asp->segments == NULL) ) ++ return tmp; ++ */ ++ seg = asp->segments; ++ last = NULL; ++ while (seg->next != NULL) ++ { ++ if (seg->type != AS_SET && seg->type != AS_CONFED_SET) ++ last = seg; ++ seg = seg->next; ++ } ++ ++ if (seg->type == AS_SET || seg->type == AS_CONFED_SET) ++ seg = last; ++ ++ assert (seg); ++ tmp.length = 1; ++ tmp.ases = &seg->as[seg->length - 1]; ++ ++ /* ++ if (seg->type == AS_SET || seg->type == AS_CONFED_SET) ++ { ++ tmp.length = seg->length; ++ tmp.ases = seg->as; ++ } ++ else ++ { ++ tmp.length = 1; ++ tmp.ases = &seg->as[seg->length - 1]; ++ } ++ */ ++ assert (tmp.length >= 1); ++ return tmp; ++ // return seg->as[seg->length-1]; ++} ++ ++int ++bgp_pgbgp_reuse (time_t t_now) ++{ ++ ++ struct bgp_pgbgp_reuse *cur = NULL; ++ ++ while (pgbgp->rq_size > 0) ++ { ++ cur = pqueue_dequeue (pgbgp->reuse_q); ++ pgbgp->rq_size -= 1; ++ ++ // Is the next item ready to be reused? ++ if (t_now < cur->deprefUntil) ++ { ++ pqueue_enqueue (cur, pgbgp->reuse_q); ++ pgbgp->rq_size += 1; ++ break; ++ } ++ ++ // Okay, it needs to be reused now ++ if (cur->type == PGBGP_REUSE_ORIGIN) ++ bgp_pgbgp_reuseOrigin (cur->data.origin); ++ ++ else if (cur->type == PGBGP_REUSE_PREFIX) ++ bgp_pgbgp_reusePrefix (cur->data.prefix); ++ ++ else if (cur->type == PGBGP_REUSE_EDGE) ++ bgp_pgbgp_reuseEdge (cur->data.edge); ++ ++ ++ XFREE (MTYPE_BGP_PGBGP_REUSE, cur); ++ } ++ return 0; ++} ++ ++/* Check bit of the prefix. */ ++static int ++check_bit (u_char * prefix, u_char prefixlen) ++{ ++ int offset; ++ int shift; ++ u_char *p = (u_char *) prefix; ++ ++ assert (prefixlen <= 128); ++ ++ offset = prefixlen / 8; ++ shift = 7 - (prefixlen % 8); ++ ++ return (p[offset] >> shift & 1); ++} ++ ++/* ++ Find a super-net in the tree that's not currently anomalous if one exists ++*/ ++struct bgp_node * ++findSuper (struct bgp_table *table, struct prefix *p, time_t t_now) ++{ ++ struct bgp_node *node; ++ struct bgp_node *matched; ++ ++ matched = NULL; ++ node = table->top; ++ ++ while (node && node->p.prefixlen < p->prefixlen && ++ prefix_match (&node->p, p)) ++ { ++ // Node may not yet have its info set when reading in from pgbgp log files ++ if (node->hist && node->p.prefixlen >= 8) ++ { ++ if (node->hist->p != NULL && node->hist->p->ignoreUntil < t_now) ++ //if (node->hist->p != NULL && prefixInRIB (node, NULL)) ++ //if (node->hist->p != NULL) ++ matched = node; ++ } ++ node = node->link[check_bit (&p->u.prefix, node->p.prefixlen)]; ++ } ++ if (matched) ++ return bgp_lock_node (matched); ++ return NULL; ++} ++ ++ ++ ++ ++ ++/*! --------------- Helper Functions ------------------ !*/ ++ ++ ++ ++ ++ ++ ++ ++/* --------------- Public PGBGP Interface ------------------ */ ++int ++bgp_pgbgp_enable (struct bgp *bgp, afi_t afi, safi_t safi, ++ int ost, int est, int sst, int oht, int pht, int eht, ++ const char *file, const char *anoms) ++{ ++ ++ if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_PGBGP)) ++ { ++ if (pgbgp->storage && pgbgp->anomalies) ++ { ++ if (pgbgp->origin_sus_time == ost ++ && pgbgp->edge_sus_time == est ++ && pgbgp->sub_sus_time == sst ++ && pgbgp->origin_hist_time == oht ++ && pgbgp->prefix_hist_time == pht ++ && pgbgp->edge_hist_time == eht ++ && strcmp (pgbgp->storage, file) == 0 ++ && strcmp (pgbgp->anomalies, anoms) == 0) ++ ++ return 0; ++ } ++ } ++ ++ SET_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_PGBGP); ++ ++#ifndef PGBGP_DEBUG ++ time_t hour = 3600; ++ time_t day = 86400; ++#endif ++#ifdef PGBGP_DEBUG ++ time_t hour = 2; ++ time_t day = 5; ++#endif ++ ++ pgbgp->origin_sus_time = ost * hour; ++ pgbgp->edge_sus_time = est * hour; ++ pgbgp->sub_sus_time = sst * hour; ++ pgbgp->origin_hist_time = oht * day; ++ pgbgp->prefix_hist_time = pht * day; ++ pgbgp->edge_hist_time = eht * day; ++ pgbgp->peer_hist_time = DEFAULT_ORIGIN_HIST; ++ ++ if (file != NULL) ++ pgbgp->storage = strdup (file); ++ else ++ pgbgp->storage = NULL; ++ ++ if (anoms != NULL) ++ pgbgp->anomalies = strdup (anoms); ++ else ++ pgbgp->anomalies = NULL; ++ ++ ++ pgbgp->reuse_q = pqueue_create (); ++ pgbgp->reuse_q->cmp = reuse_cmp; ++ pgbgp->rq_size = 0; ++ pgbgp->lastgc = time (NULL); ++ pgbgp->lastStore = time (NULL); ++ pgbgp->startTime = time (NULL); ++ install_element (VIEW_NODE, &show_ip_bgp_pgbgp_cmd); ++ install_element (ENABLE_NODE, &show_ip_bgp_pgbgp_cmd); ++ install_element (VIEW_NODE, &show_ip_bgp_pgbgp_neighbors_cmd); ++ install_element (ENABLE_NODE, &show_ip_bgp_pgbgp_neighbors_cmd); ++ install_element (VIEW_NODE, &show_ip_bgp_pgbgp_origins_cmd); ++ install_element (ENABLE_NODE, &show_ip_bgp_pgbgp_origins_cmd); ++ pgbgp->edgeT = hash_create_size (131072, edge_key_make, edge_cmp); ++ bgp_pgbgp_restore (); ++ return 0; ++} ++ ++int ++bgp_pgbgp_disable (struct bgp *bgp, afi_t afi, safi_t safi) ++{ ++ UNSET_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_PGBGP); ++ ++ // Clean the tables ++ if (bgp->rib[afi][safi] != NULL) ++ bgp_pgbgp_clean (bgp->rib[afi][safi]); ++ ++ bgp_pgbgp_cleanEdges (); ++ ++ if (pgbgp->storage != NULL) ++ free (pgbgp->storage); ++ ++ if (pgbgp->anomalies != NULL) ++ free (pgbgp->anomalies); ++ ++ struct bgp_pgbgp_peerTime *pr = pgbgp->peerLast; ++ while (pr) ++ { ++ struct bgp_pgbgp_peerTime *cur = pr; ++ pr = pr->next; ++ XFREE (MTYPE_BGP_PGBGP_PEER, cur); ++ } ++ ++ return 0; ++} ++ ++int ++bgp_pgbgp_clean (struct bgp_table *table) ++{ ++ struct bgp_pgbgp_reuse *rnode = NULL; ++ ++ while (pgbgp->rq_size > 0) ++ { ++ rnode = (struct bgp_pgbgp_reuse *) pqueue_dequeue (pgbgp->reuse_q); ++ pgbgp->rq_size -= 1; ++ XFREE (MTYPE_BGP_PGBGP_REUSE, rnode); ++ } ++ pqueue_delete (pgbgp->reuse_q); ++ ++ if (table == NULL) ++ return 0; ++ ++ // Clean the detectors ++ bgp_pgbgp_cleanHistTable (table); ++ ++ bgp_pgbgp_cleanEdges (); ++ ++ ++ // Clean up the RIB nodes ++ for (struct bgp_node * rn = bgp_table_top (table); rn; ++ rn = bgp_route_next (rn)) ++ { ++ int changed = 0; ++ for (struct bgp_info * ri = rn->info; ri; ri = ri->next) ++ { ++ if (CHECK_FLAG (ri->flags, BGP_INFO_SUSPICIOUS_O ++ | BGP_INFO_SUSPICIOUS_P | BGP_INFO_SUSPICIOUS_E ++ | BGP_INFO_IGNORED_P)) ++ { ++ changed = 1; ++ UNSET_FLAG (ri->flags, BGP_INFO_SUSPICIOUS_O ++ | BGP_INFO_SUSPICIOUS_P | BGP_INFO_SUSPICIOUS_E ++ | BGP_INFO_IGNORED_P); ++ } ++ } ++ if (changed && rn->info) ++ { ++ struct bgp_info *ri = rn->info; ++ bgp_process (ri->peer->bgp, rn, rn->table->afi, rn->table->safi); ++ } ++ } ++ ++ hash_free (pgbgp->edgeT); ++ return 0; ++} ++ ++ ++int ++bgp_pgbgp_gc (struct bgp_table *table) ++{ ++ struct bgp *bgp = bgp_get_default (); ++ if (!bgp) ++ return 0; ++ ++ // Collect each AFI/SAFI RIB ++ for (afi_t afi = AFI_IP; afi < AFI_MAX; afi++) ++ for (safi_t safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) ++ { ++ if (!CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_PGBGP)) ++ continue; ++ struct bgp_table *curTable = bgp->rib[afi][safi]; ++ if (!curTable) ++ continue; ++ bgp_pgbgp_garbageCollectHistTable (curTable); ++ } ++ ++ bgp_pgbgp_garbageCollectEdges (table); ++ ++ return 0; ++} ++ ++int ++bgp_pgbgp_restore (void) ++{ ++ ++ if (pgbgp->storage == NULL) ++ return 0; ++ FILE *file = fopen (pgbgp->storage, "r"); ++ if (!file) ++ return 0; ++ ++ int type = 0; ++ struct prefix p; ++ struct bgp *bgp = bgp_get_default (); ++ struct bgp_node *curNode = NULL; ++ ++ // Get the log store time ++ long long int writetime; ++ fscanf (file, "%lld", &writetime); ++ time_t swtime = writetime; ++ ++ // If it's too old (more than 1 week old), start fresh ++ if (time (NULL) - swtime > 86400 * 7) ++ { ++ fclose (file); ++ return 0; ++ } ++ ++ ++ // Get the PGBGP init time ++ long long int stime; ++ fscanf (file, "%lld", &stime); ++ pgbgp->startTime = stime; ++ ++ while (fscanf (file, "%d", &type) != EOF) ++ { ++ ++ if (type == PREFIX_ID) ++ { ++ char pre[128]; ++ unsigned int afi; ++ unsigned int safi; ++ long long int time; ++ fscanf (file, "%s %u %u %lld", pre, &afi, &safi, &time); ++ str2prefix (pre, &p); ++ struct bgp_table *curTable = bgp->rib[afi][safi]; ++ assert (curTable != NULL); ++ ++ // Create and lock the node ++ curNode = bgp_node_get (curTable, &p); ++ assert (curNode->hist == NULL); ++ ++ // bgp_lock_node(curNode); ++ ++ curNode->hist = ++ XCALLOC (MTYPE_BGP_PGBGP_HIST, sizeof (struct bgp_pgbgp_hist)); ++ assert (curNode->hist != NULL); ++ ++ curNode->hist->p = ++ XCALLOC (MTYPE_BGP_PGBGP_PREFIX, ++ sizeof (struct bgp_pgbgp_prefix)); ++ assert (curNode->hist->p != NULL); ++ ++ curNode->hist->p->lastSeen = time; ++ } ++ else if (type == ORIGIN_ID) ++ { ++ unsigned int ASN; ++ long long int time; ++ fscanf (file, "%u %lld", &ASN, &time); ++ struct bgp_pgbgp_origin *or = XCALLOC (MTYPE_BGP_PGBGP_ORIGIN, ++ sizeof (struct ++ bgp_pgbgp_origin)); ++ or->lastSeen = time; ++ or->originAS = ASN; ++ or->next = curNode->hist->o; ++ curNode->hist->o = or; ++ } ++ else if (type == EDGE_ID) ++ { ++ bgp_pgbgp_restoreEdge (file); ++ } ++ else if (type == PEER_ID) ++ { ++ struct bgp_pgbgp_peerTime *pr; ++ long long int time; ++ union sockunion su; ++ char szsu[128]; ++ fscanf (file, "%s %lld", szsu, &time); ++ str2sockunion (szsu, &su); ++ pr = ++ XCALLOC (MTYPE_BGP_PGBGP_PEER, ++ sizeof (struct bgp_pgbgp_peerTime)); ++ pr->su = su; ++ pr->lastSeen = time; ++ pr->next = pgbgp->peerLast; ++ pgbgp->peerLast = pr; ++ } ++ } ++ ++ fclose (file); ++ return 0; ++} ++ ++int ++bgp_pgbgp_store (struct bgp_table *table) ++{ ++ if (pgbgp->storage == NULL) ++ return 0; ++ char *tmpname = malloc (sizeof (char) * (1 + 4 + strlen (pgbgp->storage))); ++ strcpy (tmpname, pgbgp->storage); ++ strcat (tmpname, ".tmp"); ++ FILE *file = fopen (tmpname, "w"); ++ ++ if (!file) ++ { ++ free (tmpname); ++ return 0; ++ } ++ ++ // Store the current time ++ fprintf (file, "%lld\n", (long long int) time (NULL)); ++ ++ // Store the init time ++ fprintf (file, "%lld\n", (long long int) pgbgp->startTime); ++ ++ // Store the peer times ++ for (struct bgp_pgbgp_peerTime * pr = pgbgp->peerLast; pr; pr = pr->next) ++ { ++ char strSock[128]; ++ sockunion2str (&pr->su, strSock, sizeof (strSock)); ++ ++ if (pr->deprefUntil < time (NULL)) ++ { ++ fprintf (file, "%d %s %lld\n", PEER_ID, strSock, ++ (long long int) pr->lastSeen); ++ } ++ } ++ ++ // Store the tables ++ bgp_pgbgp_storeHistTable (table, file); ++ bgp_pgbgp_storeEdges (table, file); ++ ++ fclose (file); ++ ++ rename (tmpname, pgbgp->storage); ++ ++ free (tmpname); ++ return 0; ++} ++ ++/* ++ Check to see if we've seen the peer recently ++ If not, then we need to return true and not delay routes ++ for awhile ++*/ ++int ++bgp_pgbgp_updatePeer (struct bgp_info *binfo, time_t now) ++{ ++ int status = false; ++ // Find the peer ++ struct bgp_pgbgp_peerTime *pr = pgbgp->peerLast; ++ for (; pr; pr = pr->next) ++ if (sockunion_same (&pr->su, &binfo->peer->su)) ++ break; ++ ++ // If this is a new peer, create it ++ if (pr == NULL) ++ { ++ pr = XCALLOC (MTYPE_BGP_PGBGP_PEER, sizeof (struct bgp_pgbgp_peerTime)); ++ pr->su = binfo->peer->su; ++ pr->next = pgbgp->peerLast; ++ pgbgp->peerLast = pr; ++ ++ } ++ // Is it currently marked as new? ++ if (pr->deprefUntil > now) ++ goto UPPEER_DEPREF; ++ ++ // Have we seen the peer recently? ++ if (pr->lastSeen + pgbgp->peer_hist_time > now) ++ goto UPPEER_CLEAN; ++ ++ // It must not have been seen lately, depref it ++ pr->deprefUntil = now + PGBGP_PEER_GRACE; ++ ++ ++UPPEER_DEPREF: ++ status = true; ++ ++UPPEER_CLEAN: ++ pr->lastSeen = now; ++ ++ return status; ++} ++ ++ ++/* ++ Returns whether or not the sub-prefix should be ignored ++*/ ++int ++bgp_pgbgp_shouldIgnore (struct bgp_node *super, struct bgp_info *selected) ++{ ++ if (!selected || CHECK_FLAG (selected->flags, BGP_INFO_SUSPICIOUS_P)) ++ return false; ++ return true; ++} ++ ++/* ++ This is a special case function for smoothly handling sub-prefix hijacks. ++ ++ It handles the following 2 events: ++ ++ Event 1: The super-prefix of an anomalous prefix has a route through a non-anomalous ++ ++ Event 1: An anomalous sub-prefix is ignored, but no best route for the super-prefix exists ++ Response: Announce the sub-prefix until the super-prefix comes back ++ ++ Event 2: A super-prefix comes back to the RIB and its anomalous sub-prefix is in use ++ Response: Ignore the sub-prefix again ++ */ ++ ++ ++int ++bgp_pgbgp_rib_updated (struct bgp_node *rn, struct bgp_info *old_best, ++ struct bgp_info *new_best) ++{ ++ // return 0; ++ struct bgp_pgbgp_hist *hist = rn->hist; ++ if (!hist) ++ return 0; ++ if (!hist->p) ++ return 0; ++ time_t t_now = time (NULL); ++ ++ /* ++ If we can't avoid the sub-prefix by routing to the super-prefix, ++ then route as normal to the sub-prefix ++ */ ++ if (!bgp_pgbgp_shouldIgnore (rn, new_best)) ++ { ++ for (struct bgp_pgbgp_avoid * cur = hist->p->avoid; cur; ++ cur = cur->next) ++ { ++ if (cur->avoidUntil > t_now) ++ { ++ int changed = false; ++ for (struct bgp_info * ri = cur->sub->info; ri; ri = ri->next) ++ { ++ if (CHECK_FLAG (ri->flags, BGP_INFO_IGNORED_P)) ++ { ++ changed = true; ++ UNSET_FLAG (ri->flags, BGP_INFO_IGNORED_P); ++ } ++ } ++ if (changed) ++ { ++ struct bgp_info *ri = cur->sub->info; ++ if (ri && ri->peer && ri->peer->bgp) ++ bgp_process (ri->peer->bgp, cur->sub, ++ cur->sub->table->afi, cur->sub->table->safi); ++ ++ } ++ ++ } ++ } ++ } ++ ++ /* ++ If we can avoid the sub-prefix by routing to the super-prefix, ++ then do so ++ */ ++ ++ else ++ { ++ for (struct bgp_pgbgp_avoid * cur = hist->p->avoid; cur; ++ cur = cur->next) ++ { ++ if (cur->avoidUntil > t_now) ++ { ++ int changed = false; ++ for (struct bgp_info * ri = cur->sub->info; ri; ri = ri->next) ++ { ++ if (!CHECK_FLAG (ri->flags, BGP_INFO_IGNORED_P)) ++ { ++ changed = true; ++ SET_FLAG (ri->flags, BGP_INFO_IGNORED_P); ++ } ++ } ++ if (changed) ++ { ++ struct bgp_info *ri = cur->sub->info; ++ if (ri && ri->peer && ri->peer->bgp) ++ bgp_process (ri->peer->bgp, cur->sub, ++ cur->sub->table->afi, cur->sub->table->safi); ++ } ++ } ++ } ++ } ++ ++ /* ++ if (old_best && !new_best) ++ { ++ time_t t_now = time(NULL); ++ for (struct bgp_pgbgp_avoid * cur = hist->p->avoid; cur; ++ cur = cur->next) ++ { ++ if (cur->avoidUntil > t_now) ++ { ++ for (struct bgp_info * ri = cur->sub->info; ri; ri = ri->next) ++ UNSET_FLAG (ri->flags, BGP_INFO_IGNORED_P); ++ ++ struct bgp_info *ri = cur->sub->info; ++ if (ri && ri->peer && ri->peer->bgp) ++ bgp_process (ri->peer->bgp, cur->sub, cur->sub->table->afi, ++ cur->sub->table->safi); ++ } ++ } ++ } ++ ++ ++ else if (!old_best && new_best) ++ { ++ time_t t_now = time(NULL); ++ for (struct bgp_pgbgp_avoid * av = hist->p->avoid; av; av = av->next) ++ { ++ struct bgp_info * ri = av->sub->info; ++ if (av->avoidUntil > t_now && ri && !CHECK_FLAG(ri->flags, BGP_INFO_IGNORED_P)) ++ { ++ for (; ri; ri = ri->next) ++ SET_FLAG (ri->flags, BGP_INFO_IGNORED_P); ++ ri = av->sub->info; ++ if (ri && ri->peer && ri->peer->bgp) ++ bgp_process (ri->peer->bgp, av->sub, ++ av->sub->table->afi, av->sub->table->safi); ++ ++ } ++ } ++ } ++ */ ++ return 0; ++} ++ ++int ++bgp_pgbgp_update (struct bgp_info *binfo, struct attr *at, ++ struct bgp_node *rn) ++{ ++ time_t t_now = time (NULL); ++ ++ // Clean up the reuse list ++ bgp_pgbgp_reuse (t_now); ++ ++ ++ if (!rn->hist) ++ { ++ rn->hist = ++ XCALLOC (MTYPE_BGP_PGBGP_HIST, sizeof (struct bgp_pgbgp_hist)); ++ // Get the PGBGP history lock on rn ++ bgp_lock_node (rn); ++ } ++ ++ struct bgp_node *superhn = NULL; ++ ++ // implicit lock from node_get ++ superhn = findSuper (rn->table, &rn->p, t_now); ++ ++ int newPeer = bgp_pgbgp_updatePeer (binfo, t_now); ++ bgp_pgbgp_updateOrigin (rn->hist, binfo, at, rn, t_now, newPeer); ++ bgp_pgbgp_updatePrefix (rn->hist, superhn, binfo, at, rn, t_now, newPeer); ++ bgp_pgbgp_updateEdge (rn->hist, binfo, at, rn, t_now, newPeer); ++ ++ if (superhn != NULL) ++ bgp_unlock_node (superhn); ++ ++ ++ ++ // GC and storage must be last, as they update lastSeen values of objects ++ // which would cause new routes to be recently seen, which is undesired behavior ++ // Make sure you don't collect anything that might be in use! ++ if (t_now >= pgbgp->lastgc + PGBGP_GC_DELTA) ++ { ++ bgp_pgbgp_gc (rn->table); ++ pgbgp->lastgc = t_now; ++ } ++ ++ if (t_now >= pgbgp->lastStore + PGBGP_STORE_DELTA) ++ { ++ bgp_pgbgp_store (rn->table); ++ pgbgp->lastStore = t_now; ++ } ++ ++ ++ ++ return 0; ++} ++ ++ ++ ++ ++/*! --------------- Public PGBGP Interface ------------------ !*/ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++/* --------------- MOAS Detection ------------------ */ ++void ++bgp_pgbgp_storeHistTable (struct bgp_table *table, FILE * file) ++{ ++ time_t t_now; ++ t_now = time (NULL); ++ ++ struct bgp *bgp = bgp_get_default (); ++ if (!bgp) ++ return; ++ ++ // Store each AFI/SAFI RIB ++ for (afi_t afi = AFI_IP; afi < AFI_MAX; afi++) ++ for (safi_t safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) ++ { ++ if (!CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_PGBGP)) ++ continue; ++ struct bgp_table *curTable = bgp->rib[afi][safi]; ++ if (!curTable) ++ continue; ++ ++ for (struct bgp_node * rn = bgp_table_top (curTable); rn; ++ rn = bgp_route_next (rn)) ++ { ++ struct bgp_pgbgp_hist *hist = rn->hist; ++ if (hist == NULL) ++ continue; ++ char szPrefix[128]; ++ prefix2str (&rn->p, szPrefix, sizeof (szPrefix)); ++ ++ ++ struct bgp_pgbgp_prefix *pre = hist->p; ++ if (pre && pre->ignoreUntil <= t_now) ++ { ++ if (pre->lastSeen + pgbgp->prefix_hist_time > t_now) ++ fprintf (file, "%d %s %u %u %lld\n", PREFIX_ID, szPrefix, ++ (unsigned int) afi, (unsigned int) safi, ++ (long long int) pre->lastSeen); ++ else ++ continue; ++ } ++ /* Need a prefix in the file before the origins, ++ if no prefix.. skip origins */ ++ else ++ continue; ++ ++ for (struct bgp_pgbgp_origin * cur = hist->o; cur; ++ cur = cur->next) ++ { ++ if (cur->deprefUntil > t_now) ++ continue; ++ ++ if (cur->lastSeen + pgbgp->origin_hist_time > t_now) ++ fprintf (file, "%d %u %lld\n", ORIGIN_ID, cur->originAS, ++ (long long int) cur->lastSeen); ++ } ++ ++ } ++ } ++} ++ ++ ++int ++bgp_pgbgp_garbageCollectHistTable (struct bgp_table *table) ++{ ++ time_t t_now; ++ t_now = time (NULL); ++ ++ ++ for (struct bgp_node * rn = bgp_table_top (table); rn; ++ rn = bgp_route_next (rn)) ++ { ++ int collect = false; ++ struct bgp_pgbgp_hist *hist = rn->hist; ++ if (hist == NULL) ++ continue; ++ ++ struct bgp_pgbgp_origin *cur = hist->o; ++ struct bgp_pgbgp_prefix *pre = hist->p; ++ struct bgp_pgbgp_origin *parent = NULL; ++ ++ int used = false; ++ if (cur != NULL || pre != NULL) ++ used = true; ++ ++ while (cur != NULL) ++ { ++ // Update the lastSeen time w/ originInRIB ++ if (originInRIB (rn, cur)) ++ cur->lastSeen = t_now; ++ ++ collect = false; ++ ++ // Collect if old ++ if (cur->lastSeen + pgbgp->origin_hist_time <= t_now) ++ collect = true; ++ ++ // Collect if anomaly just became okay but not seen since last collection ++ if (cur->deprefUntil != 0 && cur->deprefUntil < t_now) ++ { ++ if (cur->lastSeen < pgbgp->lastgc) ++ collect = true; ++ cur->deprefUntil = 0; ++ } ++ ++ if (collect) ++ { ++ if (parent == NULL) ++ hist->o = cur->next; ++ else ++ parent->next = cur->next; ++ ++ // Delete cur, parent doesn't change ++ struct bgp_pgbgp_origin *del = cur; ++ cur = cur->next; ++ XFREE (MTYPE_BGP_PGBGP_ORIGIN, del); ++ } ++ else ++ { ++ parent = cur; ++ cur = cur->next; ++ } ++ } ++ ++ // Update the lastSeen time w/ prefixInRIB ++ if (pre && prefixInRIB (rn, pre)) ++ pre->lastSeen = t_now; ++ ++ collect = false; ++ ++ // Collect if old ++ if (pre && pre->lastSeen + pgbgp->prefix_hist_time <= t_now) ++ collect = true; ++ ++ // Collect if anomaly just became okay but not seen since last collection ++ if (pre && pre->ignoreUntil != 0 && pre->ignoreUntil < t_now) ++ { ++ if (pre->lastSeen < pgbgp->lastgc) ++ collect = true; ++ pre->ignoreUntil = 0; ++ } ++ ++ if (collect) ++ { ++ for (struct bgp_pgbgp_avoid * av = pre->avoid; av;) ++ { ++ struct bgp_pgbgp_avoid *del = av; ++ av = av->next; ++ bgp_unlock_node (del->sub); ++ XFREE (MTYPE_BGP_PGBGP_AVOID, del); ++ } ++ ++ XFREE (MTYPE_BGP_PGBGP_PREFIX, pre); ++ hist->p = NULL; ++ } ++ ++ // If the node isn't in use, remove it ++ if (used && hist->o == NULL && hist->p == NULL) ++ { ++ XFREE (MTYPE_BGP_PGBGP_HIST, hist); ++ rn->hist = NULL; ++ bgp_unlock_node (rn); ++ } ++ } ++ ++ return 0; ++} ++ ++void ++bgp_pgbgp_cleanHistTable (struct bgp_table *table) ++{ ++ // Clean up the RIB nodes ++ for (struct bgp_node * rn = bgp_table_top (table); rn; ++ rn = bgp_route_next (rn)) ++ { ++ struct bgp_pgbgp_hist *hist = rn->hist; ++ if (hist == NULL) ++ continue; ++ ++ if (hist->p) ++ { ++ for (struct bgp_pgbgp_avoid * av = hist->p->avoid; av;) ++ { ++ struct bgp_pgbgp_avoid *del = av; ++ av = av->next; ++ bgp_unlock_node (del->sub); ++ XFREE (MTYPE_BGP_PGBGP_AVOID, del); ++ } ++ hist->p->avoid = NULL; ++ XFREE (MTYPE_BGP_PGBGP_PREFIX, hist->p); ++ hist->p = NULL; ++ } ++ ++ for (struct bgp_pgbgp_origin * cur = hist->o; cur;) ++ { ++ struct bgp_pgbgp_origin *next = cur->next; ++ XFREE (MTYPE_BGP_PGBGP_ORIGIN, cur); ++ cur = next; ++ } ++ hist->o = NULL; ++ XFREE (MTYPE_BGP_PGBGP_HIST, hist); ++ rn->hist = NULL; ++ bgp_unlock_node (rn); ++ } ++} ++ ++void ++bgp_pgbgp_logOriginAnomaly (as_t asn, struct bgp_node *rn, struct attr *at) ++{ ++ assert (pgbgp); ++ if (!pgbgp->anomalies) ++ return; ++ FILE *file = fopen (pgbgp->anomalies, "a"); ++ if (!file) ++ return; ++ ++ char pre[256]; ++ prefix2str (&rn->p, pre, sizeof (pre)); ++ ++ // MOAS | TIME | NEXTHOP | PREFIX | SUSPICIOUS_ORIGIN | TRUSTED_ORIGINS | PATH ++ fprintf (file, "%d|%lld|%s|%s|%d|", MOAS, (long long int) time (NULL), ++ inet_ntoa (at->nexthop), pre, asn); ++ ++ ++ // Print the trusted origins ++ assert (rn->hist); ++ assert (rn->hist->o); ++ ++ struct bgp_pgbgp_hist *hist = rn->hist; ++ ++ for (struct bgp_pgbgp_origin * cur = hist->o; cur != NULL; cur = cur->next) ++ { ++ if (cur->deprefUntil > time (NULL)) ++ continue; ++ fprintf (file, "%d", cur->originAS); ++ if (cur->next != NULL) ++ fprintf (file, " "); ++ } ++ ++ fprintf (file, " |%s\n", aspath_print (at->aspath)); ++ fclose (file); ++} ++ ++int ++bgp_pgbgp_updateOrigin (struct bgp_pgbgp_hist *hist, struct bgp_info *binfo, ++ struct attr *at, struct bgp_node *rn, time_t t_now, ++ int newPeer) ++{ ++ struct bgp_pgbgp_pathSet pathOrigins; ++ struct bgp_pgbgp_origin *pi = NULL; ++ int status = 0; ++ struct bgp_pgbgp_reuse *r; ++ pathOrigins = bgp_pgbgp_pathOrigin (at->aspath); ++ ++ ++ for (int i = 0; i < pathOrigins.length; i++) ++ { ++ as_t pathOrigin = pathOrigins.ases[i]; ++ ++ /* Is the Origin AS in the history? */ ++ for (pi = hist->o; pi; pi = pi->next) ++ if (pi->originAS == pathOrigin) ++ break; ++ ++ if (pi == NULL) ++ { ++ pi = ++ XCALLOC (MTYPE_BGP_PGBGP_ORIGIN, ++ sizeof (struct bgp_pgbgp_origin)); ++ pi->next = hist->o; ++ pi->originAS = pathOrigin; ++ hist->o = pi; ++ } ++ ++ // If this is our first origin for the prefix, let the sub-prefix ++ // check take care of it ++ if (pi->next == NULL) ++ goto UPO_CLEAN; ++ ++ /* Is the origin currently marked as suspicious? */ ++ if (pi->deprefUntil > t_now) ++ goto UPO_DEPREF; ++ ++ /* Have we seen the origin recently? */ ++ if (pi->lastSeen + pgbgp->origin_hist_time > t_now) ++ goto UPO_CLEAN; ++ ++#ifndef PGBGP_DEBUG ++ /* Are we within the initial grace period? */ ++ if (newPeer) ++ goto UPO_CLEAN; ++#endif ++ ++ /* It must not be in recent history, depref origin for first time */ ++ pi->deprefUntil = t_now + pgbgp->origin_sus_time; ++ bgp_pgbgp_logOriginAnomaly (pathOrigin, rn, at); ++ ++ r = XCALLOC (MTYPE_BGP_PGBGP_REUSE, sizeof (struct bgp_pgbgp_reuse)); ++ r->type = PGBGP_REUSE_ORIGIN; ++ r->deprefUntil = pi->deprefUntil; ++ r->data.origin.originAS = pathOrigin; ++ r->data.origin.rn = rn; ++ bgp_lock_node (rn); ++ pqueue_enqueue (r, pgbgp->reuse_q); ++ pgbgp->rq_size += 1; ++ ++ ++ UPO_DEPREF: ++ SET_FLAG (binfo->flags, BGP_INFO_SUSPICIOUS_O); ++ status = BGP_INFO_SUSPICIOUS_O; ++ ++ UPO_CLEAN: ++ pi->lastSeen = t_now; ++ } ++ return status; ++} ++ ++int ++bgp_pgbgp_reuseOrigin (struct bgp_pgbgp_r_origin data) ++{ ++ struct bgp_info *ri; ++ int numChanged = 0; ++ time_t t_now = time (NULL); ++ assert (data.rn->hist != NULL); ++ ++ // Repreference paths for this prefix that are now okay ++ for (ri = data.rn->info; ri; ri = ri->next) ++ { ++ if (CHECK_FLAG (ri->flags, BGP_INFO_SUSPICIOUS_O)) ++ { ++ struct bgp_pgbgp_pathSet pathOrigins; ++ pathOrigins = bgp_pgbgp_pathOrigin (ri->attr->aspath); ++ int numOkay = 0; ++ for (int i = 0; i < pathOrigins.length; i++) ++ { ++ as_t pathOrigin = pathOrigins.ases[i]; ++ // Find the origin ++ struct bgp_pgbgp_origin *o = NULL; ++ for (o = data.rn->hist->o; o != NULL; o = o->next) ++ if (o->originAS == pathOrigin) ++ break; ++ /* ++ if (o == NULL) { ++ for(struct bgp_pgbgp_origin * z = data.rn->hist->o; z != NULL; z = z->next) ++ printf("Known origin: %d\n", z->originAS); ++ char pre[128]; ++ prefix2str(&data.rn->p, pre, 128); ++ printf("%s : %s : %d\n", pre, ri->attr->aspath->str, pathOrigin); ++ } ++ */ ++ assert (o != NULL); ++ ++ if (o->deprefUntil <= t_now) ++ numOkay += 1; ++ } ++ if (numOkay == pathOrigins.length) ++ { ++ UNSET_FLAG (ri->flags, BGP_INFO_SUSPICIOUS_O); ++ numChanged += 1; ++ } ++ } ++ } ++ ++ ri = data.rn->info; ++ ++ // Rerun the decision process? ++ if (numChanged > 0) ++ bgp_process (ri->peer->bgp, data.rn, data.rn->table->afi, ++ data.rn->table->safi); ++ ++ ++ /* ++ // Remove this (origin,prefix) pair from the normal database ++ // if it's not still in the RIB ++ struct bgp_pgbgp_hist *hist = rn->hist; ++ struct bgp_pgbgp_origin * cur = hist->o; ++ struct bgp_pgbgp_origin * parent = NULL; ++ ++ // Find the origin AS node ++ while(cur != NULL) ++ { ++ if (cur->originAS == data.originAS) ++ { ++ // Delete the node if it hasn't been seen ++ // since the last storage run ++ if (cur->lastSeen < pgbgp->lastStore) { ++ // Delete this node ++ if (parent == NULL) ++ hist->o = cur->next; ++ else ++ parent->next = cur->next; ++ ++ XFREE(MTYPE_BGP_PGBGP_ORIGIN, cur); ++ } ++ break; ++ } ++ parent = cur; ++ cur = cur->next; ++ } ++ */ ++ ++ bgp_unlock_node (data.rn); ++ return 0; ++} ++ ++/*! --------------- MOAS Detection ------------------ !*/ ++ ++ ++/* --------------- Sub-Prefix Detection ------------------ */ ++ ++ ++ ++ ++ ++void ++bgp_pgbgp_logSubprefixAnomaly (as_t asn, struct bgp_node *rn, struct attr *at, ++ struct bgp_node *super) ++{ ++ assert (pgbgp); ++ if (!pgbgp->anomalies) ++ return; ++ FILE *file = fopen (pgbgp->anomalies, "a"); ++ if (!file) ++ return; ++ ++ char pre[256]; ++ prefix2str (&rn->p, pre, sizeof (pre)); ++ ++ char superpre[256]; ++ prefix2str (&super->p, superpre, sizeof (superpre)); ++ ++ // SUBPREFIX | TIME | NEXTHOP | PREFIX | SUPER-PREFIX | SUSPICIOUS_ORIGIN | TRUSTED_ORIGINS | PATH ++ fprintf (file, "%d|%lld|%s|%s|%s|%d|", SUBPREFIX, ++ (long long int) time (NULL), inet_ntoa (at->nexthop), pre, ++ superpre, asn); ++ ++ // Print the trusted origins ++ assert (super->hist); ++ assert (super->hist->o); ++ ++ struct bgp_pgbgp_hist *hist = super->hist; ++ ++ for (struct bgp_pgbgp_origin * cur = hist->o; cur != NULL; cur = cur->next) ++ { ++ if (cur->deprefUntil > time (NULL)) ++ continue; ++ fprintf (file, "%d", cur->originAS); ++ if (cur->next != NULL) ++ fprintf (file, " "); ++ } ++ ++ fprintf (file, " |%s\n", aspath_print (at->aspath)); ++ fclose (file); ++} ++ ++/* ++ If the first path is a prefix of the second, then return true ++ */ ++ ++static int ++bgp_pgbgp_pathIsPrefix(struct aspath *trusted, struct aspath * new) ++{ ++ if (trusted == new) ++ return true; ++ ++ struct assegment *seg1 = trusted->segments; ++ struct assegment *seg2 = new->segments; ++ ++ while (seg1 || seg2) ++ { ++ if ((!seg1 && seg2) || (seg1 && !seg2)) ++ return false; ++ if (seg1->type != seg2->type) ++ return false; ++ ++ if (seg1->length > seg2->length) ++ return false; ++ ++ for(int i = 0; i < seg1->length; i++) ++ if (seg1->as[i] != seg2->as[i]) ++ return false; ++ ++ seg1 = seg1->next; ++ seg2 = seg2->next; ++ } ++ ++ return true; ++} ++ ++int ++bgp_pgbgp_updatePrefix (struct bgp_pgbgp_hist *hist, ++ struct bgp_node *supernode, struct bgp_info *binfo, ++ struct attr *at, struct bgp_node *rn, time_t t_now, ++ int newPeer) ++{ ++ struct bgp_pgbgp_prefix *pre = NULL; ++ struct bgp_pgbgp_reuse *r = NULL; ++ int status = 0; ++ int changed = false; ++ ++ pre = hist->p; ++ ++ ++ /* Do we have this prefix? */ ++ if (pre == NULL) ++ { ++ pre = ++ XCALLOC (MTYPE_BGP_PGBGP_PREFIX, sizeof (struct bgp_pgbgp_prefix)); ++ hist->p = pre; ++ } ++ ++ /* Is the prefix currently marked as suspicious? */ ++ if (pre->ignoreUntil > t_now) ++ { ++ goto UPP_IGNORE; ++ } ++ ++ /* Should this neighbor be avoided for this prefix because it ++ sent us info. about a suspicious sub-prefix? */ ++ for (struct bgp_pgbgp_avoid * av = hist->p->avoid; av; av = av->next) ++ { ++ if (binfo->peer->as == av->peerASN && av->avoidUntil > t_now) ++ { ++ SET_FLAG (binfo->flags, BGP_INFO_SUSPICIOUS_P); ++ status = BGP_INFO_SUSPICIOUS_P; ++ goto UPP_DONE; ++ } ++ } ++ ++ /* Have we seen the prefix recently? */ ++ if (pre->lastSeen + pgbgp->prefix_hist_time > t_now) ++ goto UPP_DONE; ++ ++#ifndef PGBGP_DEBUG ++ /* Are we within the initial grace period? */ ++ if (newPeer) ++ goto UPP_DONE; ++#endif ++ ++ /* Is there a less specific *in recent history* that this could be hijacking? */ ++ if (supernode == NULL) ++ goto UPP_DONE; ++ ++ /* Does this path the super-net's non-anomalous path from this peer? If so it's okay */ ++ int found = false; ++ for (struct bgp_info * ri = supernode->info; ri; ri = ri->next) ++ { ++ if (ri->peer->as == binfo->peer->as) ++ { ++ if (!ANOMALOUS(ri->flags) && bgp_pgbgp_pathIsPrefix(ri->attr->aspath, at->aspath)) ++ found = true; ++ break; ++ } ++ } ++ ++ if (found) ++ goto UPP_DONE; ++ ++ /* ++ It's not in recent history, and there is a less specific currently in use ++ Response: ++ . Ignore this prefix ++ . Make the less specific's route for this neighbor suspicious ++ */ ++ ++ ++ pre->ignoreUntil = t_now + pgbgp->sub_sus_time; ++ ++ struct bgp_pgbgp_pathSet pathOrigins; ++ pathOrigins = bgp_pgbgp_pathOrigin (at->aspath); ++ for (int i = 0; i < pathOrigins.length; i++) ++ bgp_pgbgp_logSubprefixAnomaly (pathOrigins.ases[i], rn, at, supernode); ++ ++ ++ ++ r = XCALLOC (MTYPE_BGP_PGBGP_REUSE, sizeof (struct bgp_pgbgp_reuse)); ++ r->type = PGBGP_REUSE_PREFIX; ++ r->deprefUntil = pre->ignoreUntil; ++ r->data.prefix.rn = rn; ++ r->data.prefix.rnsuper = supernode; ++ bgp_lock_node (rn); ++ bgp_lock_node (supernode); ++ pqueue_enqueue (r, pgbgp->reuse_q); ++ pgbgp->rq_size += 1; ++ ++UPP_IGNORE: ++ // Sanity check ++ if (supernode == NULL) ++ goto UPP_DONE; ++ ++ /* Set the less specific's route from this peer to suspicious */ ++ changed = false; ++ ++ for (struct bgp_info * ri = supernode->info; ri; ri = ri->next) ++ { ++ if (ri->peer->as == binfo->peer->as) ++ { ++ if (!CHECK_FLAG (ri->flags, BGP_INFO_SUSPICIOUS_P)) ++ { ++ SET_FLAG (ri->flags, BGP_INFO_SUSPICIOUS_P); ++ changed = true; ++ } ++ break; ++ } ++ } ++ ++ // Make note of it in the less specific's history information ++ found = false; ++ struct bgp_pgbgp_hist *superhist = supernode->hist; ++ ++ if (superhist && superhist->p) ++ { ++ for (struct bgp_pgbgp_avoid * av = superhist->p->avoid; av; ++ av = av->next) ++ { ++ if (av->peerASN == binfo->peer->as) ++ { ++ if (av->avoidUntil < pre->ignoreUntil) ++ av->avoidUntil = pre->ignoreUntil; ++ found = true; ++ break; ++ } ++ } ++ if (!found) ++ { ++ struct bgp_pgbgp_avoid *newavoid = ++ XCALLOC (MTYPE_BGP_PGBGP_AVOID, sizeof (struct bgp_pgbgp_avoid)); ++ newavoid->peerASN = binfo->peer->as; ++ newavoid->avoidUntil = pre->ignoreUntil; ++ newavoid->next = superhist->p->avoid; ++ newavoid->sub = rn; ++ bgp_lock_node (rn); ++ superhist->p->avoid = newavoid; ++ } ++ } ++ /* ++ ignore this route unless the supernet's node ++ is only a placeholder from loaded pgbgp data ++ */ ++ if (bgp_pgbgp_shouldIgnore (supernode, bgp_pgbgp_selected (supernode))) ++ { ++ SET_FLAG (binfo->flags, BGP_INFO_IGNORED_P); ++ status = BGP_INFO_IGNORED_P; ++ } ++ if (changed) ++ { ++ struct bgp_info *ri = supernode->info; ++ bgp_process (ri->peer->bgp, supernode, supernode->table->afi, ++ supernode->table->safi); ++ } ++ ++UPP_DONE: ++ pre->lastSeen = t_now; ++ ++ return status; ++} ++ ++int ++bgp_pgbgp_reusePrefix (struct bgp_pgbgp_r_prefix data) ++{ ++ struct bgp_info *ri = NULL; ++ ++ time_t t_now = time (NULL); ++ ++ // Repreference all routes for this node ++ for (ri = data.rn->info; ri; ri = ri->next) ++ UNSET_FLAG (ri->flags, BGP_INFO_IGNORED_P); ++ ri = data.rn->info; ++ ++ // Rerun the decision process ++ if (ri != NULL) ++ bgp_process (ri->peer->bgp, data.rn, data.rn->table->afi, ++ data.rn->table->safi); ++ ++ ++ // Remove the avoid nodes from the super ++ struct bgp_pgbgp_hist *superhist = data.rnsuper->hist; ++ if (superhist != NULL && superhist->p != NULL) ++ { ++ struct bgp_pgbgp_avoid *parent = NULL; ++ for (struct bgp_pgbgp_avoid * av = superhist->p->avoid; av;) ++ { ++ int numChanged = 0; ++ if (av->avoidUntil <= t_now) ++ { ++ struct bgp_pgbgp_avoid *del = av; ++ av = av->next; ++ if (parent == NULL) ++ superhist->p->avoid = av; ++ else ++ parent->next = av; ++ ++ // Repreference any routes ++ for (ri = data.rnsuper->info; ri; ri = ri->next) ++ { ++ if (ri->peer->as == del->peerASN) ++ { ++ UNSET_FLAG (ri->flags, BGP_INFO_SUSPICIOUS_P); ++ numChanged += 1; ++ break; ++ } ++ } ++ ri = data.rnsuper->info; ++ ++ if (numChanged > 0 && ri != NULL) ++ bgp_process (ri->peer->bgp, data.rnsuper, ++ data.rnsuper->table->afi, ++ data.rnsuper->table->safi); ++ bgp_unlock_node (del->sub); ++ XFREE (MTYPE_BGP_PGBGP_AVOID, del); ++ } ++ else ++ { ++ parent = av; ++ av = av->next; ++ } ++ } ++ } ++ ++ // Remove this prefix from the normal database ++ // if it hasn't been seen in the RIB since the last ++ // storage run ++ /* ++ struct bgp_pgbgp_hist *hist = rn->hist; ++ struct bgp_pgbgp_prefix * pre = hist->p; ++ ++ if (pre && pre->lastSeen < pgbgp->lastStore) ++ { ++ // Delete this node ++ for(struct bgp_pgbgp_avoid * av = hist->p->avoid; av;) ++ { ++ struct bgp_pgbgp_avoid *del = av; ++ av = av->next; ++ bgp_unlock_node(del->sub); ++ XFREE (MTYPE_BGP_PGBGP_AVOID, del); ++ } ++ XFREE(MTYPE_BGP_PGBGP_PREFIX, pre); ++ hist->p = NULL; ++ } ++ */ ++ bgp_unlock_node (data.rn); ++ bgp_unlock_node (data.rnsuper); ++ return 0; ++} ++ ++/*! --------------- Sub-Prefix Detection ------------------ !*/ ++ ++ ++ ++ ++ ++/* --------------- Edge Detection ------------------ */ ++ ++static void ++edge_store_clear_iterator (struct hash_backet *backet, void *file) ++{ ++ struct bgp_pgbgp_edge *hedge = backet->data; ++} ++ ++static void ++edge_store_iterator (struct hash_backet *backet, FILE * file) ++{ ++ struct bgp_pgbgp_edge *hedge = backet->data; ++ time_t t_now = time (NULL); ++ if (hedge->deprefUntil > t_now) ++ return; ++ if (hedge->lastSeen + pgbgp->edge_hist_time > t_now) ++ { ++ fprintf (file, "%d %u %u %lld\n", EDGE_ID, hedge->e.a, hedge->e.b, ++ (long long int) hedge->lastSeen); ++ } ++} ++ ++ ++void ++bgp_pgbgp_storeEdges (struct bgp_table *table, FILE * file) ++{ ++ hash_iterate (pgbgp->edgeT, ++ (void (*)(struct hash_backet *, void *)) ++ edge_store_iterator, file); ++ return; ++} ++ ++ ++int ++bgp_pgbgp_restoreEdge (FILE * file) ++{ ++ unsigned int a, b; ++ long long int lastSeen; ++ fscanf (file, "%u %u %lld", &a, &b, &lastSeen); ++ struct bgp_pgbgp_edge finder; ++ finder.e.a = a; ++ finder.e.b = b; ++ finder.lastSeen = lastSeen; ++ struct bgp_pgbgp_edge *hedge = ++ hash_get (pgbgp->edgeT, &finder, edge_hash_alloc); ++ hedge->lastSeen = finder.lastSeen; ++ return 0; ++} ++ ++unsigned int ++edge_key_make (void *p) ++{ ++ struct bgp_pgbgp_edge *pe = p; ++ struct edge *e = &pe->e; ++ return (e->a << 16) + e->b; ++} ++ ++static int ++edge_cmp (const void *arg1, const void *arg2) ++{ ++ ++ const struct edge *e1 = &((const struct bgp_pgbgp_edge *) arg1)->e; ++ const struct edge *e2 = &((const struct bgp_pgbgp_edge *) arg2)->e; ++ if (e1->a == e2->a && e1->b == e2->b) ++ return 1; ++ return 0; ++} ++ ++static void * ++edge_hash_alloc (void *arg) ++{ ++ struct bgp_pgbgp_edge *hedge = ++ XCALLOC (MTYPE_BGP_PGBGP_EDGE, sizeof (struct bgp_pgbgp_edge)); ++ struct bgp_pgbgp_edge *lookup = arg; ++ if (hedge == NULL) ++ return NULL; ++ hedge->e = lookup->e; ++ return hedge; ++} ++ ++ ++static void ++edge_gc_iterator (struct hash_backet *backet, time_t * time) ++{ ++ time_t t_now = *time; ++ struct bgp_pgbgp_edge *hedge = backet->data; ++ ++ int collect = false; ++ ++ // Collect if we haven't seen it in awhile ++ if (hedge->lastSeen + pgbgp->edge_hist_time <= t_now) ++ collect = true; ++ ++ // Collect if it has just gotten out of anomaly stage ++ // but hasn't been in the RIB since the last GC ++ if (hedge->deprefUntil != 0 && hedge->deprefUntil < t_now) ++ { ++ if (hedge->lastSeen < pgbgp->lastgc) ++ collect = true; ++ hedge->deprefUntil = 0; ++ } ++ ++ if (collect) ++ { ++ struct bgp_pgbgp_edge *ret = hash_release (pgbgp->edgeT, hedge); ++ assert (ret != NULL); ++ XFREE (MTYPE_BGP_PGBGP_EDGE, hedge); ++ } ++} ++ ++ ++ ++static void ++edge_update_iterator (struct hash_backet *backet, void *v) ++{ ++ struct aspath *p = backet->data; ++ time_t t_now = time (NULL); ++ int first = true; ++ ++ struct edge cur; ++ cur.a = 0; ++ cur.b = 0; ++ struct assegment *seg; ++ struct bgp_pgbgp_edge *hedge = NULL; ++ for (seg = p->segments; seg; seg = seg->next) ++ { ++ for (int i = 0; i < seg->length; i++) ++ { ++ cur.a = cur.b; ++ cur.b = seg->as[i]; ++ if (first) ++ { ++ first = false; ++ continue; ++ } ++ if (cur.a == cur.b) ++ continue; ++ // printf("%d -- %d\n", cur.a, cur.b); ++ struct bgp_pgbgp_edge finder; ++ finder.e = cur; ++ hedge = hash_lookup (pgbgp->edgeT, &finder); ++ ++ if (!hedge) ++ continue; ++ hedge->lastSeen = t_now; ++ } ++ } ++} ++ ++int ++bgp_pgbgp_garbageCollectEdges (struct bgp_table *table) ++{ ++ // Update the timings ++ hash_iterate (ashash, ++ (void (*)(struct hash_backet *, void *)) ++ edge_update_iterator, NULL); ++ ++ // Perform the collection ++ time_t t_now = time (NULL); ++ hash_iterate (pgbgp->edgeT, ++ (void (*)(struct hash_backet *, void *)) ++ edge_gc_iterator, &t_now); ++ return 0; ++} ++ ++static void ++edge_clean_iterator (struct hash_backet *backet, void *a1) ++{ ++ struct bgp_pgbgp_edge *hedge = backet->data; ++ struct bgp_pgbgp_edge *ret = hash_release (pgbgp->edgeT, hedge); ++ assert (ret != NULL); ++ XFREE (MTYPE_BGP_PGBGP_EDGE, hedge); ++} ++ ++static void ++bgp_pgbgp_cleanEdges (void) ++{ ++ if (pgbgp->edgeT != NULL) ++ { ++ hash_iterate (pgbgp->edgeT, ++ (void (*)(struct hash_backet *, void *)) ++ edge_clean_iterator, NULL); ++ hash_free (pgbgp->edgeT); ++ } ++ return; ++} ++ ++void ++bgp_pgbgp_logEdgeAnomaly (struct bgp_node *rn, struct attr *at, ++ struct edge *edge) ++{ ++ assert (pgbgp); ++ if (!pgbgp->anomalies) ++ return; ++ FILE *file = fopen (pgbgp->anomalies, "a"); ++ if (!file) ++ return; ++ ++ char pre[256]; ++ prefix2str (&rn->p, pre, sizeof (pre)); ++ ++ // EDGE | TIME | NEXTHOP | PREFIX | PATH | Edge.a | Edge.b ++ ++ fprintf (file, "%d|%lld|%s|%s|%s|%d|%d\n", EDGE, ++ (long long int) time (NULL), inet_ntoa (at->nexthop), pre, ++ aspath_print (at->aspath), edge->a, edge->b); ++ ++ fclose (file); ++} ++ ++ ++int ++bgp_pgbgp_updateEdge (struct bgp_pgbgp_hist *hist, struct bgp_info *binfo, ++ struct attr *at, struct bgp_node *rn, time_t t_now, ++ int newPeer) ++{ ++ ++ char first = true; ++ struct edge curEdge; ++ curEdge.a = 0; ++ curEdge.b = 0; ++ ++ ++ if (at->aspath == NULL) ++ return 0; ++ struct assegment *seg = at->aspath->segments; ++ if (seg == NULL) ++ return 0; ++ time_t max_depref = 0; ++ for (seg = at->aspath->segments; seg; seg = seg->next) ++ { ++ for (int i = 0; i < seg->length; i++) ++ { ++ curEdge.a = curEdge.b; ++ curEdge.b = seg->as[i]; ++ if (first) ++ { ++ first = false; ++ continue; ++ } ++ if (curEdge.a == curEdge.b) ++ continue; ++ ++ // We have an edge to consider ++ struct bgp_pgbgp_edge finder; ++ finder.e = curEdge; ++ struct bgp_pgbgp_edge *hedge = ++ hash_get (pgbgp->edgeT, &finder, edge_hash_alloc); ++ ++ // Is this edge marked as suspicious? ++ if (hedge->deprefUntil > t_now) ++ goto UPE_DEPREF; ++ ++ // Have we seen the edge recently? ++ if (hedge->lastSeen + pgbgp->edge_hist_time > t_now) ++ goto UPE_CLEAN; ++#ifndef PGBGP_DEBUG ++ /* Are we within the initial grace period? */ ++ if (newPeer) ++ goto UPE_CLEAN; ++#endif ++ // It must not be in recent history, depref edge for first time ++ hedge->deprefUntil = t_now + pgbgp->edge_sus_time; ++ bgp_pgbgp_logEdgeAnomaly (rn, at, &curEdge); ++ ++ ++ UPE_DEPREF: ++ if (hedge->deprefUntil > max_depref) ++ max_depref = hedge->deprefUntil; ++ UPE_CLEAN: ++ hedge->lastSeen = t_now; ++ } ++ } ++ if (max_depref) ++ { ++ SET_FLAG (binfo->flags, BGP_INFO_SUSPICIOUS_E); ++ if (!hist->pEdgeReuse) ++ { ++ struct bgp_pgbgp_reuse *r; ++ r = ++ XCALLOC (MTYPE_BGP_PGBGP_REUSE, sizeof (struct bgp_pgbgp_reuse)); ++ r->type = PGBGP_REUSE_EDGE; ++ r->deprefUntil = max_depref; ++ r->data.edge.rn = rn; ++ bgp_lock_node (rn); ++ pqueue_enqueue (r, pgbgp->reuse_q); ++ pgbgp->rq_size += 1; ++ hist->pEdgeReuse = r; ++ } ++ return BGP_INFO_SUSPICIOUS_E; ++ } ++ ++ return 0; ++} ++ ++int ++bgp_pgbgp_reuseEdge (struct bgp_pgbgp_r_edge data) ++{ ++ ++ // Okay, go through all of the paths for the prefix ++ // and find the path that needs to be updated next and ++ // enqueue it ++ time_t minMax = 0; ++ int numChanged = 0; ++ time_t t_now = time (NULL); ++ ++ for (struct bgp_info * ri = data.rn->info; ri; ri = ri->next) ++ { ++ char first = true; ++ struct edge curEdge = { 0, 0 }; ++ struct assegment *seg; ++ time_t max_depref = 0; ++ ++ for (seg = ri->attr->aspath->segments; seg; seg = seg->next) ++ { ++ for (int i = 0; i < seg->length; i++) ++ { ++ curEdge.a = curEdge.b; ++ curEdge.b = seg->as[i]; ++ if (first) ++ { ++ first = false; ++ continue; ++ } ++ struct bgp_pgbgp_edge finder; ++ finder.e = curEdge; ++ struct bgp_pgbgp_edge *hedge = ++ hash_lookup (pgbgp->edgeT, &finder); ++ if (!hedge) ++ continue; ++ // Is this edge suspicious? ++ if (hedge->deprefUntil > t_now ++ && hedge->deprefUntil > max_depref) ++ max_depref = hedge->deprefUntil; ++ } ++ } ++ ++ if (max_depref) ++ { ++ if (!minMax || max_depref < minMax) ++ minMax = max_depref; ++ } ++ else ++ { ++ if (CHECK_FLAG (ri->flags, BGP_INFO_SUSPICIOUS_E)) ++ { ++ UNSET_FLAG (ri->flags, BGP_INFO_SUSPICIOUS_E); ++ numChanged += 1; ++ } ++ } ++ } ++ struct bgp_info *ri = data.rn->info; ++ if (numChanged > 0 && ri) ++ bgp_process (ri->peer->bgp, data.rn, data.rn->table->afi, ++ data.rn->table->safi); ++ ++ struct bgp_pgbgp_hist *hist = data.rn->hist; ++ hist->pEdgeReuse = NULL; ++ ++ if (minMax) ++ { ++ struct bgp_pgbgp_reuse *r; ++ r = XCALLOC (MTYPE_BGP_PGBGP_REUSE, sizeof (struct bgp_pgbgp_reuse)); ++ r->type = PGBGP_REUSE_EDGE; ++ r->deprefUntil = minMax; ++ r->data.edge.rn = data.rn; ++ pqueue_enqueue (r, pgbgp->reuse_q); ++ pgbgp->rq_size += 1; ++ hist->pEdgeReuse = r; ++ } ++ else ++ { ++ bgp_unlock_node (data.rn); ++ } ++ ++ return 0; ++} +--- /dev/null ++++ b/bgpd/bgp_pgbgp.h +@@ -0,0 +1,286 @@ ++/* BGP Pretty Good BGP ++ Copyright (C) 2008 University of New Mexico (Josh Karlin) ++ ++This file is part of GNU Zebra. ++ ++GNU Zebra is free software; you can redistribute it and/or modify it ++under the terms of the GNU General Public License as published by the ++Free Software Foundation; either version 2, or (at your option) any ++later version. ++ ++GNU Zebra is distributed in the hope that it will be useful, but ++WITHOUT ANY WARRANTY; without even the implied warranty of ++MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++General Public License for more details. ++ ++You should have received a copy of the GNU General Public License ++along with GNU Zebra; see the file COPYING. If not, write to the Free ++Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA ++02111-1307, USA. */ ++ ++#ifndef _QUAGGA_BGP_PGBGP_H ++#define _QUAGGA_BGP_PGBGP_H ++ ++#include "bgpd.h" ++#include "bgp_route.h" ++#include "table.h" ++ ++#define MOAS 0 ++#define SUBPREFIX 1 ++#define EDGE 2 ++ ++/* Global PGBGP data */ ++struct bgp_pgbgp_config ++{ ++ /* Depref time for a new origin AS */ ++ time_t origin_sus_time; ++ ++ /* Depref time for a new edge */ ++ time_t edge_sus_time; ++ ++ /* Depref time for a new sub-prefix */ ++ time_t sub_sus_time; ++ ++ /* Origin AS Mapping History Length */ ++ time_t origin_hist_time; ++ ++ /* Prefix Mapping History Length */ ++ time_t prefix_hist_time; ++ ++ /* Edge Mapping History Length */ ++ time_t edge_hist_time; ++ ++ /* Peer Mapping History Length */ ++ time_t peer_hist_time; ++ ++ /* The list of depreferenced routes */ ++ struct pqueue *reuse_q; ++ int rq_size; ++ ++ /* Time that the last garbage collection (gc) took place */ ++ time_t lastgc; ++ ++ /* History table */ ++ // struct route_table *histT; ++ ++ /* Edge Hash Table */ ++ struct hash *edgeT; ++ ++ /* File path for history storage */ ++ char *storage; ++ ++ /* File path for dump of anomalous routes */ ++ char *anomalies; ++ ++ /* The time that we last stored to disk */ ++ time_t lastStore; ++ ++ /* The time that PGBGP started counting */ ++ time_t startTime; ++ ++ /* Last time each peer was seen */ ++ struct bgp_pgbgp_peerTime *peerLast; ++ ++}; ++ ++ ++struct bgp_pgbgp_peerTime ++{ ++ struct bgp_pgbgp_peerTime *next; ++ time_t lastSeen; ++ union sockunion su; ++ time_t deprefUntil; ++}; ++ ++struct edge ++{ ++ as_t a; ++ as_t b; ++}; ++ ++/* ++ Avoid the neighbors for the less specific that told you about ++ the more specific ++ */ ++struct bgp_pgbgp_avoid ++{ ++ struct bgp_pgbgp_avoid *next; ++ time_t avoidUntil; ++ as_t peerASN; ++ struct bgp_node *sub; ++}; ++ ++/* A list of origin ASes for a path ++ Usually it's only one but if the last AS ++ in the path is an AS set, then the whole ++ set must be returned ++*/ ++struct bgp_pgbgp_pathSet ++{ ++ int length; ++ as_t *ases; ++}; ++ ++/* ++ Avoid paths with suspicious origins ++ */ ++struct bgp_pgbgp_origin ++{ ++ struct bgp_pgbgp_origin *next; ++ time_t lastSeen; ++ time_t deprefUntil; ++ as_t originAS; ++}; ++ ++/* ++ Ignore routes for this prefix ++ */ ++struct bgp_pgbgp_prefix ++{ ++ time_t lastSeen; ++ time_t ignoreUntil; ++ struct bgp_pgbgp_avoid *avoid; ++}; ++ ++struct bgp_pgbgp_edge ++{ ++ time_t lastSeen; ++ time_t deprefUntil; ++ struct edge e; ++}; ++ ++struct bgp_pgbgp_hist ++{ ++ struct bgp_pgbgp_origin *o; ++ struct bgp_pgbgp_prefix *p; ++ struct bgp_pgbgp_reuse *pEdgeReuse; ++}; ++ ++struct bgp_pgbgp_r_origin ++{ ++ as_t originAS; ++ struct bgp_node *rn; ++}; ++ ++struct bgp_pgbgp_r_prefix ++{ ++ struct bgp_node *rn; ++ struct bgp_node *rnsuper; ++}; ++ ++/* ++ This node contained a route with a bad edge, check ++ it again for bad edges in 24 hours ++*/ ++struct bgp_pgbgp_r_edge ++{ ++ struct bgp_node *rn; ++}; ++ ++ ++union reuseTypes ++{ ++ struct bgp_pgbgp_r_origin origin; ++ struct bgp_pgbgp_r_prefix prefix; ++ struct bgp_pgbgp_r_edge edge; ++}; ++ ++struct bgp_pgbgp_reuse ++{ ++ union reuseTypes data; ++ short type; ++ time_t deprefUntil; ++}; ++ ++#define ANOMALOUS(V) \ ++(CHECK_FLAG(V, BGP_INFO_SUSPICIOUS_O | BGP_INFO_SUSPICIOUS_P \ ++ | BGP_INFO_SUSPICIOUS_E | BGP_INFO_IGNORED_P)) ++ ++#define PGBGP_REUSE_ORIGIN 0 ++#define PGBGP_REUSE_PREFIX 1 ++#define PGBGP_REUSE_EDGE 2 ++ ++#define BGP_PGBGP_NONE 0 ++#define BGP_PGBGP_DEPREFFED 1 ++ ++// For storage ++#define ORIGIN_ID 0 ++#define PREFIX_ID 1 ++#define EDGE_ID 2 ++#define PEER_ID 3 ++ ++/* Default timing values */ ++#define DEFAULT_ORIGIN_SUS (86400 * 1) ++#define DEFAULT_EDGE_SUS (86400 * 1) ++#define DEFAULT_SUB_SUS (86400 * 1) ++#define DEFAULT_ORIGIN_HIST (86400 * 30) ++#define DEFAULT_PREFIX_HIST (86400 * 10) ++#define DEFAULT_EDGE_HIST (86400 * 60) ++// Time between garbage collections ++#define PGBGP_GC_DELTA (3600) ++// Time between file stores ++#define PGBGP_STORE_DELTA (28800) ++// Time that a new peer's routes are not considered suspicious ++#define PGBGP_PEER_GRACE (86400 * 1) ++ ++ ++ ++///////// PUBLIC PGBGP FUNCTIONS ///////// ++ ++/* ++ bgp_pgbgp_enable: ++ Enable PGBGP depreferencing / history tracking for this afi/safi ++ ++ Arguments: ++ . ost: Depref. time of new prefix origins (in hours) ++ . est: Depref. time of new edges (in hours) ++ . sst: Depref. time of new sub-prefixes (in hours) ++ . oht: Storage time of known origins for prefixes (in days) ++ . pht: Storage time of known prefixes (in days) ++ . eht: Storage time of known edges (in days) ++ . storage: File to periodically store history in (can be /dev/null) ++ . anoms: File to store history of depreferenced routes (can be /dev/null) ++ ++ Caution: ++ It is important that the storage times are longer than the depreference times ++*/ ++extern int bgp_pgbgp_enable (struct bgp *, afi_t afi, safi_t safi, int ost, ++ int est, int sst, int oht, int pht, int eht, ++ const char *storage, const char *anoms); ++extern int bgp_pgbgp_disable (struct bgp *, afi_t afi, safi_t safi); ++ ++/* ++ bgp_pgbgp_update: ++ Call on the event of an announcement update ++ ++ Arguments: ++ bgp_info: The route ++ at: The new route's attributes ++*/ ++extern int bgp_pgbgp_update (struct bgp_info *, struct attr *at, ++ struct bgp_node *); ++ ++/* ++ bgp_pgbgp_rib_updated: ++ Call upon discovery of a new best path (or lack thereof) ++ ++ This is a special case function for smoothly handling sub-prefix hijacks. ++ ++ It handles the following 2 events: ++ ++ Event 1: An anomalous sub-prefix is ignored, but no best route for the super-prefix exists ++ Response: Announce the sub-prefix until the super-prefix comes back ++ ++ Event 2: A super-prefix comes back to the RIB and its anomalous sub-prefix is in use ++ Response: Ignore the sub-prefix again ++ ++ Arguments: ++ rn: The route node that a new best path was found for ++ old_best: The old best route (NULL if one did not exist) ++ new_best: The current best route (NULL if one does not exist) ++ */ ++extern int ++bgp_pgbgp_rib_updated (struct bgp_node *rn, struct bgp_info *old_best, ++ struct bgp_info *new_best); ++ ++#endif +--- a/bgpd/bgp_route.c ++++ b/bgpd/bgp_route.c +@@ -51,6 +51,7 @@ Software Foundation, Inc., 59 Temple Pla + #include "bgpd/bgp_mplsvpn.h" + #include "bgpd/bgp_nexthop.h" + #include "bgpd/bgp_damp.h" ++#include "bgpd/bgp_pgbgp.h" + #include "bgpd/bgp_advertise.h" + #include "bgpd/bgp_zebra.h" + #include "bgpd/bgp_vty.h" +@@ -339,12 +340,19 @@ bgp_info_cmp (struct bgp *bgp, struct bg + + *paths_eq = 0; + ++ + /* 0. Null check. */ + if (new == NULL) + return 0; + if (exist == NULL) + return 1; + ++ /* 0.5 PGBGP Depref. Check */ ++ if (ANOMALOUS(exist->flags) && !ANOMALOUS(new->flags)) ++ return 1; ++ if (!ANOMALOUS(exist->flags) && ANOMALOUS(new->flags)) ++ return 0; ++ + /* 1. Weight check. */ + if (new->attr->extra) + new_weight = new->attr->extra->weight; +@@ -1583,6 +1591,10 @@ bgp_process_main (struct work_queue *wq, + UNSET_FLAG (new_select->flags, BGP_INFO_MULTIPATH_CHG); + } + ++ /* PGBGP needs to know about selected routes */ ++ if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_PGBGP)) ++ bgp_pgbgp_rib_updated(rn, old_select, new_select); ++ + + /* Check each BGP peer. */ + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) +@@ -1906,6 +1918,11 @@ bgp_update_rsclient (struct peer *rsclie + /* If the update is implicit withdraw. */ + if (ri) + { ++ /* Update PGBGP state, and mark the route as anomalous if necessary */ ++ if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_PGBGP) ++ && peer_sort(peer) == BGP_PEER_EBGP) ++ bgp_pgbgp_update(ri, attr_new, rn); ++ + ri->uptime = bgp_clock (); + + /* Same attribute comes in. */ +@@ -2337,6 +2354,11 @@ bgp_update_main (struct peer *peer, stru + /* Increment prefix */ + bgp_aggregate_increment (bgp, p, new, afi, safi); + ++ /* Update PGBGP state, and mark the route as anomalous if necessary */ ++ if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_PGBGP) ++ && peer_sort(peer) == BGP_PEER_EBGP) ++ bgp_pgbgp_update(new, attr_new, rn); ++ + /* Register new BGP information. */ + bgp_info_add (rn, new); + +@@ -5559,6 +5581,20 @@ enum bgp_display_type + static void + route_vty_short_status_out (struct vty *vty, struct bgp_info *binfo) + { ++ if (ANOMALOUS(binfo->flags)) ++ { ++ vty_out(vty, "a["); ++ if (CHECK_FLAG(binfo->flags, BGP_INFO_SUSPICIOUS_P)) ++ vty_out(vty, "i"); ++ if (CHECK_FLAG(binfo->flags, BGP_INFO_SUSPICIOUS_O)) ++ vty_out(vty, "p"); ++ if (CHECK_FLAG(binfo->flags, BGP_INFO_SUSPICIOUS_E)) ++ vty_out(vty, "e"); ++ if (CHECK_FLAG(binfo->flags, BGP_INFO_IGNORED_P)) ++ vty_out(vty, "s"); ++ vty_out(vty, "] "); ++ } ++ + /* Route status display. */ + if (CHECK_FLAG (binfo->flags, BGP_INFO_REMOVED)) + vty_out (vty, "R"); +@@ -6064,6 +6100,7 @@ route_vty_out_detail (struct vty *vty, s + } + + #define BGP_SHOW_SCODE_HEADER "Status codes: s suppressed, d damped, h history, * valid, > best, i - internal,%s r RIB-failure, S Stale, R Removed%s" ++#define BGP_SHOW_PCODE_HEADER "Status code: a (anomalous) of: [p] prefix hijack, [s] sub-prefix hijack,%s [i] informant of sub-prefix [e] new edge%s" + #define BGP_SHOW_OCODE_HEADER "Origin codes: i - IGP, e - EGP, ? - incomplete%s%s" + #define BGP_SHOW_HEADER " Network Next Hop Metric LocPrf Weight Path%s" + #define BGP_SHOW_DAMP_HEADER " Network From Reuse Path%s" +@@ -6095,7 +6132,8 @@ enum bgp_show_type + bgp_show_type_flap_route_map, + bgp_show_type_flap_neighbor, + bgp_show_type_dampend_paths, +- bgp_show_type_damp_neighbor ++ bgp_show_type_damp_neighbor, ++ bgp_show_type_anomalous_paths + }; + + static int +@@ -6262,11 +6300,17 @@ bgp_show_table (struct vty *vty, struct + || CHECK_FLAG (ri->flags, BGP_INFO_HISTORY)) + continue; + } ++ if (type == bgp_show_type_anomalous_paths) ++ { ++ if (! ANOMALOUS(ri->flags)) ++ continue; ++ } + + if (header) + { + vty_out (vty, "BGP table version is 0, local router ID is %s%s", inet_ntoa (*router_id), VTY_NEWLINE); + vty_out (vty, BGP_SHOW_SCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE); ++ vty_out (vty, BGP_SHOW_PCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE); + vty_out (vty, BGP_SHOW_OCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE); + if (type == bgp_show_type_dampend_paths + || type == bgp_show_type_damp_neighbor) +@@ -6344,6 +6388,7 @@ bgp_show (struct vty *vty, struct bgp *b + return bgp_show_table (vty, table, &bgp->router_id, type, output_arg); + } + ++ + /* Header of detailed BGP route information */ + static void + route_vty_out_detail_header (struct vty *vty, struct bgp *bgp, +@@ -11904,6 +11949,64 @@ DEFUN (bgp_damp_set, + half, reuse, suppress, max); + } + ++DEFUN (bgp_pgbgp_arg, ++ bgp_pgbgp_arg_cmd, ++ "bgp pgbgp <1-100> <1-100> <1-100> <1-365> <1-365> <1-365> WORD WORD", ++ "BGP Specific commands\n" ++ "Enable Pretty Good BGP\n" ++ "New origin depref time (in hours)\n" ++ "New edge depref time (in hours)\n" ++ "New sub-prefix depref time (in hours)\n" ++ "Origin history time (in days)\n" ++ "Prefix history time (in days)\n" ++ "Edge history time (in days)\n" ++ "Log file for history data\n" ++ "Log file of anomalies\n") ++{ ++ struct bgp *bgp; ++ ++ int ost = DEFAULT_ORIGIN_SUS; ++ int est = DEFAULT_EDGE_SUS; ++ int sst = DEFAULT_SUB_SUS; ++ int oht = DEFAULT_ORIGIN_HIST; ++ int pht = DEFAULT_PREFIX_HIST; ++ int eht = DEFAULT_EDGE_HIST; ++ const char* path = "/var/log/quagga/pgbgp_hist"; ++ const char* anoms = "/var/log/quagga/pgbgp_anomalies"; ++ ++ if (argc == 8) ++ { ++ VTY_GET_INTEGER("origin depref time", ost, argv[0]); ++ VTY_GET_INTEGER("edge depref time", est, argv[1]); ++ VTY_GET_INTEGER("sub-prefix depref time", sst, argv[2]); ++ VTY_GET_INTEGER("origin history time", oht, argv[3]); ++ VTY_GET_INTEGER("prefix history time", pht, argv[4]); ++ VTY_GET_INTEGER("edge history time", eht, argv[5]); ++ path = argv[6]; ++ anoms = argv[7]; ++ } ++ ++ bgp = vty->index; ++ return bgp_pgbgp_enable(bgp, bgp_node_afi (vty), bgp_node_safi (vty), ++ ost, est, sst, oht, pht, eht, path, anoms); ++} ++ ++ALIAS (bgp_pgbgp_arg, ++ bgp_pgbgp_cmd, ++ "bgp pgbgp", ++ "BGP specific commands\n" ++ "Enable Pretty Good BGP\n") ++ ++DEFUN (bgp_pgbgp_unset, ++ bgp_pgbgp_unset_cmd, ++ "no bgp pgbgp\n", ++ "BGP specific commands\n") ++{ ++ struct bgp *bgp; ++ bgp = vty->index; ++ return bgp_pgbgp_disable (bgp, bgp_node_afi (vty), bgp_node_safi (vty)); ++} ++ + ALIAS (bgp_damp_set, + bgp_damp_set2_cmd, + "bgp dampening <1-45>", +@@ -11953,6 +12056,19 @@ DEFUN (show_ip_bgp_dampened_paths, + NULL); + } + ++DEFUN (show_ip_bgp_anomalous_paths, ++ show_ip_bgp_anomalous_paths_cmd, ++ "show ip bgp anomalous-paths", ++ SHOW_STR ++ IP_STR ++ BGP_STR ++ "Display anomalous paths (less likely to be used)\n") ++{ ++ return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, bgp_show_type_anomalous_paths, ++ NULL); ++} ++ ++ + DEFUN (show_ip_bgp_flap_statistics, + show_ip_bgp_flap_statistics_cmd, + "show ip bgp flap-statistics", +@@ -12479,6 +12595,7 @@ bgp_route_init (void) + install_element (VIEW_NODE, &show_ip_bgp_neighbor_received_prefix_filter_cmd); + install_element (VIEW_NODE, &show_ip_bgp_ipv4_neighbor_received_prefix_filter_cmd); + install_element (VIEW_NODE, &show_ip_bgp_dampened_paths_cmd); ++ install_element (VIEW_NODE, &show_ip_bgp_anomalous_paths_cmd); + install_element (VIEW_NODE, &show_ip_bgp_flap_statistics_cmd); + install_element (VIEW_NODE, &show_ip_bgp_flap_address_cmd); + install_element (VIEW_NODE, &show_ip_bgp_flap_prefix_cmd); +@@ -12612,6 +12729,7 @@ bgp_route_init (void) + install_element (ENABLE_NODE, &show_ip_bgp_neighbor_received_prefix_filter_cmd); + install_element (ENABLE_NODE, &show_ip_bgp_ipv4_neighbor_received_prefix_filter_cmd); + install_element (ENABLE_NODE, &show_ip_bgp_dampened_paths_cmd); ++ install_element (ENABLE_NODE, &show_ip_bgp_anomalous_paths_cmd); + install_element (ENABLE_NODE, &show_ip_bgp_flap_statistics_cmd); + install_element (ENABLE_NODE, &show_ip_bgp_flap_address_cmd); + install_element (ENABLE_NODE, &show_ip_bgp_flap_prefix_cmd); +@@ -13002,6 +13120,10 @@ bgp_route_init (void) + install_element (BGP_IPV4_NODE, &bgp_damp_unset_cmd); + install_element (BGP_IPV4_NODE, &bgp_damp_unset2_cmd); + ++ install_element (BGP_NODE, &bgp_pgbgp_cmd); ++ install_element (BGP_NODE, &bgp_pgbgp_arg_cmd); ++ install_element (BGP_NODE, &bgp_pgbgp_unset_cmd); ++ + /* Deprecated AS-Pathlimit commands */ + install_element (BGP_NODE, &bgp_network_ttl_cmd); + install_element (BGP_NODE, &bgp_network_mask_ttl_cmd); +--- a/bgpd/bgp_route.h ++++ b/bgpd/bgp_route.h +@@ -1,3 +1,4 @@ ++ + /* BGP routing information base + Copyright (C) 1996, 97, 98, 2000 Kunihiro Ishiguro + +@@ -68,7 +69,7 @@ struct bgp_info + int lock; + + /* BGP information status. */ +- u_int16_t flags; ++ u_int32_t flags; + #define BGP_INFO_IGP_CHANGED (1 << 0) + #define BGP_INFO_DAMPED (1 << 1) + #define BGP_INFO_HISTORY (1 << 2) +@@ -82,6 +83,10 @@ struct bgp_info + #define BGP_INFO_COUNTED (1 << 10) + #define BGP_INFO_MULTIPATH (1 << 11) + #define BGP_INFO_MULTIPATH_CHG (1 << 12) ++#define BGP_INFO_SUSPICIOUS_O (1 << 13) ++#define BGP_INFO_SUSPICIOUS_P (1 << 14) ++#define BGP_INFO_IGNORED_P (1 << 15) ++#define BGP_INFO_SUSPICIOUS_E (1 << 16) + + /* BGP route type. This can be static, RIP, OSPF, BGP etc. */ + u_char type; +@@ -126,7 +131,7 @@ struct bgp_static + + /* Flags which indicate a route is unuseable in some form */ + #define BGP_INFO_UNUSEABLE \ +- (BGP_INFO_HISTORY|BGP_INFO_DAMPED|BGP_INFO_REMOVED) ++ (BGP_INFO_HISTORY|BGP_INFO_DAMPED|BGP_INFO_REMOVED|BGP_INFO_IGNORED_P) + /* Macro to check BGP information is alive or not. Sadly, + * not equivalent to just checking previous, because of the + * sense of the additional VALID flag. +--- a/bgpd/bgp_table.h ++++ b/bgpd/bgp_table.h +@@ -65,6 +65,8 @@ struct bgp_node + + int lock; + ++ struct bgp_pgbgp_hist *hist; ++ + u_char flags; + #define BGP_NODE_PROCESS_SCHEDULED (1 << 0) + }; +--- a/bgpd/bgpd.h ++++ b/bgpd/bgpd.h +@@ -123,6 +123,7 @@ struct bgp + /* BGP Per AF flags */ + u_int16_t af_flags[AFI_MAX][SAFI_MAX]; + #define BGP_CONFIG_DAMPENING (1 << 0) ++#define BGP_CONFIG_PGBGP (1 << 1) + + /* Static route configuration. */ + struct bgp_table *route[AFI_MAX][SAFI_MAX]; +--- a/lib/hash.c ++++ b/lib/hash.c +@@ -166,6 +166,35 @@ hash_iterate (struct hash *hash, + } + } + ++/* ++ Iterates until 0 is returned or until completion ++ Return: 1 if iteration completed ++ Return: 0 if iteration was interrupted ++*/ ++ ++int ++hash_iterate_until(struct hash *hash, ++ int (*func) (struct hash_backet *, void *), void *arg) ++{ ++ unsigned int i; ++ struct hash_backet *hb; ++ struct hash_backet *hbnext; ++ int ret; ++ ++ for (i = 0; i < hash->size; i++) ++ for (hb = hash->index[i]; hb; hb = hbnext) ++ { ++ /* get pointer to next hash backet here, in case (*func) ++ * decides to delete hb by calling hash_release ++ */ ++ hbnext = hb->next; ++ ret = (*func) (hb, arg); ++ if (!ret) ++ return 0; ++ } ++ return 1; ++} ++ + /* Clean up hash. */ + void + hash_clean (struct hash *hash, void (*free_func) (void *)) +--- a/lib/hash.h ++++ b/lib/hash.h +@@ -66,7 +66,8 @@ extern void *hash_release (struct hash * + + extern void hash_iterate (struct hash *, + void (*) (struct hash_backet *, void *), void *); +- ++extern int hash_iterate_until(struct hash *, ++ int (*) (struct hash_backet *, void *), void *); + extern void hash_clean (struct hash *, void (*) (void *)); + extern void hash_free (struct hash *); + +--- a/lib/memtypes.c ++++ b/lib/memtypes.c +@@ -148,6 +148,15 @@ struct memory_list memory_list_bgp[] = + { MTYPE_PEER_UPDATE_SOURCE, "BGP peer update interface" }, + { MTYPE_BGP_DAMP_INFO, "Dampening info" }, + { MTYPE_BGP_DAMP_ARRAY, "BGP Dampening array" }, ++ { 0, NULL }, ++ { MTYPE_BGP_PGBGP_ORIGIN, "BGP PGBGP Origin AS Node" }, ++ { MTYPE_BGP_PGBGP_PREFIX, "BGP PGBGP Prefix AS Node" }, ++ { MTYPE_BGP_PGBGP_EDGE, "BGP PGBGP Edge Node" }, ++ { MTYPE_BGP_PGBGP_REUSE, "BGP PGBGP Reuse Node" }, ++ { MTYPE_BGP_PGBGP_HIST, "BGP PGBGP History Node" }, ++ { MTYPE_BGP_PGBGP_AVOID, "BGP PGBGP Avoid Peer Node" }, ++ { MTYPE_BGP_PGBGP_PEER, "BGP PGBGP Peer Timing" }, ++ { 0, NULL }, + { MTYPE_BGP_REGEXP, "BGP regexp" }, + { MTYPE_BGP_AGGREGATE, "BGP aggregate" }, + { -1, NULL } diff --git a/packages/qmp-quagga/patches/161-pgbgp-addon.patch b/packages/qmp-quagga/patches/161-pgbgp-addon.patch new file mode 100644 index 0000000..817cdb0 --- /dev/null +++ b/packages/qmp-quagga/patches/161-pgbgp-addon.patch @@ -0,0 +1,318 @@ +From: Paul Jakma +Date: Thu, 4 Sep 2008 22:27:13 +0000 (+0100) +Subject: [bgp/pgbgp] Add some pgbgp commands to restricted-mode and other command tweaks +X-Git-Url: http://git.ozo.com/?p=quagga-pgbg.git;a=commitdiff_plain;h=06ac72f9f6021635e9e1e5105c3e22bf7eb0d6c3 + +[bgp/pgbgp] Add some pgbgp commands to restricted-mode and other command tweaks + +* bgp_pgbgp.c: + (edge_neighbor_iterator) make ASN==0 mean 'iterate over all ASNs' + (bgp_pgbgp_stats_origin_one) new function, to display one origin AS status. + (bgp_pgbgp_stats_origins) adapt to use previous. + Adapt to iterate over all stats if no prefix was giving. + (show_ip_bgp_pgbgp_neighbors_cmd) recognise no ASN argument case + (show_ip_bgp_pgbgp_neighbors_all_cmd) Iterate over all + (show_ip_bgp_pgbgp_origins_cmd) similar + (show_ip_bgp_pgbgp_origins_all_cmd) + (bgp_pgbgp_enable) install the lookup commands to ther new RESTRICTED_NODE +* bgp_route.c: + (route_vty_short_status_out) only allowed to print one char for anomalous + status. + (route_vty_out_detail) Add support for printing out more detail on + PG-BGP status +--- + +--- a/bgpd/bgp_pgbgp.c ++++ b/bgpd/bgp_pgbgp.c +@@ -227,7 +227,7 @@ static void + edge_neighbor_iterator (struct hash_backet *backet, struct nsearch *pns) + { + struct bgp_pgbgp_edge *hedge = backet->data; +- if ((hedge->e.a == pns->asn || hedge->e.b == pns->asn) ++ if ((!pns->asn || hedge->e.a == pns->asn || hedge->e.b == pns->asn) + && hedge->e.a != hedge->e.b) + { + struct vty *vty = pns->pvty; +@@ -254,13 +254,39 @@ bgp_pgbgp_stats_neighbors (struct vty *v + return CMD_SUCCESS; + } + ++static void ++bgp_pgbgp_stats_origin_one (struct vty *vty, struct bgp_node *rn, ++ time_t t_now) ++{ ++ char str[INET6_BUFSIZ]; ++ ++ if (!rn->hist) ++ return; ++ ++ prefix2str (&rn->p, str, sizeof(str)); ++ vty_out (vty, "%s%s", str, VTY_NEWLINE); ++ ++ for (struct bgp_pgbgp_origin * cur = rn->hist->o; cur != NULL; ++ cur = cur->next) ++ { ++ if (cur->deprefUntil > t_now) ++ vty_out (vty, "Untrusted Origin AS: %d%s", cur->originAS, ++ VTY_NEWLINE); ++ else ++ vty_out (vty, "Trusted Origin AS: %d%s", cur->originAS, ++ VTY_NEWLINE); ++ } ++} ++ + static int + bgp_pgbgp_stats_origins (struct vty *vty, afi_t afi, safi_t safi, + const char *prefix) + { + struct bgp *bgp; + struct bgp_table *table; ++ struct bgp_node *rn; + time_t t_now = time (NULL); ++ + bgp = bgp_get_default (); + if (bgp == NULL) + return CMD_WARNING; +@@ -269,28 +295,22 @@ bgp_pgbgp_stats_origins (struct vty *vty + table = bgp->rib[afi][safi]; + if (table == NULL) + return CMD_WARNING; +- +- struct prefix p; +- str2prefix (prefix, &p); +- struct bgp_node *rn = bgp_node_match (table, &p); +- vty_out (vty, "%s%s", prefix, VTY_NEWLINE); +- if (rn) ++ ++ if (prefix) + { ++ struct prefix p; ++ str2prefix (prefix, &p); ++ rn = bgp_node_match (table, &p); + if (rn->hist) +- { +- for (struct bgp_pgbgp_origin * cur = rn->hist->o; cur != NULL; +- cur = cur->next) +- { +- if (cur->deprefUntil > t_now) +- vty_out (vty, "Untrusted Origin AS: %d%s", cur->originAS, +- VTY_NEWLINE); +- else +- vty_out (vty, "Trusted Origin AS: %d%s", cur->originAS, +- VTY_NEWLINE); +- } +- } ++ bgp_pgbgp_stats_origin_one (vty, rn, t_now); + bgp_unlock_node (rn); ++ return CMD_SUCCESS; + } ++ ++ for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) ++ if (rn->hist) ++ bgp_pgbgp_stats_origin_one (vty, rn, t_now); ++ + return CMD_SUCCESS; + } + +@@ -377,7 +397,7 @@ bgp_pgbgp_stats (struct vty *vty, afi_t + DEFUN (show_ip_bgp_pgbgp, + show_ip_bgp_pgbgp_cmd, + "show ip bgp pgbgp", +- SHOW_STR IP_STR BGP_STR "Display PGBGP statistics\n") ++ SHOW_STR IP_STR BGP_STR "Pretty-Good BGP statistics\n") + { + return bgp_pgbgp_stats (vty, AFI_IP, SAFI_UNICAST); + } +@@ -385,29 +405,46 @@ DEFUN (show_ip_bgp_pgbgp, + DEFUN (show_ip_bgp_pgbgp_neighbors, + show_ip_bgp_pgbgp_neighbors_cmd, + "show ip bgp pgbgp neighbors WORD", +- SHOW_STR +- IP_STR +- BGP_STR +- "BGP pgbgp\n" +- "BGP pgbgp neighbors\n" "ASN whos neighbors should be displayed\n") ++ SHOW_STR IP_STR BGP_STR ++ "Pretty-Good BGP statistics\n" ++ "PG-BGP neighbor information\n" ++ "AS to show neighbors of\n") + { + return bgp_pgbgp_stats_neighbors (vty, AFI_IP, SAFI_UNICAST, +- atoi (argv[0])); ++ argc == 1 ? atoi (argv[0]) : 0); + } + ++ALIAS (show_ip_bgp_pgbgp_neighbors, ++ show_ip_bgp_pgbgp_neighbors_all_cmd, ++ "show ip bgp pgbgp neighbors", ++ SHOW_STR ++ IP_STR ++ BGP_STR ++ "Pretty-Good BGP statistics\n" ++ "PG-BGP neighbors information\n") ++ + DEFUN (show_ip_bgp_pgbgp_origins, + show_ip_bgp_pgbgp_origins_cmd, + "show ip bgp pgbgp origins A.B.C.D/M", + SHOW_STR + IP_STR + BGP_STR +- "BGP pgbgp\n" +- "BGP pgbgp neighbors\n" "Prefix to look up origin ASes of\n") ++ "Pretty-Good BGP statistics\n" ++ "PG-BGP prefix origin information\n" ++ "Prefix to look up origin ASes of\n") + { +- return bgp_pgbgp_stats_origins (vty, AFI_IP, SAFI_UNICAST, argv[0]); ++ return bgp_pgbgp_stats_origins (vty, AFI_IP, SAFI_UNICAST, ++ argc == 1 ? argv[0] : NULL); + } + +- ++ALIAS (show_ip_bgp_pgbgp_origins, ++ show_ip_bgp_pgbgp_origins_all_cmd, ++ "show ip bgp pgbgp origins", ++ SHOW_STR ++ IP_STR ++ BGP_STR ++ "Pretty-Good BGP statistics\n" ++ "PG-BGP prefixes origin information") + + + /*! --------------- VTY (others exist in bgp_route.c) ------------------ !*/ +@@ -749,12 +786,19 @@ bgp_pgbgp_enable (struct bgp *bgp, afi_t + pgbgp->lastgc = time (NULL); + pgbgp->lastStore = time (NULL); + pgbgp->startTime = time (NULL); ++ install_element (RESTRICTED_NODE, &show_ip_bgp_pgbgp_cmd); ++ install_element (RESTRICTED_NODE, &show_ip_bgp_pgbgp_neighbors_cmd); ++ install_element (RESTRICTED_NODE, &show_ip_bgp_pgbgp_origins_cmd); + install_element (VIEW_NODE, &show_ip_bgp_pgbgp_cmd); +- install_element (ENABLE_NODE, &show_ip_bgp_pgbgp_cmd); + install_element (VIEW_NODE, &show_ip_bgp_pgbgp_neighbors_cmd); +- install_element (ENABLE_NODE, &show_ip_bgp_pgbgp_neighbors_cmd); + install_element (VIEW_NODE, &show_ip_bgp_pgbgp_origins_cmd); ++ install_element (VIEW_NODE, &show_ip_bgp_pgbgp_neighbors_all_cmd); ++ install_element (VIEW_NODE, &show_ip_bgp_pgbgp_origins_all_cmd); ++ install_element (ENABLE_NODE, &show_ip_bgp_pgbgp_cmd); ++ install_element (ENABLE_NODE, &show_ip_bgp_pgbgp_neighbors_cmd); + install_element (ENABLE_NODE, &show_ip_bgp_pgbgp_origins_cmd); ++ install_element (ENABLE_NODE, &show_ip_bgp_pgbgp_neighbors_all_cmd); ++ install_element (ENABLE_NODE, &show_ip_bgp_pgbgp_origins_all_cmd); + pgbgp->edgeT = hash_create_size (131072, edge_key_make, edge_cmp); + bgp_pgbgp_restore (); + return 0; +--- a/bgpd/bgp_route.c ++++ b/bgpd/bgp_route.c +@@ -5581,20 +5581,6 @@ enum bgp_display_type + static void + route_vty_short_status_out (struct vty *vty, struct bgp_info *binfo) + { +- if (ANOMALOUS(binfo->flags)) +- { +- vty_out(vty, "a["); +- if (CHECK_FLAG(binfo->flags, BGP_INFO_SUSPICIOUS_P)) +- vty_out(vty, "i"); +- if (CHECK_FLAG(binfo->flags, BGP_INFO_SUSPICIOUS_O)) +- vty_out(vty, "p"); +- if (CHECK_FLAG(binfo->flags, BGP_INFO_SUSPICIOUS_E)) +- vty_out(vty, "e"); +- if (CHECK_FLAG(binfo->flags, BGP_INFO_IGNORED_P)) +- vty_out(vty, "s"); +- vty_out(vty, "] "); +- } +- + /* Route status display. */ + if (CHECK_FLAG (binfo->flags, BGP_INFO_REMOVED)) + vty_out (vty, "R"); +@@ -5610,6 +5596,17 @@ route_vty_short_status_out (struct vty * + /* Selected */ + if (CHECK_FLAG (binfo->flags, BGP_INFO_HISTORY)) + vty_out (vty, "h"); ++ else if (ANOMALOUS(binfo->flags)) ++ { ++ if (CHECK_FLAG(binfo->flags, BGP_INFO_SUSPICIOUS_O)) ++ vty_out(vty, "p"); ++ else if (CHECK_FLAG(binfo->flags, BGP_INFO_IGNORED_P)) ++ vty_out(vty, "P"); ++ else if (CHECK_FLAG(binfo->flags, BGP_INFO_SUSPICIOUS_P)) ++ vty_out(vty, "a"); ++ if (CHECK_FLAG(binfo->flags, BGP_INFO_SUSPICIOUS_E)) ++ vty_out(vty, "a"); ++ } + else if (CHECK_FLAG (binfo->flags, BGP_INFO_DAMPED)) + vty_out (vty, "d"); + else if (CHECK_FLAG (binfo->flags, BGP_INFO_SELECTED)) +@@ -6088,7 +6085,22 @@ route_vty_out_detail (struct vty *vty, s + if (binfo->extra && binfo->extra->damp_info) + bgp_damp_info_vty (vty, binfo); + +- /* Line 7 display Uptime */ ++ /* 8: PGBGP status */ ++ if (ANOMALOUS(binfo->flags)) ++ { ++ vty_out (vty, " Anomalous:"); ++ if (CHECK_FLAG(binfo->flags, BGP_INFO_SUSPICIOUS_P)) ++ vty_out (vty, " divergent sub-prefixes,"); ++ if (CHECK_FLAG(binfo->flags, BGP_INFO_SUSPICIOUS_O)) ++ vty_out (vty, " origin AS (prefix hijack?),"); ++ if (CHECK_FLAG(binfo->flags, BGP_INFO_SUSPICIOUS_E)) ++ vty_out (vty, " new edge in path,"); ++ if (CHECK_FLAG(binfo->flags, BGP_INFO_IGNORED_P)) ++ vty_out (vty, " origin AS (sub-prefix hijack?),"); ++ vty_out (vty, "%s", VTY_NEWLINE); ++ } ++ ++ /* Line 9 display Uptime */ + #ifdef HAVE_CLOCK_MONOTONIC + tbuf = time(NULL) - (bgp_clock() - binfo->uptime); + vty_out (vty, " Last update: %s", ctime(&tbuf)); +@@ -6099,8 +6111,9 @@ route_vty_out_detail (struct vty *vty, s + vty_out (vty, "%s", VTY_NEWLINE); + } + +-#define BGP_SHOW_SCODE_HEADER "Status codes: s suppressed, d damped, h history, * valid, > best, i - internal,%s r RIB-failure, S Stale, R Removed%s" +-#define BGP_SHOW_PCODE_HEADER "Status code: a (anomalous) of: [p] prefix hijack, [s] sub-prefix hijack,%s [i] informant of sub-prefix [e] new edge%s" ++#define BGP_SHOW_SCODE_HEADER "Status codes: s suppressed, d damped, h history, * valid, > best, i - internal,%s" \ ++ " r RIB-failure, S Stale, R Removed, %s" \ ++ " p prefix hijack, P sub-prefix hijack, a other anomaly%s" + #define BGP_SHOW_OCODE_HEADER "Origin codes: i - IGP, e - EGP, ? - incomplete%s%s" + #define BGP_SHOW_HEADER " Network Next Hop Metric LocPrf Weight Path%s" + #define BGP_SHOW_DAMP_HEADER " Network From Reuse Path%s" +@@ -6309,8 +6322,7 @@ bgp_show_table (struct vty *vty, struct + if (header) + { + vty_out (vty, "BGP table version is 0, local router ID is %s%s", inet_ntoa (*router_id), VTY_NEWLINE); +- vty_out (vty, BGP_SHOW_SCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE); +- vty_out (vty, BGP_SHOW_PCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE); ++ vty_out (vty, BGP_SHOW_SCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + vty_out (vty, BGP_SHOW_OCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE); + if (type == bgp_show_type_dampend_paths + || type == bgp_show_type_damp_neighbor) +@@ -9842,7 +9854,7 @@ show_adj_route (struct vty *vty, struct + PEER_STATUS_DEFAULT_ORIGINATE)) + { + vty_out (vty, "BGP table version is 0, local router ID is %s%s", inet_ntoa (bgp->router_id), VTY_NEWLINE); +- vty_out (vty, BGP_SHOW_SCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE); ++ vty_out (vty, BGP_SHOW_SCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + vty_out (vty, BGP_SHOW_OCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE); + + vty_out (vty, "Originating default network 0.0.0.0%s%s", +@@ -9859,7 +9871,7 @@ show_adj_route (struct vty *vty, struct + if (header1) + { + vty_out (vty, "BGP table version is 0, local router ID is %s%s", inet_ntoa (bgp->router_id), VTY_NEWLINE); +- vty_out (vty, BGP_SHOW_SCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE); ++ vty_out (vty, BGP_SHOW_SCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + vty_out (vty, BGP_SHOW_OCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE); + header1 = 0; + } +@@ -9883,7 +9895,7 @@ show_adj_route (struct vty *vty, struct + if (header1) + { + vty_out (vty, "BGP table version is 0, local router ID is %s%s", inet_ntoa (bgp->router_id), VTY_NEWLINE); +- vty_out (vty, BGP_SHOW_SCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE); ++ vty_out (vty, BGP_SHOW_SCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + vty_out (vty, BGP_SHOW_OCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE); + header1 = 0; + } diff --git a/packages/qmp-quagga/patches/170-use-supported-pagers.patch b/packages/qmp-quagga/patches/170-use-supported-pagers.patch new file mode 100644 index 0000000..d42e145 --- /dev/null +++ b/packages/qmp-quagga/patches/170-use-supported-pagers.patch @@ -0,0 +1,29 @@ +--- a/vtysh/vtysh.c ++++ b/vtysh/vtysh.c +@@ -269,7 +269,7 @@ vtysh_pager_init (void) + if (pager_defined) + vtysh_pager_name = strdup (pager_defined); + else +- vtysh_pager_name = strdup ("more"); ++ vtysh_pager_name = strdup ("cat"); + } + + /* Command execution over the vty interface. */ +@@ -1885,7 +1885,7 @@ DEFUN (vtysh_terminal_length, + { + int lines; + char *endptr = NULL; +- char default_pager[10]; ++ char default_pager[12]; + + lines = strtol (argv[0], &endptr, 10); + if (lines < 0 || lines > 512 || *endptr != '\0') +@@ -1902,7 +1902,7 @@ DEFUN (vtysh_terminal_length, + + if (lines != 0) + { +- snprintf(default_pager, 10, "more -%i", lines); ++ snprintf(default_pager, 12, "head -n %i", lines); + vtysh_pager_name = strdup (default_pager); + } + -- 2.30.2